Roslyn - 通过替换语法根中的文本范围来修复文档中的错误

Roslyn - fix error in document by replacing text span in syntax root

我这里有一些具体的内容,基本上,我在一个临时工作区中加载并在编译中获取与特定值相关的错误,因为我确切地知道我需要替换哪些文本但是不在哪里。下面的代码。

public static async Task<Solution> UpdateEntityReferences(Solution solution, ProjectId servicesId, string oldValue, string newValue)
{
     var project = solution.GetProject(servicesId);
     var compilation = await project.GetCompilationAsync();
     var diagnostics = compilation.GetDiagnostics().Where(diag => diag.GetMessage().Contains($"'{oldValue}'"));

     foreach (var diagnostic in diagnostics)
     {
         var errorLineSpan = diagnostic.Location.GetLineSpan();
         var document = project.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
         var syntaxRoot = await document.GetSyntaxRootAsync();
         var errorSpan = errorLineSpan.Span;
     }
     return solution;
}

所以到目前为止的代码我得到了错误的位置并且基本上想要 return 该文档的新版本,其中 "errorSpan" 替换为 "newValue" 文本但是找不到办法,这可行吗?

编辑:在 Get the SyntaxNode given the linenumber in a SyntaxTree 的帮助下,我能够从 SyntaxTree 中获取 SyntaxNode,并且应该能够替换文本范围(for 循环变为下面的内容),但这不起作用.

foreach (var diagnostic in diagnostics)
{
    var errorLineSpan = diagnostic.Location.GetLineSpan();
    var document = project.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
    var syntaxTree = await document.GetSyntaxTreeAsync();

    var errorSpan = errorLineSpan.Span;
    var lineSpan = syntaxTree.GetText().Lines[errorSpan.Start.Line].Span;

    var node = syntaxTree.GetRoot().DescendantNodes(lineSpan)
                .First(n => lineSpan.Contains(n.FullSpan));

    var errorTextSpan = TextSpan.FromBounds(errorSpan.Start.Character, errorSpan.End.Character);

    var newNodeText = node.GetText().Replace(errorTextSpan, newValue);
}

文本被替换(有一半的时间不正确),然后我剩下一个 SourceText 对象,我不知道如何在文档中替换它。有什么想法吗?

最终到达那里 - 所以如果不使用 document editor 之类的东西就无法替换 textspan 并取回文档,因为您需要替换 SyntaxNode 并且不能保证 LineSpan 的字符位置抓取节点还是正确的

解决方案是简单地使用 LineSpan 中的行号来获取该行上的节点,然后使用 LINQ 向下过滤到令牌。一旦抓取了确切的节点,就可以在父节点中替换它,然后是 syntaxRoot,最后是文档。下面的代码。

foreach (var diagnostic in diagnostics)
{
     servicesProject = solution.GetProject(servicesId);
     var errorLineSpan = diagnostic.Location.GetLineSpan();
     var document = servicesProject.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
     var syntaxTree = await document.GetSyntaxTreeAsync();
     var errorSpan = errorLineSpan.Span;
     var lineSpan = syntaxTree.GetText().Lines[errorSpan.Start.Line].Span;

     var node = syntaxTree.GetRoot().DescendantNodes(lineSpan)
.First(n => lineSpan.Contains(n.FullSpan)).DescendantNodes()
.OfType<IdentifierNameSyntax>().FirstOrDefault(c => c.Identifier.Text == oldValue);

        var newNode = node.ReplaceNode(node, SyntaxFactory.IdentifierName(newValue));
        var newSyntaxRoot = syntaxTree.GetRoot().ReplaceNode(node, newNode);
        document = document.WithSyntaxRoot(newSyntaxRoot);
        solution = document.Project.Solution;           
}