Visual Studio 2017 Extension development tutorial part 6: Follow a span of code with TrackingSpan

Tutorial Table of Contents:

Part 1: Introduction to VS extensibility
Part 2: Add Menu Item
Part 3: Add command to context menu and get selected code
Part 4: Show a popup Window
Part 5: Highlight code in Editor

During the tutorial we are going to build a VS extensions called CodyDocs and place it on GitHub. Each tutorial part is a standalone tutorial on a specific topic and can be viewed individually. CodyDocs will save code documentation (code comments) in a separate file and the extension will allow to view and edit the documentation in the editor itself.

Part 6: Follow a span of code with TrackingSpan

In this tutorial we’re going to learn how to follow (track) code range (aka Span) in Visual Studio’s code editor. By tracking code span, I mean being able to follow the location of a specific code span, while the user edits the document.

For example, in our case, say we added documentation to a certain method MyMethod, which was in row 20 and column 1-12. This documentation was serialized to a file. Now, the user added some code and MyMethod is no longer in row 20. It’s now in row 30.

We’ll see how to “mark” the initial code span in row 20 and find out the new location when the document changes.

ITrackingSpan

To track a span of code, we will need an ITrackingSpan object. To create this object, we need to use ITextBuffer.

The text in each document of the editor is represented by an ITextBuffer object . A specific version of the text is held in a Snapshot object, which can be obtained with TextBugger.CurrentSnapshot. A Snapshot is immutable, so for each change in the text, a new Snapshot is created.

With the Snapshaot, we can create an ITrackingSpan object with the ITextSnapshot.CreateTrackingSpan(Span, SpanTrackingMode) method. Once created, an ITrackingSpan will “follow” the span. We can call the ITrackibngSpan.GetSpan method, which will return the new Span location.

How we can use TrackingSpan in the CodyDocs extension

In the previous tutorial, we saw how to highlight code in the editor. Our extension CodyDocs is now saving documentation to a file with a special extension (.cs.cdocs) and also shows in the editor the documented code spans by highlighting them. The problem is that once we edit the document, the highlight is no longer on top of the correct text span:

To solve this issue, instead of relying on the initial location of the documented text, we will create Tracking Spans when the document is first opened. We will then set the highlight positions according to the Tracking Spans. When the document is saved, we will need to serialize the new locations to the documentation .cs.cdocs file.

Register to Document Saved event

Before showing how to use Tracking Spans, we need to register to document saved event. This is fairly easy:

Now, whenever a document is saved, we will publish a “DocumentSaved” event with EventAggregator.

According to MSDN (and my personal experience implementing this), we need to save strong references to _events and _documentEvents or they will be garbage collected.

Using Tracking Spans

We are now ready to change out highlighting mechanism to use Tracking Spans. In the previous tutorial, we implemented ITagger to return a list Tags. Each Tag is attached to a span of code that needs to be highlighted.

Let’s look at the new code that uses Tacking Spans:

That’s a lot of code, but we’ll go over each part with explanation. Also, to follow more easily, you can open  this picture of the code difference between before and after using Tracking Spans.

Here is the code above, broken into parts:

CreateTrackingSpans

First, we deserialize the document from the file. The documentation data is divided into Fragments, with each Fragment holding the code span being serialized and the documentation text (the comment).

We use CreateTrackingSpan to capture the initial code span. Now we will be able to follow that code span as the user edits the code.

_trackingSpans holds all the documentation data of this document. The Key, which is an ITrackingSpan, will contain the Span and the Value is the documentation itself.

OnDocumentSaved

OnDocumentSaved is the event handler called when the user saves a document. Here we will serialize to documentation file the new Spans (considering they might be changed  after edit).

RemoveEmptyTrackingSpans is necessary, since the user might have deleted the documented code span entirely . This will give the Tracking Span length of 0.

CreateFileDocumentationFromTrackingSpans is the main method here. We go over all the Tracking Spans and use GetStartPoint and GetEndPoint to find out the Span for current Snapshot. We also use GetText to find out the current Text captured in the Tracking Span. This text might have been changed during edit.

GetTags

GetTags is the method that indicates which code parts need to be highlighted. We use our Tracking Spans to find out the current Spans with the GetSpan method.

The Result

Now as we edit the document, the Tracking Span makes sure our highlighting stays on the correct span. Even as we edit the highlighted span itself:

 

Summary

We saw how to use Tracking Span to track a span of code as the document is being edited. Tracking Span offers a a very useful functionality that is often needed.

CodyDocs is going along nicely but we still have a long way to go until it’s a finished product. In the next tutorial, we’ll see learn about Adorners in the editor. In our case, we will add a small button, right after the documented text span. On click, the button will open a dialog to view and edit the documentation.

The code is available on GitHub. To checkout to the extension code right after this tutorial part, use the Tag Part6:

 

Leave a Reply