文档编辑器插入之前似乎无法在文档中查找节点



我正在使用roslyn为Visual Studio开发代码重构。

我正在使用DocumentEditor类删除一些SyntaxNodes,删除它们后,我插入了一些新的SyntaxNodes。我正在使用一个语法节点,我知道我没有删除它作为我插入新语法节点的位置的参考。但是当我使用

foreach (var nodeToRemove in nodesToRemove)
{
editor.RemoveNode(directive);
}
editor.InsertBefore(untouchedNode, newNodesToAdd); // No exception here, only when getting the new document

为了调试它,我尝试找到引用节点,然后按照编辑器进行操作

var ns = editor.OriginalRoot.DescendantNodesAndSelf().OfType<NamespaceDeclarationSyntax>().FirstOrDefault(); // ns is not null
var IshouldExist = editor.OriginalRoot.GetCurrentNode(ns); //it is null, weird

我不知道为什么GetCurrentNode()在这里返回null。

在堆栈跟踪中,我注意到插入是在更改的文档中完成的,我认为这应该不是问题,因为如果我不尝试插入新节点,我用作引用的 SytaxNode 仍在更改的文档中。

And throws an exception with the following stacktrace:
System.InvalidCastException : Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax' to type 'Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax'.
at System.Linq.Enumerable.<CastIterator>d__97`1.MoveNext()
at System.Collections.Generic.List`1.InsertRange(Int32 index,IEnumerable`1 collection)
at Microsoft.CodeAnalysis.SyntaxList`1.InsertRange(Int32 index,IEnumerable`1 nodes)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.NodeListEditor.VisitList[TNode](SyntaxList`1 list)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCompilationUnit(CompilationUnitSyntax node)
at Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.BaseListEditor.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.NodeListEditor.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.InsertNodeInList(SyntaxNode root,SyntaxNode nodeInList,IEnumerable`1 nodesToInsert,Boolean insertBefore)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode.InsertNodesInListCore(SyntaxNode nodeInList,IEnumerable`1 nodesToInsert,Boolean insertBefore)
at Microsoft.CodeAnalysis.SyntaxNodeExtensions.InsertNodesBefore[TRoot](TRoot root,SyntaxNode nodeInList,IEnumerable`1 newNodes)
at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.InsertNodesBeforeInternal(SyntaxNode root,SyntaxNode declaration,IEnumerable`1 newDeclarations)
at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.<>c__DisplayClass183_0.<InsertNodesBefore>b__0(SyntaxNode r)
at Microsoft.CodeAnalysis.Editing.SyntaxGenerator.PreserveTrivia[TNode](TNode node,Func`2 nodeChanger)
at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.InsertNodesBefore(SyntaxNode root,SyntaxNode declaration,IEnumerable`1 newDeclarations)
at Microsoft.CodeAnalysis.Editing.SyntaxEditor.InsertChange.Apply(SyntaxNode root,SyntaxGenerator generator)
at Microsoft.CodeAnalysis.Editing.SyntaxEditor.GetChangedRoot()
at Microsoft.CodeAnalysis.Editing.DocumentEditor.GetChangedDocument()

我做错了什么?

今天早些时候我自己遇到了一个类似的问题。我没有使用 DocumentEditor,而是在文档对象本身上以更"原始"的性质执行操作,但是,当我尝试通过自定义谓词对 Using 指令进行排序时,我注意到如果我在插入新指令之前清除所有旧的 Using 指令,使用 *。插入前面或 *。InsertAfter,当引用节点不是Using 指令时,整个系统就会爆炸。(似乎在内部,它可能试图将引用节点视为 UsingDirectiveSyntax 对象,然后无法比较它,但这纯粹是我的猜想,基于异常细节......

如果您正在使用不再具有 Using 语句的文档对象,则需要考虑使用不同的方法来插入 using 语句:

List<UsingDirectiveSyntax> myNewUsingList = /* Construct Using List somehow */
NameSpaceDeclarationSyntax newNameSpace = myOldNameSpace.AddUsings(myUsingList);
CompilationUnitSyntax newCompileRootSyntax = (documentRoot as CompilationUnitSyntax).ReplaceNode(myOldNameSpace, newNameSpace);
return document.WithSyntaxRoot(newCompileRootSyntax);

这将与使用 DocumentEditor 实例时使用的语义不同,但由于我在很大程度上不熟悉当前正在修改的文档上的节点遍历,因此我不知道在将这种单独的方法翻译到您的需求时能提供多少帮助。

这对我来说有点烦人的代码,因为这种方法适用于不可变的对象,因此上面调用的所有内容都返回了正在操作的对象的新实例,并通过实现的方法参数进行定向修改。上面的代码部分,

document.WithSyntaxRoot(/*variable*/);

创建一个全新的 Document() 对象,其中包含表示从旧文档到新文档的更改的内部数据,以及您使用先前代码发起的所有替换、插入、删除等操作。

基于基于DocumentEditor方法的一些初步吐槽,使用VS 2019的SDK工具创建CodeRefactoring项目,DocumentEditor方法可能看起来更像这样:

DocumentEditor ed = await DocumentEditor.CreateAsync(document, cancellationToken);
NamespaceDeclarationSyntax nameSpace = ed.GetChangedRoot()
.DescendantNodes()
.FirstOrDefault(t => t.GetType() == typeof(NamespaceDeclarationSyntax)) as NamespaceDeclarationSyntax;
if (nameSpace != null)
{
NamespaceDeclarationSyntax modifiedWithMyUsings = nameSpace.AddUsings(myUsingLists);
ed.ReplaceNode(nameSpace, modifiedWithMyUsings);
}

最新更新