I ended up using some of the new roslyn stuff. The code below does (to a large extent) all the same stuff as my code above in the question, with the addition of Moniker return.
I mark this as an answer, but since Sergey was very helpful in his answer, plus the inspiration for my Roslyn code was actually from this SO answer , which was ALSO his answer, it definitely deserves points :).
The code
public static (string, ImageMoniker)[] GetSyntaxHierarchyAtCaret(IWpfTextView textView) { var caretPosition = textView.Caret.Position.BufferPosition; var document = caretPosition.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); var syntaxRoot = document.GetSyntaxRootAsync().Result; var caretParent = syntaxRoot.FindToken(caretPosition).Parent; var returnValue = new List<(string, ImageMoniker)>(); while (caretParent != null) { var kind = caretParent.Kind(); switch (kind) { case SyntaxKind.ClassDeclaration: { var dec = caretParent as ClassDeclarationSyntax; returnValue.Add((dec.Identifier.ToString(),KnownMonikers.Class)); break; } case SyntaxKind.MethodDeclaration: { var dec = caretParent as MethodDeclarationSyntax; returnValue.Add((dec.Identifier.ToString(),KnownMonikers.Method)); break; } case SyntaxKind.PropertyDeclaration: { var dec = caretParent as PropertyDeclarationSyntax; returnValue.Add((dec.Identifier.ToString(), KnownMonikers.Property)); break; } } caretParent = caretParent.Parent; } return returnValue.ToArray(); }
Dependencies
Since I am returning Tuple, you will need System.ValueTuple , and Roslyn material requires Microsoft.CodeAnalysis.EditorFeatures.Text , Microsoft.CodeAnalysis.CSharp , as well as all the dependencies.
Indicative versions of VS2015 / 2017 and the required version of .NET
CodeAnalysis builds require target (I think) .NET 4.6.1 or higher. The CodeAnalysis build version is also directly related to the version of VS that it can support. I have not seen any official documentation about this (which, I think, should be placed in big bold red letters at the top of every msdn page about it!), But here is the SO answer with versions for use for different purposes by VS. The earliest you can target seems to be VS2015 (RTM). I personally use v1.3.2, which should support VS2015 update or higher.
Performance
I did not run this through the profiler, but it works much more smoothly. Firstly, on large files, there are a couple of seconds that it does not work (I assume that the file is indexed), but if you look carefully, many functions in VS do not work as long as it is indexed (or whatever it is ) You hardly notice this. In a small file this is immaterial.
(slightly unrelated to the question, but may help someone ...)
One tip for anyone who uses the CaretChanged event to manage a feature that faces performance issues: I would recommend using a dispatcher and reducing the number of calls. In the code below, a delay of 200 ms for the call will be added and more than one call every 200 ms is not allowed. Well, 200 ms by measure. This is unpredictable, but it will work when it can - with low priority (DispatcherPriority.ApplicationIdle):
private readonly IWpfTextView _textView; private readonly DispatcherTimer _throttleCursorMove; ... // constructor { _textView.Caret.PositionChanged += HandleCaretPositionChanged; _throttleCursorMove = new DispatcherTimer(DispatcherPriority.ApplicationIdle); _throttleCursorMove.Tick += (sender, args) => CaretPositionChanged(); _throttleCursorMove.Interval = new TimeSpan(0, 0, 0, 0, 200); } private void HandleCaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { if (!_throttleCursorMove.IsEnabled) _throttleCursorMove.Start(); } private void CaretPositionChanged() { _throttleCursorMove.Stop(); ... var hierarchy = CodeHierarchyHelper.GetSyntaxHierarchyAtCaret(_textView); ... } ...