Roslyn - исправить ошибку в документе, заменив текстовый диапазон в корне синтаксиса

У меня есть немного конкретного здесь, в основном, я загружаю в специальное рабочее пространство и получаю ошибки, связанные с определенным значением в компиляции, потому что я точно знаю, какой текст мне нужно заменить, но не где. Код ниже.

public static async Task<Solution> UpdateEntityReferences(Solution solution, ProjectId servicesId, string oldValue, string newValue)
{
     var project = solution.GetProject(servicesId);
     var compilation = await project.GetCompilationAsync();
     var diagnostics = compilation.GetDiagnostics().Where(diag => diag.GetMessage().Contains($"'{oldValue}'"));

     foreach (var diagnostic in diagnostics)
     {
         var errorLineSpan = diagnostic.Location.GetLineSpan();
         var document = project.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
         var syntaxRoot = await document.GetSyntaxRootAsync();
         var errorSpan = errorLineSpan.Span;
     }
     return solution;
}

Итак, с кодом до сих пор я получаю местоположение ошибки и в основном хочу вернуть новую версию этого документа с заменой «errorSpan» текстом «newValue», но не могу найти способ сделать это, возможно ли это ?

РЕДАКТИРОВАТЬ: с помощью Получите указанный SyntaxNode номер строки в SyntaxTree Я могу получить SyntaxNode из SyntaxTree и должен иметь возможность заменить текстовый диапазон (цикл for становится показанным ниже), но это не работает.

foreach (var diagnostic in diagnostics)
{
    var errorLineSpan = diagnostic.Location.GetLineSpan();
    var document = project.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
    var syntaxTree = await document.GetSyntaxTreeAsync();

    var errorSpan = errorLineSpan.Span;
    var lineSpan = syntaxTree.GetText().Lines[errorSpan.Start.Line].Span;

    var node = syntaxTree.GetRoot().DescendantNodes(lineSpan)
                .First(n => lineSpan.Contains(n.FullSpan));

    var errorTextSpan = TextSpan.FromBounds(errorSpan.Start.Character, errorSpan.End.Character);

    var newNodeText = node.GetText().Replace(errorTextSpan, newValue);
}

Текст заменяется (неправильно в половине случаев), а затем у меня остается объект SourceText, который я не могу понять, как заменить в документе. Любые идеи?


person James Morrison    schedule 27.07.2018    source источник
comment
Что произойдет, если вы просто поместите ‹‹errorLineSpan.Span = newValue››, не назначая его для errorSpan?   -  person nuriselcuk    schedule 27.07.2018
comment
@nuriselcuk выдаст ошибку, потому что я попытаюсь присвоить строку LinePositionSpan, я, вероятно, тоже не смогу получить из нее документ.   -  person James Morrison    schedule 27.07.2018
comment
как насчет toString из LinePositionSpan?   -  person nuriselcuk    schedule 27.07.2018
comment
Я уверен, что вы хотите использовать Document.WithText(SourceText text) для получения новой версии Document, затем использовать Project.RemoveDocument(...) для удаления старой версии и Project.AddDocument(...) для добавления новой. Кстати, если вы попытаетесь написать собственный провайдер исправления кода, будет лучше, если вы будете исправлять документ, а не решение.   -  person George Alexandria    schedule 27.07.2018
comment
@GeorgeAlexandria Я, вероятно, буду изменять синтаксический узел, и мне нужно будет заменить корень синтаксиса документа. Мне нужно выяснить, как заменить текстовый диапазон из диагностики ошибок компиляции и после этого вернуть полный документ.   -  person James Morrison    schedule 28.07.2018
comment
@JamesMorrison, если вы хотите заменить SyntaxRoot, просто используйте Document.WithSyntaxRoot(newRoot) или если вы хотите заменить SourceText, используйте Document.WithText(SourceText text), а затем, в обоих случаях, добавьте новый документ в текущий проект и удалите старый документ: Project.AddDocument(...), ... Project.RemoveDocument(...);. Если у вас все еще есть проблема, сообщите об этом.   -  person George Alexandria    schedule 28.07.2018
comment
Проблема, с которой я сталкиваюсь, связана с заменой текстового сообщения об ошибке   -  person James Morrison    schedule 28.07.2018


Ответы (1)


В конце концов, получилось, поэтому вы не можете заменить textspan и вернуть документ, не используя что-то вроде редактор документов, потому что вам нужно заменить SyntaxNode, и нет гарантии, что размещение символов в LineSpan по-прежнему правильное, если узел захвачен.

Решение состоит в том, чтобы просто использовать номер строки в LineSpan, чтобы захватить узел в этой строке, а затем отфильтровать токен с помощью LINQ. Как только точный узел захвачен, его можно заменить в родительском узле, а затем в синтаксисеRoot и, наконец, в документе. Код ниже.

foreach (var diagnostic in diagnostics)
{
     servicesProject = solution.GetProject(servicesId);
     var errorLineSpan = diagnostic.Location.GetLineSpan();
     var document = servicesProject.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
     var syntaxTree = await document.GetSyntaxTreeAsync();
     var errorSpan = errorLineSpan.Span;
     var lineSpan = syntaxTree.GetText().Lines[errorSpan.Start.Line].Span;

     var node = syntaxTree.GetRoot().DescendantNodes(lineSpan)
.First(n => lineSpan.Contains(n.FullSpan)).DescendantNodes()
.OfType<IdentifierNameSyntax>().FirstOrDefault(c => c.Identifier.Text == oldValue);

        var newNode = node.ReplaceNode(node, SyntaxFactory.IdentifierName(newValue));
        var newSyntaxRoot = syntaxTree.GetRoot().ReplaceNode(node, newNode);
        document = document.WithSyntaxRoot(newSyntaxRoot);
        solution = document.Project.Solution;           
}
person James Morrison    schedule 30.07.2018