Reputation: 34238
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);
Upvotes: 9
Views: 1917
Reputation: 2016
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.
Upvotes: 3