Question

I have created a Visual Studio extension that provider syntax highlighting by implementing IClassifierProvider. I would like to add additional features such as support for the standard Edit.CommentSelection and Edit.FormatDocument commands, but I have no idea how to do that. All the documentation I can find is about adding new commands, but the commands I want to handle already exist.

How can I handle these commands?

Was it helpful?

Solution

I considering the specific Comment Selection and Uncomment Selection commands you refer to as special cases, because I'm working on a Commenter Service specifically intended to support these two actions. The service is being developed on GitHub and will be released via NuGet when it is ready. I'll start with a description of this service, and follow with some general information about implementing support for specific commands, including the Format Document command.

I would like to release the library and its dependencies this week, but the restriction that the Commenter Interfaces assembly be an immutable assembly demands more testing than is generally given to a library prior to its initial release. Fortunately the only thing in this particular assembly is two interfaces is the Tvl.VisualStudio.Text.Commenter.Interfaces namespace.

Using the Commenter Service

Source: Commenter Service (Tunnel Vision Labs' Base Extensions Library for Visual Studio)

This services allows extension developers to easily support the Comment and Uncomment commands for new languages in Visual Studio.

Providing a Standard Commenter

The easiest way to provide commenting features is to use the standard Commenter implementation of the ICommenter interface. The following steps show how to create an instance of Commenter and provide it to the Commenter Service by exporting an instance of ICommenterProvider.

  1. Create a new class derived from ICommenterProvider. This class is exported using the MEF ExportAttribute for one or more specific content types using the ContentTypeAttribute. The commenter in the example supports C++-style line and block comments, for the SimpleC content type.

    [Export(typeof(ICommenterProvider))]
    [ContentType("SimpleC")]
    public sealed class SimpleCCommenterProvider : ICommenterProvider
    {
      public ICommenter GetCommenter(ITextView textView)
      {
        // TODO: provide a commenter 
        throw new NotImplementedException();
      }
    }
    
  2. Define the comment format(s) the commenter will support.

    private static readonly LineCommentFormat LineCommentFormat =
      new LineCommentFormat("//");
    private static readonly BlockCommentFormat BlockCommentFormat =
      new BlockCommentFormat("/*", "*/");
    
  3. Implement the GetCommenter(ITextView) method by returning an instance of Commenter. The ITextUndoHistoryRegistry service is imported in order for Commenter to correctly support the Undo and Redo commands. The following code is the complete implementation of ICommenterProvider required to support the Comment and Uncomment commands for a simple language.

    [Export(typeof(ICommenterProvider))]
    [ContentType("SimpleC")]
    public sealed class SimpleCCommenterProvider : ICommenterProvider
    {
      private static readonly LineCommentFormat LineCommentFormat =
        new LineCommentFormat("//");
      private static readonly BlockCommentFormat BlockCommentFormat =
        new BlockCommentFormat("/*", "*/");
    
      [Import]
      private ITextUndoHistoryRegistry TextUndoHistoryRegistry
      {
        get;
        set;
      }
    
      public ICommenter GetCommenter(ITextView textView)
      {
        Func<Commenter> factory =
          () => new Commenter(textView, TextUndoHistoryRegistry, LineCommentFormat, BlockCommentFormat);
        return textView.Properties.GetOrCreateSingletonProperty<Commenter>(factory);
      }
    }
    

Command Handling in Visual Studio

The following are general steps for handling commands in Visual Studio. Keep in mind that the implementation details are quite complicated; I've created some abstract base classes to simplify specific implementations. After this overview, I will point to both those and a concrete example of their use for you to reference.

  1. Create a class which implements IOleCommandTarget. The QueryStatus method should check for the specific commands handled by your command target and return the appropriate status flags. The Exec method should be implemented to execute the commands.
  2. Register the command target with a specific text view by calling IVsTextView.AddCommandFilter. If you are working with an MEF-based extension, you can obtain the IVsTextView by either exporting an instance of IVsTextViewCreationListener, or by importing the IVsEditorAdaptersFactoryService component and using the GetViewAdapter method to obtain an IVsTextView from an instance of ITextView.

Here are some specific implementations of the interfaces described here:

  1. CommandFilter: This class implements the basic requirements for IOleCommandTarget
  2. TextViewCommandFilter: This class implements additional functionality to simplify the attachment of a command filter to a text view
  3. CommenterFilter: This class is a concrete implementation of a command filter used by the Commenter Service implementation to handle the Comment Selection and Uncomment Selection commands
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top