Search code examples
c#visual-studio-extensions

How to create a tag on a specific line/column/length in Visual Studio editor?


I would like to create tags in the Visual Studio editor to insert all sorts of glyphs, adornments, text hightlightings, etc., based on line/column/length locations in the code.

I have been carefully reading the documentation walkthrough pages (https://learn.microsoft.com/en-us/visualstudio/extensibility/walkthrough-creating-a-margin-glyph?view=vs-2017 and related pages). Although a bit complex and hard to understand, it seems like the API is very much oriented on giving the means to analyse the code: it is able to give your code split into spans, with classifications, etc.

However, I have the "opposite" need: I already have the analysis done by my external analysis engine. And I already have a set of results to be displayed in the editor with line/column/length for each one. Like:

  • function "foo", located at line 345, column 1, length 3, and other fields containing information to be displayed,

  • variable "my_var", located at line 349, column 13, length 6, and other fields containing information to be displayed,

Is it possible to create tags in the Visual Studio editor directly based on their line/column/length location? Any hint, any pointer to more detailed documentation or tutorial would be greatly appreciated.


Solution

  • Lance's link was quite helpful to understand another way to create tags different from the MS documentation example.

    Indeed, I don't analyse the text contained into the spans, the analysis is already done outside. I get some list of "defects" locations.

    I get them into a defectsLocation dictionary (defectsLocation[filename][line] = location data (...)

    Here is was I did:

    internal class MyDefectTagger : ITagger<MyDefectTag>
    {
        private IClassifier m_classifier;
        private ITextBuffer m_buffer;
    
        internal MyDefectTagger(IClassifier classifier, ITextBuffer buffer)
        {
            m_classifier = classifier;
            m_buffer = buffer;
        }
    
        IEnumerable<ITagSpan<MyDefectTag>>
            ITagger<MyDefectTag>.GetTags(NormalizedSnapshotSpanCollection spans)
        {
            if (MyModel.Instance == null || MyModel.Instance.defectsLocation == null)
            {
                yield return null;
            }
    
            var filename = GetFileName(m_buffer);
    
            if (!MyModel.Instance.defectsLocation.ContainsKey(filename))
            {
                yield return null;
            }
    
            foreach (SnapshotSpan span in spans)
            {
                ITextSnapshot textSnapshot = span.Snapshot;
    
                foreach (ITextSnapshotLine textSnapshotLine in textSnapshot.Lines)
                {
                    var line = textSnapshotLine.LineNumber + 1; // Lines start at 1 in VS Editor
    
                    if (MyModel.Instance.defectsLocation[filename].ContainsKey(line) &&
                        !MyModel.Instance.defectsLocation[filename][line].rendered)
                    {
                        var rendered = MyModel.Instance.defectsLocation[filename][line].rendered;
                        yield return new TagSpan<MyDefectTag>(
                            new SnapshotSpan(textSnapshotLine.Start, 0),
                            new MyDefectTag()
                        );
                    }
                }
            }
        }
    }