簡體   English   中英

如何使用Roslyn以編程方式從代碼中刪除區域?

[英]How do I remove regions from code programatically using Roslyn?

我正在使用Roslyn從文本中解析C#代碼。 一些代碼具有圍繞多個類的區域。 例:

#region Classes
public class MyClass
{
}

public class MyClass2
{
    #region Methods
    #endregion
}
#endregion

我想刪除類周圍的區域(上例中的“類”),但保留內部區域,就像上面示例中名為“Methods”的區域一樣。 我該怎么做呢?

區域相當特殊,因為它們不遵循通常的樹形結構。 例如,您可以創建一個這樣的構造:

public class TestClass{
    public void TestMethod(){
        #region TestRegion
    }
}
#endregion

這仍然有效。 考慮到這一點,在分析區域時還有一個問題:它們是瑣事中的節點。 因此,要獲取相關節點,您可以使用SyntaxRewriter(並傳遞構造函數“true”以啟用瑣事分析)或使用node.DescendantNodes(descendIntoTrivia:true)查找后代節點。

由於區域的開始和結束可能位於文件中的任何位置,因此您應始終在語法樹的根目錄開始分析,以確保您可以找到區域的結束/開始。

為了找到該區域,您可以覆蓋VisitRegionDirectiveTrivia以及VisitEndRegionDirectiveTrivia。 由於RegionTrivia的開始和結束彼此不了解,您需要自己匹配它們。 在下面的例子中,我簡單地計算了我已經傳遞了多少個區域,並注意了一個#endregion位置列表,這些位置在走出區域時應該被刪除。

為了識別相關區域,我提供了兩種方法:您可以使用區域名稱或標識附加的節點是否為ClassDeclaration。

這兩種方法都沒有在類聲明之前考慮諸如屬性聲明之類的情況。 如果你想要處理這個問題,你需要看看兄弟節點,並檢查它們是否在該區域的范圍內開始。

private class RegionSyntaxRewriter : CSharpSyntaxRewriter
{
    int currentPosition = 0;
    private List<int> EndRegionsForDeletion = new List<int>();
    private string deletedRegion;
    private bool useRegionNameForAnalysis = false;

    public RegionSyntaxRewriter(string deletedRegion) : base(true)
    {
        this.deletedRegion = deletedRegion;
    }

    public override SyntaxNode VisitRegionDirectiveTrivia(
            RegionDirectiveTriviaSyntax node)
    {
        currentPosition++;
        var regionText = node.ToFullString().Substring(8).Trim();
        if (!useRegionNameForAnalysis &&
            node.ParentTrivia.Token.Parent is ClassDeclarationSyntax)
        {
            EndRegionsForDeletion.Add(currentPosition);
            return SyntaxFactory.SkippedTokensTrivia();
        }
        if (useRegionNameForAnalysis && 
            regionText == deletedRegion)
        {
            EndRegionsForDeletion.Add(currentPosition);
            return SyntaxFactory.SkippedTokensTrivia();
        }

        return base.VisitRegionDirectiveTrivia(node);
    }

    public override SyntaxNode VisitEndRegionDirectiveTrivia(
            EndRegionDirectiveTriviaSyntax node)
    {
        var oldPosition = currentPosition;
        currentPosition--;
        if (EndRegionsForDeletion.Contains(oldPosition))
        {
            EndRegionsForDeletion.Remove(currentPosition);
            return SyntaxFactory.SkippedTokensTrivia();
        }

        return base.VisitEndRegionDirectiveTrivia(node);
    }
}

正如Sievajet建議的那樣,您可以使用CSharpSyntaxRewriter刪除附加到特定節點的Region (在您的情況下: ClassDeclarationSyntax )。

這是讓你入門的代碼:

 public class RegionRemoval : CSharpSyntaxRewriter
    {
        public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
        {

            if(node.HasLeadingTrivia)
            {
                var enumerator = node.GetLeadingTrivia().GetEnumerator();

                while(enumerator.MoveNext())
                {
                    var syntaxTrivia = enumerator.Current;
                    if(syntaxTrivia.Kind().Equals(SyntaxKind.RegionDirectiveTrivia))
                    {
                        node = node.ReplaceTrivia(syntaxTrivia, SyntaxFactory.Whitespace("\n"));
                    }
                }

            }
            return node;
        }
    }

    class RoslynTry
    {
        public static void RegionRemover()
        {
            //A syntax tree with an unnecessary semicolon on its own line
            var tree = CSharpSyntaxTree.ParseText(@"
                  #region Classes
        public class MyClass
        {
        }

        public class MyClass2
        {
            #region Methods
            #endregion
        }
        #endregion
        ");

            var rewriter = new RegionRemoval();
            var result = rewriter.Visit(tree.GetRoot());
            Console.WriteLine(result.ToFullString());
        }
    }

您的輸出應如下所示:

        public class MyClass
        {
        }

        public class MyClass2
        {
            #region Methods
            #endregion
        }
        #endregion

PS:這不是完整的解決方案。 我同意mjwills ,你應該在發布問題之前先顯示一些進展。

PS:代碼的靈感來自於JoshVartyEmptyStatementRemoval

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM