2016-12-10 7 views
1

私は、Roslynを介してユーザーから提供されたC#スクリプトファイルを解析することを目指しています。私は、任意のスクリプト内のできるだけ早い場所でいくつかの変数の初期化ステートメントを挿入する一般的な方法を探していますRoslyn - 変数宣言文をスクリプトの最初に挿入する方法(使用後)?

using System; 
return "Hello"; 

: のは、エンドユーザーは次のようにスクリプトを提供しますと仮定しましょう。 私の理解によれば、それは最後のusingステートメントの後になるでしょう。

例のために、「var xyz = 123;」を挿入するだけでよいとします。最古の場所にある。最終結果はこの場合は

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

どのようにすればよいですか?

私は以下を試みました。

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(); 

同じ問題私が代わりに直接DocumentEditorの

root.InsertNodesAfter(last_using, my_statement); 

と交換してみてください。

これはなぜ失敗するのですか?なぜ私のステートメントをusingディレクティブにキャストしようとしているのか分かりません - 同じタイプのノードだけを追加できますか?

誰でも私にこのベストを達成する方法のポインタを教えてもらえますか?

ありがとうございます!ノードのリストに

+0

私はRoslynに戸惑っていないので、これは単なる推測ですが、C#ではディレクティブを使用した後に変数宣言を許可しない可能性があります(C#ではサポートされていないグローバル変数) 。したがって、その行を解析するときには、別のディレクティブ(または名前空間宣言)を使用することを期待していますが、その代わりに変数宣言が得られます。 – Abion47

+1

通常はyesですが、これはスクリプトとして解析すると有効な構文です。私はプロジェクトの解析オプションでSourceCodeKind.Scriptを明示的に指定していたので、これらのルールで有効な構文変換は(うまくいけば)動作するはずです。 – Bogey

答えて

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 }); 

InsertNodesBeforeInsertNodesAfter仕事なので、あなたがそれの前または後に追加するノードは、リスト内になければならない、そして、挿入したいノードは、同じタイプである必要があります。あなたがしたい場合

方法のコメントそれを言及しない(しかし、その何それほど明確)

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

は、実際に置き換えるんsource codeを参照してください。

+0

素晴らしい。ありがとう、あなたのソリューションを非常に感謝します! – Bogey

関連する問題