Is there an easy way to remove a SyntaxNode (i.e. a method) from the tree, but keeping the structured trivia?
In the following code I want to remove MethodA:
public class Sample
{
#region SomeRegion
public void MethodA()
{
}
#endregion
}
I use a CSharpSyntaxRewriter to rewrite the SyntaxTree. In the VisitMethodDeclaration method, I simply return null for MethodA. The problem with this approach is that the StructuredTrivia of the #region tag is also removed. This is the result result:
public class Sample
{
#endregion
}
In my CSharpSyntaxRewriter:
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
if (...)
return null;
else
return node;
}
EDIT: As mentioned in one of the answers below, I could use SyntaxNode.RemoveNodes with the SyntaxRemoveOptions.KeepDirectives option. This solution has two big downsides:
EDIT2: Here is some code with what I'm trying to do: https://dotnetfiddle.net/1Cg6UZ
When deleting the node you're actually removing the trivia with it. To retain the trivia you need to modify the ClassDeclarationSyntax instead of the MethodDeclaration.
When visiting the ClassDeclarationSyntax you can modify the class by removing the appropriate nodes - and by using the SyntaxRemoveOptions.KeepTrailingTrivia | SyntaxRemoveOptions.KeepLeadingTrivia you retain the comments and region statements before and after the actual method definition.
public class ClassDeclarationChanger : CSharpSyntaxRewriter
{
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
var methods = node.Members.OfType<MethodDeclarationSyntax>();
if (methods.Any())
{
node = node.RemoveNodes(methods, SyntaxRemoveOptions.KeepTrailingTrivia |
SyntaxRemoveOptions.KeepLeadingTrivia);
}
return base.VisitClassDeclaration(node);
}
}
In case you wish to visit the child nodes first you can of course also execute base.VisitClassDeclaration(node) first and remove the method node only afterwars.
A different approach would be to return another declaration. However you cannot simply return an EmptyStatement (as this will result in an exception) but you can insert a new method declaration with no content:
public class SampleChanger : CSharpSyntaxRewriter
{
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
// Generates a node containing only parenthesis
// with no identifier, no return type and no parameters
var newNode = SyntaxFactory.MethodDeclaration(SyntaxFactory.IdentifierName(""), "");
// Removes the parenthesis from the Parameter List
// and replaces them with MissingTokens
newNode = newNode.ReplaceNode(newNode.ParameterList,
newNode.ParameterList.WithOpenParenToken(
SyntaxFactory.MissingToken(SyntaxKind.OpenParenToken)).
WithCloseParenToken(SyntaxFactory.MissingToken(SyntaxKind.CloseParenToken)));
// Returns the new method containing no content
// but the Leading and Trailing trivia of the previous node
return newNode.WithLeadingTrivia(node.GetLeadingTrivia()).
WithTrailingTrivia(node.GetTrailingTrivia());
}
}
This approach of course does have the downside that it requires a specific SyntaxNode for different syntax types so reusing it for other parts of your code might be difficult.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With