Skip to content

Commit

Permalink
Merge pull request #1834 from jaredpar/setmode
Browse files Browse the repository at this point in the history
Ability to set mode through command window
  • Loading branch information
jaredpar committed Sep 23, 2016
2 parents b3824b0 + 2375dce commit a8e37ab
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 27 deletions.
1 change: 1 addition & 0 deletions Src/VsVim/PkgCmdID.cs
Expand Up @@ -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;
};
}
11 changes: 11 additions & 0 deletions Src/VsVim/VsVim.vsct
Expand Up @@ -123,6 +123,16 @@
</Strings>
</Button>

<Button guid="guidVsVimCmdSet" id="cmdidVsVimSetMode" priority="0x0100" type="Button">
<Parent guid="guidVsVimCmdSet" id="VsVimMenuGroup" />
<CommandFlag>AllowParams</CommandFlag>
<Strings>
<CommandName>cmdidVsVimSetMode</CommandName>
<ButtonText>VsVim Set mode</ButtonText>
<CanonicalName>.VsVim.SetMode</CanonicalName>
<LocCanonicalName>.VsVim.SetMode</LocCanonicalName>
</Strings>
</Button>
</Buttons>

<!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
Expand Down Expand Up @@ -151,6 +161,7 @@
<IDSymbol name="cmdidVsVimToggleEnabled" value="0x0103" />
<IDSymbol name="cmdidVsVimSetEnabled" value="0x0104" />
<IDSymbol name="cmdidVsVimSetDisabled" value="0x0105" />
<IDSymbol name="cmdidVsVimSetMode" value="0x0106" />
</GuidSymbol>

<GuidSymbol name="guidImages" value="{b83708de-165c-4166-bc93-05f4efaebdf2}">
Expand Down
153 changes: 126 additions & 27 deletions Src/VsVim/VsVimPackage.cs
Expand Up @@ -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;
Expand Down Expand Up @@ -31,6 +32,7 @@ public sealed class VsVimPackage : Package, IOleCommandTarget
private IComponentModel _componentModel;
private ExportProvider _exportProvider;
private IVim _vim;
private IVsAdapter _vsAdapter;

public VsVimPackage()
{
Expand All @@ -43,6 +45,7 @@ protected override void Initialize()
_componentModel = (IComponentModel)GetService(typeof(SComponentModel));
_exportProvider = _componentModel.DefaultExportProvider;
_vim = _exportProvider.GetExportedValue<IVim>();
_vsAdapter = _exportProvider.GetExportedValue<IVsAdapter>();
}

protected override void Dispose(bool disposing)
Expand Down Expand Up @@ -73,6 +76,9 @@ private void DumpKeyboard()
{
keyBindingService.DumpKeyboard(streamWriter);
}

var message = string.Format("Keyboard data dumped to: {0}", filePath);
PrintToCommandWindow(message);
}

/// <summary>
Expand Down Expand Up @@ -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;
}

/// <summary>
/// Used to determine if the shell is querying for the parameter list of our command.
/// </summary>
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)
Expand Down
6 changes: 6 additions & 0 deletions Src/VsVimShared/IVsAdapter.cs
Expand Up @@ -117,6 +117,12 @@ public interface IVsAdapter

Result<ITextBuffer> GetTextBufferForDocCookie(uint cookie);

/// <summary>
/// Get the <see cref="IWpfTextView"/> which has focus.
/// </summary>
/// <returns></returns>
bool TryGetActiveTextView(out IWpfTextView textView);

/// <summary>
/// Open a file with the specified name in Visual Studio
/// </summary>
Expand Down
26 changes: 26 additions & 0 deletions Src/VsVimShared/Implementation/Misc/VsAdapter.cs
Expand Up @@ -461,6 +461,27 @@ private void OpenFile(string filePath)
}
}

private bool TryGetActiveTextView(out IWpfTextView textView)
{
var textManager = _serviceProvider.GetService<SVsTextManager, IVsTextManager2>();
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
Expand Down Expand Up @@ -563,6 +584,11 @@ void IVsAdapter.OpenFile(string filePath)
OpenFile(filePath);
}

bool IVsAdapter.TryGetActiveTextView(out IWpfTextView textView)
{
return TryGetActiveTextView(out textView);
}

#endregion
}
}

0 comments on commit a8e37ab

Please sign in to comment.