Using the Visual Studio SDK

In Chapter 15, “Extending the IDE,” we introduced the concept of VSIX projects and Visual Studio extensions. We discussed the fact that extensions are built by adding different project item types to a VSIX project. Code Editor extensions are built the same way: we add project items to a VSIX project. This means that code editor extensions also require the Visual Studio software development kit (SDK) to be installed.


Note

The Visual Studio SDK, generally speaking, is a collection of tools and project templates that help developers customize the IDE. The SDK is particularly germane to the topic of editor extensions because it ships with a set of project templates and code samples that help kick-start your extension development efforts.

The SDK download links for Visual Studio 2015 are located at the Visual Studio Extensibility center on MSDN: https://www.visualstudio.com/integrate/explore/explore-vside-vsi.


To review from Chapter 15: after downloading and installing the SDK, you see a new set of project templates—including the VSIX project type—available (under the Extensibility category) when you launch the New Project dialog box (see Figure 16.2).

Image

FIGURE 16.2 The VSIX Project template added by the Visual Studio SDK.

Once a VSIX project has been created, editor extensions are built by adding specific project items (which also live under the Extensibility category in the Add New Item dialog).

There are four MEF-centered project types: the Editor Classifier, Editor Margin, Editor Text Adornment, and Editor Viewport Adornment.

Based on their names and the prior list of extension points, you can get a good idea for the specific capabilities of each template. Seeing the extensions in action is as simple as running the template project; just as we saw with general extensions in Chapter 15, a new instance of Visual Studio launches with the extension running.

Keep in mind that extensions can be deployed by simply copying their binaries; they do not need to be deployed to the Global Assembly Cache (GAC) or otherwise registered. The default mechanism for deployment of extensions is the VSIX file. (To refresh your memory on what VSIX is, see Chapter 11, “Deploying Code.”) The VSIX file, when run, automatically creates the correct folder in the correct location and copies the extension binaries.

After an extension has been deployed in this fashion, you can manage via the Extensions and Updates window (located under the Tools menu), discussed later in this chapter.

Editor Classifier

The Editor Classifier template creates an MEF part that exports a classifier; this classifier handles syntax highlighting within the editor. This is implemented with a set of default classes that can be customized to implement your own classifier. By using this project as a starting point, you can direct the editor to recognize certain syntax and display the matching text in a certain way.


Note

As you examine the content generated by these extension projects, notice that the code generated for you maps directly back to the extension points previously discussed. For example, the Editor Classifier template relates directly back to the classification types/formats extension point.


The Editor Classifier project exports a new classifier type that the editor uses when it’s loaded in the IDE. The definition of the classifier (that is, the type of text that the classifier recognizes) is implemented in the EditorClassifier1 class. This class implements the IClassifier interface and has a GetClassificationSpans property that is responsible for recognizing a certain class of text. By default, this template recognizes any text, but you are free to tweak this code to parse out and match any sort of text pattern you want.


Note

All these MEF extension templates end up creating classes and types that are named after the project. By using the default EditorClassifier1 project name, you end up with classes such as EditorClassifier1, EditorClassifier1Form, and so on. If you have used a different name for your project, your type names should look different from those presented here.


Besides recognizing a class of text, a classifier is responsible for how that text is displayed within the editor. For instance, keywords are shaded a different color, comments are colored, and so on. All this work is performed within the EditorClassifier1Format class. The code generated for us sets the background color to blue violet and underlines the text, but you can change those details to whatever you want.

/// <summary>
/// Defines an editor format for the EditorClassifier1 type
/// that has a purple background
/// and is underlined.
/// </summary>internal sealed class EditorClassifier1Format
        : ClassificationFormatDefinition
    {
        /// <summary>
        /// Initializes a new instance of the
        /// <see cref="EditorClassifier1Format"/> class.
        /// </summary>
        public EditorClassifier1Format()
        {
            // human readable version of the name
            this.DisplayName = "EditorClassifier1";
            this.BackgroundColor = Colors.BlueViolet;
            this.TextDecorations =
                System.Windows.TextDecorations.Underline;
        }
    }

Editor Margin

The Editor Margin template creates a project that exports a margin displayed on one of the editor’s borders. The main class here is EditorMargin1, which derives from the WPF Canvas class and places a canvas with a child text box (with the text "Hello World!") at the bottom of the editor window (see Figure 16.3).

Image

FIGURE 16.3 A "Hello World" margin added to the bottom of the code editor.

Here is the code responsible for displaying the margin. Again, if you had a need to display meaningful data within a margin (code review comments? bug counts?), you could easily use the code here as a building block.

/// <summary>
/// Creates a <see cref="EditorMargin1"/> for a given
/// <see cref="IWpfTextView"/>.
/// </summary>
/// <param name="textView">The <see cref="IWpfTextView"/> to
/// attach the marginto.</param>
public EditorMargin1(IWpfTextView textView)
{
    _textView = textView;

    this.Height = 20;
    this.ClipToBounds = true;
    this.Background = new SolidColorBrush(Colors.LightGreen);

    //Add a green colored label that says "Hello World!"
    Label label = new Label();
    label.Background = new SolidColorBrush(Colors.LightGreen);
    label.Content = "Hello world!";
    this.Children.Add(label);

}

Editor Text Adornment

A text adornment is just what it sounds like: a graphical “markup” of text within the editor window. The Editor Text Adornment template creates a project that adorns every instance of the character A with a purple background and a red bordered box (see Figure 16.4). As with the editor margin project, you use WPF objects.

Image

FIGURE 16.4 Creating a text adornment in the editor.

Adornments reside on different “layers” within the editor. In this case, the adornment is implemented on the same layer that the text is rendered in, and that’s what makes this specifically a text adornment implementation. Note that this isn’t actually a case of the editor changing the font of those A’s to include the background decoration. The adorner uses WPF to paint a red block behind the text. This block is then synced to any movements of the text within the editor (by re-creating its visuals every time the layout of the editor window changes).

public TextAdornment1(IWpfTextView view)
{
    _view = view;
    _layer = view.GetAdornmentLayer("TextAdornment1");

    //Listen to any event that changes the layout (text changes, scrolling,
    //etc.)
    _view.LayoutChanged += OnLayoutChanged;

    //Create the pen and brush to color the box behind the A's
    Brush brush = new SolidColorBrush(Color.FromArgb(0x20, 0x00,
        0x00, 0xff));
    brush.Freeze();
    Brush penBrush = new SolidColorBrush(Colors.Red);
    penBrush.Freeze();
    Pen pen = new Pen(penBrush, 0.5);
    pen.Freeze();

    _brush = brush;
    _pen = pen;
}

Editor Viewport Adornment

The viewport adornment project is similar to the text adornment project, but it applies to a different layer of the editor (one where the text does not reside, which is a small but important distinction). Adorning the viewport in this fashion enables you to introduce visuals within the editor that aren’t tied to any particular piece of text and can float in front of or in back of the text layer.

The sample effect produced by this template places a simple purple box in the upper-right corner of the editor (see Figure 16.5).

Image

FIGURE 16.5 Example of Editor Viewport Adornment

Structurally, the code differs only slightly from the code for the text adornment sample. The adornment class (ViewportAdornment1) uses the same WPF brush objects to paint its adornment. The key difference is the visual layer used by the factory class (ViewportAdornment1Factory). To compare and contrast the two, first examine the previous project’s factory.

[Export(typeof(IWpfTextViewCreationListener))]
[ContentType("text")]
[TextViewRole(PredefinedTextViewRoles.Document)]
internal sealed class TextAdornment1Factory : IWpfTextViewCreationListener
{
    /// <summary>
    /// Defines the adornment layer for the adornment. This layer is ordered
    /// after the selection layer in the Z-order
    /// </summary>
    [Export(typeof(AdornmentLayerDefinition))]
    [Name("TextAdornment1")]
    [Order(After = PredefinedAdornmentLayers.Selection,
        Before = PredefinedAdornmentLayers.Text)]
    [TextViewRole(PredefinedTextViewRoles.Document)]
    public AdornmentLayerDefinition editorAdornmentLayer = null;

    /// <summary>
    /// Instantiates a TextAdornment1 manager when a textView is created.
    /// </summary>
    /// <param name="textView">The <see cref="IWpfTextView"/> upon which the
    /// adornment should be placed</param>
    public void TextViewCreated(IWpfTextView textView)
    {
        new TextAdornment1(textView);
    }
}

And now, here is the viewport adornment factory.

[Export(typeof(IWpfTextViewCreationListener))]
[ContentType("text")]
[TextViewRole(PredefinedTextViewRoles.Document)]
internal sealed class PurpleBoxAdornmentFactory : IWpfTextViewCreationListener
{
    /// <summary>
    /// Defines the adornment layer for the scarlet adornment. This layer is ordered
    /// after the selection layer in the Z-order
    /// </summary>
    [Export(typeof(AdornmentLayerDefinition))]
    [Name("ViewportAdornment1")]
    [Order(After = PredefinedAdornmentLayers.Caret)]
    [TextViewRole(PredefinedTextViewRoles.Document)]
    public AdornmentLayerDefinition editorAdornmentLayer = null;

    /// <summary>
    /// Instantiates a ViewportAdornment1 manager when a textView is created.
    /// </summary>
    /// <param name="textView">The <see cref="IWpfTextView"/> upon which the
    ///    adornment should be placed</param>
    public void TextViewCreated(IWpfTextView textView)
    {
        new ViewportAdornment1(textView);
    }
}

Note the different layer order defined by each via the Order attribute ([Order(After = PredefinedAdornmentLayers.Caret)] versus [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset