Building Visual Studio Extensions with Roslyn
Yesterday we talked about the Roslyn Compiler and Workspace APIs. Today we take a look at the Roslyn Service APIs and how they can be used to extend Visual Studio. The extensions we will look at today are Code Issue, Code Refactoring, Completion Provider, and Outliner.
Like all modern Visual Studio extensions, Service APIs are registered using MEF. This means developers simply need to implement certain interfaces and include matching MEF-style attributes, a much welcomed change from previous versions of Visual Studio which required code signing and COM registration.
The Code Issue extension allows developers to write their own compiler warnings and errors. The sample project included with the CTP shows compiler warnings wherever the letter ‘a’ appears in the syntax tree. As you can see from the images below, they integrate quite nicely into Visual Studio’s overall workflow.
The ICodeIssueProvider interface is quite simple, consisting only of three variants of the GetIssues method. Each variant receives an IDocument containing all of the information about the file being processed including the raw text, the syntax tree, the semantic model, and a back-reference to the containing project. A cancellation token is also included in case the IDE needs to abort analysis, perhaps because the user edited a file.
The three overloads also accept one of the three types of syntax: a node, a token, or a trivia. Most of the analysis work will probably be done at the node level. Trivia represents information not needed by the compiler such as whitespace and comments and tokens lack the context to be informative. Nodes, on the other hand, represent everything from the top level namespace declaration down to the smallest expression.
When errors are detected they can be returned to the IDE via an enumeration of CodeIssue. A CodeIssue consists of a severity level (Information, Warning, or Error), a Span object indicating where the error is located, and a description of the error.
Code Issues may also include one or more ICodeAction objects. These objects allow developers to provide auto-correct options much like what is seen in the below image.
Building an ICodeAction and matching ICodeActionEdit is significantly more difficult than creating code issues. One needs to learn how to edit syntax trees and publish the updates via the IWorkstation interface. The Roslyn site includes a walk-through for writing Quick Fixes.
The support for Code Refactoring looks a lot like the “quick fix” support correction for Code Issues, but it is applied at the text level. The ICodeRefactoringProvider is supplied with a document and TextSpan and is expected to return a CodeRefactoring object. This object simple contains a collection of ICodeAction objects just a like the CodeIssue object discussed above.
The project template for code refactoring doesn’t include a usable demo, but the same techniques shown in the Quick Fix walkthrough can be used here.
The ICompletionProvider interface has a single method called GetItems. This accepts an IDocument and a position parameter represented as an integer. From this an enumeration of CompletionItem is returned. Each CompletionItem requires the text to be displayed. Developers may also include an icon, description, and/or alternate text to be inserted. (Presumably the insertionText, if not supplied, defaults to the display text.)
While nowhere near as useful as the other providers, one could still do interesting tricks like build templates that are too complex for the normal code snippet infrastructure.
The final project template is the Syntax Outliner, exposed via the ISyntaxOutliner interface. This is used to create collapsible outlines in the text editor much like we have for regions, classes, and methods. The interface receives a Syntax node and is expected to return an enumeration of OutliningSpan objects, each of which has a TextSpan to encompass, HintSpan (for mouse-over text), banner text, and the option to AutoCollapse.