Search code examples
c#.netvisual-studiovisual-studio-extensions

Why is this drawing Adornments twice? (Visual Studio Extension)


I was creating an extension that draws adornments around certain Tokens and came up with the code below. It works fine as long as the code is on one line. As soon as I add a new line then the adornments gets drawn repeatedly on top of existing adornments on each key press or scroll (LayoutChanged event).

What might I be doing wrong? Also, I came up with the code below on myself so there may be something fundamentally wrong.

private void CreateVisuals(ITextViewLine line)
{
    var textViewLines = _wpfTextView.TextViewLines;
    var text = line.Snapshot.GetText();
    //var text = textViewLines.FormattedSpan.Snapshot.GetText();
    var todoRegex = new Regex(@"\/\/\s*TODO\b");
    var match = todoRegex.Match(text);
    while (match.Success)
    {
        var matchStart = match.Index;
        var span = new SnapshotSpan(_wpfTextView.TextSnapshot, Span.FromBounds(matchStart, matchStart + match.Length));
        DrawAdornment(textViewLines, span);
        match = match.NextMatch();
    }
}

Solution

  • So if you're adding adornments during LayoutChanged, you need to only add adornments for the text that is being laid out. You can get the list of lines being relaid out from TextViewLayoutChangedEventArgs.NewOrReformattedLines. If you create a new text adornment via the VSSDK templates you'll see how that is done.

    The LayoutChanged event is raised any time the editor needs to layout some lines, but it doesn't always have to lay out all lines. For example, when scrolling, it only has to compute the layout of lines being scrolled into view. If you insert/delete lines, the other lines being shifted up or down don't have to be relaid out either.

    (And unrelated to your question but still important: it's terribly slow to call ITextSnapshot.GetText(). If you have a multi-megabyte file open, this will require us to allocate a single string which will be terribly slow. In practice, GetText() should really only be used for debugging. If you're looking for TODO comments, and you're OK restricting this to C# or VB code in Visual Studio 2015, you really should look at Roslyn which gives you access to the syntax tree information.)