C# - >罗斯林 - > DocumentEditor.ReplaceNode - >代码缩进和格式

时间:2018-03-22 09:17:57

标签: c# compilation roslyn

我遇到的问题是,当我使用DocumentEditor.ReplaceNode时,一切正常,但生成的代码难以阅读。

Roslyn - replace node and fix the whitespaces

输出看起来像这样,在同一行上有几个字符串:

using System;
using System.IO;
using System.Linq;
using System.Text;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {

            string test5 = @"test symbols \r\n © @ {} [] <> | / \ $£@!\#¤%&/()=?` hello";
            string varTest1 = @"test var hello"; string varTest2 = @"test var hello";
            string test1 = @"test string hello";
            string test2 = @"test String hello"; string test3 = @"test const hello"; string test4 = @"test readonly hello";
            int i = 0;

            var i2 = 0;
        }

    }
}

我可以通过在字符串的末尾添加{System.Environment.NewLine}并删除所有格式化来获取新行,但代码不会缩进。

我尝试过:

1:

var newVariable = SyntaxFactory.ParseStatement($"string {variable.Identifier.ValueText} = @\"{value + " hello"}\";").WithAdditionalAnnotations(Formatter.Annotation);

newVariable = newVariable.NormalizeWhitespace();

2:

var newVariable = SyntaxFactory.ParseStatement($"string {variable.Identifier.ValueText} = @\"{value + " hello"}\";").WithAdditionalAnnotations(Formatter.Annotation);

3:

var newVariable = SyntaxFactory.ParseStatement($"string {variable.Identifier.ValueText} = @\"{value + " hello"}\";").WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);

newVariable = newVariable.NormalizeWhitespace();

4:

var newVariable = SyntaxFactory.ParseStatement($"string {variable.Identifier.ValueText} = @\"{value + " hello"}\";");

newVariable = newVariable.NormalizeWhitespace();

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Text;

namespace CodeAnalysisApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var workspace = new AdhocWorkspace();
            var projectId = ProjectId.CreateNewId();
            var versionStamp = VersionStamp.Create();
            var projectInfo = ProjectInfo.Create(projectId, versionStamp, "NewProject", "projName", LanguageNames.CSharp);
            var newProject = workspace.AddProject(projectInfo);

            var sourceText = SourceText.From(
                @"
                  using System;
                  using System.IO;
                  using System.Linq;
                  using System.Text;

                  namespace HelloWorld
                  {
                      class Program
                      {
                          static void Main(string[] args)
                          {

                              string test5 = ""test symbols \r\n © @ {} [] <> | / \ $£@!\#¤%&/()=?`""; 

                              var varTest1 = ""test var"";

                              var varTest2 = ""test var"";

                              string test1 = ""test string"";

                              String test2 = ""test String"";

                              const string test3 = ""test const""; 

                              readonly string test4 = ""test readonly""; 

                              int i = 0;

                              var i2 = 0;
                          }

                      }
                  }");

            var document = workspace.AddDocument(newProject.Id, "NewFile.cs", sourceText);
            var syntaxRoot = document.GetSyntaxRootAsync().Result;

            var root = (CompilationUnitSyntax)syntaxRoot;

            var editor = DocumentEditor.CreateAsync(document).Result;


            var localDeclaration = new LocalDeclarationVirtualizationVisitor();
            localDeclaration.Visit(root);

            var localDeclarations = localDeclaration.LocalDeclarations;

            foreach (var localDeclarationStatementSyntax in localDeclarations)
            {
                foreach (VariableDeclaratorSyntax variable in localDeclarationStatementSyntax.Declaration.Variables)
                {

                    var stringKind = variable.Initializer.Value.Kind();

                    //Replace string variables
                    if (stringKind == SyntaxKind.StringLiteralExpression)
                    {
                        //Remove " from string
                        var value = variable.Initializer.Value.ToString().Remove(0, 1);
                        value = value.Remove(value.Length - 1, 1);

                        var newVariable = SyntaxFactory.ParseStatement($"string {variable.Identifier.ValueText} = @\"{value + " hello"}\";").WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);

                        newVariable = newVariable.NormalizeWhitespace();

                        editor.ReplaceNode(variable, newVariable);

                        Console.WriteLine($"Key: {variable.Identifier.Value} Value:{variable.Initializer.Value}");
                    }
                }
            }

            var newDocument = editor.GetChangedDocument();

            var text = newDocument.GetTextAsync().Result.ToString();
        }
    }

    class LocalDeclarationVirtualizationVisitor : CSharpSyntaxRewriter
    {
        public LocalDeclarationVirtualizationVisitor()
        {
            LocalDeclarations = new List<LocalDeclarationStatementSyntax>();
        }

        public List<LocalDeclarationStatementSyntax> LocalDeclarations { get; set; }

        public override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
        {
            node = (LocalDeclarationStatementSyntax)base.VisitLocalDeclarationStatement(node);
            LocalDeclarations.Add(node);
            return node;
        }
    }
}

1 个答案:

答案 0 :(得分:1)

Normalize Whitespace规范化当前对象的空白 - 而不是SyntaxTree中包含的对象。

例如,如果要使用值

调用newVariable上的规范化空格
string varTest2 = @"test var hello";

变量声明也在语法树中并不重要 - 重要的是当前上下文。规范化上述语句的空格基本上没有任何意义,因为没有BlockStatements,Declarations或其他会产生缩进的元素。

但是,如果您在包含范围上调用规范化空格,例如方法,则可以获得以下内容:

static void Main(string[] args)
{
    string test5 = @"test symbols \r\n © @ {} [] <> | / \ $£@!\#¤%&/()=?` hello";
    string varTest1 = @"test var hello";
    string varTest2 = @"test var hello";
    string test1 = @"test string hello";
    string test2 = @"test String hello";
    string test3 = @"test const hello";
    string test4 = @"test readonly hello";
    int i = 0;
    var i2 = 0;
}

正如您所看到的,这将为您提供正确的缩进方法。因此,为了获得格式正确的文档,您必须在完成其他所有操作后在SyntaxRoot上调用NormalizeWhitespace:

editor.GetChangedRoot().NormalizeWhitespace().ToFullString()

如果你想保留一些神器(例如你样本中的声明声明之间的附加行),这当然会阻止你保留旧的格式。

如果你想保留这种格式和评论,你可以尝试从原始陈述中复制琐事(或部分琐事):

foreach (var localDeclarationStatementSyntax in localDeclarations)
{
    foreach (VariableDeclaratorSyntax variable in localDeclarationStatementSyntax.Declaration.Variables)
    {

        var stringKind = variable.Initializer.Value.Kind();

        //Replace string variables
        if (stringKind == SyntaxKind.StringLiteralExpression)
        {
            //Remove " from string
            var value = variable.Initializer.Value.ToString().Remove(0, 1);
            value = value.Remove(value.Length - 1, 1);

            var newVariable = SyntaxFactory.ParseStatement($"string {variable.Identifier.ValueText} = @\"{value + " hello"}\";").WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);

            newVariable = newVariable.NormalizeWhitespace();

            // This is new, copies the trivia (indentations, comments, etc.)
            newVariable = newVariable.WithLeadingTrivia(localDeclarationStatementSyntax.GetLeadingTrivia());
            newVariable = newVariable.WithTrailingTrivia(localDeclarationStatementSyntax.GetTrailingTrivia());


            editor.ReplaceNode(variable, newVariable);

            Console.WriteLine($"Key: {variable.Identifier.Value} Value:{variable.Initializer.Value}");
        }
    }
}