Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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);
like image 716
Not loved Avatar asked Jul 03 '12 09:07

Not loved


1 Answers

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.

like image 150
Matt Warren Avatar answered Oct 05 '22 00:10

Matt Warren