diff --git a/Src/VsVim/PkgCmdID.cs b/Src/VsVim/PkgCmdID.cs index f27d41d0bf..62c1d8a9a5 100644 --- a/Src/VsVim/PkgCmdID.cs +++ b/Src/VsVim/PkgCmdID.cs @@ -13,5 +13,6 @@ internal static class CommandIds internal const uint ToggleEnabled = 0x103; internal const uint SetEnabled = 0x104; internal const uint SetDisabled = 0x105; + internal const uint SetMode = 0x106; }; } \ No newline at end of file diff --git a/Src/VsVim/VsVim.vsct b/Src/VsVim/VsVim.vsct index bb2adc78a4..b27dbcdd3a 100644 --- a/Src/VsVim/VsVim.vsct +++ b/Src/VsVim/VsVim.vsct @@ -123,6 +123,16 @@ + @@ -151,6 +161,7 @@ + diff --git a/Src/VsVim/VsVimPackage.cs b/Src/VsVim/VsVimPackage.cs index 880e69bb1b..3fefed37f8 100644 --- a/Src/VsVim/VsVimPackage.cs +++ b/Src/VsVim/VsVimPackage.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text.Editor; using Microsoft.Win32; using System; using System.ComponentModel.Composition.Hosting; @@ -31,6 +32,7 @@ public sealed class VsVimPackage : Package, IOleCommandTarget private IComponentModel _componentModel; private ExportProvider _exportProvider; private IVim _vim; + private IVsAdapter _vsAdapter; public VsVimPackage() { @@ -43,6 +45,7 @@ protected override void Initialize() _componentModel = (IComponentModel)GetService(typeof(SComponentModel)); _exportProvider = _componentModel.DefaultExportProvider; _vim = _exportProvider.GetExportedValue(); + _vsAdapter = _exportProvider.GetExportedValue(); } protected override void Dispose(bool disposing) @@ -73,6 +76,9 @@ private void DumpKeyboard() { keyBindingService.DumpKeyboard(streamWriter); } + + var message = string.Format("Keyboard data dumped to: {0}", filePath); + PrintToCommandWindow(message); } /// @@ -143,41 +149,134 @@ private void ToggleEnabled() _vim.IsDisabled = !_vim.IsDisabled; } - #region IOleCommandTarget + private void PrintToCommandWindow(string text) + { + var commandWindow = (IVsCommandWindow)GetService(typeof(SVsCommandWindow)); + if (commandWindow == null) + { + return; + } - int IOleCommandTarget.Exec(ref Guid commandGroup, uint commandId, uint commandExecOpt, IntPtr variantIn, IntPtr variantOut) + // Only print the text if a command is being run in the command window. If it's being run via another extension + // then don't print any output. + int running; + if (VSConstants.S_OK != commandWindow.RunningCommandWindowCommand(out running) || running == 0) + { + return; + } + + commandWindow.Print(text); + } + + private int SetMode(IntPtr variantIn, IntPtr variantOut, uint commandExecOpt) + { + if (IsQueryParameterList(variantIn, variantOut, commandExecOpt)) + { + Marshal.GetNativeVariantForObject("mode", variantOut); + return VSConstants.S_OK; + } + + var name = GetStringArgument(variantIn) ?? ""; + + ModeKind mode; + if (!Enum.TryParse(name, out mode)) + { + PrintToCommandWindow(string.Format("Invalid mode name: {0}", name)); + + var all = string.Join(", ", Enum.GetNames(typeof(ModeKind))); + PrintToCommandWindow(string.Format("Valid names: {0}", all)); + return VSConstants.E_INVALIDARG; + } + + IWpfTextView activeTextView; + if (!_vsAdapter.TryGetActiveTextView(out activeTextView)) + { + PrintToCommandWindow("Could not detect an active vim buffer"); + return VSConstants.E_FAIL; + } + + IVimBuffer vimBuffer; + if (!_vim.TryGetVimBuffer(activeTextView, out vimBuffer)) + { + PrintToCommandWindow("Active view isn't a vim buffer"); + return VSConstants.E_FAIL; + } + + vimBuffer.SwitchMode(mode, ModeArgument.None); + return VSConstants.S_OK; + } + + private string GetStringArgument(IntPtr variantIn) + { + if (variantIn == IntPtr.Zero) + { + return null; + } + + var obj = Marshal.GetObjectForNativeVariant(variantIn); + return obj as string; + } + + /// + /// Used to determine if the shell is querying for the parameter list of our command. + /// + private static bool IsQueryParameterList(IntPtr variantIn, IntPtr variantOut, uint nCmdexecopt) { - if (commandGroup == GuidList.VsVimCommandSet) + ushort lo = (ushort)(nCmdexecopt & (uint)0xffff); + ushort hi = (ushort)(nCmdexecopt >> 16); + if (lo == (ushort)OLECMDEXECOPT.OLECMDEXECOPT_SHOWHELP) { - switch (commandId) + if (hi == VsMenus.VSCmdOptQueryParameterList) { - case CommandIds.Options: - ShowOptionPage(typeof(Vim.VisualStudio.Implementation.OptionPages.KeyboardOptionPage)); - break; - case CommandIds.DumpKeyboard: - DumpKeyboard(); - break; - case CommandIds.ClearTSQLBindings: - ClearTSQLBindings(); - break; - case CommandIds.ToggleEnabled: - ToggleEnabled(); - break; - case CommandIds.SetEnabled: - _vim.IsDisabled = false; - break; - case CommandIds.SetDisabled: - _vim.IsDisabled = true; - break; - default: - Debug.Assert(false); - break; + if (variantOut != IntPtr.Zero) + { + return true; + } } + } - return VSConstants.S_OK; + return false; + } + + #region IOleCommandTarget + + int IOleCommandTarget.Exec(ref Guid commandGroup, uint commandId, uint commandExecOpt, IntPtr variantIn, IntPtr variantOut) + { + if (commandGroup != GuidList.VsVimCommandSet) + { + return VSConstants.E_FAIL; } - return VSConstants.E_FAIL; + var hr = VSConstants.S_OK; + switch (commandId) + { + case CommandIds.Options: + ShowOptionPage(typeof(Vim.VisualStudio.Implementation.OptionPages.KeyboardOptionPage)); + break; + case CommandIds.DumpKeyboard: + DumpKeyboard(); + break; + case CommandIds.ClearTSQLBindings: + ClearTSQLBindings(); + break; + case CommandIds.ToggleEnabled: + ToggleEnabled(); + break; + case CommandIds.SetEnabled: + _vim.IsDisabled = false; + break; + case CommandIds.SetDisabled: + _vim.IsDisabled = true; + break; + case CommandIds.SetMode: + hr = SetMode(variantIn, variantOut, commandExecOpt); + break; + default: + Debug.Assert(false); + break; + } + + return hr; } int IOleCommandTarget.QueryStatus(ref Guid commandGroup, uint commandsCount, OLECMD[] commands, IntPtr pCmdText) diff --git a/Src/VsVimShared/IVsAdapter.cs b/Src/VsVimShared/IVsAdapter.cs index 390f1c899c..b5b58a5eec 100644 --- a/Src/VsVimShared/IVsAdapter.cs +++ b/Src/VsVimShared/IVsAdapter.cs @@ -117,6 +117,12 @@ public interface IVsAdapter Result GetTextBufferForDocCookie(uint cookie); + /// + /// Get the which has focus. + /// + /// + bool TryGetActiveTextView(out IWpfTextView textView); + /// /// Open a file with the specified name in Visual Studio /// diff --git a/Src/VsVimShared/Implementation/Misc/VsAdapter.cs b/Src/VsVimShared/Implementation/Misc/VsAdapter.cs index 6123938d5f..189dbf6b82 100644 --- a/Src/VsVimShared/Implementation/Misc/VsAdapter.cs +++ b/Src/VsVimShared/Implementation/Misc/VsAdapter.cs @@ -461,6 +461,27 @@ private void OpenFile(string filePath) } } + private bool TryGetActiveTextView(out IWpfTextView textView) + { + var textManager = _serviceProvider.GetService(); + if (textManager == null) + { + textView = null; + return false; + } + + IVsTextView vsTextView; + var hr = textManager.GetActiveView2(fMustHaveFocus: 0, pBuffer: null, grfIncludeViewFrameType: (uint)_VIEWFRAMETYPE.vftCodeWindow, ppView: out vsTextView); + if (ErrorHandler.Failed(hr)) + { + textView = null; + return false; + } + + textView = _editorAdaptersFactoryService.GetWpfTextView(vsTextView); + return textView != null; + } + #region IVsAdapter bool IVsAdapter.InAutomationFunction @@ -563,6 +584,11 @@ void IVsAdapter.OpenFile(string filePath) OpenFile(filePath); } + bool IVsAdapter.TryGetActiveTextView(out IWpfTextView textView) + { + return TryGetActiveTextView(out textView); + } + #endregion } }