如何使用Roslyn代码修复提供程序API从文档中删除SyntaxNode列表

本文关键字:文档 API 删除 列表 SyntaxNode 程序 Roslyn 何使用 代码 | 更新日期: 2023-09-27 18:27:22

我有一个使用SyntaxFactory.VariableDeclaration自定义生成的变量声明,以及根据某些条件收集的SyntaxNode的列表。

我做了以下事情:

  1. 修改节点

    var newRoot = root.ReplaceNode(expression, newVariableDeclaration)
    

    这成功地用newVariableDeclaration修改了节点。

  2. 在循环中删除与列表中存在的节点相对应的节点

    foreach (var listObject in listObjects)
    {
        newRoot = newRoot.RemoveNode(listObject, SyntaxRemoveOptions.KeepNoTrivia);
    }
    

    这不会改变newRoot,并且需要改变的所有listObject保持不变。

如果我们使用root.RemoveNode(listObject, SyntaxRemoveOptions.KeepNoTrivia),它显然会继续替换以前的更改

因此,这里newVariableDeclaration是整个文档中唯一被更改的节点,这是因为newRoot SyntaxNodes与我从root获得的SyntaxNode相比有变化吗。

如果我做错了,请纠正我。

编辑:我查看了CSharpSyntaxRewriter,但它似乎每次访问节点都在分析单个节点,并且一次只能修改单个节点。在我的场景中,我必须访问一个特定的节点,对其进行更改,并删除与对访问节点所做的更改相关的其他节点。

如何使用Roslyn代码修复提供程序API从文档中删除SyntaxNode列表

您的方法的问题是,无论何时更改树(使用ReplaceNode()RemoveNode()),都意味着所有节点也会更改。这就是为什么您在ReplaceNode()之后对RemoveNode()的呼叫没有任何作用。

解决此问题的一种方法是使用TrackNodes(),这样您就可以找到修改后的树中的哪些节点对应于原始树中的节点。

因此,用单个节点替换节点序列的方法可能如下所示:

public static T ReplaceNodes<T>(
    this T root, IReadOnlyList<SyntaxNode> oldNodes, SyntaxNode newNode)
    where T : SyntaxNode
{
    if (oldNodes.Count == 0)
        throw new ArgumentException();
    var newRoot = root.TrackNodes(oldNodes);
    var first = newRoot.GetCurrentNode(oldNodes[0]);
    newRoot = newRoot.ReplaceNode(first, newNode);
    var toRemove = oldNodes.Skip(1).Select(newRoot.GetCurrentNode);
    newRoot = newRoot.RemoveNodes(toRemove, SyntaxRemoveOptions.KeepNoTrivia);
    return newRoot;
}