One drawback of Roslyn’s immutability is that it can sometimes make it tricky to apply multiple changes to a
SyntaxTree. Immutability means that every time we apply changes to a syntax tree, we’re given an entirely new syntax tree. By default we can’t compare nodes across trees, so what do we do when we want to make multiple changes to a syntax tree?
Roslyn gives us four options:
- Use the
CSharpSyntaxRewriterand rewrite from the bottom up (See LRN: Part 5)
- Use Annotations (See LRN: Part 13)
- Use the
DocumentEditor allows us to make multiple changes to a document and get the resulting document after the changes have been applied. Under the covers, the
DocumentEditor is a thin layer over the
We’ll use the
DocumentEditor to change:
We’ll use the
DocumentEditor to simultaneously insert an invocation before the first
Console.WriteLine() and to insert another after the second.
Unfortunately there’s a ton of boiler plate when creating a
Document from scratch. Typically you’ll get a
Document from a
Workspace so it shouldn’t be this bad:
All the familiar
SyntaxNode methods are here. We can
Remove nodes as we see fit, all based off of nodes in our original syntax tree. Many people find this approach more intuitive than building an entire
It can be somewhat difficult to debug things when they go wrong. When writing this post I was mistakenly trying to insert nodes after
ifStatement.Else instead of
ifStatement.Else.Statement. I was receiving an
InvalidOperationException but the message wasn’t very useful and it took me quite some time to figure out what I was doing wrong. The documentation on
This node must be of a compatible type to be placed in the same list containing the existing node.
How can we know which types of nodes are compatible with one another? I don’t think there’s a good answer here. We essentially have to learn which nodes are compatible ourselves. As usual the Syntax Visualizer and Roslyn Quoter are the best tools for figuring out what kinds of nodes you should be creating.
It’s worth noting that the
DocumentEditor exposes the
SemanticModel of your original document. You may need this when editing the original document and making decisions about what you’d like to change.
It’s also worth noting that the underlying
SyntaxEditor exposes a
SyntaxGenerator that you can use to build syntax nodes without relying on the more verbose