How to map a Roslyn SyntaxNode back and forth to a DTE CodeElement

Topics: APIs
Apr 4, 2014 at 6:37 AM
We have a large codebase built on the DTE code model and would like to integrate with Roslyn. In order to be able to reuse existing functionality, it would be much easier for us if we could map a Roslyn SyntaxNode to a DTE CodeElement. Is this possible?

Thank you.
Apr 4, 2014 at 12:06 PM
what are you using to get Roslyn side types? Workspace? or are you just parsing a file to get node?
Apr 4, 2014 at 12:16 PM
I have two use cases:
  1. I'm using Code Actions, so I have a SyntaxNode and I need to get the corresponding CodeElement, from inside the ICodeFixProvider.
  2. I have a class that provides adornments (using IWpfTextView.GetAdornmentLayer), and currently it reacts to the IWpfTextView.LayoutChanged, somehow gets the FileCodeModel for this buffer, and adds the adornments. The same logic is used to add tooltips (we implement a IQuickInfoSource).
I'm happy to drop my point 2 if I am allowed to integrate with Code Lens. I sent a question on the insider mail group regarding this.

In both cases, we have a lot of logic already written for DTE code model, both predicates ("code action applies to all public methods of a class") and transformations ("add a custom attribute and install NuGet package").

I'm just at the beginning of the porting effort.

Apr 4, 2014 at 2:16 PM
Edited Apr 4, 2014 at 2:21 PM
for #1, unfortunately, CodeElement we provide doesn't expose information on how it is backed by Roslyn syntaxnode. so, probably best way you can covert code element to syntax node is you finding it through code element location information.

in code fixer, you should have Document. get SourceText from Document.GetTextAsync, and then convert line, column info of code element to position using SourceText. once you have position, use SyntaxNode.FindNode or FindToken (Document.GetSyntaxRootAsync) to retrieve a node or token, and then use GetAncestor to find the syntax node you want. probably this is best way for now.

if you want, you can open a suggestion bug to us to provide a mapping service for this kind of scenario since we internally have all these information. it is just not exposed.

for #2, we provide a way to convert ITextSnapshot to Document. just do this.
var document = textSnapshot.GetOpenDocumentInCurrentContextWithChanges();

once you have document, you can do same thing as #1 to convert your code element to corresponding Roslyn one.
Apr 9, 2014 at 7:42 AM
Thank you.

For #1, I managed to do the SyntaxNode->CodeElement mapping, but in a very inefficient way because I have to enumerate all ProjectItems to find the right one, then visit all nodes of the FileCodeModel (which fortunately is not very deep) to find mine. It would be good to have some ready-made facility that has some reasonable performance characteristics.

For #2, I didn't get to it yet, as I'm now focusing on keeping my existing CodeModel code working.

I filed a suggestion here:
Apr 9, 2014 at 4:38 PM
gfraiteur, I think this should be easier. You can get the source loc info from the node, then using the DTE editor model, you can get a TextPoint for that location, and using the VS Code Model, you can ask for the CodeElement that contains the TextPoint. I don't recall all the APIs to do that, but the DTE model was designed for this two way mapping (that is, between the editor model of points/locations to VS Code Model).

Apr 9, 2014 at 5:46 PM

Thank you for your answer.

Yes, I have that code actually. But it takes a Microsoft.VisualStudio.Text.ITextBuffer. How do I get an ITextBuffer from a Document or SourceText?

I see it on ((Microsoft.CodeAnalysis.Text.Extensions.SnapshotSourceText)(text)).EditorSnapshot but it's internal.

Alternatively, how do I get a ProjectItem from a Document?


Apr 10, 2014 at 1:31 AM
you can do this.

var textBuffer = sourceText.TextContainer.TryGetTextBuffer();

but, SourceText should already have all information you need. you can get position or line/column info from sourcetext.Lines.
  • heejae