如何跨 Workspace.TryApplyChanges() 跟踪 SyntaxNodes

How to track SyntaxNodes across Workspace.TryApplyChanges()

我想跟踪 SyntaxNodes 和 SyntaxTrivias Solution/Workspace.

的不同版本

我尝试用 SyntaxAnnotation 注释一些节点。 只要我不更新工作区,它就可以正常工作。

调用 Workspace.TryApplyChanges(成功)似乎删除了 所有 SyntaxAnnotations.

示例代码如下:

var workspace = new AdhocWorkspace();
var project   = workspace.AddProject("TestProject", LanguageNames.CSharp);

var klass = SyntaxFactory
           .ClassDeclaration("Klass")
           .WithAdditionalAnnotations(new SyntaxAnnotation("Foo"));

var compUnit = SyntaxFactory.CompilationUnit().AddMembers(klass);
var document = project.AddDocument("TestFile.cs", compUnit);
var docId    = document.Id;
var solution = document.Project.Solution;

var root1  = document.GetSyntaxRootAsync().Result;
var klass1 = root1.GetAnnotatedNodes("Foo").FirstOrDefault();
var eq1    = klass1.IsEquivalentTo(klass); // returns true

var apply = workspace.TryApplyChanges(solution);  // returns true

var root2  = workspace.CurrentSolution.GetDocument(docId).GetSyntaxRootAsync().Result;
var klass2 = root2.GetAnnotatedNodes("Foo").FirstOrDefault(); // returns null, why?

发生这种情况是因为 TryApplyChanges 实际上 re-use 您的节点并不是原样。相反,它 "replays" 与实际解决方案的文本更改相同,然后是解析器 re-parse。

发生这种情况有几个原因:

  1. 为了避免随着时间的推移注释在树中堆积并相互干扰(考虑在应用修复后仍然存在代码修复中使用的格式或重命名注释之类的东西)。
  2. 防止 round-trip 中没有的树木出现在 CurrentSolution 中。可以构造解析器永远不会生成的树(例如考虑更改运算符优先级)。
  3. 要确保实际应用更改,需要更改原始表示 - 磁盘上的文件或内存中的文本缓冲区,而不仅仅是使用工作区中的新树。

您可以考虑使用 Roslyn 源中的 SyntaxPath 类型来尝试找到等效节点。