c# - DocumentEditor InsertBefore 似乎无法在文档中找到节点
问题描述
我正在使用 roslyn 为 Visual Studio 开发代码重构。
我正在使用 DocumentEditor 类来删除一些 SyntaxNode,在删除它们之后,我正在插入一些新的。我正在使用我知道我没有删除的语法节点作为我插入新语法节点的位置的参考。但是我用的时候编辑器好像找不到
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,而是在 Document 对象本身上以更“原始”的性质执行操作,但是,当我尝试通过自定义谓词对 Using Directives 进行排序时,我注意到如果我清除了所有旧的在插入新的 using 之前使用指令,使用 *.InsertBefore 或 *.InsertAfter,当引用节点不是Using 指令时,整个系统就会爆炸。(似乎在内部它可能试图将引用节点视为 UsingDirectiveSyntax 对象,然后无法对其进行比较,但这是我的纯粹猜想,基于异常详细信息......)
如果您正在使用不再具有 Using 语句的 Document 对象,您将需要考虑使用不同的方法来插入 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);
}
推荐阅读
- javascript - 在php中使用foreach选择框时如何使用java脚本选择选择框元素
- angular - http://localhost:8000/api/... 的 Http 失败响应 500 角度 5 中的内部服务器错误
- ibm-cloud-infrastructure - SoftLayer:softlayer 资源 ID 是否因订单而异?
- php - 无法在php mysql的表中插入记录
- javascript - 如果第二个项目的 package.json 不在项目的根目录中,如何在 package.json 中添加 github 依赖项
- python - 如何将自定义张量流模型与 CV2 DNN 模块一起使用?
- jsp - 在jsp中包含jsp,单击jsp中的按钮时如何删除包含jsp?
- c++ - QImage:加载图像时自动检测格式
- python - 如何使用 Flask 会话在 Twilio 中分离应用程序用户之间的会话?
- arrays - laravel ->get() 没有正确返回关系