问题
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