The Visual Studio 2010 Editor looks for a number of contracts, which it uses to extend the editor
behavior at run time. Usually you need to create at least two classes for each type of extension
that you are exposing. One class will perform the work of the extension and the other will
typically be imported by Visual Studio and asked to provide instances of your main extension
class when required.
Content Types
Each ITextBuffer is assigned a content type when it is created that identifies the type of text it
contains. Examples of content types include Text, Code, CSharp, or Basic. Content types are used
as filters for the various editor extensions that you can create by adding a ContentTypeAttribute
1046 . chaPter 53 mAnAged exTenSibiliTy FrAmeWork (meF)
to the exported extension. An example would be an IntelliSense provider that is only valid for
Xml content.
Even though content type is assigned to an ITextBuffer when it is created, it
can be changed by calling the ChangeContentType method.
You can create your own content types by exporting a property or fi eld with the
ContentTypeDefinition contract. Each content type can have multiple parent content types, which
are defi ned by adding a BaseDefinitionAttribute to the exported content type for each parent
type. To get a full list of content types you can import the IContentTypeRegistryService , which
maintains a list of registered content types.
A content type can be associated with a fi le extension using a
FileExtensionAttribute. Note that the fi le extension must be one that has been
registered with Visual Studio already. Search for “ProvideLanguageExtension
Attribute Class” on MSDN for more information on how to do this.
Classifi cation Types and formats
A classifi cation type is metadata that can be applied to any span of text. Some examples of
classifi cation types include “ keyword ” or “ comment, ” both of which inherit from the classifi cation
type “ code. ” You can create your own classifi cation types by exporting a property or fi eld of the
ClassificationTypeDefinition class. This allows you to attach custom behavior to the text.
Classifi cation types are not the same thing as content types. Each ITextBuffer
has a single content type but may contain spans of text that have many different
classifi cations.
Classifi cation types are attached to spans of text using an IClassifier instance. A classifi er
aggregator collects the classifi cations from a number of different classifi ers for a text buffer
and creates a unique non - overlapping set of classifi cations from that buffer. In effect, a classifi er
aggregator is a classifi er itself because it also provides classifi cations for a span of text. To
get the classifi er aggregator for a particular ITextBuffer instance of text you can import the
IClassificationAggregatorService and call its GetClassifier method, passing in
the text buffer.
You can defi ne a format for a specifi c classifi cation type by deriving a new class from
ClassificationFormatDefinition and exporting it with an EditorFormatDefinition
contract. The base class contains a number of properties that you can use to change the way
extending the editor .
1047
text is rendered. You associate the format definition with the classification type by using the
ClassificationTypeAttribute on the exported class. This attribute accepts a string that
is a comma-separated list of classification types that the format applies to. You can also use
DisplayNameAttribute and UserVisibleAttribute to show this classification format in the Fonts
and Settings page of the Options dialog. You can also specify a PriorityAttribute, which will
help to determine when the format is applied.
Margins
A margin is a piece of UI around the edges of the main editor window. There are four predefined
margins names: Top, Bottom, Left, and Right, which act as containers for other margins that you
can define. You could define a margin that turns red when a generated file is opened to warn the
user that they should not edit the file.
To create a margin, you need to make a new class that implements IWpfTextViewMargin, which
contains properties for the margin size as well as for the actual UIElement that draws the margin
on the screen. To register your margin with Visual Studio, you need to export a class with the
IWpfTextViewMarginProvider contract. This interface contains a single method that should return
an instance of your IWpfTextViewMargin. In addition to the MEF export, the margin provider can
also provide the following:
.
A NameAttribute, which is used to give the provider a human-readable name.
.
A ContentTypeAttribute, which identifies the content type that the margin should be
made available for.
.
An OrderAttribute and a MarginContainerAttribute, which are both used to determine
where the margin should be drawn. The order is specified by supplying the string name of
another margin to run either Before or After. The container identifies which border the
margin should be displayed against (top, bottom, left, or right).
Tags
A tag is a piece of metadata that is applied to a specific span of text. Examples of tags include
SquiggleTag, TextMarkerTag, and OutliningRegionTag. Tags are associated with spans of text
using instances of the ITagger interface. To register an ITagger, you need to create a class that
implements the ITaggerProvider interface, override the CreateTagger method, and then export
the new class with the ITaggerProvider contract. Your tagger provider should also be marked up
with the TagTypeAttribute, which identifies the type of tag its taggers will produce.
Classification is a special case of tagging provided by a ClassifactionTag.
adornments
An adornment is a special effect that can be applied to a span of text or to the editor surface itself.
You can define your own adornments, which are just standard WPF UIElements. Each type of
adornment gets rendered in a separate layer so that different adornment types don’t interfere with
1048 .
chaPter 53 mAnAged exTenSibiliTy FrAmeWork (meF)
each other. To specify a layer on which your adornment belongs, your adornment class should
export an AdornmentLayerDefinition along with a NameAttribute and an OrderAttribute.
The Order can be defined as Before or After one of four built-in adornment layers: Selection,
Outlining, Caret, and Text. When the adornment wants to display itself, it can request an instance
of the IAdornmentLayer from the IWpfTextView by name. This interface exposes methods to add
UIElements to the layer and clear all adornments out of the layer.
To create your adornment, you need to export a class with the IWpfTextViewCreationListener
contract. This class should simply instantiate a new adornment whenever a text view is created. It is
up to the adornment to wire itself up to events that it needs to use to redraw its contents. This class
can be marked up with a standard ContentTypeAttribute to filter the content types on which it
will appear. It can also include a TextViewRoleAttribute that defines for which kind of text view
it should appear. The PredefinedTextViewRoles contains a list of valid values.
Mouse Processors
Mouse processors are able to capture events from the mouse. Each mouse processor should
derive from MouseProcessorBase and override the event handlers that they want to
handle. To expose your mouse processor to Visual Studio, you must export a class under the
IMouseProcessorProvider contract. You also need to apply a ContentTypeAttribute to identify
the types of content for which the mouse processor is available.
Drop Handlers
Drop handlers customize the behavior of the Visual Studio editor when content is dropped into it.
Each drop handler should implement IDropHandler and you will need an IDropHandlerProvider
to provide your drop handler to Visual Studio. This provider class should export the
IDropHandlerProvider contract along with the following metadata:
.
A NameAttribute to identify your drop handler.
.
A DropFormatAttribute, which specifies the format of text for which this handler is valid.
Twenty-three built-in formats are supported, which are all handled in a specific order.
Check the MSDN documentation for the full list.
.
An OrderAttribute, which identifies where in the order of drop handlers this handler
should execute. You do this by providing Before and After components of the Order. Each
component is just a text name for the handler. The default drop handler provided by Visual
Studio is called DefaultFileDropHandler.
editor options
Editor options allow your extensions to expose settings. These settings can be imported into other
components of the system and used to alter their behavior. This type of export is used to expose the
value of your option to other components, but Visual Studio does nothing to expose these options to
the user. If you want the user to be able to manage these options, you need to create your own UI.
To create a new editor option, you must derive from one of the three abstract base classes
EditorOptionDefinition<T>, ViewOptionDefinition<T>, or WpfViewOptionDefintion<T> and
extending the editor .
1049
specify the type of the option value being created (that is, Boolean or String). These base classes
provide abstract properties for you to implement containing information about the option, including
its current value and its default value. To make the editor option available to Visual Studio, you
should export it with the EditorOptionDefinition
contract.
intellisense
IntelliSense is a term that is used to describe a set of features that provide contextual information
and statement completion services. No matter what type of IntelliSense extension you are providing,
the components and the process are always the same:
.
A broker controls the overall process.
.
A session represents the sequence of events, which typically start with a user gesture triggering
the presenter and end with the committal or cancellation of the selection.
.
A controller determines when a session should begin and end. It also decides the manner in
which the session ends.
.
A source provides content for the IntelliSense session and determines the best match for
display.
.
A presenter is responsible for displaying the content of a session.
It is recommended that you provide at least a source and a controller when defining IntelliSense
extensions. You should only provide a presenter if you want to customize the display of your
feature.
To provide an IntelliSense source, you need to create a class that implements one (or more)
of these interfaces: ICompletionSource, IQuickInfoSource, ISignatureHelpSource, or
ISmartTagSource. Each of these interfaces defines methods that provide you with the context for
the session and allow you to return the information that will be displayed.
For each of the interfaces implemented you need another class that implements the
corresponding provider interface: ICompletionSourceProvider, IQuickInfoSourceProvider,
ISignatureHelpSourceProvider, or ISmartTagSourceProvider. This provider class must be
exported using its provider interface as a contract. In addition to the export, you can specify a
NameAttribute, an OrderAttribute, and a ContentTypeAttribute.
To provide an IntelliSense controller, you need a class that implements
IIntellisenseController. This interface provides methods for the controller to attach and
detach ITextBuffers. When the controller senses an event that should begin an IntelliSense
session, it requests one from the correct type of broker: ICompletionBroker, IQuickInfoBroker,
ISignatureHelpBroker, or ISmartTagBroker. The easiest way to get access to a broker is to
import one into the controller provider (defined next) and pass it into the constructor of the
IntelliSense controller.
Finally, you need an IIntellisenseControllerProvider that is exported along with a
NameAttribute, an OrderAttribute, and a ContentTypeAttribute.
1050 .
chaPter 53 mAnAged exTenSibiliTy FrAmeWork (meF)
editor services
Visual Studio exposes a large number of editor services under well-known contracts that you can
import into your extension classes. Here are a few common ones (see the MSDN documentation for
a complete list):
.
IContentTypeRegistryService manages the collection of content types that are available
to be assigned to ITextBuffers. This service allows you add and remove content types, as
well as query the currently registered content types.
.
ITextDocumentFactoryService provides the ability to create new documents and load
existing documents from the file system. It also has events for when ITextDocuments are
created and disposed.
.
IClassifierAggregatorService contains only a single method, GetClassifier, that
returns a classifier for a given ITextBuffer. It will create and cache classifiers if they don’t
already exist.
.
ITextSearchService is responsible for locating specific text within a defined region of
text. It has methods to find all instances or just find the next instance.
.
IWpfKeyboardTrackingService allows you to switch the keyboard tracking over to WPF
in the editor. Normally Visual Studio performs its own keyboard tracking, so if you are
using WPF controls that listen for keyboard events they will never be detected. This service
allows you toggle the ability for WPF to have the first shot at handling keyboard events.
Keyboard events that are left unhandled by WPF will be passed to Visual Studio and
handled as normal.
the check comment highlighter extension
This section shows the complete source code for a sample extension with explanations along the
way. In our office, whenever we come across something that doesn’t seem to be quite right we attach
a comment asking for an explanation using the special token check: followed by a few sentences
going into what aspect we think is wrong. Normally, if we encounter a piece of code with a check