2016-12-10 4 views
1

Roslyn을 통해 사용자가 제공 한 C# 스크립트 파일을 구문 분석하려고합니다. 나는 주어진 스크립트에서 가장 빠른 위치에서 몇 가지 변수 초기화 문을 삽입하는 일반적인 방법을 찾고 있어요Roslyn - 변수 선언 문을 스크립트 시작 부분에 삽입하는 방법 (사용 후)?

using System; 
return "Hello"; 

: 의 최종 사용자가 같은 스크립트를 제공합니다 가정하자. 제 생각에, 그것은 마지막으로 사용한 성명서 이후 꽤 될 것입니다.

예를 들어, "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