Visual Studio customization with lightweight extensions and Visual Commander

Visual Studio is a very capable and mature IDE, plus there are 5,942 items in Visual Studio Gallery from 3rd parties, extending functionality even further in all possible directions. Still, if it is not written by you it can’t be perfect šŸ™‚

The hardest part when writing a Visual Studio extension is to find the right interface for the task. In this article I will show most “popular” interfaces to help you get started with Visual Studio customization using code. When there is no public API to perform your task, I’ll show possible workarounds.

All samples below are available as commands and extensions for Visual Commander. You can use this code as part of your own extension or you can run it directly with Visual Commander. Generally, for relatively simple functionality Visual Commander helps you write, run, customize and share VS extension code faster than with a full VSIX package. Most of the code will also run unmodified for all Visual Studio versions from VS 2010 to VS 2015 (when creating a full VSIX package you should carefully package it to provide compatibility with different versions of Visual Studio). Visual Commander also provides the common VCmd menu to run and organize multiple commands and extensions.

The most basic task for an extension is to modify selected text in the code editor. Block comment and Insert date and time commands demonstrate access to the current selection as string:

EnvDTE.TextSelection ts = DTE.ActiveDocument.Selection as EnvDTE.TextSelection;
ts.Text = "/* " + ts.Text + " */";

You can run existing Visual Studio commands from your code. They are easier to discover than APIs and easier to invoke. See for example Close the current document tab and activate next:

if (IsCommandAvailable("Window.NextTab"))
{
	EnvDTE.Window w = DTE.ActiveWindow;
	DTE.ExecuteCommand("Window.NextTab");
	w.Close();
}
else if (IsCommandAvailable("File.Close"))
	DTE.ExecuteCommand("File.Close");

Another very common requirement is to perform some task automatically on a Visual Studio event. See the list of Common Visual Studio environment events and see Open a file from the solution directory on opening a solution, Run Cppcheck on the saved file samples:

public void SetSite(EnvDTE80.DTE2 DTE_, Microsoft.VisualStudio.Shell.Package package)
{
	events = DTE.Events;
	documentEvents = events.DocumentEvents;
	documentEvents.DocumentSaved += OnDocumentSaved;
}

private void OnDocumentSaved(EnvDTE.Document doc)
{
	if(doc.Language == "C/C++")
		RunCppcheck(doc.FullName);
}

You can create custom commands to programmatically setup different Visual Studio options. For example, Toggle line numbers for C# files and Set font size:

dim v = DTE.Properties("TextEditor", "CSharp").Item("ShowLineNumbers").Value
DTE.Properties("TextEditor", "CSharp").Item("ShowLineNumbers").Value = Not v

To analyze code, you can use Visual Studio code model, for example Move the caret to the beginning of the containing function, Copy to the clipboard properties of the selected class in Visual Studio text editor and Copy current file, line, method. Alternatively, you can use Roslyn, for example Create a typed variable from the current method invocation:

Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax invocationExpressionNode = 
	document.GetSyntaxRootAsync().Result.FindToken(caretPosition).Parent.AncestorsAndSelf().
	OfType<Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax>().FirstOrDefault();
if (invocationExpressionNode != null)
{
	Microsoft.CodeAnalysis.SemanticModel semanticModel = 
		document.GetSemanticModelAsync().Result;
	Microsoft.CodeAnalysis.IMethodSymbol methodSymbol = 
		semanticModel.GetSymbolInfo(invocationExpressionNode).Symbol as 
			Microsoft.CodeAnalysis.IMethodSymbol;
	textView.TextBuffer.Insert(invocationExpressionNode.SpanStart, 
		methodSymbol.ReturnType.ToString() + " v = ");
}

Visual Studio is a .NET, Windows, WPF application and your code runs as a part of it. If there is no public API for your needs, you can directly enumerate and modify WPF UI controls (Hide Sign in and Feedback smiley buttons in Visual Studio 2013/2015), subscribe to Application events (AutoSave files when Visual Studio loses focus), modify the MainWindow (Add version overlay to the Visual Studio 2015 taskbar icon) and simulate keyboard input with SendKeys (Toggle CodeLens on/off). As a last resort you can use .NET reflection to access internal Visual Studio assemblies.

I hope you are now feeling empowered to spend some time and add a feature to Visual Studio that you always wanted. Don’t hesitate to share your code and ask questions in comments.

This entry was posted in Vlasov Studio tools, Visual Studio tips and tagged . Bookmark the permalink.

2 Responses to Visual Studio customization with lightweight extensions and Visual Commander

  1. Denis says:

    Hi Sergey. Thanks for your great tool. My question – is it possible to define some public function that can be used in several commands? I want to write a method that returns the current task description(and that will be often changing) as string and use this text in different commands

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s