使用Roslyn处理C#代码如何为region和endregion添加缩进对齐
问题原因
Roslyn 内置的NormalizeWhitespace()方法默认不会为#region、#endregion这类预处理指令添加缩进,这类指令默认会被对齐到行首,因此原有代码自带的和你手动新增的指令都会出现缩进丢失的问题。
修改方案
你只需要调整两处代码即可实现自动缩进对齐:
- 给手动创建的region/endregion指令添加弹性前导空白,让
NormalizeWhitespace()可以自动计算匹配的缩进值 - 增加简单的语法重写器,统一给所有预处理指令添加弹性前导空白,兼容处理原有代码中的region
具体修改代码
第一步:调整手动创建region/endregion的逻辑
将你原来创建指令的代码替换为以下内容:
// 给新增的region添加弹性前导空白,NormalizeWhitespace会自动填充对应层级的缩进 var beginRegion = SyntaxFactory.Trivia(SyntaxFactory.RegionDirectiveTrivia(true) .WithLeadingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed, SyntaxFactory.ElasticWhitespace("")) .WithTrailingTrivia(new[] { SyntaxFactory.Space, SyntaxFactory.PreprocessingMessage("Classes internes") })); var endRegion = SyntaxFactory.Trivia(SyntaxFactory.EndRegionDirectiveTrivia(true) .WithLeadingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed, SyntaxFactory.ElasticWhitespace("")));
第二步:添加语法重写器处理原有代码的region
在ConvertRoslyn类同级新增以下重写器类:
public class PreprocessorIndentRewriter : CSharpSyntaxRewriter { public override SyntaxNode VisitDirectiveTrivia(DirectiveTriviaSyntax node) { // 仅处理region/endregion指令 if (node.IsKind(SyntaxKind.RegionDirectiveTrivia) || node.IsKind(SyntaxKind.EndRegionDirectiveTrivia)) { // 给原有指令添加弹性前导空白 return node.WithLeadingTrivia(SyntaxFactory.ElasticWhitespace("")); } return base.VisitDirectiveTrivia(node); } }
第三步:调用重写器后再执行格式化
将你原来生成结果的代码调整如下:
var compilationUnit = SyntaxFactory.CompilationUnit() .WithUsings(root.Usings) .AddMembers(SyntaxFactory.NamespaceDeclaration(SyntaxFactory.IdentifierName("TestNamespace")) .AddMembers(aspClass .AddMembers(otherMembers.ToArray()) .WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken) .WithLeadingTrivia(endRegion))) ); // 先调用重写器处理所有预处理指令 var processedUnit = (CompilationUnitSyntax)new PreprocessorIndentRewriter().Visit(compilationUnit); // 再执行格式化 result = processedUnit.NormalizeWhitespace().ToFullString();
修改后运行代码即可得到你预期的缩进效果。
内容的提问来源于stack exchange,提问作者Philippe Auriou
相关产品推荐
相关产品推荐

