Walkthrough: Displaying SmartTags
Smart tags are tags on text that expand to display a set of actions. For example, in a Visual Basic or Visual C# project, a red line appears under a word when you rename an identifier such as a variable name. When you move the pointer over the underline, a button is displayed near the pointer. If you click the button, a suggested action is displayed, for example, Rename IsRead to IsReady. If you click the action, all references to IsRead in the project are renamed IsReady.
Although smart tags are part of the IntelliSense implementation in the editor, you can implement smart tags by subclassing SmartTag, and then implementing the ITagger<T> interface and the IViewTaggerProvider interface.
Note
|
|---|
|
Other kinds of tags can be implemented in a similar manner. |
The following walkthrough shows how to create a smart tag that appears on the current word and has two suggested actions: Convert to upper case and Convert to lower case.
To complete this walkthrough, you must install the Visual Studio 2012 SDK.
Note
|
|---|
|
For more information about the Visual Studio SDK, see Extending Visual Studio Overview. To find out how to download the Visual Studio SDK, see Visual Studio Extensibility Developer Center on the MSDN Web site. |
To create a MEF project
-
Create an Editor Classifier project. Name the solution SmartTagTest.
-
Open the source.extension.vsixmanifest file in the VSIX Manifest Editor.
-
Make sure that the Content heading contains a MEF Component content type and that the Path is set to SmartTagTest.dll.
-
Save and close source.extension.vsixmanifest.
-
Add the following reference to the project, and set CopyLocal to false:
Microsoft.VisualStudio.Language.Intellisense
-
Delete the existing class files.
To implement a tagger for smart tags
-
Add a class file and name it TestSmartTag.
-
Add the following imports:
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Collections.ObjectModel; using System.Windows.Media; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities;
-
Add a class named TestSmartTag that inherits from SmartTag.
-
Add a constructor for this class that calls the base constructor with a SmartTagType of Factoid, which will cause a blue line to appear under the first character of a word. (If you use Ephemeral, a red line will appear under the last character of the word.)
-
Add a class named TestSmartTagger that inherits from ITagger<T> of type TestSmartTag, and implements IDisposable.
-
Add the following private fields to the tagger class.
-
Add a constructor that sets the private fields, and subscribes to the LayoutChanged event.
-
Implement GetTags so that the tag is created for the current word. (This method also calls a private method GetSmartTagActions that is explained later.)
public IEnumerable<ITagSpan<TestSmartTag>> GetTags(NormalizedSnapshotSpanCollection spans) { ITextSnapshot snapshot = m_buffer.CurrentSnapshot; if (snapshot.Length == 0) yield break; //don't do anything if the buffer is empty //set up the navigator ITextStructureNavigator navigator = m_provider.NavigatorService.GetTextStructureNavigator(m_buffer); foreach (var span in spans) { ITextCaret caret = m_view.Caret; SnapshotPoint point; if (caret.Position.BufferPosition > 0) point = caret.Position.BufferPosition - 1; else yield break; TextExtent extent = navigator.GetExtentOfWord(point); //don't display the tag if the extent has whitespace if (extent.IsSignificant) yield return new TagSpan<TestSmartTag>(extent.Span, new TestSmartTag(GetSmartTagActions(extent.Span))); else yield break; } }
-
Add a GetSmartTagActions method to set up the smart tag actions. The actions themselves are implemented in later steps.
private ReadOnlyCollection<SmartTagActionSet> GetSmartTagActions(SnapshotSpan span) { List<SmartTagActionSet> actionSetList = new List<SmartTagActionSet>(); List<ISmartTagAction> actionList = new List<ISmartTagAction>(); ITrackingSpan trackingSpan = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeInclusive); actionList.Add(new UpperCaseSmartTagAction(trackingSpan)); actionList.Add(new LowerCaseSmartTagAction(trackingSpan)); SmartTagActionSet actionSet = new SmartTagActionSet(actionList.AsReadOnly()); actionSetList.Add(actionSet); return actionSetList.AsReadOnly(); }
-
Declare the SmartTagsChanged event.
-
Implement the OnLayoutChanged event handler to raise the TagsChanged event, which causes GetTags to be called.
private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { ITextSnapshot snapshot = e.NewSnapshot; //don't do anything if this is just a change in case if (!snapshot.GetText().ToLower().Equals(e.OldSnapshot.GetText().ToLower())) { SnapshotSpan span = new SnapshotSpan(snapshot, new Span(0, snapshot.Length)); EventHandler<SnapshotSpanEventArgs> handler = this.TagsChanged; if (handler != null) { handler(this, new SnapshotSpanEventArgs(span)); } } }
-
Implement the Dispose method so that it unsubscribes from the LayoutChanged event.
To implement the smart tag tagger provider
-
Add a class named TestSmartTagTaggerProvider that inherits from IViewTaggerProvider. Export it with a ContentTypeAttribute of "text", a OrderAttribute of Before="default", and a TagTypeAttribute of SmartTag.
-
Import the ITextStructureNavigatorSelectorService as a property.
-
Implement the CreateTagger<T> method.
public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag { if (buffer == null || textView == null) { return null; } //make sure we are tagging only the top buffer if (buffer == textView.TextBuffer) { return new TestSmartTagger(buffer, textView, this) as ITagger<T>; } else return null; }
To implement smart tag actions
-
Create two classes, the first named UpperCaseSmartTagAction and the second named LowerCaseSmartTagAction. Both classes implement ISmartTagAction.
Both classes are alike except that one calls ToUpper and the other calls ToLower. The following steps cover only the uppercase action class, but you must implement both classes. Use the steps for implementing the uppercase action as a pattern for implementing the lowercase action.
-
Declare a set of private fields.
-
Add a constructor that sets the fields.
-
Implement the properties as follows.
-
Implement the Invoke method by replacing the text in the span with its uppercase equivalent.
To test this code, build the SmartTagTest solution and run it in the experimental instance.
To build and test the SmartTagTest solution
-
Build the solution.
-
When you run this project in the debugger, a second instance of Visual Studio is instantiated.
-
Create a text file and type some text.
A blue line should be displayed under the first letter of the first word of the text.
-
Move the pointer over the blue line.
A button should be displayed near the pointer.
-
When you click the button, two suggested actions should be displayed: Convert to upper case and Convert to lower case. If you click the first action, all the text in the current word should be converted to upper case. If you click the second action, all the text should be converted to lower case.
Note