comment and we can answer the query, we will try and find a way to refactor the code so that the
answer is obvious or supply a comment explaining why the code is the way it is (on the rare
occasion that the check comment exposes an error, we fix it). Using this technique, our code
becomes more maintainable over time as it gets easier to read and understand. We have tools that
allow us to extract a list of these comments from the code base, but it would be really handy if we
could highlight them within the code editor itself. The Check Comment Margin Highlighter does
just that by adding a glyph in the margin on the left (where breakpoints normally appear) for any
line that contains a comment that contains the token check:.
The code comes in two parts: a tagger and a glyph factory. Here is the complete code listing for
the tagger:
c#
using System;
using System.Collections.Generic;
extending the editor .
1051
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
namespace CheckCommentHighlighter
{
class CheckCommentTag : IGlyphTag { }
class CheckCommentTagger : ITagger<CheckCommentTag>
{
private readonly IClassifier _classifier;
public CheckCommentTagger(IClassifier classifier)
{
_classifier = classifier;
}
public IEnumerable<ITagSpan<CheckCommentTag>> GetTags(
NormalizedSnapshotSpanCollection spans)
{
foreach (var span in spans)
{
foreach (var classification in
_classifier.GetClassificationSpans(span))
{
var isComment = classification.ClassificationType
.Classification
.ToLower()
.Contains("comment");
if (isComment)
{
var index = classification.Span.GetText()
.ToLower().IndexOf("check:");
if (index != -1)
{
var tag = new CheckCommentTag();
var snapshotSpan = new SnapshotSpan(
classification.Span.Start + index, 6);
yield return new TagSpan<CheckCommentTag>(
snapshotSpan,
tag);
}
}
}
}
}
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
1052 .
chaPter 53 mAnAged exTenSibiliTy FrAmeWork (meF)
}
[Export(typeof(ITaggerProvider))]
[TagType(typeof(CheckCommentTag))]
[ContentType("code")]
class CheckCommentTaggerProvider : ITaggerProvider
{
[Import]
private IClassifierAggregatorService AggregatorService;
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
if( buffer == null )
throw new ArgumentNullException("buffer");
var classifier = AggregatorService.GetClassifier(buffer);
return new CheckCommentTagger(classifier) as ITagger<T>;
}
}
}
Code snippet CheckCommentHighlighter\CheckCommentTagger.cs
Vb
Imports System.ComponentModel.Composition
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Tagging
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.Text.Classification
Imports Microsoft.VisualStudio.Utilities
Friend Class CheckCommentTag
Inherits IGlyphTag
End Class
Friend Class CheckCommentTagger
Implements ITagger(Of CheckCommentTag)
Private m_classifier As IClassifier
Friend Sub New(ByVal classifier As IClassifier)
m_classifier = classifier
End Sub
Private Function GetTags(ByVal spans As NormalizedSnapshotSpanCollection)
As IEnumerable(Of ITagSpan(Of CheckCommentTag))
extending the editor .
1053
Implements ITagger(Of CheckCommentTag).GetTags
Dim Tags As New List(Of ITagSpan(Of CheckCommentTag))
For Each span As SnapshotSpan In spans
For Each classification As ClassificationSpan In
m_classifier.GetClassificationSpans(span)
If classification.ClassificationType.Classification.ToLower()
.Contains("comment") Then
Dim index As Integer = classification.Span.GetText().ToLower()
.IndexOf("check:")
If index <> -1 Then
Dim snapshotSpan As New SnapshotSpan(classification.Span.Start
+ index, 6)
Dim tag As New CheckCommentTag
Tags.Add(New TagSpan(Of CheckCommentTag)(snapshotSpan, tag))
End If
End If
Next classification
Next span
Return Tags
End Function
Public Event TagsChanged As EventHandler(Of SnapshotSpanEventArgs)
Implements ITagger(Of CheckCommentTag).TagsChanged
End Class
<Export(GetType(ITaggerProvider)), ContentType("code"),
TagType(GetType(CheckCommentTag))>
Friend Class CheckCommentTaggerProvider
Implements ITaggerProvider
<Import()>
Friend AggregatorService As IClassifierAggregatorService
Public Function CreateTagger(Of T As ITag)(ByVal buffer As ITextBuffer)
As ITagger(Of T) Implements ITaggerProvider.CreateTagger
If buffer Is Nothing Then
Throw New ArgumentNullException("buffer")
End If
Dim Classifier = AggregatorService.GetClassifier(buffer)
Dim tagger As New CheckCommentTagger(Classifier)
Return TryCast(tagger, ITagger(Of T))
End Function
End Class
Code snippet CheckCommentHighlighter\CheckCommentTagger.vb
1054 .
chaPter 53 mAnAged exTenSibiliTy FrAmeWork (meF)
Three classes are defined here. The first is the CheckCommentTag class. It inherits from IGlyphTag
but has no implementation on its own. It is purely a marker that identifies when a particular span of
text should have this glyph applied. We could have supplied some properties on the tag class to pass
information to the glyph factory later that could be used to affect the type of UIElement displayed.
The second class is the CheckCommentTagger class. This class is responsible for identifying
spans of text that should have the CheckCommentTag applied. It does this by implementing the
ITagger<CheckCommentTag> interface. This interface consists of a method called GetTags and a
TagsChanged event. GetTags takes a collection of spans and returns a collection of ITagSpans. In
this implementation, it finds all of the comments with the help of a classifier and searches for the
string check:. If it finds this string, it creates a new TagSpan<CheckCommentTag> item, which it
applies to just the span of text that covers the check: string.
The final class is CheckCommentTaggerProvider, which contains the MEF export metadata that
Visual Studio is looking for in the extension. This class is exported using the ITaggerProvider
contract, which means that Visual Studio will add it to an internal list of tagger providers to be called
upon whenever taggers are required. Two other pieces of metadata are also attached to this class. The
TagTypeAttribute specifies the type of tags that will be produced by any taggers that this provider
creates. The ContentTypeAttribute supplies a filter on the kinds of content on which this tagger
provider should be used. In this case, the attribute specifies that this tagger provider should only
be called upon when the editor contains code, which is a common base content type provided by
the editor.
The tagger provider class also has an import requirement for an IClassifierAggregatorService.
This service is used in the construction of taggers, which occurs in the CreateTagger<T>
method. This method is passed an ITextbuffer for which it is to provide a tagger. It uses
the AggregatorService to retrieve a classifier and then uses the classifier to construct the
CheckCommentTagger defined in the previous code snippet.
This code is enough to allow Visual Studio to mark up check comments as requiring a glyph, but if
you deploy the extension as it is right now you won’t see anything because there are no components
offering to draw a CheckCommentTag. For that you need a glyph factory, which is the other half of
the extension. Here is the code:
c#
using System.ComponentModel.Composition;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
namespace CheckCommentHighlighter
{
class CheckCommentGlyphFactory : IGlyphFactory
{
public UIElement GenerateGlyph(IWpfTextViewLine line, IGlyphTag tag)
{
extending the editor .
1055
var validTag = tag as CheckCommentTag != null;
if (!validTag)
return null;
return new Polygon
{
Fill = Brushes.LightBlue,
Stroke = Brushes.DarkBlue,
StrokeThickness = 2,
Points = new PointCollection
{
new Point(0, 0),
new Point(16, 8),
new Point(0, 16)
}
};
}
}
[Export(typeof(IGlyphFactoryProvider))]
[TagType(typeof(CheckCommentTag))]
[Name("CheckCommentGlyph")]
[ContentType("code")]
[Order(After="VSTextMarker")]
class CheckCommentGlyphFactoryProvider : IGlyphFactoryProvider
{
public IGlyphFactory GetGlyphFactory(IWpfTextView view,
IWpfTextViewMargin margin)
{
return new CheckCommentGlyphFactory();
}
}
}
Code snippet CheckCommentHighlighter\CheckCommentGlyphFactory.cs
Vb
Imports System.ComponentModel.Composition
Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Shapes
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.Text.Formatting
Imports Microsoft.VisualStudio.Text.Tagging
Imports Microsoft.VisualStudio.Utilities
Friend Class CheckCommentGlyphFactory
Implements IGlyphFactory
1056 .
chaPter 53 mAnAged exTenSibiliTy FrAmeWork (meF)
Public Function GenerateGlyph(ByVal line As IWpfTextViewLine,
ByVal tag As IGlyphTag) As UIElement Implements IGlyphFactory.GenerateGlyph
If tag Is Nothing OrElse Not (TypeOf tag Is CheckCommentTag) Then
Return Nothing
End If
Dim triangle As New System.Windows.Shapes.Polygon()
With triangle
.Fill = Brushes.LightBlue
.Stroke = Brushes.DarkBlue
.StrokeThickness = 2
.Points = New PointCollection()
With .Points
.Add(New Point(0, 0))
.Add(New Point(16, 8))
.Add(New Point(0, 16))
End With
End With
Return triangle
End Function
End Class
<Export(GetType(IGlyphFactoryProvider)), Name("CheckCommentGlyph"),
Order(After:="VsTextMarker"), ContentType("code"),
TagType(GetType(CheckCommentTag))>
Friend NotInheritable Class TodoGlyphFactoryProvider
Implements IGlyphFactoryProvider
Public Function GetGlyphFactory(
ByVal view As Microsoft.VisualStudio.Text.Editor.IWpfTextView,
ByVal margin As Microsoft.VisualStudio.Text.Editor.IWpfTextViewMargin)
As Microsoft.VisualStudio.Text.Editor.IGlyphFactory
Implements Microsoft.VisualStudio.Text.Editor.IGlyphFactoryProvider.GetGlyphFactory
Return New CheckCommentGlyphFactory()
End Function
End Class
Code snippet CheckCommentHighlighter\CheckCommentGlyphFactory.vb
Just as with the code to expose the check comment tagger to Visual Studio, two classes are at
work here: one class that actually creates glyphs and another class that provides instances of this
glyph factory to Visual Studio on demand. The CheckCommentGlyphFactory is very simple. It just
checks to ensure that the tag is of the correct type and then creates the visual element that is to
be displayed. This can be any WPF UIElement. In this implementation, it is a light blue triangle
pointing to the right with a dark blue border.
The second class is the actual gateway into Visual Studio. It is exported using the
IGlyphFactoryProvider contract, associated with a specific tag and content type. It also
specifies a name that makes it easier to identify. Finally, it specifies that it should be drawn after
items in the “VSTextMarker” layer, which means it will appear to be on top of items in this
summary .
1057
layer. The actual implementation of this class is a simple factory method for instances of the
CheckCommentGlyphFactory
class.
If you run this extension it will start up in the
Experimental Instance of Visual Studio. Load a code file
and add a comment that starts with Check: and a blue
triangle appears in the margin to the left as fiGure 53-3
in Figure 53-3.
suMMary
The Managed Extensibility Framework simplifies the process of creating extensible applications by
allowing you to think of your application as a collection of composable parts, each of which exposes
exports and requires imports. Extensions can be added to your application by creating appropriate
catalogs of parts and providing them to your composition container. MEF is able to cover a much
wider range of capabilities than those covered in this chapter. Be sure to check out the MEF
Programming Guide on http://mef.codeplex.com for more information.
Visual Studio 2010 is able to create a highly extensible run time by taking advantage of MEF. It
watches extensions for known exported contracts, which it will use when composing the new WPF
Editor control, allowing you to easily extend its behavior. In addition to this, Visual Studio exports
a number of services on well-known contracts that you can import for use in your extensions. For
more information about the new Visual Studio Editor and how to extend it using MEF, consult the
Visual Studio 2010 Editor topic on MSDN, which contains many examples of extensions.
PART XII
Visual studio ultimate
. Visual Studio Ultimate for Architects
. Visual Studio Ultimate for Developers
. Visual Studio Ultimate for Testers
. Team Foundation Server
: chaPter 54
: chaPter 55
: chaPter 56
: chaPter 57
54 54
Visual studio Ultimate
for architects