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