2016-12-10 15 views
1

Я пытаюсь разобрать файлы сценариев C#, предоставленные пользователями через Roslyn. Давайте предположим, что конечный пользователь предоставляет такой скрипт: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(); 

Тот же вопрос, когда я пытаюсь непосредственно заменяя

root.InsertNodesAfter(last_using, my_statement); 

вместо DocumentEditor.

Почему это не удается? Я не уверен, почему его попытка включить мое утверждение в директиву использования - могу ли я только добавлять узлы того же типа ?!

Может ли кто-нибудь дать мне указатель на то, как достичь этого лучше всего?

Большое спасибо!

+0

Я не возился с Roslyn, так что это просто догадка, но это может быть так, потому что C# не разрешает объявления переменных после использования директив (такая вещь будет глобальными переменными, которые C# не поддерживает) , Поэтому, когда он анализирует эту строку, он ожидает другого использования директивы (или декларации пространства имен), но вместо этого получает объявление переменной, что ему не нравится. – Abion47

+1

Обычно да - однако это допустимый синтаксис при анализе сценария. Поскольку я явно указал 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 }); 

InsertNodesBefore и InsertNodesAfter работа над списком узлов, поэтому узел, который вы хотите добавить до или после него, должно быть в пределах списка, а узел вы хотите вставить, должен быть из того же типа.

Комментарий метода упомянуть его (но не так ясно)

/// <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