Roslyn - how to insert variable declaration statements into beginning of script (after usings)?

半世苍凉 提交于 2019-12-11 03:28:55

问题


I'm aiming to parse C# script files supplied by users via Roslyn. Let's assume the end user provides a script like:

using System;
return "Hello";

I'm looking for a general way to insert a few variable initialization statements at the earliest possible location in any given script. To my understanding, that would pretty much be after the last using statement.

For the sake of the example, let's assume I just need to insert "var xyz = 123;" at the earliest location. So the end result should, in this case, be

using System;
var xyz = 123;
return "Hello";

How could I do that?

I tried the following;

Solution solution = new AdhocWorkspace().CurrentSolution;
var project = solution.AddProject("projectName", "assemblyName", LanguageNames.CSharp)
     .WithMetadataReferences(new[] {MetadataReference.CreateFromFile(typeof(object).Assembly.Location) })
     .WithParseOptions(new CSharpParseOptions(kind: Microsoft.CodeAnalysis.SourceCodeKind.Script));

// scriptCode contains the user script input, e.g.:
// using System;
// return "Hello";
Document document = project.AddDocument("SCRIPT-TEMP-DOCUMENT.cs", scriptCode);
var root = document.GetSyntaxRootAsync().Result;

var my_statement = SyntaxFactory.ParseStatement("var xyz = 123;");

// will return the node: "using System;"
var last_using = root.DescendantNodes().Where(x => x is UsingDirectiveSyntax).Last();

var documentEditor = DocumentEditor.CreateAsync(document).Result;
documentEditor.InsertAfter(last_using, my_statement);

// This step will throw an exception:
// An exception of type 'System.InvalidCastException' occurred in System.Core.dll but was not handled in user code
// non-English message, so losely translated --> Additional information:  object of type "Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax" cannot be converted to "Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax"
var newDocument = documentEditor.GetChangedDocument();

Same issue when I try directly replacing with

root.InsertNodesAfter(last_using, my_statement);

instead of the DocumentEditor.

Why does this fail? I'm not sure why its trying to cast my statement into a using directive - can I only append nodes of the same type?!

Could anybody give me a pointer of how to achieve this best?

Thanks a lot!


回答1:


SyntaxTree tree = CSharpSyntaxTree.ParseText(
    @"using System;
    return 1;", new CSharpParseOptions(LanguageVersion.CSharp6, DocumentationMode.Parse, SourceCodeKind.Script)
);

var root = (CompilationUnitSyntax)tree.GetRoot();
var global = SyntaxFactory.GlobalStatement(SyntaxFactory.ParseStatement("var xyz = 123;"));
root = root.InsertNodesBefore(root.Members.First(), new SyntaxNode[] { global });

InsertNodesBefore and InsertNodesAfter work on list of nodes, so the node that you want to add before or after it, must be within a list, and the node you want to insert, must be from the same type.

The comment of the method mention it (but its no so clear)

/// <param name="nodeInList">The node to insert after; a descendant of the root node an element of a list member.</param>

See the source code that actually do the replacing if you want.



来源:https://stackoverflow.com/questions/41079449/roslyn-how-to-insert-variable-declaration-statements-into-beginning-of-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!