2016-08-05 30 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); 
    } 
}