diff --git a/Docs/Ignore.md b/Docs/Ignore.md index 4121e8721..f80108e8d 100644 --- a/Docs/Ignore.md +++ b/Docs/Ignore.md @@ -36,7 +36,7 @@ public class ClassName // csharpier-ignore public void MethodName( ) { var unformatted = ""; -} + } public void MethodName() { @@ -52,3 +52,41 @@ public class ClassName } ``` + +Use a ranged ignore to exclude multiple lines from formatting. A range is valid around statements and members. +```c# +// csharpier-ignore-start +public class Unformatted1 { } +public class Unformatted2 { } +// csharpier-ignore-end + +public class ClassName +{ + // csharpier-ignore-start + private string unformatted1; + private string unformatted2; + public void MethodName( ) { + var unformatted = ""; + } + // csharpier-ignore-end + public void MethodName() + { + // csharpier-ignore-start + var unformatted1 = true; + var unformatted2 = true; + if (false) { + return; + } + // csharpier-ignore-end + if (true) + { + // csharpier-ignore-start + var unformatted3 = true; + var unformatted4 = true; + // csharpier-ignore-end + + var formatted = true; + } + } +} +``` \ No newline at end of file diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore.cst index a4db609b0..c968bea30 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore.cst @@ -17,4 +17,63 @@ public class ClassName var unformatted = true; } } + + void MethodName() + { + var formatMe = true; + // csharpier-ignore-start + var unformatted = true; + var unformatted = true; + // csharpier-ignore-end + var formatMe = true; + } + + void MethodName() + { + var formatMe = true; + // csharpier-ignore-start + var unformatted = true; + var unformatted = true; + // csharpier-ignore-end + } + + void MethodName() + { + var formatMe = true; + // csharpier-ignore-start + var unformatted = true; + var unformatted = true; + } +} + +public class ClassName2 +{ + // csharpier-ignore-start + private string unformatted1; + // csharpier-ignore-end + + private string formatMe; + // csharpier-ignore-start + private string noNewLine; + // csharpier-ignore-end + + // csharpier-ignore-start + private string keepNewLine; + // csharpier-ignore-end + + public void Method() + { + // csharpier-ignore-start + string unformatted1; + // csharpier-ignore-end + + string formatMe; + // csharpier-ignore-start + string noNewLine; + // csharpier-ignore-end + + // csharpier-ignore-start + string keepNewLine; + // csharpier-ignore-end + } } diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore.expected.cst new file mode 100644 index 000000000..30c75656b --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore.expected.cst @@ -0,0 +1,79 @@ +// csharpier-ignore +public class Unformatted { } + +public class ClassName +{ + // csharpier-ignore + private string unformatted; + + public void MethodName() + { + // csharpier-ignore + var unformatted = true; + + if (true) + { + // csharpier-ignore + var unformatted = true; + } + } + + void MethodName() + { + var formatMe = true; + // csharpier-ignore-start + var unformatted = true; + var unformatted = true; + // csharpier-ignore-end + var formatMe = true; + } + + void MethodName() + { + var formatMe = true; + // csharpier-ignore-start + var unformatted = true; + var unformatted = true; + // csharpier-ignore-end + } + + void MethodName() + { + var formatMe = true; + // csharpier-ignore-start + var unformatted = true; + var unformatted = true; + } +} + +public class ClassName2 +{ + // csharpier-ignore-start + private string unformatted1; + // csharpier-ignore-end + + private string formatMe; + // csharpier-ignore-start + private string noNewLine; + // csharpier-ignore-end + + // csharpier-ignore-start + private string keepNewLine; + // csharpier-ignore-end + + public void Method() + { + // csharpier-ignore-start + string unformatted1; + // csharpier-ignore-end + + string formatMe; + // csharpier-ignore-start + string noNewLine; + // csharpier-ignore-end + + // csharpier-ignore-start + string keepNewLine; + // csharpier-ignore-end + } +} diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_CompilationUnit.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_CompilationUnit.cst new file mode 100644 index 000000000..3c5b0bf7a --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_CompilationUnit.cst @@ -0,0 +1,8 @@ +namespace Namespace +{ + // csharpier-ignore-start + public class Unformatted { } + // csharpier-ignore-end + + public class FormatMe { } +} \ No newline at end of file diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_CompilationUnit.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_CompilationUnit.expected.cst new file mode 100644 index 000000000..681e8f917 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_CompilationUnit.expected.cst @@ -0,0 +1,8 @@ +namespace Namespace +{ + // csharpier-ignore-start + public class Unformatted { } + // csharpier-ignore-end + + public class FormatMe { } +} diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_TopLevel.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_TopLevel.cst new file mode 100644 index 000000000..2ffb105e5 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_TopLevel.cst @@ -0,0 +1,19 @@ +using System; + +// csharpier-ignore-start +string unformatted1; +// csharpier-ignore-end + + string formatMe; +// csharpier-ignore-start + string noNewLine; +// csharpier-ignore-end + +// csharpier-ignore-start + string keepNewLine; +// csharpier-ignore-end + + +// csharpier-ignore-start + string removeNewLine; +// csharpier-ignore-end \ No newline at end of file diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_TopLevel.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_TopLevel.expected.cst new file mode 100644 index 000000000..8ca05435c --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CSharpierIgnore_TopLevel.expected.cst @@ -0,0 +1,19 @@ +using System; + +// csharpier-ignore-start +string unformatted1; +// csharpier-ignore-end + +string formatMe; +// csharpier-ignore-start + string noNewLine; +// csharpier-ignore-end + +// csharpier-ignore-start +string keepNewLine; +// csharpier-ignore-end + + +// csharpier-ignore-start +string removeNewLine; +// csharpier-ignore-end diff --git a/Src/CSharpier/SyntaxPrinter/CSharpierIgnore.cs b/Src/CSharpier/SyntaxPrinter/CSharpierIgnore.cs index 9248ff7a4..b6962addf 100644 --- a/Src/CSharpier/SyntaxPrinter/CSharpierIgnore.cs +++ b/Src/CSharpier/SyntaxPrinter/CSharpierIgnore.cs @@ -17,13 +17,34 @@ or NamespaceDeclarationSyntax return false; } - return syntaxNode - .GetLeadingTrivia() - .Any( - o => - o.RawSyntaxKind() is SyntaxKind.SingleLineCommentTrivia - && o.ToString().Equals("// csharpier-ignore") + return (Token.HasLeadingComment(syntaxNode, "// csharpier-ignore")); + } + + public static List PrintNodesRespectingRangeIgnore( + SyntaxList list, + FormattingContext context + ) where T : SyntaxNode + { + var statements = new List(); + var printUnformatted = false; + + foreach (var node in list) + { + if (Token.HasLeadingComment(node, "// csharpier-ignore-end")) + { + printUnformatted = false; + } + else if (Token.HasLeadingComment(node, "// csharpier-ignore-start")) + { + printUnformatted = true; + } + + statements.Add( + printUnformatted ? PrintWithoutFormatting(node) : Node.Print(node, context) ); + } + + return statements; } public static Doc PrintWithoutFormatting(SyntaxNode syntaxNode) diff --git a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs index baca51eaa..3db1ddec5 100644 --- a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs +++ b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs @@ -11,18 +11,41 @@ FormattingContext context ) where T : MemberDeclarationSyntax { var result = new List { Doc.HardLine }; + var printUnformatted = false; var lastMemberForcedBlankLine = false; - for (var x = 0; x < members.Count; x++) + for (var memberIndex = 0; memberIndex < members.Count; memberIndex++) { - void AddSeparatorIfNeeded() + var skipAddingLineBecauseIgnoreEnded = false; + var member = members[memberIndex]; + + if (Token.HasLeadingComment(member, "// csharpier-ignore-end")) + { + skipAddingLineBecauseIgnoreEnded = true; + printUnformatted = false; + } + else if (Token.HasLeadingComment(member, "// csharpier-ignore-start")) { - if (members is SeparatedSyntaxList list && x < list.SeparatorCount) + if (!printUnformatted && memberIndex > 0) { - result.Add(Token.Print(list.GetSeparator(x), context)); + result.Add(Doc.HardLine); + result.Add(ExtraNewLines.Print(member)); } + printUnformatted = true; + } + + if (printUnformatted) + { + result.Add(CSharpierIgnore.PrintWithoutFormatting(member)); + continue; } - var member = members[x]; + void AddSeparatorIfNeeded() + { + if (members is SeparatedSyntaxList list && memberIndex < list.SeparatorCount) + { + result.Add(Token.Print(list.GetSeparator(memberIndex), context)); + } + } var blankLineIsForced = ( member is MethodDeclarationSyntax && node is not InterfaceDeclarationSyntax @@ -54,7 +77,7 @@ member is MethodDeclarationSyntax methodDeclaration blankLineIsForced = false; } - if (x == 0) + if (memberIndex == 0) { lastMemberForcedBlankLine = blankLineIsForced; result.Add(Node.Print(member, context)); @@ -62,7 +85,7 @@ member is MethodDeclarationSyntax methodDeclaration continue; } - var addBlankLine = blankLineIsForced || lastMemberForcedBlankLine; + var addBlankLine = (blankLineIsForced || lastMemberForcedBlankLine); var triviaContainsCommentOrNewLine = false; var printExtraNewLines = false; @@ -108,7 +131,9 @@ or SyntaxKind.EndRegionDirectiveTrivia { result.Add(ExtraNewLines.Print(member)); } - else if (addBlankLine && !triviaContainsEndIfOrRegion) + else if ( + addBlankLine && !triviaContainsEndIfOrRegion && !skipAddingLineBecauseIgnoreEnded + ) { result.Add(Doc.HardLine); } diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/Block.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/Block.cs index d53ec80f6..9fc456fb5 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/Block.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/Block.cs @@ -27,11 +27,13 @@ public static Doc Print(BlockSyntax node, FormattingContext context) if (node.Statements.Count > 0) { - innerDoc = Doc.Indent( - statementSeparator, - Doc.Join(statementSeparator, node.Statements.Select(o => Node.Print(o, context))) + var statements = CSharpierIgnore.PrintNodesRespectingRangeIgnore( + node.Statements, + context ); + innerDoc = Doc.Indent(statementSeparator, Doc.Join(statementSeparator, statements)); + DocUtilities.RemoveInitialDoubleHardLine(innerDoc); } diff --git a/Src/CSharpier/SyntaxPrinter/Token.cs b/Src/CSharpier/SyntaxPrinter/Token.cs index a3ccfe8ae..73db4453c 100644 --- a/Src/CSharpier/SyntaxPrinter/Token.cs +++ b/Src/CSharpier/SyntaxPrinter/Token.cs @@ -324,4 +324,14 @@ is not (SyntaxKind.WhitespaceTrivia or SyntaxKind.EndOfLineTrivia) is not (SyntaxKind.WhitespaceTrivia or SyntaxKind.EndOfLineTrivia) ); } + + public static bool HasLeadingComment(SyntaxNode node, string comment) + { + return node.GetLeadingTrivia() + .Any( + o => + o.RawSyntaxKind() is SyntaxKind.SingleLineCommentTrivia + && o.ToString().Equals(comment) + ); + } }