Django Editor in VS 2010 – Part 3 (Squiggles)

Look here for complete source

Squiggles are wavy lines marking problematic areas. I would like to use them to indicate that there is a problem with certain django construct - tag and/or keyword. It also would be nice to give more specific diagnostic about what's wrong. For this purpose I will use the SquiggleTag class.

Creating a squiggle using SquiggleTag is very similar to working with classifiers described in Part 1 of the series. As with the classifiers it takes 3 steps:

  • create tag definition
  • create tagger
  • create tagger provider

Because it is so similar, I think it is better just to show you the code and then give some brief explanation.

Error Tag definition:

        internal class ErrorTag : SquiggleTag
        {
            public ErrorTag()
                : base("error") { }
        }

The lonely parameter of the SquiggleTag constructor controls the squiggle color. The SquiggleTag also has a property ToolTip and initially I thought of using it to show the diagnostic message, but it appears that this property is broken in Beta 1, so I decided to go the same route the VsEditor team is going and use the quick info for this purpose. I plan to cover quick info in the next post. Among other things using quick info gives you more flexibility - i.e. you can show in ToolTip both the error message as well as regular tips.

Now the provider:

    [Export(typeof(ITaggerProvider))]
    [ContentType(Constants.NDJANGO)]
    [TagType(typeof(SquiggleTag))]
    class TaggerProvider : ITaggerProvider
    {
        [Import]
        internal INodeProviderBroker nodeProviderBroker { get; set; }
 
        public ITagger<T> CreateTagger<T>(ITextBuffer buffer, IEnvironment context) where T : ITag
        {
            if (nodeProviderBroker.IsNDjango(buffer))
                return (ITagger<T>)new Tagger(nodeProviderBroker, buffer);
            else
                return null;
        }
    }

No surprises here.

And finally the tagger:

    class Tagger : ITagger<Constants.ErrorTag>
    {
        private NodeProvider nodeProvider;
 
        public Tagger(INodeProviderBroker nodeProviderBroker, ITextBuffer buffer)
        {
            nodeProvider = nodeProviderBroker.GetNodeProvider(buffer);
            nodeProvider.NodesChanged += new NodeProvider.SnapshotEvent(provider_TagsChanged);
        }
 
        void provider_TagsChanged(SnapshotSpan snapshotSpan)
        {
            if (TagsChanged != null)
                TagsChanged(this, new SnapshotSpanEventArgs(snapshotSpan));
        }
 
        public IEnumerable<ITagSpan<Constants.ErrorTag>> GetTags(Microsoft.VisualStudio.Text.NormalizedSnapshotSpanCollection spans)
        {
            foreach (SnapshotSpan span in spans)
            {
                foreach (NodeSnapshot node in nodeProvider.GetNodes(span))
                {
                    if (node.SnapshotSpan.OverlapsWith(span) && node.Node.ErrorMessage.Severity > 0)
                        yield return new TagSpan<Constants.ErrorTag>(node.SnapshotSpan, new Constants.ErrorTag());
                }
            }
        }
 
        public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
    }

In the next post I will tackle something a little more complex - quick info

Leave a Reply