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