I have a tagger (subclass of ITagger) and I'm trying to call GetClassificationSpans so I can use the classifications to find comments to format using tags. This worked in Visual Studio 2013, but now when running it in Visual Studio 2015, GetClassificationSpans always returns an empty list - even when I've examined the span in the debugger and it is definitely passing a span with a comment in it.
Does anyone know what could have changed in 2015 in regards to calling GetClassificationSpans?
BTW: I'm getting the classifier by importing the IClassifierAggregatorService in my tagger provider (subclass of ITaggerProvider) and passing it along to the constructor of the tagger:
[import]
IClassifierAggregatorService aggregator;
Then I use the following call in the tagger on the aggregator that I got from the provider:
IList<ClassificationSpan> lstClassifiers = aggregator.GetClassifier(span.Snapshot.TextBuffer).GetClassificationSpans(span);
And, as I said, the lstClassifiers list is always empty. The exact same code ran find in VS2013. I can't seem to find anything on the net that mentions any changes in VS2015 that may be causing this.
Thanks,
I ran into the same problem, though in a different context. From my googling, it looks like they've changed the classifiers so that they're lazily initialized... and I guess GetClassificationSpans()
won't forceably initialize them. MSFT still considers this a bug, so you might want to up-vote the issue on VS Connect.
A potential workaround (as suggested by MSFT) is to switch to using TagAggregator
instead of IClassifier
. So instead of:
var service = container.GetService<IClassifierAggregatorService>();
var classifier = service.GetClassifier(textView.TextBuffer);
var spans = classifier.GetClassificationSpans(new SnapshotSpan(...));
You can instead write something like this:
var service = container.GetService<IViewTagAggregatorFactoryService>();
var aggregator = service.CreateTagAggregator<IClassificationTag>(textView);
var tags = aggregator.GetTags(new SnapshotSpan(...)));
This returns a list of IMappingTagSpan<IClassificationTag>
instead of a list of ClassificationSpan
, so the way you'll work with them is slightly different. But the underlying data appears to be mostly the same---i.e., you can get a classification and span for each lexical element. There are some subtle differences (see the description on VS Connect), but the results were good enough for my application.
Well. After trying different things, it looks like it was several problems:
- Execution order must have changed so that the classifications don't seem to be set-up prior to the place I was calling GetClassificationSpans before. (In the constructor of a buffer-level-tagger [rather than in a view-level-tagger].) I'm now calling GetCLassificationSpans only during a handling of BufferChanged/LayoutChanged. (Only problem I have now is that I don't seem to get a BufferChanged event when the file is opened. Hopefully, it shouldn't be too hard to get around that.)
- It seems to work better if I use the buffer reference passed into the provider, set-up a reference to the IClassifier there and pass that along to my tagger instead of the aggregator (and so I stopped using the buffer reference in the snapshot span).
- I'm having much better results by updating my references to the v14 of the SDK DLL's from the v12 references I was using before.
Hope that helps anyone that runs into the same issue.
So in general, the ITagger API doesn't mandate that a tagger returns the "real" information. Instead, it is an API that returns "whatever information you had at that point". If the buffers that are giving you trouble are C# or VB buffers, it's because in VS2015 we moved everything in Roslyn to be asynchronous, and so a call to GetClassificationSpans/GetTags might not return anything until we've recomputed the data. There's a new API, IAccurateTagger, that exists to request that taggers give "real" data, although that's not something you want to be running in text buffer changes since you'll kill performance.
If you are trying to find comments in VS2015 and the files that are tripping you up are C# or VB buffers, you're best off calling Roslyn APIs which give you the direct syntax tree data that you need. If it's a different type of buffer, you're at least better off calling IAccurateTagger, but try doing that as carefully as you can because of performance ramifications.
(Also, if you're trying to build an extension which auto-formats as you type, using the classification taggers will give you other headaches. There were bugs in VS2013 you'll have to work around where C# would give stale tags if you called it during a tags changed.)