【问题标题】:Replace trivia with node in Roslyn用 Roslyn 中的节点替换琐事
【发布时间】:2016-07-21 03:21:21
【问题描述】:

例如,我的 C# 代码文件中有以下文档注释:

/// add k+5

我想用节点替换

_tst.AddElement(k+5);

如何使用 C#/Roslyn 来实现?我找到了如何添加这一行,但没有找到如何替换。我添加节点的代码:

public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax method)
{
    var newmethod = method;
    var TestEntryArgName = "_tst";

    /* Adding _tst.AddElement(i); */
    foreach (var s in newmethod.Body.DescendantNodes())
    {
        SyntaxTrivia st = SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
        bool fl = false;
        bool before = true;
        var lt = s.GetLeadingTrivia();

        foreach (var triviaEntry in lt)
        {
            if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
            {
                fl = true;
                st = triviaEntry;
                break;
            }
        }

        if (!fl)
        {
            lt = s.GetTrailingTrivia();
            before = false;
            foreach (var triviaEntry in lt)
            {
                if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
                {
                    fl = true;
                    st = triviaEntry;
                    break;
                }
            }
            if (!fl) continue;
        }

        var commentContents = st.ToString();
        char[] delim = { ' ', '\n', '\t', '\r' };
        var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries);
        if (ar.Length != 2 || ar[0] != "add") continue;

        var lineToAdd = TestEntryArgName + ".AddElement(" + ar[1] + ")";
        var linelist = new List<ExpressionStatementSyntax>();
        linelist.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(lineToAdd)));

        var childlist = s.Parent.ChildNodes();

        foreach (var si in childlist)
        {
            if (s != si) continue;
            if (before) newmethod = newmethod.InsertNodesBefore(si, linelist);
            else newmethod = newmethod.InsertNodesAfter(si, linelist);
            break;
        }

        break;
    }

    return newmethod;
}

我需要在我的方法中替换所有此类 cmets。该函数只插入节点,并且只插入一次。

编辑。此刻,我有以下解决方案,但似乎太复杂且不明显......

   public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax method)
    {
        var TestEntryArgName = "__tst";
        /* Adding last param */
        var parlist = method.ChildNodes().OfType<ParameterListSyntax>().First();
        var newparlist = parlist.AddParameters(SyntaxFactory.Parameter(
                                                                SyntaxFactory.Identifier(TestEntryArgName))
                                                                                .WithType(SyntaxFactory.ParseTypeName("Heap ")));
        var newmethod = method.ReplaceNode(parlist, newparlist);

        /* Adding __tst.AddElement(i); */
        while (true) {
            IEnumerable<SyntaxNode> desc;
            bool triviaFound;
            desc = newmethod.Body.DescendantNodes();
            triviaFound = false;
            foreach (var s in desc)
            {
                SyntaxTrivia st = SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
                bool fl = false;
                bool before = true;
                var lt = s.GetLeadingTrivia();

                foreach (var triviaEntry in lt)
                {
                    if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
                    {
                        fl = true;
                        st = triviaEntry;
                        break;
                    }
                }

                if (!fl)
                {
                    lt = s.GetTrailingTrivia();
                    before = false;
                    foreach (var triviaEntry in lt)
                    {
                        if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
                        {
                            fl = true;
                            st = triviaEntry;
                            break;
                        }
                    }
                    if (!fl) continue;
                }

                var commentContents = st.ToString();
                char[] delim = { ' ', '\n', '\t', '\r' };
                var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries);
                if (ar.Length != 2 || ar[0] != "add") continue;

                var lineToAdd = TestEntryArgName + ".AddElement(" + ar[1] + ")";
                var linelist = new List<ExpressionStatementSyntax>();
                linelist.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(lineToAdd)));

                var childlist = s.Parent.ChildNodes();

                foreach (var si in childlist)
                {
                    if (s != si) continue;
                    if (before) newmethod = newmethod.InsertNodesBefore(si, linelist);
                    else newmethod = newmethod.InsertNodesAfter(si, linelist);
                    break;
                }

                var newTrvias = newmethod.DescendantTrivia().Where((t) =>
                                {
                                    if (t.Kind() != SyntaxKind.SingleLineDocumentationCommentTrivia)
                                        return false;
                                    var arr = t.ToString().Split(delim, StringSplitOptions.RemoveEmptyEntries);
                                    return arr.Length == 2 && arr[0] == "add";
                                });

                newmethod = newmethod.ReplaceTrivia(newTrvias.First(), SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " "));
                triviaFound = true;
                break;
            }
            if (!triviaFound) break;
        }
        return newmethod;
    }

【问题讨论】:

    标签: c# parsing roslyn


    【解决方案1】:

    您正在寻找的可能是 CSharpSyntaxRewriter。这是 roslyn 中的一个类,它访问您代码的句法模型中的每个节点、令牌和琐事。您可以制作自己的重写器,覆盖 VisitTrivia 并返回您想要获取的表达式。例如:

        public class MyRewriter : CSharpSyntaxRewriter
    {
    public MyRewriter(): base(true)
    {
    }
    public override SyntaxTrivia VisitTrivia(SyntaxTrivia trivia)
    {
        if(trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
        {
            string xml = trivia.ToFullString();
    
            var TestEntryArgName = "__tst";
    
            char[] delim = { ' ', '\n', '\t', '\r' };
            var ar = xml.Split(delim, StringSplitOptions.RemoveEmptyEntries);
            if (ar.Length != 3 || ar[1] != "add")
            {
                return base.VisitTrivia(trivia);
            }
            var lineToAdd = TestEntryArgName + ".AddElement(" + ar[2] + ")";
    
            var expression = SyntaxFactory.SyntaxTrivia(SyntaxKind.SingleLineCommentTrivia, lineToAdd);
    
            return expression;
        }
        return base.VisitTrivia(trivia);
        }
    }
    

    示例使用:

        var myRewriter = new MyRewriter();
    
        string code = "";
    
        using (StreamReader sr = new StreamReader("Program.cs"))
        {
            code = sr.ReadToEnd();
        }
        var tree = CSharpSyntaxTree.ParseText(code);
        var node = tree.GetRoot();
    
    
        using(StreamWriter sw = new StreamWriter("Program.cs"))
        {
            sw.Write(myRewriter.Visit(node));
        }
    

    【讨论】:

    • 哦,真漂亮!谢谢!
    • 我试过了,还是不行 :D 这个被覆盖的方法根本没有被调用 :( 问题可能出在哪里?
    • 您必须为您的CSharpSyntaxRewriter 定义一个构造函数,并使用告诉它访问琐事的参数调用 base() 构造函数。
    • @JoshVarty 好的,这是我的错误,但现在我收到运行时错误:无法将“Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax”转换为“Microsoft.CodeAnalysis.CSharp.Syntax.StructuredTriviaSyntax” .我猜,rewriter 试图转换我的访问方法的结果,它不能
    • 我的错误。应该重写的方法是 VisitTrivia,因为这是我们想要替换的。 DocumentationCommentTriviaSyntax 是 SyntaxTrivia 的子级。我编辑了我的答案,所以现在它应该可以正常工作了。
    【解决方案2】:

    我找到了一个简单的解决方案,我认为它适合我,但是,我知道,它不能被称为“正确”的解决方案,因为它涉及源代码。无论如何:

    public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax newmethod)
    {
        /* Adding __tst.AddElement(i); */
        while (true)
        {
            var desc = newmethod.Body.DescendantTrivia().Where((t) => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia));
            var triviaFound = false;
    
            foreach (var s in desc)
            {
                var commentContents = s.ToString();
                char[] delim = { ' ', '\n', '\t', '\r' };
                var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries);
                if (ar.Length != 2 || ar[0] != "add") continue;
    
                var lineToAdd = "\r\n__tst.AddElement(" + ar[1] + ");\r\n";
                newmethod = CSharpSyntaxTree.ParseText(newmethod.GetText().Replace(s.FullSpan, lineToAdd))
                    .GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().First();
                triviaFound = true;
                break;
            }
            if (!triviaFound) break;
        }
        return newmethod;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-05
      • 2017-04-23
      • 1970-01-01
      • 2015-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多