Django Editor in VS 2010 – Part 5 (Quick Info Source)
Thursday, August 13th, 2009Look here for complete source
In the previous post I explained the two components of the Quick Info implementation - the controller and the source. I also covered the Quick Info controller. Now, before diving into the source, let me tell you that you may not necessarily need the controller (or the source).
The thing is that these two are to certain degree independent of each other. The task of the controller is to manage the session, but it does not explicitly invoke the source - the system knows what source(s) to invoke based on the attributes you provide for the source.В The controllerВ alsoВ may (or may not)В provide something to help source generate the tooltip, or the source can be smart enough to figure it out on its own. If the latter is true, and if some other Quick Info controller is already implemented, you do not need a new controller just to show tooltips from your new source - the existing one will do just fine. And vice versa - if you only need to change how the Quick Info session is triggered (or dismissed) you do not need to re-implement the source.
Alright, may be you are lucky, I am not, so let's get to it. Here it is, the QuickInfo source. Once more we have two objects a provider source and the actual provider. The provider source is decorated with the attributes the Visual Studio editor uses to determine when to invoke it. Here is the code:
[Export(typeof(IQuickInfoSourceProvider))] [Name("NDjango QuickInfo Source")] [Order(Before = "default")] [ContentType(Constants.NDJANGO)] class SourceProvider : IQuickInfoSourceProvider { [Import] internal INodeProviderBroker nodeProviderBroker { get; set; } public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer, IEnvironment environment) { if (nodeProviderBroker.IsNDjango(textBuffer)) return new Source(); return null; } }
There is nothing to add to what's written in the code. The actual source is not this much more complex than the provider:
class Source : IQuickInfoSource { public object GetToolTipContent(IQuickInfoSession session, out Microsoft.VisualStudio.Text.ITrackingSpan applicableToSpan) { StringBuilder message = new StringBuilder(); int position = session.SubjectBuffer.CurrentSnapshot.Length; int length = 0; List nodes; if (session.Properties.TryGetProperty>(typeof(SourceProvider), out nodes)) { nodes.ForEach( node => { if (!String.IsNullOrEmpty(node.Description)) message.Insert(0, node.Description + "\n"); if (node.ErrorMessage.Severity >= 0) message.Append("\n" + node.ErrorMessage.Message); if (node.Length > length) length = node.Length; if (node.Position < position) position = node.Position; } ); } applicableToSpan = session.SubjectBuffer.CurrentSnapshot.CreateTrackingSpan( position, length, Microsoft.VisualStudio.Text.SpanTrackingMode.EdgeExclusive); if (message.Length > 0) return message.ToString(); else return null; } }
As you can see the GetToolTipContent after some messing around with the node list returns the text message to be shown in the tooltip. Setting of the applicableToSpan forces dismissal of the QuickInfo session when the mouse cursor leaves the said span.
The next post will be about the final element - code completion