/// <summary> /// Try and convert the provided KeyInput value into OleCommandData. This conversion is meant /// to simulate the standard converison of key input into OLE information in Visual Studio. This /// means we need to reproduce all of the behavior including not converting textual input /// here (unless it maps to a command). Textual input typcially gets routed through WPF and /// is routed to IOleCommandTarget in the default handler /// </summary> private bool TryConvertToOleCommandData(KeyInput keyInput, out OleCommandData oleCommandData) { if (keyInput.RawChar.IsSome()) { if (Char.IsLetterOrDigit(keyInput.Char)) { oleCommandData = OleCommandData.Empty; return(false); } } return(OleCommandUtil.TryConvert(keyInput, SimulateStandardKeyMappings, out oleCommandData)); }
private void SendTypeChar(char c) { var oleCommandData = OleCommandData.Allocate(c); try { var commandGuid = VSConstants.VSStd2K; OleCommandTarget.Exec(ref commandGuid, oleCommandData.CommandId, oleCommandData.CommandExecOpt, oleCommandData.VariantIn, oleCommandData.VariantOut); } finally { OleCommandData.Release(ref oleCommandData); } }
/// <summary> /// Actually run the QueryStatus command and report the result /// </summary> private bool RunQueryStatusCore(OleCommandData oleCommandData) { var hr = _commandTarget.QueryStatus(oleCommandData, out OLECMD command); if (!ErrorHandler.Succeeded(hr)) { return(false); } // TODO: Visual Studio has slightly different behavior here IIRC. I believe it will // only cache if it's at least supported. Need to check on that var result = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); return(result == (result & command.cmdf)); }
/// <summary> /// Run the KeyInput value through Exec /// </summary> private void RunExec(KeyInput keyInput) { OleCommandData data; Guid commandGroup; Assert.IsTrue(OleCommandUtil.TryConvert(keyInput, out commandGroup, out data)); try { _target.Exec(ref commandGroup, data.CommandId, data.CommandExecOpt, data.VariantIn, data.VariantOut); } finally { OleCommandData.Release(ref data); } }
/// <summary> /// Actually run the QueryStatus command and report the result /// </summary> private bool RunQueryStatusCore(Guid commandGroup, OleCommandData oleCommandData) { var commands = new OLECMD[1]; commands[0].cmdID = oleCommandData.CommandId; var hr = _commandTarget.QueryStatus(ref commandGroup, 1, commands, oleCommandData.VariantIn); if (!ErrorHandler.Succeeded(hr)) { return(false); } // TODO: Visual Studio has slightly different behavior here IIRC. I believe it will // only cache if it's at least supported. Need to check on that var result = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); return(result == (result & commands[0].cmdf)); }
/// <summary> /// Exec the operation in question /// </summary> private bool RunExecCore(OleCommandData oleCommandData) { var variantOut = IntPtr.Zero; try { var hr = _commandTarget.Exec(oleCommandData); return(ErrorHandler.Succeeded(hr)); } finally { if (variantOut != IntPtr.Zero) { NativeMethods.VariantClear(variantOut); Marshal.FreeCoTaskMem(variantOut); } } }
/// <summary> /// Run QueryStatus and return whether or not it should be enabled /// </summary> private bool RunQueryStatus(OleCommandData oleCommandData) { // First check and see if this represents a cached call to QueryStatus. Visual Studio // will cache the result of QueryStatus for most types of commands that Vim will be // interested in handling. // // I haven't figured out the exact rules by which this cache is reset yet but it appears // to be when a QueryStatus / Exec pair executes succesfully or when Visual Studio loses // and gains focus again. These may be related var commandId = oleCommandData.CommandId; if (_cachedQueryStatusMap.TryGetValue(commandId, out bool result)) { return(result); } result = RunQueryStatusCore(oleCommandData); _cachedQueryStatusMap[commandId] = result; return(result); }
/// <summary> /// Run the KeyInput value through QueryStatus. Returns true if the QueryStatus call /// indicated the command was supported /// </summary> private bool RunQueryStatus(KeyInput keyInput) { OleCommandData data; Guid commandGroup; Assert.IsTrue(OleCommandUtil.TryConvert(keyInput, out commandGroup, out data)); try { var cmds = new OLECMD[1]; cmds[0] = new OLECMD { cmdID = data.CommandId }; return (ErrorHandler.Succeeded(_target.QueryStatus(ref commandGroup, 1, cmds, data.VariantIn)) && cmds[0].cmdf == (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED)); } finally { OleCommandData.Release(ref data); } }
/// <summary> /// Actually run the QueryStatus command and report the result /// </summary> private bool RunQueryStatusCore(OleCommandData oleCommandData) { OLECMD command; var hr = _commandTarget.QueryStatus(oleCommandData, out command); if (!ErrorHandler.Succeeded(hr)) { return false; } // TODO: Visual Studio has slightly different behavior here IIRC. I believe it will // only cache if it's at least supported. Need to check on that var result = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); return result == (result & command.cmdf); }
/// <summary> /// Run QueryStatus and return whether or not it should be enabled /// </summary> private bool RunQueryStatus(OleCommandData oleCommandData) { // First check and see if this represents a cached call to QueryStatus. Visual Studio // will cache the result of QueryStatus for most types of commands that Vim will be // interested in handling. // // I haven't figured out the exact rules by which this cache is reset yet but it appears // to be when a QueryStatus / Exec pair executes succesfully or when Visual Studio loses // and gains focus again. These may be related bool result; var key = new CommandKey(oleCommandData.CommandGroup, oleCommandData.CommandId); if (_cachedQueryStatusMap.TryGetValue(key, out result)) { return result; } result = RunQueryStatusCore(oleCommandData); _cachedQueryStatusMap[key] = result; return result; }
/// <summary> /// Exec the operation in question /// </summary> private bool RunExecCore(OleCommandData oleCommandData) { var variantOut = IntPtr.Zero; try { var hr = _commandTarget.Exec(oleCommandData); return ErrorHandler.Succeeded(hr); } finally { if (variantOut != IntPtr.Zero) { NativeMethods.VariantClear(variantOut); Marshal.FreeCoTaskMem(variantOut); } } }
/// <summary> /// Run the Exec operation. Make sure to properly manage the QueryStatus cache /// </summary> private void RunExec(OleCommandData oleCommandData) { var result = RunExecCore(oleCommandData); if (result) { _cachedQueryStatusMap.Clear(); } }
/// <summary> /// Try and convert the provided KeyInput value into OleCommandData. This conversion is meant /// to simulate the standard converison of key input into OLE information in Visual Studio. This /// means we need to reproduce all of the behavior including not converting textual input /// here (unless it maps to a command). Textual input typcially gets routed through WPF and /// is routed to IOleCommandTarget in the default handler /// </summary> private bool TryConvertToOleCommandData(KeyInput keyInput, out OleCommandData oleCommandData) { if (keyInput.RawChar.IsSome()) { if (Char.IsLetterOrDigit(keyInput.Char)) { oleCommandData = OleCommandData.Empty; return false; } } return OleCommandUtil.TryConvert(keyInput, SimulateStandardKeyMappings, out oleCommandData); }
/// <summary> /// Actually run the QueryStatus command and report the result /// </summary> private bool RunQueryStatusCore(Guid commandGroup, OleCommandData oleCommandData) { var commands = new OLECMD[1]; commands[0].cmdID = oleCommandData.CommandId; var hr = _commandTarget.QueryStatus(ref commandGroup, 1, commands, oleCommandData.VariantIn); if (!ErrorHandler.Succeeded(hr)) { return false; } // TODO: Visual Studio has slightly different behavior here IIRC. I believe it will // only cache if it's at least supported. Need to check on that var result = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); return result == (result & commands[0].cmdf); }
/// <summary> /// Run the Exec operation. Make sure to properly manage the QueryStatus cache /// </summary> private void RunExec(Guid commandGroup, OleCommandData oleCommandData) { var result = RunExecCore(commandGroup, oleCommandData); if (result) { _cachedQueryStatusMap.Clear(); } }
public void TryConvert_TextInputToOleCommandData() { var textView = EditorUtil.CreateTextView(""); var buffer = EditorUtil.FactoryService.Vim.CreateVimBuffer(textView); buffer.SwitchMode(ModeKind.Insert, ModeArgument.None); foreach (var cur in KeyInputUtil.VimKeyInputList) { if (!buffer.InsertMode.IsDirectInsert(cur)) { continue; } var oleCommandData = new OleCommandData(); try { KeyInput converted; Guid commandGroup; Assert.IsTrue(OleCommandUtil.TryConvert(cur, out commandGroup, out oleCommandData)); // We lose fidelity on these keys because they both get written out as numbers // at this point if (VimKeyUtil.IsKeypadKey(cur.Key)) { continue; } Assert.IsTrue(OleCommandUtil.TryConvert(commandGroup, oleCommandData, out converted)); Assert.AreEqual(converted, cur); } finally { OleCommandData.Release(ref oleCommandData); } } }