2016-08-05 27 views
3

有没有简单的方法从树中删除SyntaxNode(即方法),但保留结构化的琐事?删除节点时保留结构化的琐事

在下面的代码我想删除治法:

public class Sample 
{ 
    #region SomeRegion 
    public void MethodA() 
    { 

    } 
    #endregion 
} 

我用CSharpSyntaxRewriter重写SyntaxTree。在VisitMethodDeclaration方法中,我简单地为MethodA返回null。这种方法的问题是#region标签的StructuredTrivia也被删除。这是结果的结果:

public class Sample 
{ 
    #endregion 
} 

在我CSharpSyntaxRewriter:

public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) 
{ 
    if (...) 
     return null; 
    else 
     return node; 
} 

编辑: 正如下面的答案之一提到的,我可以用SyntaxNode.RemoveNodes与SyntaxRemoveOptions.KeepDirectives选项。此解决方案有两大缺点:

  1. 我需要知道哪些SyntaxNode类型可以包含此子类型。例如,一个方法可以在Struct,Class,Interface等中声明......这意味着我需要在多个位置上进行过滤。
  2. 我放弃了自下而上构建语法树的能力。比较SyntaxTree对象时会出现问题。随后对访问者方法的所有调用都会看到在“RemoveNodes”方法中创建的新节点。 使用SyntaxNode.RemoveNodes方法可以指定SyntaxRemoveOptions.KeepDirectives,但这也可以用CSharpSyntaxRewriter?

EDIT2:下面是一些代码,使我想要做的事:https://dotnetfiddle.net/1Cg6UZ

+3

关于_“但是当我这样做时”_:你究竟做了什么?你可以添加一个(最小完整的)代码示例,显示你如何从该输入中删除一个'SyntaxNode'? – stakx

+0

我使用CSharpSyntaxRewriter来重写SyntaxTree。在VisitMethodDeclaration方法中,我简单地为MethodA返回null。公众覆盖SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax节点) \t \t { \t \t \t如果(...) \t \t \t \t返回NULL; \t \t \t else \t \t \t \t return node; \t \t} – TWT

+1

你传递给'RemoveNode()'的参数是什么? –

回答

3

当删除你实际上是用它去除琐事的节点。要保留琐事,您需要修改ClassDeclarationSyntax而不是MethodDeclaration。

访问ClassDeclarationSyntax时,您可以通过删除适当的节点来修改该类 - 并使用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(节点)第一和删除方法节点只沿袭。

另一种方法是返回另一个声明。然而,你不能简单地返回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下行不同的语法类型,因此将其用于代码的其他部分可能会很困难。

+0

“如果你想首先访问子节点,你当然也可以首先执行base.VisitClassDeclaration(节点),并且只能在战后才移除方法节点。”在这种情况下,如何识别我想要删除的节点?在base.VisitClassDeclaration调用之后返回一个新的树。我的CSharpSyntaxRewriter包含一个HashSet,其中包含需要保留的原始树中的所有SyntaxNodes。 – TWT

+0

我已经用我想要做的代码示例来扩展原始问题。 – TWT

+0

只是为了澄清 - 在你的例子中,你希望保留名称空间,第一个类声明(带区域),第二个方法调用,但删除第二个类中包含的所有东西(方法和区域)? – SJP

0

我认为你正在寻找的标志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); 
    } 
}