C# -> Roslyn -> DocumentEditor.ReplaceNode -> 代码缩进和



我有一个问题,当我使用DocumentEditor.ReplaceNode时,一切正常,但生成的代码很难阅读。

罗斯林 - 替换节点并修复空格

输出如下所示,同一行上有多个字符串:

using System;
using System.IO;
using System.Linq;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
string test5 = @"test symbols rn © @ {} [] <> | /  $£@!#¤%&/()=?` 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();

阿拉伯数字:

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 rn © @ {} [] <> | /  $£@!#¤%&/()=?`""; 
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;
}
}
}

规范化空格规范化当前对象的空格 - 而不是语法树中包含的对象。

例如,如果您调用 newVariable 上的空格与值规范化

string varTest2 = @"test var hello";

变量声明是否也在语法树中并不重要 - 重要的是当前上下文。规范化上述语句的空格基本上没有任何作用,因为没有 BlockStatement、声明或其他元素会创建缩进。

但是,如果您在包含作用域(例如方法)上调用规范化空格,则会得到以下内容:

static void Main(string[] args)
{
string test5 = @"test symbols rn © @ {} [] <> | /  $£@!#¤%&/()=?` 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()

当然,如果您有一些工件想要保留(例如,示例中的 DeclarationStatementStatement(DeclarationStatement) 之间的附加行),这将阻止您保留旧格式。

如果你想保留这种格式和注释,你可以尝试从原始语句中复制琐事(或琐事的一部分):

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

最新更新