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

Jer*_*ohn 3 c# vsix roslyn visual-studio-2013

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

我做了以下事情:

  1. 修改节点

    var newRoot = root.ReplaceNode(expression, newVariableDeclaration)
    
    Run Code Online (Sandbox Code Playgroud)

    这样就成功修改了节点newVariableDeclaration

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

    foreach (var listObject in listObjects)
    {
        newRoot = newRoot.RemoveNode(listObject, SyntaxRemoveOptions.KeepNoTrivia);
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

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

所以这里newVariableDeclaration是整个文档中唯一发生更改的节点,这是因为我从 获得的newRoot SyntaxNodes的节点发生了更改。SyntaxNoderoot

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

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

svi*_*ick 7

您的方法的问题在于,每当您更改树(使用ReplaceNode()RemoveNode())时,都意味着所有节点也会更改。RemoveNode()这就是为什么您对after 的调用ReplaceNode()不会执行任何操作。

解决此问题的一种方法是使用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;
}
Run Code Online (Sandbox Code Playgroud)