public static void Release(ref OleCommandData oleCommandData) { if (oleCommandData.VariantIn != IntPtr.Zero) { NativeMethods.VariantClear(oleCommandData.VariantIn); Marshal.FreeCoTaskMem(oleCommandData.VariantIn); } if (oleCommandData.VariantOut != IntPtr.Zero) { NativeMethods.VariantClear(oleCommandData.VariantOut); Marshal.FreeCoTaskMem(oleCommandData.VariantOut); } oleCommandData = new OleCommandData(); }
int IOleCommandTarget.Exec(ref Guid commandGroup, uint commandId, uint commandExecOpt, IntPtr variantIn, IntPtr variantOut) { try { EditCommand editCommand; if (TryConvert(commandGroup, commandId, variantIn, out editCommand)) { if (editCommand.IsUndo) { // The user hit the undo button. Don't attempt to map anything here and instead just // run a single Vim undo operation _buffer.UndoRedoOperations.Undo(1); return(NativeMethods.S_OK); } else if (editCommand.IsRedo) { // The user hit the redo button. Don't attempt to map anything here and instead just // run a single Vim redo operation _buffer.UndoRedoOperations.Redo(1); return(NativeMethods.S_OK); } else if (editCommand.HasKeyInput) { var keyInput = editCommand.KeyInput; // Discard the input if it's been flagged by a previous QueryStatus if (_bufferCoordinator.DiscardedKeyInput.IsSome(keyInput)) { return(NativeMethods.S_OK); } // Try and process the command with the IVimBuffer var commandData = new OleCommandData(commandId, commandExecOpt, variantIn, variantOut); if (TryProcessWithBuffer(ref commandGroup, ref commandData, keyInput)) { return(NativeMethods.S_OK); } } } } finally { _bufferCoordinator.DiscardedKeyInput = FSharpOption <KeyInput> .None; } return(_nextTarget.Exec(commandGroup, commandId, commandExecOpt, variantIn, variantOut)); }
/// <summary> /// Try and exec this KeyInput in an intercepted fashion /// </summary> private bool TryExecIntercepted(ref Guid commandGroup, ref OleCommandData oleCommandData, KeyInput originalKeyInput, KeyInput mappedKeyInput) { bool intercepted; bool result; Guid mappedCommandGroup; OleCommandData mappedOleCommandData; if (originalKeyInput == mappedKeyInput) { // No changes so just use the original OleCommandData result = VSConstants.S_OK == _nextTarget.Exec( ref commandGroup, oleCommandData.CommandId, oleCommandData.CommandExecOpt, oleCommandData.VariantIn, oleCommandData.VariantOut); intercepted = true; } else if (OleCommandUtil.TryConvert(mappedKeyInput, out mappedCommandGroup, out mappedOleCommandData)) { result = VSConstants.S_OK == _nextTarget.Exec( ref mappedCommandGroup, mappedOleCommandData.CommandId, mappedOleCommandData.CommandExecOpt, mappedOleCommandData.VariantIn, mappedOleCommandData.VariantOut); intercepted = true; OleCommandData.Release(ref mappedOleCommandData); } else { // If we couldn't process it using intercepting mechanism then just go straight to the IVimBuffer // for processing. result = _buffer.Process(originalKeyInput); intercepted = false; } if (intercepted) { // We processed the input and bypassed the IVimBuffer instance. We need to tell IVimBuffer this // KeyInput was processed so it can track it for macro purposes. Make sure to track the mapped // KeyInput value. The SimulateProcessed method does not mapping _buffer.SimulateProcessed(mappedKeyInput); } return(result); }
internal static int QueryStatus(this IOleCommandTarget oleCommandTarget, OleCommandData oleCommandData, out OLECMD command) { var commandGroup = oleCommandData.Group; var cmds = new OLECMD[1]; cmds[0] = new OLECMD { cmdID = oleCommandData.Id }; var result = oleCommandTarget.QueryStatus( ref commandGroup, 1, cmds, oleCommandData.VariantIn); command = cmds[0]; return(result); }
/// <summary> /// Try and process the given KeyInput for insert mode in the middle of an Exec. This is /// called for commands which can't be processed directly like edits. We'd prefer these /// go through Visual Studio's command system so items like Intellisense work properly. /// </summary> private bool TryProcessWithExec(Guid commandGroup, OleCommandData oleCommandData, IInsertMode insertMode, KeyInput originalKeyInput, KeyInput mappedKeyInput) { Func <bool> customProcess = () => { var versionNumber = _textBuffer.CurrentSnapshot.Version.VersionNumber; int? hr = null; Guid mappedCommandGroup; OleCommandData mappedOleCommandData; if (originalKeyInput == mappedKeyInput) { // No changes so just use the original OleCommandData hr = _nextTarget.Exec( ref commandGroup, oleCommandData.CommandId, oleCommandData.CommandExecOpt, oleCommandData.VariantIn, oleCommandData.VariantOut); } else if (OleCommandUtil.TryConvert(mappedKeyInput, out mappedCommandGroup, out mappedOleCommandData)) { hr = _nextTarget.Exec( ref mappedCommandGroup, mappedOleCommandData.CommandId, mappedOleCommandData.CommandExecOpt, mappedOleCommandData.VariantIn, mappedOleCommandData.VariantOut); OleCommandData.Release(ref mappedOleCommandData); } if (hr.HasValue) { // Whether or not an Exec succeeded is a bit of a heuristic. IOleCommandTarget implementations like // C++ will return E_ABORT if Intellisense failed but the character was actually inserted into // the ITextBuffer. VsVim really only cares about the character insert. However we must also // consider cases where the character successfully resulted in no action as a success return(ErrorHandler.Succeeded(hr.Value) || versionNumber < _textBuffer.CurrentSnapshot.Version.VersionNumber); } // Couldn't map to a Visual Studio command so it didn't succeed return(false); }; return(insertMode.CustomProcess(mappedKeyInput, customProcess.ToFSharpFunc())); }
int IOleCommandTarget.Exec(ref Guid commandGroup, uint commandId, uint commandExecOpt, IntPtr variantIn, IntPtr variantOut) { try { EditCommand editCommand; if (TryConvert(commandGroup, commandId, variantIn, out editCommand)) { if (editCommand.IsUndo) { _buffer.UndoRedoOperations.Undo(1); return(NativeMethods.S_OK); } else if (editCommand.IsRedo) { _buffer.UndoRedoOperations.Redo(1); return(NativeMethods.S_OK); } else if (editCommand.HasKeyInput) { var keyInput = editCommand.KeyInput; // Swallow the input if it's been flagged by a previous QueryStatus if (SwallowIfNextExecMatches.IsSome() && SwallowIfNextExecMatches.Value == keyInput) { return(NativeMethods.S_OK); } var commandData = new OleCommandData(commandId, commandExecOpt, variantIn, variantOut); if (TryExec(ref commandGroup, ref commandData, keyInput)) { return(NativeMethods.S_OK); } } } } finally { SwallowIfNextExecMatches = FSharpOption <KeyInput> .None; } return(_nextTarget.Exec(commandGroup, commandId, commandExecOpt, variantIn, variantOut)); }
/// <summary> /// Try and convert the given insert command to an OleCommand. This should only be done /// for InsertCommand values which we want to custom process /// </summary> private bool TryGetOleCommandData(InsertCommand command, out OleCommandData commandData) { if (command.IsBack) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.BACKSPACE); return(true); } if (command.IsDelete) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.DELETE); return(true); } if (command.IsInsert) { var insert = (InsertCommand.Insert)command; if (insert.Item != null && insert.Item.Length == 1) { commandData = OleCommandData.CreateTypeChar(insert.Item[0]); return(true); } } if (command.IsInsertTab) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.TAB); return(true); } if (command.IsInsertNewLine) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.RETURN); return(true); } commandData = OleCommandData.Empty; return(false); }
internal static bool TryConvert(EditCommand editCommand, out OleCommandData oleCommandData) { switch (editCommand.EditCommandKind) { case EditCommandKind.GoToDefinition: oleCommandData = new OleCommandData(VSConstants.VSStd97CmdID.GotoDecl); return(true); case EditCommandKind.Paste: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.PASTE); return(true); case EditCommandKind.Undo: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.UNDO); return(true); case EditCommandKind.Redo: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.REDO); return(true); case EditCommandKind.Comment: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.COMMENTBLOCK); return(true); case EditCommandKind.Uncomment: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.UNCOMMENTBLOCK); return(true); case EditCommandKind.UserInput: return(TryConvert(editCommand.KeyInput, out oleCommandData)); case EditCommandKind.VisualStudioCommand: default: oleCommandData = OleCommandData.Empty; return(false); } }
/// <summary> /// Try and convert the given insert command to an OleCommand. This should only be done /// for InsertCommand values which we want to custom process /// </summary> private bool TryGetOleCommandData(InsertCommand command, out OleCommandData commandData) { if (command.IsBack) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.BACKSPACE); return(true); } if (command.IsDelete) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.DELETE); return(true); } if (command.IsDirectInsert) { var directInsert = (InsertCommand.DirectInsert)command; commandData = OleCommandData.CreateTypeChar(directInsert.Item); return(true); } if (command.IsInsertTab) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.TAB); return(true); } if (command.IsInsertNewLine) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.RETURN); return(true); } commandData = OleCommandData.Empty; return(false); }
/// <summary> /// Try and convert the KeyInput into the appropriate Visual Studio command. The conversion will be done /// without any consideration of Visual Studio standard commands. It will map as if VsVim was in /// complete control of key bindings /// </summary> internal static bool TryConvert(KeyInput keyInput, out OleCommandData oleCommandData) { return(TryConvert(keyInput, false, out oleCommandData)); }
/// <summary> /// Try and convert the given insert command to an OleCommand. This should only be done /// for InsertCommand values which we want to custom process /// </summary> private bool TryGetOleCommandData(InsertCommand command, out OleCommandData commandData) { if (command.IsBack) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.BACKSPACE); return true; } if (command.IsDelete) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.DELETE); return true; } if (command.IsDirectInsert) { var directInsert = (InsertCommand.DirectInsert)command; commandData = OleCommandData.CreateTypeChar(directInsert.Item); return true; } if (command.IsInsertTab) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.TAB); return true; } if (command.IsInsertNewLine) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.RETURN); return true; } commandData = OleCommandData.Empty; return false; }
/// <summary> /// Try and convert the KeyInput value into an OleCommandData instance /// </summary> internal static bool TryConvert(KeyInput keyInput, out Guid commandGroup, out OleCommandData oleCommandData) { var success = true; commandGroup = VSConstants.VSStd2K; switch (keyInput.Key) { case VimKey.Enter: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.RETURN); break; case VimKey.Escape: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.CANCEL); break; case VimKey.Delete: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.DELETE); break; case VimKey.Back: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.BACKSPACE); break; case VimKey.Up: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.UP); break; case VimKey.Down: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.DOWN); break; case VimKey.Left: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.LEFT); break; case VimKey.Right: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.RIGHT); break; case VimKey.Tab: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.TAB); break; case VimKey.PageUp: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.PAGEUP); break; case VimKey.PageDown: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.PAGEDN); break; default: if (keyInput.RawChar.IsSome()) { oleCommandData = OleCommandData.Allocate(keyInput.Char); } else { oleCommandData = new OleCommandData(); success = false; } break; } return success; }
internal static bool TryConvert(EditCommand editCommand, out OleCommandData oleCommandData) { switch (editCommand.EditCommandKind) { case EditCommandKind.GoToDefinition: oleCommandData = new OleCommandData(VSConstants.VSStd97CmdID.GotoDecl); return true; case EditCommandKind.Paste: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.PASTE); return true; case EditCommandKind.Undo: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.UNDO); return true; case EditCommandKind.Redo: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.REDO); return true; case EditCommandKind.Comment: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.COMMENTBLOCK); return true; case EditCommandKind.Uncomment: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.UNCOMMENTBLOCK); return true; case EditCommandKind.UserInput: return TryConvert(editCommand.KeyInput, out oleCommandData); case EditCommandKind.VisualStudioCommand: default: oleCommandData = OleCommandData.Empty; return false; } }
/// <summary> /// Try and convert the KeyInput value into an OleCommandData instance /// </summary> internal static bool TryConvert(KeyInput keyInput, out Guid commandGroup, out OleCommandData oleCommandData) { var success = true; commandGroup = VSConstants.VSStd2K; switch (keyInput.Key) { case VimKey.Enter: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.RETURN); break; case VimKey.Escape: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.CANCEL); break; case VimKey.Delete: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.DELETE); break; case VimKey.Back: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.BACKSPACE); break; case VimKey.Up: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.UP); break; case VimKey.Down: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.DOWN); break; case VimKey.Left: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.LEFT); break; case VimKey.Right: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.RIGHT); break; case VimKey.Tab: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.TAB); break; case VimKey.PageUp: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.PAGEUP); break; case VimKey.PageDown: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.PAGEDN); break; case VimKey.Insert: oleCommandData = new OleCommandData(VSConstants.VSStd2KCmdID.TOGGLE_OVERTYPE_MODE); break; default: if (keyInput.RawChar.IsSome()) { oleCommandData = OleCommandData.Allocate(keyInput.Char); } else { oleCommandData = new OleCommandData(); success = false; } break; } return(success); }
/// <summary> /// Try and exec this KeyInput in an intercepted fashion /// </summary> private bool TryExecIntercepted(ref Guid commandGroup, ref OleCommandData oleCommandData, KeyInput originalKeyInput, KeyInput mappedKeyInput) { bool intercepted; bool result; Guid mappedCommandGroup; OleCommandData mappedOleCommandData; if (originalKeyInput == mappedKeyInput) { // No changes so just use the original OleCommandData result = VSConstants.S_OK == _nextTarget.Exec( ref commandGroup, oleCommandData.CommandId, oleCommandData.CommandExecOpt, oleCommandData.VariantIn, oleCommandData.VariantOut); intercepted = true; } else if (OleCommandUtil.TryConvert(mappedKeyInput, out mappedCommandGroup, out mappedOleCommandData)) { result = VSConstants.S_OK == _nextTarget.Exec( ref mappedCommandGroup, mappedOleCommandData.CommandId, mappedOleCommandData.CommandExecOpt, mappedOleCommandData.VariantIn, mappedOleCommandData.VariantOut); intercepted = true; OleCommandData.Release(ref mappedOleCommandData); } else { // If we couldn't process it using intercepting mechanism then just go straight to the IVimBuffer // for processing. result = _buffer.Process(originalKeyInput); intercepted = false; } if (intercepted) { // We processed the input and bypassed the IVimBuffer instance. We need to tell IVimBuffer this // KeyInput was processed so it can track it for macro purposes. Make sure to track the mapped // KeyInput value. The SimulateProcessed method does not mapping _buffer.SimulateProcessed(mappedKeyInput); } return result; }
int IOleCommandTarget.Exec(ref Guid commandGroup, uint commandId, uint commandExecOpt, IntPtr variantIn, IntPtr variantOut) { try { EditCommand editCommand; if (TryConvert(commandGroup, commandId, variantIn, out editCommand)) { if (editCommand.IsUndo) { _buffer.UndoRedoOperations.Undo(1); return NativeMethods.S_OK; } else if (editCommand.IsRedo) { _buffer.UndoRedoOperations.Redo(1); return NativeMethods.S_OK; } else if (editCommand.HasKeyInput) { var keyInput = editCommand.KeyInput; // Swallow the input if it's been flagged by a previous QueryStatus if (SwallowIfNextExecMatches.IsSome() && SwallowIfNextExecMatches.Value == keyInput) { return NativeMethods.S_OK; } var commandData = new OleCommandData(commandId, commandExecOpt, variantIn, variantOut); if (TryExec(ref commandGroup, ref commandData, keyInput)) { return NativeMethods.S_OK; } } } } finally { SwallowIfNextExecMatches = FSharpOption<KeyInput>.None; } return _nextTarget.Exec(commandGroup, commandId, commandExecOpt, variantIn, variantOut); }
/// <summary> /// Try and process the KeyInput from the Exec method /// </summary> private bool TryProcessWithBuffer(ref Guid commandGroup, ref OleCommandData oleCommandData, KeyInput keyInput) { if (!_buffer.CanProcess(keyInput)) { // If the IVimBuffer can't process it then it doesn't matter return false; } // Next we need to determine if we can process this directly or not. The only mode // we actively intercept KeyInput for is InsertMode because we need to route it // through IOleCommandTarget to get Intellisense and many other features. var mode = _buffer.ModeKind == ModeKind.Insert ? _buffer.InsertMode : null; if (mode == null) { return _buffer.Process(keyInput).IsAnyHandled; } // Next we need to consider here are Key mappings. The CanProcess and Process APIs // will automatically map the KeyInput under the hood at the IVimBuffer level but // not at the individual IMode. Have to manually map here and test against the // mapped KeyInput KeyInput mapped; if (!TryGetSingleMapping(KeyRemapMode.Insert, keyInput, out mapped) || CanProcessWithInsertMode(mode, mapped)) { return _buffer.Process(keyInput).IsAnyHandled; } // We've successfully mapped the KeyInput (even if t's a no-op) and determined that // we don't want to process it directly if possible. Now we try and process the // potentially mapped value return TryProcessWithExec(ref commandGroup, ref oleCommandData, keyInput, mapped); }
/// <summary> /// Try and convert the KeyInput value into an OleCommandData instance. If simulateStandardKeyBindings is set /// to true then "standard" Visual Studio key bindings will be assumed and this will be reflected in the /// resulting command information /// </summary> internal static bool TryConvert(KeyInput keyInput, bool simulateStandardKeyBindings, out OleCommandData oleCommandData) { var hasShift = 0 != (keyInput.KeyModifiers & KeyModifiers.Shift); VSConstants.VSStd2KCmdID?cmdId = null; switch (keyInput.Key) { case VimKey.Enter: cmdId = VSConstants.VSStd2KCmdID.RETURN; break; case VimKey.Escape: cmdId = VSConstants.VSStd2KCmdID.CANCEL; break; case VimKey.Delete: cmdId = VSConstants.VSStd2KCmdID.DELETE; break; case VimKey.Back: cmdId = VSConstants.VSStd2KCmdID.BACKSPACE; break; case VimKey.Up: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.UP_EXT : VSConstants.VSStd2KCmdID.UP; break; case VimKey.Down: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.DOWN_EXT : VSConstants.VSStd2KCmdID.DOWN; break; case VimKey.Left: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.LEFT_EXT : VSConstants.VSStd2KCmdID.LEFT; break; case VimKey.Right: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.RIGHT_EXT : VSConstants.VSStd2KCmdID.RIGHT; break; case VimKey.Tab: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.BACKTAB : VSConstants.VSStd2KCmdID.TAB; break; case VimKey.PageUp: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.PAGEUP_EXT : VSConstants.VSStd2KCmdID.PAGEUP; break; case VimKey.PageDown: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.PAGEDN_EXT : VSConstants.VSStd2KCmdID.PAGEDN; break; case VimKey.Insert: cmdId = VSConstants.VSStd2KCmdID.TOGGLE_OVERTYPE_MODE; break; } if (cmdId.HasValue) { oleCommandData = new OleCommandData(cmdId.Value); return(true); } if (keyInput.RawChar.IsSome()) { oleCommandData = OleCommandData.CreateTypeChar(keyInput.Char); return(true); } else { oleCommandData = OleCommandData.Empty; return(false); } }
/// <summary> /// Try and process the given KeyInput for insert mode in the middle of an Exec. This is /// called for commands which can't be processed directly like edits. We'd prefer these /// go through Visual Studio's command system so items like Intellisense work properly. /// </summary> private bool TryProcessWithExec(ref Guid commandGroup, ref OleCommandData oleCommandData, KeyInput originalKeyInput, KeyInput mappedKeyInput) { var versionNumber = _textBuffer.CurrentSnapshot.Version.VersionNumber; int? hr = null; Guid mappedCommandGroup; OleCommandData mappedOleCommandData; if (originalKeyInput == mappedKeyInput) { // No changes so just use the original OleCommandData hr = _nextTarget.Exec( ref commandGroup, oleCommandData.CommandId, oleCommandData.CommandExecOpt, oleCommandData.VariantIn, oleCommandData.VariantOut); } else if (OleCommandUtil.TryConvert(mappedKeyInput, out mappedCommandGroup, out mappedOleCommandData)) { hr = _nextTarget.Exec( ref mappedCommandGroup, mappedOleCommandData.CommandId, mappedOleCommandData.CommandExecOpt, mappedOleCommandData.VariantIn, mappedOleCommandData.VariantOut); OleCommandData.Release(ref mappedOleCommandData); } if (hr.HasValue) { // Whether or not an Exec succeeded is a bit of a heuristic. IOleCommandTarget implementations like // C++ will return E_ABORT if Intellisense failed but the character was actually inserted into // the ITextBuffer. VsVim really only cares about the character insert. However we must also // consider cases where the character successfully resulted in no action as a success var result = ErrorHandler.Succeeded(hr.Value) || versionNumber < _textBuffer.CurrentSnapshot.Version.VersionNumber; // We processed the input and bypassed the IVimBuffer instance. We need to tell IVimBuffer this // KeyInput was processed so it can track it for macro purposes. Make sure to track the mapped // KeyInput value. The SimulateProcessed method does not do any mapping if (result) { _buffer.SimulateProcessed(mappedKeyInput); } // Whether or not this succeeded it was processed to the fullest possible extent return true; } // If we couldn't map the KeyInput value into a Visual Studio command then go straight to the // ITextBuffer. Insert mode is already designed to handle these KeyInput values we'd just prefer // to pass them through Visual Studio. return _buffer.Process(originalKeyInput).IsAnyHandled; }
internal static bool TryConvert(OleCommandData oleCommandData, out KeyInput keyInput) { EditCommandKind editCommandKind; return(TryConvert(oleCommandData.Group, oleCommandData.Id, oleCommandData.VariantIn, out keyInput, out editCommandKind)); }
/// <summary> /// Try and convert the KeyInput value into an OleCommandData instance. If simulateStandardKeyBindings is set /// to true then "standard" Visual Studio key bindings will be assumed and this will be reflected in the /// resulting command information /// </summary> internal static bool TryConvert(KeyInput keyInput, bool simulateStandardKeyBindings, out OleCommandData oleCommandData) { var hasShift = 0 != (keyInput.KeyModifiers & KeyModifiers.Shift); VSConstants.VSStd2KCmdID? cmdId = null; switch (keyInput.Key) { case VimKey.Enter: cmdId = VSConstants.VSStd2KCmdID.RETURN; break; case VimKey.Escape: cmdId = VSConstants.VSStd2KCmdID.CANCEL; break; case VimKey.Delete: cmdId = VSConstants.VSStd2KCmdID.DELETE; break; case VimKey.Back: cmdId = VSConstants.VSStd2KCmdID.BACKSPACE; break; case VimKey.Up: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.UP_EXT : VSConstants.VSStd2KCmdID.UP; break; case VimKey.Down: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.DOWN_EXT : VSConstants.VSStd2KCmdID.DOWN; break; case VimKey.Left: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.LEFT_EXT : VSConstants.VSStd2KCmdID.LEFT; break; case VimKey.Right: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.RIGHT_EXT : VSConstants.VSStd2KCmdID.RIGHT; break; case VimKey.Tab: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.BACKTAB : VSConstants.VSStd2KCmdID.TAB; break; case VimKey.PageUp: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.PAGEUP_EXT : VSConstants.VSStd2KCmdID.PAGEUP; break; case VimKey.PageDown: cmdId = simulateStandardKeyBindings && hasShift ? VSConstants.VSStd2KCmdID.PAGEDN_EXT : VSConstants.VSStd2KCmdID.PAGEDN; break; case VimKey.Insert: cmdId = VSConstants.VSStd2KCmdID.TOGGLE_OVERTYPE_MODE; break; } if (cmdId.HasValue) { oleCommandData = new OleCommandData(cmdId.Value); return true; } if (keyInput.RawChar.IsSome()) { oleCommandData = OleCommandData.CreateTypeChar(keyInput.Char); return true; } else { oleCommandData = OleCommandData.Empty; return false; } }
/// <summary> /// Try and convert the KeyInput into the appropriate Visual Studio command. The conversion will be done /// without any consideration of Visual Studio standard commands. It will map as if VsVim was in /// complete control of key bindings /// </summary> internal static bool TryConvert(KeyInput keyInput, out OleCommandData oleCommandData) { return TryConvert(keyInput, false, out oleCommandData); }
/// <summary> /// Try and process the KeyInput from the Exec method /// </summary> private bool TryProcessWithBuffer(ref Guid commandGroup, ref OleCommandData oleCommandData, KeyInput keyInput) { if (!_buffer.CanProcess(keyInput)) { // If the IVimBuffer can't process it then it doesn't matter return false; } // Next we need to determine if we can process this directly or not. The only mode // we actively intercept KeyInput for is InsertMode because we need to route it // through IOleCommandTarget to get Intellisense and many other features. var insertMode = _buffer.ModeKind == ModeKind.Insert ? _buffer.InsertMode : null; if (insertMode == null) { return _buffer.Process(keyInput).IsAnyHandled; } // Next we need to consider here are Key mappings. The CanProcess and Process APIs // will automatically map the KeyInput under the hood at the IVimBuffer level but // not at the individual IMode. Have to manually map here and test against the // mapped KeyInput KeyInput mapped; if (!TryGetSingleMapping(keyInput, out mapped) || !ShouldProcessWithCommandTargetOverInsertMode(insertMode, mapped)) { return _buffer.Process(keyInput).IsAnyHandled; } // We are now intentionally by passing insert mode here. If there is an active // IWordCompletionSession here we need to manually dismiss it. Else it will remain // as we start typing a new word if (insertMode.ActiveWordCompletionSession.IsSome()) { insertMode.ActiveWordCompletionSession.Value.Dismiss(); } // We've successfully mapped the KeyInput (even if t's a no-op) and determined that // we don't want to process it directly if possible. Now we try and process the // potentially mapped value if (TryProcessWithExec(commandGroup, oleCommandData, insertMode, keyInput, mapped)) { return true; } // If we couldn't map the KeyInput value into a Visual Studio command then go straight to the // ITextBuffer. Insert mode is already designed to handle these KeyInput values we'd just prefer // to pass them through Visual Studio.. Remember to use the original KeyInput value here as // going through IVimBuffer will process mappings return _buffer.Process(keyInput).IsAnyHandled; }
internal static bool TryConvert(OleCommandData oleCommandData, out KeyInput keyInput) { EditCommandKind editCommandKind; return TryConvert(oleCommandData.Group, oleCommandData.Id, oleCommandData.VariantIn, out keyInput, out editCommandKind); }
/// <summary> /// Try and process the given KeyInput for insert mode in the middle of an Exec. This is /// called for commands which can't be processed directly like edits. We'd prefer these /// go through Visual Studio's command system so items like Intellisense work properly. /// </summary> private bool TryProcessWithExec(Guid commandGroup, OleCommandData oleCommandData, IInsertMode insertMode, KeyInput originalKeyInput, KeyInput mappedKeyInput) { Func<bool> customProcess = () => { var versionNumber = _textBuffer.CurrentSnapshot.Version.VersionNumber; int? hr = null; Guid mappedCommandGroup; OleCommandData mappedOleCommandData; if (originalKeyInput == mappedKeyInput) { // No changes so just use the original OleCommandData hr = _nextTarget.Exec( ref commandGroup, oleCommandData.CommandId, oleCommandData.CommandExecOpt, oleCommandData.VariantIn, oleCommandData.VariantOut); } else if (OleCommandUtil.TryConvert(mappedKeyInput, out mappedCommandGroup, out mappedOleCommandData)) { hr = _nextTarget.Exec( ref mappedCommandGroup, mappedOleCommandData.CommandId, mappedOleCommandData.CommandExecOpt, mappedOleCommandData.VariantIn, mappedOleCommandData.VariantOut); OleCommandData.Release(ref mappedOleCommandData); } if (hr.HasValue) { // Whether or not an Exec succeeded is a bit of a heuristic. IOleCommandTarget implementations like // C++ will return E_ABORT if Intellisense failed but the character was actually inserted into // the ITextBuffer. VsVim really only cares about the character insert. However we must also // consider cases where the character successfully resulted in no action as a success return ErrorHandler.Succeeded(hr.Value) || versionNumber < _textBuffer.CurrentSnapshot.Version.VersionNumber; } // Couldn't map to a Visual Studio command so it didn't succeed return false; }; return insertMode.CustomProcess(mappedKeyInput, customProcess.ToFSharpFunc()); }
/// <summary> /// Try and process the KeyInput from the Exec method /// </summary> private bool TryExec(ref Guid commandGroup, ref OleCommandData oleCommandData, KeyInput keyInput) { if (!_buffer.CanProcess(keyInput)) { // If the IVimBuffer can't process it then it doesn't matter return false; } // Next we need to determine if we can process this directly or not. The only mode // we actively intercept KeyInput for is InsertMode because we need to route it // through IOleCommandTarget to get Intellisense and many other features. var mode = _buffer.ModeKind == ModeKind.Insert ? _buffer.InsertMode : null; if (mode == null) { return _buffer.Process(keyInput); } // Next we need to consider here are Key mappings. The CanProcess and Process APIs // will automatically map the KeyInput under the hood at the IVimBuffer level but // not at the individual IMode. Have to manually map here and test against the // mapped KeyInput KeyInput mapped; if (!TryGetSingleMapping(KeyRemapMode.Insert, keyInput, out mapped) || CanProcessDirectly(mode, mapped)) { return _buffer.Process(keyInput); } // At this point we've determined that we need to intercept this return TryExecIntercepted(ref commandGroup, ref oleCommandData, keyInput, mapped); }
int IOleCommandTarget.Exec(ref Guid commandGroup, uint commandId, uint commandExecOpt, IntPtr variantIn, IntPtr variantOut) { try { EditCommand editCommand; if (TryConvert(commandGroup, commandId, variantIn, out editCommand)) { if (editCommand.IsUndo) { // The user hit the undo button. Don't attempt to map anything here and instead just // run a single Vim undo operation _buffer.UndoRedoOperations.Undo(1); return NativeMethods.S_OK; } else if (editCommand.IsRedo) { // The user hit the redo button. Don't attempt to map anything here and instead just // run a single Vim redo operation _buffer.UndoRedoOperations.Redo(1); return NativeMethods.S_OK; } else if (editCommand.HasKeyInput) { var keyInput = editCommand.KeyInput; // Discard the input if it's been flagged by a previous QueryStatus if (_bufferCoordinator.DiscardedKeyInput.IsSome(keyInput)) { return NativeMethods.S_OK; } // Try and process the command with the IVimBuffer var commandData = new OleCommandData(commandId, commandExecOpt, variantIn, variantOut); if (TryProcessWithBuffer(ref commandGroup, ref commandData, keyInput)) { return NativeMethods.S_OK; } } } } finally { _bufferCoordinator.DiscardedKeyInput = FSharpOption<KeyInput>.None; } return _nextTarget.Exec(commandGroup, commandId, commandExecOpt, variantIn, variantOut); }
internal static int QueryStatus(this IOleCommandTarget oleCommandTarget, OleCommandData oleCommandData) { OLECMD command; return(QueryStatus(oleCommandTarget, oleCommandData, out command)); }
/// <summary> /// Try and convert the given insert command to an OleCommand. This should only be done /// for InsertCommand values which we want to custom process /// </summary> private bool TryGetOleCommandData(InsertCommand command, out OleCommandData commandData) { if (command.IsBack) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.BACKSPACE); return true; } if (command.IsDelete) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.DELETE); return true; } if (command.IsInsert) { var insert = (InsertCommand.Insert)command; if (insert.Item != null && insert.Item.Length == 1) { commandData = OleCommandData.CreateTypeChar(insert.Item[0]); return true; } } if (command.IsInsertTab) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.TAB); return true; } if (command.IsInsertNewLine) { commandData = new OleCommandData(VSConstants.VSStd2KCmdID.RETURN); return true; } commandData = OleCommandData.Empty; return false; }