Find the type of the item at the current position in a C# editor window
Asked Answered
B

1

9

I am writing an extension to Visual Studio intellisense and would like to get the type of the item just before the cursor in a C# editor.

I currently have a ITextBuffer which I can use to get the current source file.

I can also get the current position in the editor as below:

var dte = Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(EnvDTE._DTE)) as EnvDTE.DTE;            
TextSelection sel = (TextSelection)dte.ActiveDocument.Selection;

However I'm not too sure how to detect the type of the item currently behind the cursor in the editor. I've tried using Roslyn but it seems like this should be a much simpler thing to do than this. Is Roslyn the best tool to do this (via compiling the document and navigating to the correct position in the document) or is there a better way.

Below is my attempt at finding the type of the item using Roslyn:

ITextSnapshot snapshot = m_textBuffer.CurrentSnapshot;
SnapshotPoint? triggerPoint = session.GetTriggerPoint(snapshot);

var tree = SyntaxTree.ParseCompilationUnit(m_textBuffer.CurrentSnapshot.GetText());

var nodes = tree.GetRoot().DescendantNodes();

var element = nodes.Where(n => n.Span.End <= triggerPoint.Value.Position).Last();

var comp = Compilation.Create("test", syntaxTrees: new[] { tree });
var semModel = comp.GetSemanticModel(tree);

//I cant work out what to do here to get the type as the element doesnt seem to be of the required type
var s = semModel.GetTypeInfo((AttributeSyntax)element);
Branen answered 3/7, 2012 at 9:29 Comment(8)
+1 It will be interesting to hear what Eric has to say about this.Wafer
Why arent you using ITextView.Caret.Position ?Seasonseasonable
@Seasonseasonable im pretty new to the VS sdk but that sounds like a much easier way to get the current cursor pos, I'll change itBranen
@LukeMcGregor: That obviously requires that you have a reference to the view and not just the buffer :)Seasonseasonable
@Seasonseasonable yeah I'm just trying to work out how to get one of those, I'm currently in an ICompletionSourceProvider (which doesnt seem to have a ref) but im assuming you get this somehow via a static ref to the editor via WPF?Branen
@LukeMcGregor: You can try using session.GetTriggerPoint(snapshot); instead. See here for an example: github.com/leppie/IronScheme.VisualStudio2/blob/master/…Seasonseasonable
@Seasonseasonable that seems heaps tidier, thanks :) it also gives me a position that fits better with Roslyn so its actually finding the correct node in the tree, Ill have to take a bit more of a look through the source too and see how it works, looks like some great example codeBranen
@LukeMcGregor: Thanks, it is mostly based on the MS examples, but then cut down for clarity. I have no idea why those samples always have to be so over complicated, especially the parts that serves no purpose in the sample.Seasonseasonable
D
3

The compiler API's are very deliberate and require you to ask the right question (no fuzzy logic.) Simply finding the type of the thing at the cursor position requires some context, and the answer that might seem obvious to you at first may not be the correct answer for other uses.

For general expressions you can do something like this: (Note it is not very robust)

var root = tree.GetRoot();
var token = root.FindToken(pos);
var nearestExpr = token.Parent.AncestorsAndSelf().OfType<ExpressionSyntax>().First();
var type = semModel.GetTypeInfo(nearestExpr).Type;

A more comprehensive solution would check the parent node of the token and go from there:

var node = token.Parent;
if (node is ExpressionSyntax)
{
    type = semModel.GetTypeInfo((ExpressionSyntax)node).Type;
}
else if (node is VariableDeclaratorSyntax && ((VariableDeclaratorSyntax)node).Identifier == token)
{
    type = (TypeSymbol)semModel.GetDeclaredSymbol((VariableDeclaratorSyntax)node);   
}

...

There are a lot of interesting cases, and what you want to show as the type corresponding to any particular identifier or token in the source file could vary depending on what you are trying to accomplish.

Dicrotic answered 3/7, 2012 at 18:54 Comment(2)
Awesome that looks like what im after, ill try it out tonight.Branen
I gave that a go but I'm having trouble with the GetTypeInfo(IdentifierNameSyntax) call, when I run it I get an ExtendedErrorTypeSymbol back, any idea how to get around this?Branen

© 2022 - 2024 — McMap. All rights reserved.