删除节点时保持结构化琐事
Keep structured trivia when removing a node
有没有简单的方法从树中删除 SyntaxNode(即方法),但保留结构化琐事?
在下面的代码中,我想删除 MethodA:
public class Sample
{
#region SomeRegion
public void MethodA()
{
}
#endregion
}
我使用 CSharpSyntaxRewriter 来重写 SyntaxTree。在 VisitMethodDeclaration 方法中,我简单地为 MethodA return null。这种方法的问题是 #region 标签的 StructuredTrivia 也被删除了。这是结果结果:
public class Sample
{
#endregion
}
在我的 CSharpSyntaxRewriter 中:
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
if (...)
return null;
else
return node;
}
编辑:
如以下答案之一所述,我可以将 SyntaxNode.RemoveNodes 与 SyntaxRemoveOptions.KeepDirectives 选项一起使用。该解决方案有两大缺点:
- 我需要知道哪些 SyntaxNode 类型可以包含此子类型。例如,可以在结构、Class、接口等中声明一个方法...这意味着我需要在多个位置进行过滤。
- 我失去了自下而上构建语法树的能力。这在比较 SyntaxTree 对象时会产生问题。对访问者方法的所有后续调用都已经看到在 "RemoveNodes" 方法中创建的新节点。
使用 SyntaxNode.RemoveNodes 方法可以指定 SyntaxRemoveOptions.KeepDirectives,但这是否也可以使用 CSharpSyntaxRewriter?
EDIT2:这是我正在尝试做的一些代码:https://dotnetfiddle.net/1Cg6UZ
删除节点时,您实际上是在删除它的琐事。要保留琐事,您需要修改 ClassDeclarationSyntax 而不是 MethodDeclaration。
访问 ClassDeclarationSyntax 时,您可以通过删除适当的节点来修改 class - 并使用 SyntaxRemoveOptions.KeepTrailingTrivia | SyntaxRemoveOptions.KeepLeadingTrivia 你保留实际方法定义前后的注释和区域语句。
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);
}
}
如果您希望首先访问子节点,您当然也可以先执行 base.VisitClassDeclaration(node) 并仅在之后删除方法节点。
另一种方法是 return 另一个声明。但是,您不能简单地 return 一个 EmptyStatement(因为这会导致异常),但您可以插入一个没有内容的新方法声明:
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());
}
}
这种方法当然有缺点,它需要针对不同语法类型的特定 SyntaxNode,因此可能很难在代码的其他部分重用它。
我想你正在寻找标志 KeepExteriorTrivia
如果你检查枚举的来源,你会看到他们有一个预编译的标志
[Flags]
public enum SyntaxRemoveOptions
{
KeepNoTrivia = 0,
KeepLeadingTrivia = 1,
KeepTrailingTrivia = 2,
KeepExteriorTrivia = KeepTrailingTrivia | KeepLeadingTrivia,
KeepUnbalancedDirectives = 4,
KeepDirectives = 8,
KeepEndOfLine = 16,
AddElasticMarker = 32,
}
您的函数调用现在如下所示:
public class ClassDeclarationChanger : CSharpSyntaxRewriter
{
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
var methods = node.Members.OfType<MethodDeclarationSyntax>();
if (methods.Any())
{
node = node.RemoveNodes(methods, SyntaxRemoveOptions.KeepExteriorTrivia);
}
return base.VisitClassDeclaration(node);
}
}
有没有简单的方法从树中删除 SyntaxNode(即方法),但保留结构化琐事?
在下面的代码中,我想删除 MethodA:
public class Sample
{
#region SomeRegion
public void MethodA()
{
}
#endregion
}
我使用 CSharpSyntaxRewriter 来重写 SyntaxTree。在 VisitMethodDeclaration 方法中,我简单地为 MethodA return null。这种方法的问题是 #region 标签的 StructuredTrivia 也被删除了。这是结果结果:
public class Sample
{
#endregion
}
在我的 CSharpSyntaxRewriter 中:
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
if (...)
return null;
else
return node;
}
编辑: 如以下答案之一所述,我可以将 SyntaxNode.RemoveNodes 与 SyntaxRemoveOptions.KeepDirectives 选项一起使用。该解决方案有两大缺点:
- 我需要知道哪些 SyntaxNode 类型可以包含此子类型。例如,可以在结构、Class、接口等中声明一个方法...这意味着我需要在多个位置进行过滤。
- 我失去了自下而上构建语法树的能力。这在比较 SyntaxTree 对象时会产生问题。对访问者方法的所有后续调用都已经看到在 "RemoveNodes" 方法中创建的新节点。 使用 SyntaxNode.RemoveNodes 方法可以指定 SyntaxRemoveOptions.KeepDirectives,但这是否也可以使用 CSharpSyntaxRewriter?
EDIT2:这是我正在尝试做的一些代码:https://dotnetfiddle.net/1Cg6UZ
删除节点时,您实际上是在删除它的琐事。要保留琐事,您需要修改 ClassDeclarationSyntax 而不是 MethodDeclaration。
访问 ClassDeclarationSyntax 时,您可以通过删除适当的节点来修改 class - 并使用 SyntaxRemoveOptions.KeepTrailingTrivia | SyntaxRemoveOptions.KeepLeadingTrivia 你保留实际方法定义前后的注释和区域语句。
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);
}
}
如果您希望首先访问子节点,您当然也可以先执行 base.VisitClassDeclaration(node) 并仅在之后删除方法节点。
另一种方法是 return 另一个声明。但是,您不能简单地 return 一个 EmptyStatement(因为这会导致异常),但您可以插入一个没有内容的新方法声明:
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());
}
}
这种方法当然有缺点,它需要针对不同语法类型的特定 SyntaxNode,因此可能很难在代码的其他部分重用它。
我想你正在寻找标志 KeepExteriorTrivia
如果你检查枚举的来源,你会看到他们有一个预编译的标志
[Flags]
public enum SyntaxRemoveOptions
{
KeepNoTrivia = 0,
KeepLeadingTrivia = 1,
KeepTrailingTrivia = 2,
KeepExteriorTrivia = KeepTrailingTrivia | KeepLeadingTrivia,
KeepUnbalancedDirectives = 4,
KeepDirectives = 8,
KeepEndOfLine = 16,
AddElasticMarker = 32,
}
您的函数调用现在如下所示:
public class ClassDeclarationChanger : CSharpSyntaxRewriter
{
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
var methods = node.Members.OfType<MethodDeclarationSyntax>();
if (methods.Any())
{
node = node.RemoveNodes(methods, SyntaxRemoveOptions.KeepExteriorTrivia);
}
return base.VisitClassDeclaration(node);
}
}