From a1af199216857f83a593e13fa3de246be9b3effa Mon Sep 17 00:00:00 2001 From: Timothy Makkison Date: Wed, 22 Oct 2025 20:26:56 +0100 Subject: [PATCH 1/2] perf: only run `PreprocessorSymbols` when `#if` found --- Src/CSharpier.Core/CSharp/CSharpFormatter.cs | 68 ++++++++++++-------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/Src/CSharpier.Core/CSharp/CSharpFormatter.cs b/Src/CSharpier.Core/CSharp/CSharpFormatter.cs index 984a53912..4bbbe5680 100644 --- a/Src/CSharpier.Core/CSharp/CSharpFormatter.cs +++ b/Src/CSharpier.Core/CSharp/CSharpFormatter.cs @@ -44,6 +44,7 @@ public static Task FormatAsync( syntaxTree, (options ?? new()).ToPrinterOptions(), SourceCodeKind.Regular, + true, cancellationToken ); } @@ -67,11 +68,13 @@ CancellationToken cancellationToken ) { var initialSymbolSet = Array.Empty(); + var hasPreprocessorSymbols = code.Contains("#if"); return FormatAsync( ParseText(code, initialSymbolSet, sourceCodeKind, cancellationToken), printerOptions, sourceCodeKind, + hasPreprocessorSymbols, cancellationToken ); } @@ -99,6 +102,7 @@ internal static async Task FormatAsync( SyntaxTree syntaxTree, PrinterOptions printerOptions, SourceCodeKind sourceCodeKind, + bool hasPreprocessorSymbols, CancellationToken cancellationToken ) { @@ -165,36 +169,48 @@ bool TryGetCompilationFailure(out CodeFormatterResult compilationResult) .ReorderedUsingsWithDisabledText; var movedTrailingTrivia = printingContext.State.MovedTrailingTrivia; - foreach (var symbolSet in PreprocessorSymbols.GetSets(syntaxTree)) + if (hasPreprocessorSymbols) { - syntaxTree = ParseText(formattedCode, symbolSet, sourceCodeKind, cancellationToken); - - if (TryGetCompilationFailure(out result)) + foreach (var symbolSet in PreprocessorSymbols.GetSets(syntaxTree)) { - return result; - } + syntaxTree = ParseText( + formattedCode, + symbolSet, + sourceCodeKind, + cancellationToken + ); - var formattingContext2 = new PrintingContext - { - Options = new PrintingContext.PrintingContextOptions + if (TryGetCompilationFailure(out result)) { - LineEnding = lineEnding, - IndentSize = printerOptions.IndentSize, - UseTabs = printerOptions.UseTabs, - }, - }; - document = Node.Print( - await syntaxTree.GetRootAsync(cancellationToken), - formattingContext2 - ); - formattedCode = DocPrinter.DocPrinter.Print(document, printerOptions, lineEnding); - reorderedModifiers = - reorderedModifiers || formattingContext2.State.ReorderedModifiers; - reorderedUsingsWithDisabledText = - reorderedUsingsWithDisabledText - || formattingContext2.State.ReorderedUsingsWithDisabledText; - movedTrailingTrivia = - movedTrailingTrivia || formattingContext2.State.MovedTrailingTrivia; + return result; + } + + var formattingContext2 = new PrintingContext + { + Options = new PrintingContext.PrintingContextOptions + { + LineEnding = lineEnding, + IndentSize = printerOptions.IndentSize, + UseTabs = printerOptions.UseTabs, + }, + }; + document = Node.Print( + await syntaxTree.GetRootAsync(cancellationToken), + formattingContext2 + ); + formattedCode = DocPrinter.DocPrinter.Print( + document, + printerOptions, + lineEnding + ); + reorderedModifiers = + reorderedModifiers || formattingContext2.State.ReorderedModifiers; + reorderedUsingsWithDisabledText = + reorderedUsingsWithDisabledText + || formattingContext2.State.ReorderedUsingsWithDisabledText; + movedTrailingTrivia = + movedTrailingTrivia || formattingContext2.State.MovedTrailingTrivia; + } } return new CodeFormatterResult From 6287de28ce750b411e0e5341c44e44a3b0c681c5 Mon Sep 17 00:00:00 2001 From: Timothy Makkison Date: Sun, 26 Oct 2025 19:53:50 +0000 Subject: [PATCH 2/2] perf: check for `// csharpier-ignore` --- Src/CSharpier.Core/CSharp/CSharpFormatter.cs | 13 +++-- Src/CSharpier.Core/CSharp/CSharpScanner.cs | 47 +++++++++++++++++++ .../CSharp/SyntaxPrinter/CSharpierIgnore.cs | 19 ++++---- .../SyntaxPrinter/MembersWithForcedLines.cs | 27 ++++++----- .../CSharp/SyntaxPrinter/PrintingContext.cs | 3 ++ .../SyntaxPrinter/SeparatedSyntaxList.cs | 23 +++++---- .../SyntaxNodePrinters/AttributeList.cs | 6 ++- .../BaseMethodDeclaration.cs | 19 ++++---- Src/CSharpier.Core/Xml/XmlFormatter.cs | 1 + .../NodePrinterGenerator.sbntxt | 2 +- .../SyntaxPrinter/CSharpierIgnoreTests.cs | 2 + 11 files changed, 117 insertions(+), 45 deletions(-) create mode 100644 Src/CSharpier.Core/CSharp/CSharpScanner.cs diff --git a/Src/CSharpier.Core/CSharp/CSharpFormatter.cs b/Src/CSharpier.Core/CSharp/CSharpFormatter.cs index 4bbbe5680..ab2b3c3fa 100644 --- a/Src/CSharpier.Core/CSharp/CSharpFormatter.cs +++ b/Src/CSharpier.Core/CSharp/CSharpFormatter.cs @@ -44,7 +44,7 @@ public static Task FormatAsync( syntaxTree, (options ?? new()).ToPrinterOptions(), SourceCodeKind.Regular, - true, + new PrintingContext.CodeInformation(true, true), cancellationToken ); } @@ -68,13 +68,14 @@ CancellationToken cancellationToken ) { var initialSymbolSet = Array.Empty(); - var hasPreprocessorSymbols = code.Contains("#if"); + + var codeInformation = CSharpScanner.Scan(code); return FormatAsync( ParseText(code, initialSymbolSet, sourceCodeKind, cancellationToken), printerOptions, sourceCodeKind, - hasPreprocessorSymbols, + codeInformation, cancellationToken ); } @@ -102,7 +103,7 @@ internal static async Task FormatAsync( SyntaxTree syntaxTree, PrinterOptions printerOptions, SourceCodeKind sourceCodeKind, - bool hasPreprocessorSymbols, + PrintingContext.CodeInformation information, CancellationToken cancellationToken ) { @@ -160,6 +161,7 @@ bool TryGetCompilationFailure(out CodeFormatterResult compilationResult) IndentSize = printerOptions.IndentSize, UseTabs = printerOptions.UseTabs, }, + Information = information, }; var document = Node.Print(rootNode, printingContext); var formattedCode = DocPrinter.DocPrinter.Print(document, printerOptions, lineEnding); @@ -169,7 +171,7 @@ bool TryGetCompilationFailure(out CodeFormatterResult compilationResult) .ReorderedUsingsWithDisabledText; var movedTrailingTrivia = printingContext.State.MovedTrailingTrivia; - if (hasPreprocessorSymbols) + if (printingContext.Information.HasPreprocessorSymbols) { foreach (var symbolSet in PreprocessorSymbols.GetSets(syntaxTree)) { @@ -193,6 +195,7 @@ bool TryGetCompilationFailure(out CodeFormatterResult compilationResult) IndentSize = printerOptions.IndentSize, UseTabs = printerOptions.UseTabs, }, + Information = printingContext.Information, }; document = Node.Print( await syntaxTree.GetRootAsync(cancellationToken), diff --git a/Src/CSharpier.Core/CSharp/CSharpScanner.cs b/Src/CSharpier.Core/CSharp/CSharpScanner.cs new file mode 100644 index 000000000..b243cd580 --- /dev/null +++ b/Src/CSharpier.Core/CSharp/CSharpScanner.cs @@ -0,0 +1,47 @@ +using System.Buffers; +using CSharpier.Core.CSharp.SyntaxPrinter; + +namespace CSharpier.Core.CSharp; + +internal static class CSharpScanner +{ + private const string IfPreprocessor = "#if"; + private const string CSharpierIgnore = "// csharpier-ignore"; + +#if NET9_0_OR_GREATER + private static readonly SearchValues KeyValues = SearchValues.Create( + [IfPreprocessor, CSharpierIgnore], + StringComparison.Ordinal + ); + + public static PrintingContext.CodeInformation Scan(string code) + { + var index = code.AsSpan().IndexOfAny(KeyValues); + if (index < 0) + { + return new PrintingContext.CodeInformation(false, false); + } + + var slice = code.AsSpan(index); + if (slice.StartsWith(IfPreprocessor)) + { + return new PrintingContext.CodeInformation( + true, + slice.Contains(CSharpierIgnore.AsSpan(), StringComparison.Ordinal) + ); + } + + return new PrintingContext.CodeInformation( + slice.Contains(IfPreprocessor.AsSpan(), StringComparison.Ordinal), + true + ); + } +#else + public static PrintingContext.CodeInformation Scan(string code) + { + var hasPreprocessor = code.Contains(IfPreprocessor); + var hasCSharpierIgnore = code.Contains(CSharpierIgnore); + return new PrintingContext.CodeInformation(hasPreprocessor, hasCSharpierIgnore); + } +#endif +} diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/CSharpierIgnore.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/CSharpierIgnore.cs index f295deba0..9cb7de263 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/CSharpierIgnore.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/CSharpierIgnore.cs @@ -78,15 +78,18 @@ PrintingContext context foreach (var node in list) { - if (Token.HasLeadingCommentMatching(node, IgnoreEndRegex)) + if (context.Information.HasCSharpierIgnore) { - statements.Add(unFormattedCode.AsSpan().Trim().ToString()); - unFormattedCode.Clear(); - printUnformatted = false; - } - else if (Token.HasLeadingCommentMatching(node, IgnoreStartRegex)) - { - printUnformatted = true; + if (Token.HasLeadingCommentMatching(node, IgnoreEndRegex)) + { + statements.Add(unFormattedCode.AsSpan().Trim().ToString()); + unFormattedCode.Clear(); + printUnformatted = false; + } + else if (Token.HasLeadingCommentMatching(node, IgnoreStartRegex)) + { + printUnformatted = true; + } } if (printUnformatted) diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/MembersWithForcedLines.cs index 6055aa8f1..6fb318699 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/MembersWithForcedLines.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/MembersWithForcedLines.cs @@ -34,21 +34,24 @@ public static List Print( var skipAddingLineBecauseIgnoreEnded = false; var member = members[memberIndex]; - if (Token.HasLeadingCommentMatching(member, CSharpierIgnore.IgnoreEndRegex)) + if (context.Information.HasCSharpierIgnore) { - skipAddingLineBecauseIgnoreEnded = true; - result.Add(unFormattedCode.AsSpan().Trim().ToString()); - unFormattedCode.Clear(); - printUnformatted = false; - } - else if (Token.HasLeadingCommentMatching(member, CSharpierIgnore.IgnoreStartRegex)) - { - if (!printUnformatted && memberIndex > 0) + if (Token.HasLeadingCommentMatching(member, CSharpierIgnore.IgnoreEndRegex)) + { + skipAddingLineBecauseIgnoreEnded = true; + result.Add(unFormattedCode.AsSpan().Trim().ToString()); + unFormattedCode.Clear(); + printUnformatted = false; + } + else if (Token.HasLeadingCommentMatching(member, CSharpierIgnore.IgnoreStartRegex)) { - result.Add(Doc.HardLine); - result.Add(ExtraNewLines.Print(member)); + if (!printUnformatted && memberIndex > 0) + { + result.Add(Doc.HardLine); + result.Add(ExtraNewLines.Print(member)); + } + printUnformatted = true; } - printUnformatted = true; } if (printUnformatted) diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/PrintingContext.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/PrintingContext.cs index e0f5176d4..8d006cbb5 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/PrintingContext.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/PrintingContext.cs @@ -7,6 +7,7 @@ namespace CSharpier.Core.CSharp.SyntaxPrinter; internal class PrintingContext { public required PrintingContextOptions Options { get; init; } + public required CodeInformation Information { get; init; } public PrintingContextState State { get; } = new(); private readonly Dictionary groupNumberByValue = []; @@ -59,4 +60,6 @@ public class PrintingContextState } public record TrailingCommaContext(SyntaxTrivia TrailingComment, Doc PrintedTrailingComma); + + public record struct CodeInformation(bool HasPreprocessorSymbols, bool HasCSharpierIgnore); } diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SeparatedSyntaxList.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SeparatedSyntaxList.cs index 0868c64b6..a638c1651 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SeparatedSyntaxList.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SeparatedSyntaxList.cs @@ -56,19 +56,22 @@ private static Doc Print( { var member = list[x]; - if (Token.HasLeadingCommentMatching(member, CSharpierIgnore.IgnoreEndRegex)) + if (context.Information.HasCSharpierIgnore) { - docs.Append(unFormattedCode.AsSpan().Trim().ToString()); - unFormattedCode.Clear(); - printUnformatted = false; - } - else if (Token.HasLeadingCommentMatching(member, CSharpierIgnore.IgnoreStartRegex)) - { - if (!printUnformatted && x > 0) + if (Token.HasLeadingCommentMatching(member, CSharpierIgnore.IgnoreEndRegex)) + { + docs.Append(unFormattedCode.AsSpan().Trim().ToString()); + unFormattedCode.Clear(); + printUnformatted = false; + } + else if (Token.HasLeadingCommentMatching(member, CSharpierIgnore.IgnoreStartRegex)) { - docs.Append(Doc.HardLine); + if (!printUnformatted && x > 0) + { + docs.Append(Doc.HardLine); + } + printUnformatted = true; } - printUnformatted = true; } if (printUnformatted) diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/AttributeList.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/AttributeList.cs index 2de4be6b6..9a95413ca 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/AttributeList.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/AttributeList.cs @@ -8,7 +8,11 @@ internal static class AttributeList { public static Doc Print(AttributeListSyntax node, PrintingContext context) { - if (node.Parent is BaseMethodDeclarationSyntax && CSharpierIgnore.HasIgnoreComment(node)) + if ( + context.Information.HasCSharpierIgnore + && node.Parent is BaseMethodDeclarationSyntax + && CSharpierIgnore.HasIgnoreComment(node) + ) { return CSharpierIgnore.PrintWithoutFormatting(node, context).Trim(); } diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs index 32603bdda..4a4e9b46f 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs @@ -118,19 +118,22 @@ void PrintMethodUnformattedWithoutAttributes(SyntaxTriviaList syntaxTriviaList) ); } - if (modifiers is { Count: > 0 }) + if (context.Information.HasCSharpierIgnore) { - if (CSharpierIgnore.HasIgnoreComment(modifiers.Value[0])) + if (modifiers is { Count: > 0 }) { - PrintMethodUnformattedWithoutAttributes(modifiers.Value[0].LeadingTrivia); + if (CSharpierIgnore.HasIgnoreComment(modifiers.Value[0])) + { + PrintMethodUnformattedWithoutAttributes(modifiers.Value[0].LeadingTrivia); + return Doc.Group(docs); + } + } + else if (returnType is not null && CSharpierIgnore.HasIgnoreComment(returnType)) + { + PrintMethodUnformattedWithoutAttributes(returnType.GetLeadingTrivia()); return Doc.Group(docs); } } - else if (returnType is not null && CSharpierIgnore.HasIgnoreComment(returnType)) - { - PrintMethodUnformattedWithoutAttributes(returnType.GetLeadingTrivia()); - return Doc.Group(docs); - } } if (modifiers is { Count: > 0 }) diff --git a/Src/CSharpier.Core/Xml/XmlFormatter.cs b/Src/CSharpier.Core/Xml/XmlFormatter.cs index 0fe6d6792..8b9435739 100644 --- a/Src/CSharpier.Core/Xml/XmlFormatter.cs +++ b/Src/CSharpier.Core/Xml/XmlFormatter.cs @@ -36,6 +36,7 @@ PrinterOptions printerOptions IndentSize = printerOptions.IndentSize, UseTabs = printerOptions.UseTabs, }, + Information = new PrintingContext.CodeInformation(false, false), }; var doc = Node.Print(rootNode, printingContext); var formattedXml = DocPrinter.DocPrinter.Print(doc, printerOptions, lineEnding); diff --git a/Src/CSharpier.Generators/NodePrinterGenerator.sbntxt b/Src/CSharpier.Generators/NodePrinterGenerator.sbntxt index a37650ef7..97b35d5c5 100644 --- a/Src/CSharpier.Generators/NodePrinterGenerator.sbntxt +++ b/Src/CSharpier.Generators/NodePrinterGenerator.sbntxt @@ -24,7 +24,7 @@ namespace CSharpier.Core.CSharp.SyntaxPrinter throw new InTooDeepException(); } - if (CSharpierIgnore.IsNodeIgnored(syntaxNode)) + if (context.Information.HasCSharpierIgnore && CSharpierIgnore.IsNodeIgnored(syntaxNode)) { return CSharpierIgnore.PrintWithoutFormatting(syntaxNode, context).Trim(); } diff --git a/Src/CSharpier.Tests/CSharp/SyntaxPrinter/CSharpierIgnoreTests.cs b/Src/CSharpier.Tests/CSharp/SyntaxPrinter/CSharpierIgnoreTests.cs index 1b68c76f5..4b8349b97 100644 --- a/Src/CSharpier.Tests/CSharp/SyntaxPrinter/CSharpierIgnoreTests.cs +++ b/Src/CSharpier.Tests/CSharp/SyntaxPrinter/CSharpierIgnoreTests.cs @@ -74,6 +74,8 @@ private static string PrintWithoutFormatting(string code) IndentSize = 4, UseTabs = false, }, + + Information = new PrintingContext.CodeInformation(false, false), } ) .ReplaceLineEndings("\n");