如何使用 Roslyn 以编程方式从代码中删除区域?

Mar*_*k T 5 c# roslyn

我正在使用 Roslyn 从文本中解析 C# 代码。一些代码具有围绕多个类的区域。例子:

#region Classes
public class MyClass
{
}

public class MyClass2
{
    #region Methods
    #endregion
}
#endregion
Run Code Online (Sandbox Code Playgroud)

我想删除类周围的区域(上例中的“类”),但保持内部区域完好无损,就像上例中名为“方法”的区域一样。我该怎么做呢?

SJP*_*SJP 5

区域相当奇特,因为它们不遵循通常的树结构。例如,您可以创建这样的构造:

public class TestClass{
    public void TestMethod(){
        #region TestRegion
    }
}
#endregion
Run Code Online (Sandbox Code Playgroud)

这仍然有效。考虑到这一点,在分析区域时还有一个问题:它们是琐事中的节点。因此,要获取相关节点,您可以使用 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);
    }
}
Run Code Online (Sandbox Code Playgroud)


doe*_*uvc 2

正如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());
        }
    }
Run Code Online (Sandbox Code Playgroud)

你的输出应该是这样的:

        public class MyClass
        {
        }

        public class MyClass2
        {
            #region Methods
            #endregion
        }
        #endregion
Run Code Online (Sandbox Code Playgroud)

PS:这不是完整的解决方案。我同意mjwills 的观点,你应该在发布问题之前展示一些进展。

PS:代码灵感来自JoshVartyEmptyStatementRemoval