From daf045f890f775f5cb5969dbce7485012f67d00c Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Fri, 31 Dec 2021 14:03:59 -0600 Subject: [PATCH 01/14] Starting to implement rules for when to force a line before some members closes #285 --- .../FormattingTests/TestFiles/MemberLines.cst | 118 ++++++++++++++++++ .../BaseFieldDeclaration.cs | 1 - .../BaseMethodDeclaration.cs | 2 +- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 57 ++++++++- .../SyntaxNodePrinters/CompilationUnit.cs | 4 +- Src/CSharpier/SyntaxTriviaExtensions.cs | 13 ++ Src/SyntaxFinder/Program.cs | 104 +++++++++++++-- 7 files changed, 282 insertions(+), 17 deletions(-) create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst create mode 100644 Src/CSharpier/SyntaxTriviaExtensions.cs diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst new file mode 100644 index 000000000..fe954b1c5 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst @@ -0,0 +1,118 @@ +public class ClassRules +{ + public void NeverLineBeforeFirstMember() { } + public void AlwaysLineBetweenMethods() { } + public void EvenWithLambdaBody() => ""; + private enum AlwaysLineBeforeEnum + { + One, + Two + } + + private enum NoDoubleLines + { + One, + Two + } + private class AlwaysLineBeforeClass { } + + private class NoDoubleLines { } + private interface IAlwaysLineBeforeInterface { } + + private interface INoDoubleLines { } + public ClassRules() { } + + public ClassRules() { } + public ~ClassName() { } + + public ~ClassName() { } + public static implicit operator ClassRules(string s) + { + return new Test(); + } + + public static implicit operator ClassRules(int i) + { + return new Test(); + } + public record SomeRecord1(string property); + + public record SomeRecord2(string property); + public struct SomeStruct1 { } + + public struct SomeStruct2 { } + static ClassRules operator +(ClassRules a, ClassRules b) + { + return a.Add(b); + } + + static ClassRules operator -(ClassRules a, ClassRules b) + { + return a.Subtract(b); + } + + private string FieldsCanBeGroupedBecauseLinesWillBeKept; + private string ButLinesAreNotForcedHere; + // comments force a line + private string FieldWithComment; + /* comments force a line */ + private string FieldWithComment; + /// comments force a line /// + private string FieldWithComment; + [AttributesForceALine] + private string FieldWithAttribute; + + // treat the rest of these the same as fields + // properties + // indexers + // event declarations + // event fields + // delegate declarations +} +public class LineBeforeClass { } +public interface InterfaceRulesAreLikeClassesButMethodsFollowTheFieldRulesAbove +{ + void NeverLineHere(); + + void ExistingLineHereIsKept(); + void ButNoLineIsAdded(); + + // comments do force a line + void MethodWithComment(); + + // how many of these are valid? + // class + // event field + // field - can there even occur? + // indexer + // interface + // method + // operator + // property +} + +// records +// should be the same as class rules +// constructor +// field +// method +// property +// structs +// ClassDeclarationSyntax +// ConstructorDeclarationSyntax +// ConversionOperatorDeclarationSyntax +// DelegateDeclarationSyntax +// EnumDeclarationSyntax +// EventDeclarationSyntax +// EventFieldDeclarationSyntax +// FieldDeclarationSyntax +// IndexerDeclarationSyntax +// MethodDeclarationSyntax +// OperatorDeclarationSyntax +// PropertyDeclarationSyntax +// RecordDeclarationSyntax +// StructDeclarationSyntax + +// TODO what about multiple classes in a namespace? force lines there too! +// TODO what about top level statements? make sure they don't get new lines +// TODO what about regions and other preprocessor directives? diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseFieldDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseFieldDeclaration.cs index f03bfe1ac..db0834805 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseFieldDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseFieldDeclaration.cs @@ -6,7 +6,6 @@ public static Doc Print(BaseFieldDeclarationSyntax node) { var docs = new List { - ExtraNewLines.Print(node), AttributeLists.Print(node, node.AttributeLists), Modifiers.Print(node.Modifiers) }; diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs index bb4e40cbc..3db220282 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs @@ -63,7 +63,7 @@ public static Doc Print(CSharpSyntaxNode node) semicolonToken = localFunctionStatementSyntax.SemicolonToken; } - var docs = new List { ExtraNewLines.Print(node) }; + var docs = new List(); if (attributeLists is { Count: > 0 }) { diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index fc06e9ca2..b7d1c2751 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -20,10 +20,7 @@ public static Doc Print(BaseTypeDeclarationSyntax node) hasMembers = typeDeclarationSyntax.Members.Count > 0; if (typeDeclarationSyntax.Members.Count > 0) { - members = Doc.Indent( - Doc.HardLine, - Doc.Join(Doc.HardLine, typeDeclarationSyntax.Members.Select(Node.Print)) - ); + members = CreateTypeMembers(typeDeclarationSyntax); } if (node is ClassDeclarationSyntax classDeclarationSyntax) { @@ -62,7 +59,6 @@ public static Doc Print(BaseTypeDeclarationSyntax node) } var docs = new List(); - docs.AddIfNotNull(ExtraNewLines.Print(node)); if (node.AttributeLists.Any()) { docs.Add(AttributeLists.Print(node, node.AttributeLists)); @@ -139,4 +135,55 @@ public static Doc Print(BaseTypeDeclarationSyntax node) return Doc.Concat(docs); } + + private static Doc CreateTypeMembers(TypeDeclarationSyntax typeDeclarationSyntax) + { + var members = new List { Doc.HardLine }; + for (var x = 0; x < typeDeclarationSyntax.Members.Count; x++) + { + var member = typeDeclarationSyntax.Members[x]; + + if (x == 0) + { + members.Add(Node.Print(member)); + continue; + } + + var lineIsForced = + member is MethodDeclarationSyntax + && typeDeclarationSyntax is not InterfaceDeclarationSyntax + || member + is ClassDeclarationSyntax + or EnumDeclarationSyntax + or InterfaceDeclarationSyntax + or ConstructorDeclarationSyntax + or ConversionOperatorDeclarationSyntax + or DestructorDeclarationSyntax + or RecordDeclarationSyntax + or StructDeclarationSyntax + or OperatorDeclarationSyntax; + + var addLine = lineIsForced; + + if (!lineIsForced) + { + addLine = + member.AttributeLists.Any() + || member + .GetLeadingTrivia() + .Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia || o.IsComment()); + } + + members.Add(Doc.HardLine); + + if (addLine) + { + members.Add(Doc.HardLine); + } + + members.Add(Node.Print(member)); + } + + return Doc.Indent(members); + } } diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs index adb382636..09c636d00 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs @@ -22,7 +22,9 @@ public static Doc Print(CompilationUnitSyntax node) docs.Add(AttributeLists.Print(node, node.AttributeLists)); if (node.Members.Count > 0) { - docs.Add(Doc.Join(Doc.HardLine, node.Members.Select(Node.Print))); + docs.Add( + Doc.Join(Doc.Concat(Doc.HardLine, Doc.HardLine), node.Members.Select(Node.Print)) + ); } var finalTrivia = Token.PrintLeadingTriviaWithNewLines(node.EndOfFileToken.LeadingTrivia); diff --git a/Src/CSharpier/SyntaxTriviaExtensions.cs b/Src/CSharpier/SyntaxTriviaExtensions.cs new file mode 100644 index 000000000..16b551442 --- /dev/null +++ b/Src/CSharpier/SyntaxTriviaExtensions.cs @@ -0,0 +1,13 @@ +namespace CSharpier; + +public static class SyntaxTriviaExtensions +{ + public static bool IsComment(this SyntaxTrivia syntaxTrivia) + { + return syntaxTrivia.Kind() + is SyntaxKind.SingleLineCommentTrivia + or SyntaxKind.MultiLineCommentTrivia + or SyntaxKind.SingleLineDocumentationCommentTrivia + or SyntaxKind.MultiLineDocumentationCommentTrivia; + } +} diff --git a/Src/SyntaxFinder/Program.cs b/Src/SyntaxFinder/Program.cs index eb575a2a3..7153fd4fa 100644 --- a/Src/SyntaxFinder/Program.cs +++ b/Src/SyntaxFinder/Program.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -41,6 +43,14 @@ public static void Main() } ); + // foreach (var entry in CustomWalker.Dictionary) + // { + // Console.WriteLine(entry.Key); + // foreach (var member in entry.Value.OrderBy(o => o)) + // { + // Console.WriteLine(" " + member); + // } + // } if (CustomWalker.Matching > CustomWalker.Total) { Console.WriteLine("Matching was > than Total, so you did something wrong."); @@ -63,29 +73,89 @@ private static void WriteResult(string label, string value) public class CustomWalker : CSharpSyntaxWalker { + public static readonly ConcurrentDictionary> Dictionary = new(); public static int Total; public static int Matching; private readonly string file; private bool wroteFile; + private int maxCodeWrites = 1000; + private int codeWrites = 0; public CustomWalker(string file) : base() { + Dictionary.TryAdd(nameof(ClassDeclarationSyntax), new List()); + Dictionary.TryAdd(nameof(StructDeclarationSyntax), new List()); + Dictionary.TryAdd(nameof(RecordDeclarationSyntax), new List()); + Dictionary.TryAdd(nameof(InterfaceDeclarationSyntax), new List()); + this.file = file; } + public override void VisitClassDeclaration(ClassDeclarationSyntax node) + { + this.VisitType(node); + base.VisitClassDeclaration(node); + } + + public override void VisitStructDeclaration(StructDeclarationSyntax node) + { + this.VisitType(node); + base.VisitStructDeclaration(node); + } + + public override void VisitRecordDeclaration(RecordDeclarationSyntax node) + { + this.VisitType(node); + base.VisitRecordDeclaration(node); + } + + public override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) + { + this.VisitType(node); + base.VisitInterfaceDeclaration(node); + } + + private void VisitType(TypeDeclarationSyntax node) + { + foreach (var member in node.Members) + { + Dictionary.AddOrUpdate( + node.GetType().Name, + new List(), + (key, list) => + { + if (!list.Contains(member.GetType().Name)) + { + list.Add(member.GetType().Name); + } + + return list; + } + ); + } + } + public override void VisitMethodDeclaration(MethodDeclarationSyntax node) { - if (node.ConstraintClauses.Any()) + if ( + node.Parent is not InterfaceDeclarationSyntax + || ( + node.Parent is TypeDeclarationSyntax typeDeclarationSyntax + && node == typeDeclarationSyntax.Members.First() + ) + || node.GetLeadingTrivia().Any(o => o.IsComment() || node.AttributeLists.Any()) + ) { - Interlocked.Increment(ref Total); + base.VisitMethodDeclaration(node); + return; + } - var trivia = node.ParameterList.CloseParenToken.TrailingTrivia; - if (trivia.Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia)) - { - Interlocked.Increment(ref Matching); - } + Interlocked.Increment(ref Total); - this.WriteCode(node); + if (node.GetLeadingTrivia().Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia)) + { + Interlocked.Increment(ref Matching); + this.WriteCode(node.Parent); } base.VisitMethodDeclaration(node); @@ -93,7 +163,11 @@ public override void VisitMethodDeclaration(MethodDeclarationSyntax node) private void WriteCode(SyntaxNode syntaxNode) { - Console.WriteLine(syntaxNode.SyntaxTree.GetText().ToString(syntaxNode.Span)); + if (this.codeWrites < this.maxCodeWrites) + { + Interlocked.Increment(ref this.codeWrites); + Console.WriteLine(syntaxNode.SyntaxTree.GetText().ToString(syntaxNode.Span)); + } } private void WriteFilePath() @@ -112,6 +186,18 @@ private static bool IsMultiline(SyntaxNode syntaxNode) } } +public static class SyntaxTriviaExtensions +{ + public static bool IsComment(this SyntaxTrivia syntaxTrivia) + { + return syntaxTrivia.Kind() + is SyntaxKind.SingleLineCommentTrivia + or SyntaxKind.MultiLineCommentTrivia + or SyntaxKind.SingleLineDocumentationCommentTrivia + or SyntaxKind.MultiLineDocumentationCommentTrivia; + } +} + public static class Ignored { public static bool Is(string file) From cf50c044e45a8448d25c25c35b4181a60c511014 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sat, 1 Jan 2022 12:28:24 -0600 Subject: [PATCH 02/14] Mostly finished with logic for forcing lines. --- .../TestFiles/AttributeLists.cst | 2 + .../FormattingTests/TestFiles/Comments.cst | 2 + .../TestFiles/ExternAliasDirectives.cst | 1 + .../FormattingTests/TestFiles/MemberLines.cst | 75 ++++-------- .../TestFiles/MemberLines.expected.cst | 114 ++++++++++++++++++ .../TestFiles/StringLiterals.cst | 1 + .../TestFiles/UsingDirectives.cst | 1 + Src/CSharpier/SyntaxPrinter/ExtraNewLines.cs | 5 + .../SyntaxPrinter/MembersWithForcedLines.cs | 63 ++++++++++ .../BaseMethodDeclaration.cs | 4 + .../BasePropertyDeclaration.cs | 6 +- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 60 ++------- .../SyntaxNodePrinters/CompilationUnit.cs | 4 +- .../SyntaxNodePrinters/DelegateDeclaration.cs | 1 - .../FileScopedNamespaceDeclaration.cs | 13 +- .../NamespaceDeclaration.cs | 7 +- Src/SyntaxFinder/Program.cs | 70 +++++------ 17 files changed, 269 insertions(+), 160 deletions(-) create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst create mode 100644 Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/AttributeLists.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/AttributeLists.cst index 866a4c2c9..e17bdae87 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/AttributeLists.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/AttributeLists.cst @@ -1,10 +1,12 @@ [assembly: System.Copyright("(C) 2009")] [module: System.Copyright("\n\t\u0123(C) \"2009" + "\u0123")] + [Obsolete] class ClassName { [Obsolete] private string Field; + [Obsolete] public string Property { diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/Comments.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/Comments.cst index c5b427568..6d4a9f52f 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/Comments.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/Comments.cst @@ -84,8 +84,10 @@ public class ClassName } IReadOnlyList ICommand.Arguments => Arguments; + /// IReadOnlyList ICommand.Arguments => Arguments; + // regular comment does not cause breaking IReadOnlyList ICommand.Arguments => Arguments; } diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/ExternAliasDirectives.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/ExternAliasDirectives.cst index b9e743231..2684b1930 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/ExternAliasDirectives.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/ExternAliasDirectives.cst @@ -8,6 +8,7 @@ alias // alias trailing Foo; extern alias Foo; + namespace Namespace { extern alias Bar; diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst index fe954b1c5..82a87e2e6 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst @@ -8,38 +8,38 @@ public class ClassRules One, Two } - + private enum NoDoubleLines { One, Two } private class AlwaysLineBeforeClass { } - + private class NoDoubleLines { } private interface IAlwaysLineBeforeInterface { } - + private interface INoDoubleLines { } public ClassRules() { } - + public ClassRules() { } public ~ClassName() { } - + public ~ClassName() { } public static implicit operator ClassRules(string s) { return new Test(); } - + public static implicit operator ClassRules(int i) { return new Test(); } public record SomeRecord1(string property); - + public record SomeRecord2(string property); public struct SomeStruct1 { } - + public struct SomeStruct2 { } static ClassRules operator +(ClassRules a, ClassRules b) { @@ -62,12 +62,22 @@ public class ClassRules [AttributesForceALine] private string FieldWithAttribute; - // treat the rest of these the same as fields - // properties - // indexers - // event declarations - // event fields - // delegate declarations + private string PropertiesCanBeGrouped { get; set; } + private string BecauseTheyDontForceLines { get; set; } + // comments force a line + private string PropertyWithComment; + [AttributesForceALine] + private string PropertyWithAttribute; + + delegate void DelegatesAreLikeFields(); + delegate void AndCanBeGrouped(); + // comments force a line + private string DelegateWithComment; + [AttributesForceALine] + private string DelegateWithAttribute; + + public void AlwaysLineBetweenMethods() { } + private string FieldAfterMethodGetsLine; } public class LineBeforeClass { } public interface InterfaceRulesAreLikeClassesButMethodsFollowTheFieldRulesAbove @@ -76,43 +86,6 @@ public interface InterfaceRulesAreLikeClassesButMethodsFollowTheFieldRulesAbove void ExistingLineHereIsKept(); void ButNoLineIsAdded(); - // comments do force a line void MethodWithComment(); - - // how many of these are valid? - // class - // event field - // field - can there even occur? - // indexer - // interface - // method - // operator - // property } - -// records -// should be the same as class rules -// constructor -// field -// method -// property -// structs -// ClassDeclarationSyntax -// ConstructorDeclarationSyntax -// ConversionOperatorDeclarationSyntax -// DelegateDeclarationSyntax -// EnumDeclarationSyntax -// EventDeclarationSyntax -// EventFieldDeclarationSyntax -// FieldDeclarationSyntax -// IndexerDeclarationSyntax -// MethodDeclarationSyntax -// OperatorDeclarationSyntax -// PropertyDeclarationSyntax -// RecordDeclarationSyntax -// StructDeclarationSyntax - -// TODO what about multiple classes in a namespace? force lines there too! -// TODO what about top level statements? make sure they don't get new lines -// TODO what about regions and other preprocessor directives? diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst new file mode 100644 index 000000000..63878fc55 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst @@ -0,0 +1,114 @@ +public class ClassRules +{ + public void NeverLineBeforeFirstMember() { } + + public void AlwaysLineBetweenMethods() { } + + public void EvenWithLambdaBody() => ""; + + private enum AlwaysLineBeforeEnum + { + One, + Two + } + + private enum NoDoubleLines + { + One, + Two + } + + private class AlwaysLineBeforeClass { } + + private class NoDoubleLines { } + + private interface IAlwaysLineBeforeInterface { } + + private interface INoDoubleLines { } + + public ClassRules() { } + + public ClassRules() { } + + public ~ClassName() { } + + public ~ClassName() { } + + public static implicit operator ClassRules(string s) + { + return new Test(); + } + + public static implicit operator ClassRules(int i) + { + return new Test(); + } + + public record SomeRecord1(string property); + + public record SomeRecord2(string property); + + public struct SomeStruct1 { } + + public struct SomeStruct2 { } + + static ClassRules operator +(ClassRules a, ClassRules b) + { + return a.Add(b); + } + + static ClassRules operator -(ClassRules a, ClassRules b) + { + return a.Subtract(b); + } + + private string FieldsCanBeGroupedBecauseLinesWillBeKept; + private string ButLinesAreNotForcedHere; + + // comments force a line + private string FieldWithComment; + + /* comments force a line */ + private string FieldWithComment; + + /// comments force a line /// + private string FieldWithComment; + + [AttributesForceALine] + private string FieldWithAttribute; + + private string PropertiesCanBeGrouped { get; set; } + private string BecauseTheyDontForceLines { get; set; } + + // comments force a line + private string PropertyWithComment; + + [AttributesForceALine] + private string PropertyWithAttribute; + + delegate void DelegatesAreLikeFields(); + delegate void AndCanBeGrouped(); + + // comments force a line + private string DelegateWithComment; + + [AttributesForceALine] + private string DelegateWithAttribute; + + public void AlwaysLineBetweenMethods() { } + + private string FieldAfterMethodGetsLine; +} + +public class LineBeforeClass { } + +public interface InterfaceRulesAreLikeClassesButMethodsFollowTheFieldRulesAbove +{ + void NeverLineHere(); + + void ExistingLineHereIsKept(); + void ButNoLineIsAdded(); + + // comments do force a line + void MethodWithComment(); +} diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/StringLiterals.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/StringLiterals.cst index fc184a6b9..e2f819f16 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/StringLiterals.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/StringLiterals.cst @@ -2,6 +2,7 @@ @"(C)"" 2009" )] + class ClassName { private string bothLineEndingsPersist = @"\r\n \n"; diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/UsingDirectives.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/UsingDirectives.cst index ac74a8018..9f53a36e9 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/UsingDirectives.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/UsingDirectives.cst @@ -18,6 +18,7 @@ global using System; using First; using Second; + namespace Namespace { using Third; diff --git a/Src/CSharpier/SyntaxPrinter/ExtraNewLines.cs b/Src/CSharpier/SyntaxPrinter/ExtraNewLines.cs index 87ced387a..642805e33 100644 --- a/Src/CSharpier/SyntaxPrinter/ExtraNewLines.cs +++ b/Src/CSharpier/SyntaxPrinter/ExtraNewLines.cs @@ -4,6 +4,11 @@ internal static class ExtraNewLines { public static Doc Print(CSharpSyntaxNode node) { + if (node.Parent is GlobalStatementSyntax) + { + return Doc.Null; + } + var docs = new List(); foreach (var leadingTrivia in node.GetLeadingTrivia()) { diff --git a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs new file mode 100644 index 000000000..cb82fa756 --- /dev/null +++ b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs @@ -0,0 +1,63 @@ +namespace CSharpier.SyntaxPrinter; + +internal static class MembersWithForcedLines +{ + public static List Print( + CSharpSyntaxNode node, + SyntaxList members + ) + { + var result = new List { Doc.HardLine }; + var lastMemberForcedLine = false; + for (var x = 0; x < members.Count; x++) + { + var member = members[x]; + + var lineIsForced = + member is MethodDeclarationSyntax && node is not InterfaceDeclarationSyntax + || member + is ClassDeclarationSyntax + or ConstructorDeclarationSyntax + or ConversionOperatorDeclarationSyntax + or DestructorDeclarationSyntax + or EnumDeclarationSyntax + or FileScopedNamespaceDeclarationSyntax + or InterfaceDeclarationSyntax + or NamespaceDeclarationSyntax + or OperatorDeclarationSyntax + or RecordDeclarationSyntax + or StructDeclarationSyntax; + + if (x == 0) + { + lastMemberForcedLine = lineIsForced; + result.Add(Node.Print(member)); + continue; + } + + var addLine = lineIsForced || lastMemberForcedLine; + + if (!addLine) + { + addLine = + member.AttributeLists.Any() + || member + .GetLeadingTrivia() + .Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia || o.IsComment()); + } + + result.Add(Doc.HardLine); + + if (addLine) + { + result.Add(Doc.HardLine); + } + + result.Add(Node.Print(member)); + + lastMemberForcedLine = lineIsForced; + } + + return result; + } +} diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs index 3db220282..267ba5f61 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs @@ -64,6 +64,10 @@ public static Doc Print(CSharpSyntaxNode node) } var docs = new List(); + if (node is LocalFunctionStatementSyntax) + { + docs.Add(ExtraNewLines.Print(node)); + } if (attributeLists is { Count: > 0 }) { diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BasePropertyDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BasePropertyDeclaration.cs index 380e22b66..ca278e0df 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BasePropertyDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BasePropertyDeclaration.cs @@ -73,11 +73,7 @@ public static Doc Print(BasePropertyDeclarationSyntax node) contents = ArrowExpressionClause.Print(expressionBody); } - var docs = new List - { - ExtraNewLines.Print(node), - AttributeLists.Print(node, node.AttributeLists) - }; + var docs = new List { AttributeLists.Print(node, node.AttributeLists) }; return Doc.Group( Doc.Concat( diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index b7d1c2751..373e48742 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -1,5 +1,7 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; +// TODO what about regions and other directives + internal static class BaseTypeDeclaration { public static Doc Print(BaseTypeDeclarationSyntax node) @@ -20,7 +22,12 @@ public static Doc Print(BaseTypeDeclarationSyntax node) hasMembers = typeDeclarationSyntax.Members.Count > 0; if (typeDeclarationSyntax.Members.Count > 0) { - members = CreateTypeMembers(typeDeclarationSyntax); + members = Doc.Indent( + MembersWithForcedLines.Print( + typeDeclarationSyntax, + typeDeclarationSyntax.Members + ) + ); } if (node is ClassDeclarationSyntax classDeclarationSyntax) { @@ -135,55 +142,4 @@ public static Doc Print(BaseTypeDeclarationSyntax node) return Doc.Concat(docs); } - - private static Doc CreateTypeMembers(TypeDeclarationSyntax typeDeclarationSyntax) - { - var members = new List { Doc.HardLine }; - for (var x = 0; x < typeDeclarationSyntax.Members.Count; x++) - { - var member = typeDeclarationSyntax.Members[x]; - - if (x == 0) - { - members.Add(Node.Print(member)); - continue; - } - - var lineIsForced = - member is MethodDeclarationSyntax - && typeDeclarationSyntax is not InterfaceDeclarationSyntax - || member - is ClassDeclarationSyntax - or EnumDeclarationSyntax - or InterfaceDeclarationSyntax - or ConstructorDeclarationSyntax - or ConversionOperatorDeclarationSyntax - or DestructorDeclarationSyntax - or RecordDeclarationSyntax - or StructDeclarationSyntax - or OperatorDeclarationSyntax; - - var addLine = lineIsForced; - - if (!lineIsForced) - { - addLine = - member.AttributeLists.Any() - || member - .GetLeadingTrivia() - .Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia || o.IsComment()); - } - - members.Add(Doc.HardLine); - - if (addLine) - { - members.Add(Doc.HardLine); - } - - members.Add(Node.Print(member)); - } - - return Doc.Indent(members); - } } diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs index 09c636d00..23c50dc51 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs @@ -22,9 +22,7 @@ public static Doc Print(CompilationUnitSyntax node) docs.Add(AttributeLists.Print(node, node.AttributeLists)); if (node.Members.Count > 0) { - docs.Add( - Doc.Join(Doc.Concat(Doc.HardLine, Doc.HardLine), node.Members.Select(Node.Print)) - ); + docs.AddRange(MembersWithForcedLines.Print(node, node.Members)); } var finalTrivia = Token.PrintLeadingTriviaWithNewLines(node.EndOfFileToken.LeadingTrivia); diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/DelegateDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/DelegateDeclaration.cs index b21b93e4d..64799758d 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/DelegateDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/DelegateDeclaration.cs @@ -6,7 +6,6 @@ public static Doc Print(DelegateDeclarationSyntax node) { var docs = new List { - ExtraNewLines.Print(node), AttributeLists.Print(node, node.AttributeLists), Modifiers.Print(node.Modifiers), Token.PrintWithSuffix(node.DelegateKeyword, " "), diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs index 7ab5e9952..e6fd3fc6f 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs @@ -4,7 +4,7 @@ internal static class FileScopedNamespaceDeclaration { public static Doc Print(FileScopedNamespaceDeclarationSyntax node) { - var docs = new List() + var docs = new List { ExtraNewLines.Print(node), AttributeLists.Print(node, node.AttributeLists), @@ -12,11 +12,10 @@ public static Doc Print(FileScopedNamespaceDeclarationSyntax node) Token.Print(node.NamespaceKeyword), " ", Node.Print(node.Name), - Token.Print(node.SemicolonToken) + Token.Print(node.SemicolonToken), + Doc.HardLine }; - docs.Add(Doc.HardLine); - if (node.Externs.Any()) { docs.Add(Doc.Join(Doc.HardLine, node.Externs.Select(Node.Print)), Doc.HardLine); @@ -29,7 +28,11 @@ public static Doc Print(FileScopedNamespaceDeclarationSyntax node) if (node.Members.Any()) { - docs.Add(Doc.Join(Doc.HardLine, node.Members.Select(Node.Print)), Doc.HardLine); + docs.Add( + Doc.HardLine, + Doc.Join(Doc.Concat(Doc.HardLine, Doc.HardLine), node.Members.Select(Node.Print)), + Doc.HardLine + ); } return Doc.Concat(docs); diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/NamespaceDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/NamespaceDeclaration.cs index 9f93f96ff..c626cb8fe 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/NamespaceDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/NamespaceDeclaration.cs @@ -6,7 +6,6 @@ public static Doc Print(NamespaceDeclarationSyntax node) { var docs = new List { - ExtraNewLines.Print(node), AttributeLists.Print(node, node.AttributeLists), Modifiers.Print(node.Modifiers), Token.Print(node.NamespaceKeyword), @@ -38,7 +37,11 @@ public static Doc Print(NamespaceDeclarationSyntax node) if (hasMembers) { innerDocs.Add( - Doc.Join(Doc.HardLine, node.Members.Select(Node.Print)), + Doc.HardLine, + Doc.Join( + Doc.Concat(Doc.HardLine, Doc.HardLine), + node.Members.Select(Node.Print) + ), Doc.HardLine ); } diff --git a/Src/SyntaxFinder/Program.cs b/Src/SyntaxFinder/Program.cs index 7153fd4fa..01344d394 100644 --- a/Src/SyntaxFinder/Program.cs +++ b/Src/SyntaxFinder/Program.cs @@ -43,26 +43,26 @@ public static void Main() } ); - // foreach (var entry in CustomWalker.Dictionary) - // { - // Console.WriteLine(entry.Key); - // foreach (var member in entry.Value.OrderBy(o => o)) - // { - // Console.WriteLine(" " + member); - // } - // } - if (CustomWalker.Matching > CustomWalker.Total) + foreach (var entry in CustomWalker.Dictionary) { - Console.WriteLine("Matching was > than Total, so you did something wrong."); + Console.WriteLine(entry.Key); + foreach (var member in entry.Value.OrderBy(o => o)) + { + Console.WriteLine(" " + member); + } } - - WriteResult("Matching", CustomWalker.Matching.ToString("n0")); - WriteResult("Total", CustomWalker.Total.ToString("n0")); - WriteResult( - "Percent", - (Convert.ToDecimal(CustomWalker.Matching) / CustomWalker.Total * 100).ToString("n") - + "%" - ); + // if (CustomWalker.Matching > CustomWalker.Total) + // { + // Console.WriteLine("Matching was > than Total, so you did something wrong."); + // } + // + // WriteResult("Matching", CustomWalker.Matching.ToString("n0")); + // WriteResult("Total", CustomWalker.Total.ToString("n0")); + // WriteResult( + // "Percent", + // (Convert.ToDecimal(CustomWalker.Matching) / CustomWalker.Total * 100).ToString("n") + // + "%" + // ); } private static void WriteResult(string label, string value) @@ -91,34 +91,21 @@ public CustomWalker(string file) : base() this.file = file; } - public override void VisitClassDeclaration(ClassDeclarationSyntax node) - { - this.VisitType(node); - base.VisitClassDeclaration(node); - } - - public override void VisitStructDeclaration(StructDeclarationSyntax node) + public override void VisitCompilationUnit(CompilationUnitSyntax node) { this.VisitType(node); - base.VisitStructDeclaration(node); + base.VisitCompilationUnit(node); } - public override void VisitRecordDeclaration(RecordDeclarationSyntax node) - { - this.VisitType(node); - base.VisitRecordDeclaration(node); - } - - public override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) - { - this.VisitType(node); - base.VisitInterfaceDeclaration(node); - } - - private void VisitType(TypeDeclarationSyntax node) + private void VisitType(CompilationUnitSyntax node) { foreach (var member in node.Members) { + if (member is DelegateDeclarationSyntax) + { + this.WriteCode(member.Parent); + } + Dictionary.AddOrUpdate( node.GetType().Name, new List(), @@ -138,7 +125,8 @@ private void VisitType(TypeDeclarationSyntax node) public override void VisitMethodDeclaration(MethodDeclarationSyntax node) { if ( - node.Parent is not InterfaceDeclarationSyntax + true + || node.ExpressionBody == null || ( node.Parent is TypeDeclarationSyntax typeDeclarationSyntax && node == typeDeclarationSyntax.Members.First() @@ -151,11 +139,11 @@ node.Parent is TypeDeclarationSyntax typeDeclarationSyntax } Interlocked.Increment(ref Total); + this.WriteCode(node.Parent); if (node.GetLeadingTrivia().Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia)) { Interlocked.Increment(ref Matching); - this.WriteCode(node.Parent); } base.VisitMethodDeclaration(node); From 8ba999e50f497e585e3314a0228434f9994f244c Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sat, 1 Jan 2022 12:54:33 -0600 Subject: [PATCH 03/14] Self code review --- .../SyntaxPrinter/MembersWithForcedLines.cs | 20 +++---- Src/SyntaxFinder/Program.cs | 58 ++++++++----------- 2 files changed, 32 insertions(+), 46 deletions(-) diff --git a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs index cb82fa756..266fffb7d 100644 --- a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs +++ b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs @@ -8,12 +8,12 @@ SyntaxList members ) { var result = new List { Doc.HardLine }; - var lastMemberForcedLine = false; + var lastMemberForcedBlankLine = false; for (var x = 0; x < members.Count; x++) { var member = members[x]; - var lineIsForced = + var blankLineIsForced = member is MethodDeclarationSyntax && node is not InterfaceDeclarationSyntax || member is ClassDeclarationSyntax @@ -30,32 +30,30 @@ or RecordDeclarationSyntax if (x == 0) { - lastMemberForcedLine = lineIsForced; + lastMemberForcedBlankLine = blankLineIsForced; result.Add(Node.Print(member)); continue; } - var addLine = lineIsForced || lastMemberForcedLine; + var addBlankLine = blankLineIsForced || lastMemberForcedBlankLine; - if (!addLine) + if (!addBlankLine) { - addLine = + addBlankLine = member.AttributeLists.Any() || member .GetLeadingTrivia() .Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia || o.IsComment()); } - result.Add(Doc.HardLine); - - if (addLine) + if (addBlankLine) { result.Add(Doc.HardLine); } - result.Add(Node.Print(member)); + result.Add(Doc.HardLine, Node.Print(member)); - lastMemberForcedLine = lineIsForced; + lastMemberForcedBlankLine = blankLineIsForced; } return result; diff --git a/Src/SyntaxFinder/Program.cs b/Src/SyntaxFinder/Program.cs index 01344d394..0b703fa2c 100644 --- a/Src/SyntaxFinder/Program.cs +++ b/Src/SyntaxFinder/Program.cs @@ -43,7 +43,7 @@ public static void Main() } ); - foreach (var entry in CustomWalker.Dictionary) + foreach (var entry in CustomWalker.MembersInType) { Console.WriteLine(entry.Key); foreach (var member in entry.Value.OrderBy(o => o)) @@ -51,18 +51,19 @@ public static void Main() Console.WriteLine(" " + member); } } - // if (CustomWalker.Matching > CustomWalker.Total) - // { - // Console.WriteLine("Matching was > than Total, so you did something wrong."); - // } - // - // WriteResult("Matching", CustomWalker.Matching.ToString("n0")); - // WriteResult("Total", CustomWalker.Total.ToString("n0")); - // WriteResult( - // "Percent", - // (Convert.ToDecimal(CustomWalker.Matching) / CustomWalker.Total * 100).ToString("n") - // + "%" - // ); + + if (CustomWalker.Matching > CustomWalker.Total) + { + Console.WriteLine("Matching was > than Total, so you did something wrong."); + } + + WriteResult("Matching", CustomWalker.Matching.ToString("n0")); + WriteResult("Total", CustomWalker.Total.ToString("n0")); + WriteResult( + "Percent", + (Convert.ToDecimal(CustomWalker.Matching) / CustomWalker.Total * 100).ToString("n") + + "%" + ); } private static void WriteResult(string label, string value) @@ -73,40 +74,30 @@ private static void WriteResult(string label, string value) public class CustomWalker : CSharpSyntaxWalker { - public static readonly ConcurrentDictionary> Dictionary = new(); + public static readonly ConcurrentDictionary> MembersInType = new(); public static int Total; public static int Matching; private readonly string file; private bool wroteFile; - private int maxCodeWrites = 1000; + private readonly int maxCodeWrites = 250; private int codeWrites = 0; - public CustomWalker(string file) : base() + public CustomWalker(string file) { - Dictionary.TryAdd(nameof(ClassDeclarationSyntax), new List()); - Dictionary.TryAdd(nameof(StructDeclarationSyntax), new List()); - Dictionary.TryAdd(nameof(RecordDeclarationSyntax), new List()); - Dictionary.TryAdd(nameof(InterfaceDeclarationSyntax), new List()); - this.file = file; } public override void VisitCompilationUnit(CompilationUnitSyntax node) { - this.VisitType(node); + this.VisitType(node, node.Members); base.VisitCompilationUnit(node); } - private void VisitType(CompilationUnitSyntax node) + private void VisitType(CSharpSyntaxNode node, SyntaxList members) { - foreach (var member in node.Members) + foreach (var member in members) { - if (member is DelegateDeclarationSyntax) - { - this.WriteCode(member.Parent); - } - - Dictionary.AddOrUpdate( + MembersInType.AddOrUpdate( node.GetType().Name, new List(), (key, list) => @@ -125,13 +116,10 @@ private void VisitType(CompilationUnitSyntax node) public override void VisitMethodDeclaration(MethodDeclarationSyntax node) { if ( - true - || node.ExpressionBody == null - || ( + ( node.Parent is TypeDeclarationSyntax typeDeclarationSyntax && node == typeDeclarationSyntax.Members.First() - ) - || node.GetLeadingTrivia().Any(o => o.IsComment() || node.AttributeLists.Any()) + ) || node.GetLeadingTrivia().Any(o => o.IsComment() || node.AttributeLists.Any()) ) { base.VisitMethodDeclaration(node); From e00d8abffc3d3c04440e9eeabb799c4e19db5fe6 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sat, 1 Jan 2022 13:55:22 -0600 Subject: [PATCH 04/14] Fixing an edge case with directives --- CSharpier.sln.DotSettings | 1 + .../TestFiles/Directives_CompilationUnit.cst | 13 +++++ .../Directives_FileScopedNamespace.cst | 15 ++++++ .../TestFiles/Directives_Namespace.cst | 16 ++++++ Src/CSharpier.Tests/Samples/Scratch.cst | 20 ++----- .../SyntaxPrinter/NamespaceLikePrinter.cs | 53 +++++++++++++++++++ .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 15 ++++++ .../SyntaxNodePrinters/CompilationUnit.cs | 21 +------- .../FileScopedNamespaceDeclaration.cs | 19 +------ .../NamespaceDeclaration.cs | 28 +--------- 10 files changed, 122 insertions(+), 79 deletions(-) create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_CompilationUnit.cst create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_FileScopedNamespace.cst create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_Namespace.cst create mode 100644 Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs diff --git a/CSharpier.sln.DotSettings b/CSharpier.sln.DotSettings index 5e14f3bfa..15cfdc79b 100644 --- a/CSharpier.sln.DotSettings +++ b/CSharpier.sln.DotSettings @@ -2,4 +2,5 @@ True True True + True True \ No newline at end of file diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_CompilationUnit.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_CompilationUnit.cst new file mode 100644 index 000000000..b202af70b --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_CompilationUnit.cst @@ -0,0 +1,13 @@ +#if NO_EXTRA_LINES +extern alias Foo1; +#else +extern alias Foo2; +#endif + +#if NO_EXTRA_LINES +using System.Net.Http; +#else +using System.Web.Routing; +#endif + +class ClassName { } diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_FileScopedNamespace.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_FileScopedNamespace.cst new file mode 100644 index 000000000..e9827484c --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_FileScopedNamespace.cst @@ -0,0 +1,15 @@ +namespace FileScoped; + +#if NO_EXTRA_LINES +extern alias Foo1; +#else +extern alias Foo2; +#endif + +#if NO_EXTRA_LINES +using System.Net.Http; +#else +using System.Web.Routing; +#endif + +class ClassName { } diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_Namespace.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_Namespace.cst new file mode 100644 index 000000000..dd120ac01 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_Namespace.cst @@ -0,0 +1,16 @@ +namespace Namespace +{ +#if NO_EXTRA_LINES + extern alias Foo1; +#else + extern alias Foo2; +#endif + +#if NO_EXTRA_LINES + using System.Net.Http; +#else + using System.Web.Routing; +#endif + + class ClassName { } +} diff --git a/Src/CSharpier.Tests/Samples/Scratch.cst b/Src/CSharpier.Tests/Samples/Scratch.cst index 5c5c0d3dc..91e750176 100644 --- a/Src/CSharpier.Tests/Samples/Scratch.cst +++ b/Src/CSharpier.Tests/Samples/Scratch.cst @@ -1,15 +1,5 @@ - public class FunctionParameter : EntityBase - { - public Function Function - - [Required] - [UniqueIndex(AdditionalColumns = nameof(Name))] - public Guid FunctionId { get; set; } - - public string Name { get; set; } - - public override string GetDisplayName() - { - return this.Name; - - } \ No newline at end of file +#if ASPNETWEBAPI +using System.Net.Http; +#endif + +class ClassName {} \ No newline at end of file diff --git a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs new file mode 100644 index 000000000..ca3b928c9 --- /dev/null +++ b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs @@ -0,0 +1,53 @@ +namespace CSharpier.SyntaxPrinter; + +internal static class NamespaceLikePrinter +{ + public static void Print(BaseNamespaceDeclarationSyntax node, List docs) + { + Print(node, node.Externs, node.Usings, node.Members, docs); + } + + public static void Print(CompilationUnitSyntax node, List docs) + { + Print(node, node.Externs, node.Usings, node.Members, docs); + } + + private static void Print( + CSharpSyntaxNode node, + SyntaxList externs, + SyntaxList usings, + SyntaxList members, + List docs + ) + { + if (externs.Count > 0) + { + docs.Add(Doc.Join(Doc.HardLine, externs.Select(ExternAliasDirective.Print))); + } + if (usings.Count > 0) + { + if (externs.Any()) + { + docs.Add(Doc.HardLine); + } + docs.Add(Doc.Join(Doc.HardLine, usings.Select(UsingDirective.Print))); + } + + if (node is CompilationUnitSyntax compilationUnitSyntax) + { + docs.Add(AttributeLists.Print(node, compilationUnitSyntax.AttributeLists)); + } + + if (members.Count > 0) + { + if ( + (usings.Any() || (!usings.Any() && externs.Any())) + && !members[0].GetLeadingTrivia().Any(o => o.IsDirective) + ) + { + docs.Add(Doc.HardLine); + } + docs.AddRange(MembersWithForcedLines.Print(node, members)); + } + } +} diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index 373e48742..61df2ad1d 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -1,6 +1,21 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; // TODO what about regions and other directives +/* + + // the two blank lines got addes + +using System.Collections.Generic; +using System.Globalization; +#if ASPNETWEBAPI +using System.Net.Http; + +#else +using System.Web.Routing; + +#endif + + */ internal static class BaseTypeDeclaration { diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs index 23c50dc51..9a7719899 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/CompilationUnit.cs @@ -5,25 +5,8 @@ internal static class CompilationUnit public static Doc Print(CompilationUnitSyntax node) { var docs = new List(); - if (node.Externs.Count > 0) - { - docs.Add( - Doc.Join(Doc.HardLine, node.Externs.Select(ExternAliasDirective.Print)), - Doc.HardLine - ); - } - if (node.Usings.Count > 0) - { - docs.Add( - Doc.Join(Doc.HardLine, node.Usings.Select(UsingDirective.Print)), - Doc.HardLine - ); - } - docs.Add(AttributeLists.Print(node, node.AttributeLists)); - if (node.Members.Count > 0) - { - docs.AddRange(MembersWithForcedLines.Print(node, node.Members)); - } + + NamespaceLikePrinter.Print(node, docs); var finalTrivia = Token.PrintLeadingTriviaWithNewLines(node.EndOfFileToken.LeadingTrivia); if (finalTrivia != Doc.Null) diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs index e6fd3fc6f..7217f77af 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs @@ -16,24 +16,7 @@ public static Doc Print(FileScopedNamespaceDeclarationSyntax node) Doc.HardLine }; - if (node.Externs.Any()) - { - docs.Add(Doc.Join(Doc.HardLine, node.Externs.Select(Node.Print)), Doc.HardLine); - } - - if (node.Usings.Any()) - { - docs.Add(Doc.Join(Doc.HardLine, node.Usings.Select(Node.Print)), Doc.HardLine); - } - - if (node.Members.Any()) - { - docs.Add( - Doc.HardLine, - Doc.Join(Doc.Concat(Doc.HardLine, Doc.HardLine), node.Members.Select(Node.Print)), - Doc.HardLine - ); - } + NamespaceLikePrinter.Print(node, docs); return Doc.Concat(docs); } diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/NamespaceDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/NamespaceDeclaration.cs index c626cb8fe..2692aadf5 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/NamespaceDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/NamespaceDeclaration.cs @@ -20,33 +20,7 @@ public static Doc Print(NamespaceDeclarationSyntax node) if (hasMembers || hasUsing || hasExterns) { innerDocs.Add(Doc.HardLine); - if (hasExterns) - { - innerDocs.Add( - Doc.Join(Doc.HardLine, node.Externs.Select(ExternAliasDirective.Print)), - Doc.HardLine - ); - } - if (hasUsing) - { - innerDocs.Add( - Doc.Join(Doc.HardLine, node.Usings.Select(UsingDirective.Print)), - Doc.HardLine - ); - } - if (hasMembers) - { - innerDocs.Add( - Doc.HardLine, - Doc.Join( - Doc.Concat(Doc.HardLine, Doc.HardLine), - node.Members.Select(Node.Print) - ), - Doc.HardLine - ); - } - - innerDocs.RemoveAt(innerDocs.Count - 1); + NamespaceLikePrinter.Print(node, innerDocs); } else { From 8d54bff395e7e1e5e6cf575c8bcc4c92763f5bb3 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sat, 1 Jan 2022 14:23:39 -0600 Subject: [PATCH 05/14] Adding more edge cases that need to be fixed. --- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 92 +++++++++++++++++-- 1 file changed, 82 insertions(+), 10 deletions(-) diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index 61df2ad1d..0618707d6 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -1,20 +1,92 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; -// TODO what about regions and other directives +// TODO these are all the edge cases left + any failing tests /* - - // the two blank lines got addes - -using System.Collections.Generic; -using System.Globalization; -#if ASPNETWEBAPI -using System.Net.Http; +// this loses the new line before the namespace + +using System.Diagnostics.Contracts; +using System.Linq; +using System.Reflection; + +#if ASPNETWEBAPI +namespace System.Web.Http.Internal #else -using System.Web.Routing; +namespace System.Web.WebPages +#endif + +// this adds new lines +public class ClassName +{ + #if ASPNETWEBAPI + public IEnumerable Constraints { get; private set; } + #else + public IEnumerable Constraints { get; private set; } + #endif + public void DoStuff() { } +} + +// this adds new lines too +public class ClassName +{ +#if !ASPNETWEBAPI + private readonly string _controllerName; +#endif + +#if ASPNETWEBAPI + private readonly string _prefix; +#else + private readonly string _areaPrefix; + private readonly string _controllerPrefix; #endif - +} + +// this completely loses the break +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Microsoft.Design", + "CA2210:AssembliesShouldHaveValidStrongNames", + Justification = "Assembly is delay-signed" +)] + +// this loses a new line before the comment + +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Web; +using Microsoft.Web.Helpers; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("Microsoft.Web.Helpers")] +[assembly: AssemblyDescription("")] +[assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")] +[assembly: InternalsVisibleTo( + "Microsoft.Web.Helpers.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" +)] + +// this loses the new line before pragma + +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; + +#pragma warning disable 659 // overrides AddToHashCodeCombiner instead + +namespace Microsoft.Web.Mvc.ExpressionUtil +{ } + */ internal static class BaseTypeDeclaration From a0d67b76aed957603ce31f2beb3fcd1c5abdb4b5 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sat, 1 Jan 2022 14:48:17 -0600 Subject: [PATCH 06/14] Fixing some more edge cases with preprocessor symbols --- .../TestFiles/CompilationUnitAttributes.cst | 5 ++ .../CompilationUnitAttributes.expected.cst | 6 +++ .../CompilationUnitIfDirectiveEdgeCase.cst | 10 ++++ .../FormattingTests/TestFiles/MemberLines.cst | 9 ++++ .../TestFiles/MemberLines.expected.cst | 8 +++ .../SyntaxPrinter/MembersWithForcedLines.cs | 18 ++++--- .../SyntaxPrinter/NamespaceLikePrinter.cs | 13 ++++- .../SyntaxNodePrinters/AttributeList.cs | 5 +- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 51 ------------------- 9 files changed, 65 insertions(+), 60 deletions(-) create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitAttributes.cst create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitAttributes.expected.cst create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase.cst diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitAttributes.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitAttributes.cst new file mode 100644 index 000000000..3dae1aef8 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitAttributes.cst @@ -0,0 +1,5 @@ +using System.Diagnostics.CodeAnalysis; +[assembly: ForceLineAbove] +[assembly: ButNotHere] + +[assembly: ButKeepThisOne] \ No newline at end of file diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitAttributes.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitAttributes.expected.cst new file mode 100644 index 000000000..bc69eb785 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitAttributes.expected.cst @@ -0,0 +1,6 @@ +using System.Diagnostics.CodeAnalysis; + +[assembly: ForceLineAbove] +[assembly: ButNotHere] + +[assembly: ButKeepThisOne] diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase.cst new file mode 100644 index 000000000..c953e00d0 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase.cst @@ -0,0 +1,10 @@ +using System.Diagnostics.Contracts; +using System.Linq; +using System.Reflection; + +#if KEEP_LINE_ABOVE +namespace System.Web.Http.Internal +#else +namespace System.Web.WebPages +#endif +{ } diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst index 82a87e2e6..8e3a6ffd0 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst @@ -78,6 +78,15 @@ public class ClassRules public void AlwaysLineBetweenMethods() { } private string FieldAfterMethodGetsLine; + +#if EDGE_CASE_SHOULD_KEEP_LINE_ABOVE_METHOD + private string Field; +#else + private string Field; +#endif + + public void SomeMethod() { } + } public class LineBeforeClass { } public interface InterfaceRulesAreLikeClassesButMethodsFollowTheFieldRulesAbove diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst index 63878fc55..d821c21ef 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst @@ -98,6 +98,14 @@ public class ClassRules public void AlwaysLineBetweenMethods() { } private string FieldAfterMethodGetsLine; + +#if EDGE_CASE_SHOULD_KEEP_LINE_ABOVE_METHOD + private string Field; +#else + private string Field; +#endif + + public void SomeMethod() { } } public class LineBeforeClass { } diff --git a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs index 266fffb7d..46e7d4bfd 100644 --- a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs +++ b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs @@ -13,7 +13,7 @@ SyntaxList members { var member = members[x]; - var blankLineIsForced = + var blankLineIsForced = ( member is MethodDeclarationSyntax && node is not InterfaceDeclarationSyntax || member is ClassDeclarationSyntax @@ -26,7 +26,8 @@ or InterfaceDeclarationSyntax or NamespaceDeclarationSyntax or OperatorDeclarationSyntax or RecordDeclarationSyntax - or StructDeclarationSyntax; + or StructDeclarationSyntax + ); if (x == 0) { @@ -41,12 +42,17 @@ or RecordDeclarationSyntax { addBlankLine = member.AttributeLists.Any() - || member - .GetLeadingTrivia() - .Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia || o.IsComment()); + || ( + member + .GetLeadingTrivia() + .Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia || o.IsComment()) + ); } - if (addBlankLine) + if ( + addBlankLine + && !member.GetLeadingTrivia().Any(o => o.Kind() is SyntaxKind.EndIfDirectiveTrivia) + ) { result.Add(Doc.HardLine); } diff --git a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs index ca3b928c9..0e36ea488 100644 --- a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs +++ b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs @@ -33,8 +33,15 @@ List docs docs.Add(Doc.Join(Doc.HardLine, usings.Select(UsingDirective.Print))); } - if (node is CompilationUnitSyntax compilationUnitSyntax) + if ( + node is CompilationUnitSyntax compilationUnitSyntax + && compilationUnitSyntax.AttributeLists.Any() + ) { + if (externs.Any() || usings.Any()) + { + docs.Add(Doc.HardLine, Doc.HardLine); + } docs.Add(AttributeLists.Print(node, compilationUnitSyntax.AttributeLists)); } @@ -42,7 +49,9 @@ List docs { if ( (usings.Any() || (!usings.Any() && externs.Any())) - && !members[0].GetLeadingTrivia().Any(o => o.IsDirective) + && !members[0] + .GetLeadingTrivia() + .Any(o => o.Kind() is SyntaxKind.EndIfDirectiveTrivia) ) { docs.Add(Doc.HardLine); diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/AttributeList.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/AttributeList.cs index 4fdc08cc5..552a6849a 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/AttributeList.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/AttributeList.cs @@ -5,7 +5,10 @@ internal static class AttributeList public static Doc Print(AttributeListSyntax node) { var docs = new List(); - if (node.Parent is CompilationUnitSyntax) + if ( + node.Parent is CompilationUnitSyntax compilationUnitSyntax + && compilationUnitSyntax.AttributeLists.First() != node + ) { docs.Add(ExtraNewLines.Print(node)); } diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index 0618707d6..e548380d4 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -3,57 +3,6 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; // TODO these are all the edge cases left + any failing tests /* -// this loses the new line before the namespace - -using System.Diagnostics.Contracts; -using System.Linq; -using System.Reflection; - -#if ASPNETWEBAPI -namespace System.Web.Http.Internal -#else -namespace System.Web.WebPages -#endif - -// this adds new lines -public class ClassName -{ - #if ASPNETWEBAPI - public IEnumerable Constraints { get; private set; } - #else - public IEnumerable Constraints { get; private set; } - #endif - - public void DoStuff() { } -} - -// this adds new lines too -public class ClassName -{ -#if !ASPNETWEBAPI - private readonly string _controllerName; -#endif - -#if ASPNETWEBAPI - private readonly string _prefix; -#else - private readonly string _areaPrefix; - private readonly string _controllerPrefix; -#endif -} - -// this completely loses the break -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage( - "Microsoft.Design", - "CA2210:AssembliesShouldHaveValidStrongNames", - Justification = "Assembly is delay-signed" -)] - // this loses a new line before the comment // Copyright (c) .NET Foundation. All rights reserved. From 7f90cc48d87905d910f90ab3f21a9b705eaa004b Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sat, 1 Jan 2022 15:07:30 -0600 Subject: [PATCH 07/14] More edge cases that need to be resolved --- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index e548380d4..a56f5eb00 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -1,8 +1,61 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; +// TODO should abstract methods be treated like interface methods? +/* + public abstract void WriteParameterSeparator(); + public abstract void WriteReturn(); +*/ + // TODO these are all the edge cases left + any failing tests /* +// adds line before pragma +class ClassName +{ +#pragma warning disable 0618 + public void DoStuff() { } +#pragma warning restore 0618 + + public void DoStuff() { } +} +// this should keep the line like so +class ClassName +{ + public void DoStuff() { } + +#pragma warning disable 0618 + public void DoStuff() { } +#pragma warning restore 0618 +} + +// this loses a line before the first #if +public class DirectRouteFactoryContext +{ + private readonly string _actionName; + +#if !ASPNETWEBAPI + private readonly string _controllerName; +#endif + +#if ASPNETWEBAPI + private readonly string _prefix; +#else + private readonly string _areaPrefix; + private readonly string _controllerPrefix; +#endif +} + +// this also loses the new line before the #if +using System.Text; +using System.Threading; +using System.Web.Http; + +#if NETFX_CORE +using NameValueCollection = System.Net.Http.Formatting.HttpValueCollection; +#endif + +namespace System.Net.Http.Formatting { } + // this loses a new line before the comment // Copyright (c) .NET Foundation. All rights reserved. From f02cdc41acdb211582dc1bc012bec957b317e44e Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sun, 2 Jan 2022 11:27:54 -0600 Subject: [PATCH 08/14] More edge cases --- .../ClientApp/src/FormatCode.ts | 66 ++++++++------ .../TestFiles/Directives_CompilationUnit.cst | 8 +- .../FormattingTests/TestFiles/MemberLines.cst | 25 ++++++ .../TestFiles/MemberLines.expected.cst | 27 ++++++ .../SyntaxPrinter/MembersWithForcedLines.cs | 33 ++++++- .../SyntaxPrinter/NamespaceLikePrinter.cs | 17 ++-- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 86 ------------------- 7 files changed, 141 insertions(+), 121 deletions(-) diff --git a/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts b/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts index 6a607fe0c..117253991 100644 --- a/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts +++ b/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts @@ -3,37 +3,51 @@ let marks: any[] = []; let editor: any = undefined; export const formatCode = async (code: string) => { - const response = await fetch("/Format", { - method: "POST", - body: JSON.stringify(code), - headers: { - "Content-Type": "application/json", - "cache-control": "no-cache", - }, - }); - if (response.status === 200) { - const data = await response.json(); + const doStuff = async () => { + const response = await fetch("/Format", { + method: "POST", + body: JSON.stringify(code), + headers: { + "Content-Type": "application/json", + "cache-control": "no-cache", + }, + }); + if (response.status === 200) { + const data = await response.json(); - setTimeout(() => { - setupMarks(data.errors); - }, 100); + setTimeout(() => { + setupMarks(data.errors); + }, 100); - return { - syntaxTree: JSON.parse(data.json), - formattedCode: data.code, - doc: data.doc, - hasErrors: !!data.errors.length, - }; - } else { - const text = await response.text(); - return { - formattedCode: text, - doc: text, - hasErrors: true, - }; + return { + syntaxTree: JSON.parse(data.json), + formattedCode: data.code, + doc: data.doc, + hasErrors: !!data.errors.length, + }; + } else { + const text = await response.text(); + return { + formattedCode: text, + doc: text, + hasErrors: true, + }; + } + } + + try { + return doStuff(); + } + catch { + await sleep(500); + return doStuff(); } }; +function sleep (milliseconds: number) { + return new Promise((resolve) => setTimeout(resolve, milliseconds)); +} + const setupMarks = (errors: any[]) => { if (!editor) { setTimeout(() => { diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_CompilationUnit.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_CompilationUnit.cst index b202af70b..378678c5a 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_CompilationUnit.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/Directives_CompilationUnit.cst @@ -10,4 +10,10 @@ using System.Net.Http; using System.Web.Routing; #endif -class ClassName { } +using System.Web.Http; + +#if KEEP_LINE_ABOVE +using System.Web.Http; +#endif + +namespace Namespace { } diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst index 8e3a6ffd0..e35dcad93 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst @@ -87,6 +87,11 @@ public class ClassRules public void SomeMethod() { } +#pragma warning disable 0618 + public void KeepLineAboveAndBelow() { } +#pragma warning restore 0618 + + public void SomeMethod() { } } public class LineBeforeClass { } public interface InterfaceRulesAreLikeClassesButMethodsFollowTheFieldRulesAbove @@ -98,3 +103,23 @@ public interface InterfaceRulesAreLikeClassesButMethodsFollowTheFieldRulesAbove // comments do force a line void MethodWithComment(); } + +abstract class AbstractClass +{ + abstract void NoLineHere() { } + + abstract void KeepTheLineHere(); + abstract void DoNotForceTheLineHere(); + void ButForceLineForRealMethod() { } +} + +public class ClassName +{ + private string someField1; + +#if KEEP_LINE_ABOVE + private string someField2; +#endif + + private string someField2; +} diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst index d821c21ef..cb1b48f30 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst @@ -106,6 +106,12 @@ public class ClassRules #endif public void SomeMethod() { } + +#pragma warning disable 0618 + public void KeepLineAboveAndBelow() { } +#pragma warning restore 0618 + + public void SomeMethod() { } } public class LineBeforeClass { } @@ -120,3 +126,24 @@ public interface InterfaceRulesAreLikeClassesButMethodsFollowTheFieldRulesAbove // comments do force a line void MethodWithComment(); } + +abstract class AbstractClass +{ + abstract void NoLineHere() { } + + abstract void KeepTheLineHere(); + abstract void DoNotForceTheLineHere(); + + void ButForceLineForRealMethod() { } +} + +public class ClassName +{ + private string someField1; + +#if KEEP_LINE_ABOVE + private string someField2; +#endif + + private string someField2; +} diff --git a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs index 46e7d4bfd..38b2e89dc 100644 --- a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs +++ b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs @@ -29,6 +29,16 @@ or RecordDeclarationSyntax or StructDeclarationSyntax ); + if ( + member is MethodDeclarationSyntax methodDeclaration + && node is ClassDeclarationSyntax classDeclaration + && classDeclaration.Modifiers.Any(o => o.Kind() is SyntaxKind.AbstractKeyword) + && methodDeclaration.Modifiers.Any(o => o.Kind() is SyntaxKind.AbstractKeyword) + ) + { + blankLineIsForced = false; + } + if (x == 0) { lastMemberForcedBlankLine = blankLineIsForced; @@ -50,8 +60,29 @@ or StructDeclarationSyntax } if ( + member + .GetLeadingTrivia() + .Any( + o => + o.Kind() + is SyntaxKind.PragmaWarningDirectiveTrivia + or SyntaxKind.PragmaChecksumDirectiveTrivia + or SyntaxKind.IfDirectiveTrivia + ) + ) + { + result.Add(ExtraNewLines.Print(member)); + } + else if ( addBlankLine - && !member.GetLeadingTrivia().Any(o => o.Kind() is SyntaxKind.EndIfDirectiveTrivia) + && !member + .GetLeadingTrivia() + .Any( + o => + o.Kind() + is SyntaxKind.EndIfDirectiveTrivia + or SyntaxKind.EndRegionDirectiveTrivia + ) ) { result.Add(Doc.HardLine); diff --git a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs index 0e36ea488..fc1b9d396 100644 --- a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs +++ b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs @@ -47,14 +47,17 @@ node is CompilationUnitSyntax compilationUnitSyntax if (members.Count > 0) { - if ( - (usings.Any() || (!usings.Any() && externs.Any())) - && !members[0] - .GetLeadingTrivia() - .Any(o => o.Kind() is SyntaxKind.EndIfDirectiveTrivia) - ) + if (usings.Any() || (!usings.Any() && externs.Any())) { - docs.Add(Doc.HardLine); + if ( + members[0].GetLeadingTrivia().Any(o => o.Kind() is SyntaxKind.IfDirectiveTrivia) + || !members[0] + .GetLeadingTrivia() + .Any(o => o.Kind() is SyntaxKind.EndIfDirectiveTrivia) + ) + { + docs.Add(Doc.HardLine); + } } docs.AddRange(MembersWithForcedLines.Print(node, members)); } diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index a56f5eb00..a80126822 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -1,94 +1,8 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; -// TODO should abstract methods be treated like interface methods? -/* - public abstract void WriteParameterSeparator(); - public abstract void WriteReturn(); -*/ - // TODO these are all the edge cases left + any failing tests /* -// adds line before pragma -class ClassName -{ -#pragma warning disable 0618 - public void DoStuff() { } -#pragma warning restore 0618 - - public void DoStuff() { } -} -// this should keep the line like so -class ClassName -{ - public void DoStuff() { } - -#pragma warning disable 0618 - public void DoStuff() { } -#pragma warning restore 0618 -} - -// this loses a line before the first #if -public class DirectRouteFactoryContext -{ - private readonly string _actionName; - -#if !ASPNETWEBAPI - private readonly string _controllerName; -#endif - -#if ASPNETWEBAPI - private readonly string _prefix; -#else - private readonly string _areaPrefix; - private readonly string _controllerPrefix; -#endif -} - -// this also loses the new line before the #if -using System.Text; -using System.Threading; -using System.Web.Http; - -#if NETFX_CORE -using NameValueCollection = System.Net.Http.Formatting.HttpValueCollection; -#endif - -namespace System.Net.Http.Formatting { } - -// this loses a new line before the comment - -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Web; -using Microsoft.Web.Helpers; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("Microsoft.Web.Helpers")] -[assembly: AssemblyDescription("")] -[assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")] -[assembly: InternalsVisibleTo( - "Microsoft.Web.Helpers.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" -)] - -// this loses the new line before pragma - -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; - -#pragma warning disable 659 // overrides AddToHashCodeCombiner instead - -namespace Microsoft.Web.Mvc.ExpressionUtil -{ } - */ internal static class BaseTypeDeclaration From 901348608e37f77ba1ee842e3ca4867a929cf5f9 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sun, 2 Jan 2022 12:48:00 -0600 Subject: [PATCH 09/14] Another edge case --- Src/CSharpier.Benchmarks/Program.cs | 4 +-- .../CompilationUnitIfDirectiveEdgeCase2.cst | 12 +++++++++ Src/CSharpier.Tests/Samples/Scratch.cst | 8 +++--- .../SyntaxPrinter/NamespaceLikePrinter.cs | 26 ++++++++++--------- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 1 + 5 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase2.cst diff --git a/Src/CSharpier.Benchmarks/Program.cs b/Src/CSharpier.Benchmarks/Program.cs index 02ccf0340..46354eeb9 100644 --- a/Src/CSharpier.Benchmarks/Program.cs +++ b/Src/CSharpier.Benchmarks/Program.cs @@ -369,8 +369,8 @@ private async Task FormatFile(string file, CancellationToken cancellationToken) if (!this.CommandLineOptions.Check && !this.CommandLineOptions.SkipWrite) { - // purposely avoid async here, that way the file completely writes if the process gets cancelled while running. - this.FileSystem.File.WriteAllText(file, result.Code, fileReaderResult.Encoding); +// purposely avoid async here, that way the file completely writes if the process gets cancelled while running. +this.FileSystem.File.WriteAllText(file, result.Code, fileReaderResult.Encoding); } } diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase2.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase2.cst new file mode 100644 index 000000000..44b256a5d --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase2.cst @@ -0,0 +1,12 @@ +#if DONT_ADD_EXTRA_LINES +using System.Net.Http; +#else +using System.Web.Routing; +#endif + +#if DONT_ADD_EXTRA_LINES +namespace System.Web.Http.Routing.Constraints +#else +namespace System.Web.Mvc.Routing.Constraints +#endif +{ } diff --git a/Src/CSharpier.Tests/Samples/Scratch.cst b/Src/CSharpier.Tests/Samples/Scratch.cst index 91e750176..a1a2559c5 100644 --- a/Src/CSharpier.Tests/Samples/Scratch.cst +++ b/Src/CSharpier.Tests/Samples/Scratch.cst @@ -1,5 +1,7 @@ -#if ASPNETWEBAPI -using System.Net.Http; +using System.Web.Http; + +#if KEEP_LINE_ABOVE +using System.Web.Http; #endif -class ClassName {} \ No newline at end of file +namespace Namespace { } \ No newline at end of file diff --git a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs index fc1b9d396..0da1e03f9 100644 --- a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs +++ b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs @@ -24,6 +24,7 @@ List docs { docs.Add(Doc.Join(Doc.HardLine, externs.Select(ExternAliasDirective.Print))); } + if (usings.Count > 0) { if (externs.Any()) @@ -45,21 +46,22 @@ node is CompilationUnitSyntax compilationUnitSyntax docs.Add(AttributeLists.Print(node, compilationUnitSyntax.AttributeLists)); } - if (members.Count > 0) + if (members.Count <= 0) + { + return; + } + + if (usings.Any() || (!usings.Any() && externs.Any())) { - if (usings.Any() || (!usings.Any() && externs.Any())) + if (members[0].GetLeadingTrivia().Any(o => o.IsDirective)) { - if ( - members[0].GetLeadingTrivia().Any(o => o.Kind() is SyntaxKind.IfDirectiveTrivia) - || !members[0] - .GetLeadingTrivia() - .Any(o => o.Kind() is SyntaxKind.EndIfDirectiveTrivia) - ) - { - docs.Add(Doc.HardLine); - } + docs.Add(ExtraNewLines.Print(members[0])); + } + else + { + docs.Add(Doc.HardLine); } - docs.AddRange(MembersWithForcedLines.Print(node, members)); } + docs.AddRange(MembersWithForcedLines.Print(node, members)); } } diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index a80126822..805256a14 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -3,6 +3,7 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; // TODO these are all the edge cases left + any failing tests /* + */ internal static class BaseTypeDeclaration From 92e438b6feacf28162762ceaa204a05842e7290e Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sun, 2 Jan 2022 13:02:15 -0600 Subject: [PATCH 10/14] More edge cases --- .../TestFiles/CompilationUnitIfDirectiveEdgeCase3.cst | 6 ++++++ .../SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs | 5 +++++ .../SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs | 1 - 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase3.cst diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase3.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase3.cst new file mode 100644 index 000000000..38bc81136 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase3.cst @@ -0,0 +1,6 @@ +#if NO_DOUBLE_LINE_WITH_FILE_SCOPED +using System; + +#endif + +namespace Namespace; diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index 805256a14..799b8bf5c 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -2,8 +2,13 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; // TODO these are all the edge cases left + any failing tests /* +// adds an extra line +#if DETECT_LEAKS +using System.Runtime.CompilerServices; +#endif +namespace Microsoft.AspNetCore.Razor.Language.Syntax; */ internal static class BaseTypeDeclaration diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs index 7217f77af..fb7e0ae2b 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/FileScopedNamespaceDeclaration.cs @@ -6,7 +6,6 @@ public static Doc Print(FileScopedNamespaceDeclarationSyntax node) { var docs = new List { - ExtraNewLines.Print(node), AttributeLists.Print(node, node.AttributeLists), Modifiers.Print(node.Modifiers), Token.Print(node.NamespaceKeyword), From b3e995c873c0ee3c158b7fb3651d70100546e634 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sun, 2 Jan 2022 13:48:16 -0600 Subject: [PATCH 11/14] Even more edge cases --- .../CompilationUnitIfDirectiveEdgeCase4.cst | 6 ++++++ .../FormattingTests/TestFiles/MemberLines.cst | 11 +++++++++++ .../TestFiles/MemberLines.expected.cst | 11 +++++++++++ .../SyntaxPrinter/MembersWithForcedLines.cs | 1 + Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs | 13 ++++++++++++- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 9 +++++---- 6 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase4.cst diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase4.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase4.cst new file mode 100644 index 000000000..7bd9084ee --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase4.cst @@ -0,0 +1,6 @@ +using System.Runtime.CompilerServices; +#if NO_EXTRA_LINES_WITH_ATTRIBUTE +using System.Runtime.CompilerServices; +#endif + +[assembly: Attribute] diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst index e35dcad93..7a46f464c 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst @@ -123,3 +123,14 @@ public class ClassName private string someField2; } + +public class ClassName +{ +#region RegionWithSpaceBeforeAndAfterMethod + + void SomeMethod() { } + +#endregion + + void SomeMethod() { } +} diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst index cb1b48f30..6eced4cf4 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst @@ -147,3 +147,14 @@ public class ClassName private string someField2; } + +public class ClassName +{ +#region RegionWithSpaceBeforeAndAfterMethod + + void SomeMethod() { } + +#endregion + + void SomeMethod() { } +} diff --git a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs index 38b2e89dc..4ff9f0517 100644 --- a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs +++ b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs @@ -68,6 +68,7 @@ member is MethodDeclarationSyntax methodDeclaration is SyntaxKind.PragmaWarningDirectiveTrivia or SyntaxKind.PragmaChecksumDirectiveTrivia or SyntaxKind.IfDirectiveTrivia + or SyntaxKind.EndRegionDirectiveTrivia ) ) { diff --git a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs index 0da1e03f9..cfd1e4dd2 100644 --- a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs +++ b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs @@ -41,7 +41,18 @@ node is CompilationUnitSyntax compilationUnitSyntax { if (externs.Any() || usings.Any()) { - docs.Add(Doc.HardLine, Doc.HardLine); + if ( + compilationUnitSyntax.AttributeLists[0] + .GetLeadingTrivia() + .Any(o => o.IsDirective) + ) + { + docs.Add(ExtraNewLines.Print(compilationUnitSyntax.AttributeLists[0])); + } + else + { + docs.Add(Doc.HardLine, Doc.HardLine); + } } docs.Add(AttributeLists.Print(node, compilationUnitSyntax.AttributeLists)); } diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index 799b8bf5c..342ec065f 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -2,13 +2,14 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; // TODO these are all the edge cases left + any failing tests /* -// adds an extra line -#if DETECT_LEAKS +// adds an extra lines +using System.Runtime.CompilerServices; +#if STUFF using System.Runtime.CompilerServices; - #endif -namespace Microsoft.AspNetCore.Razor.Language.Syntax; +[assembly: Attribute] + */ internal static class BaseTypeDeclaration From 53f840c56903c2d442e405ee75a3302eab974a2a Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Mon, 3 Jan 2022 10:09:48 -0600 Subject: [PATCH 12/14] Maybe the last edge case --- .../CompilationUnitIfDirectiveEdgeCase5.cst | 6 ++++++ Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs | 7 +++++-- .../SyntaxNodePrinters/BaseTypeDeclaration.cs | 12 ------------ 3 files changed, 11 insertions(+), 14 deletions(-) create mode 100644 Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst new file mode 100644 index 000000000..420715f91 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst @@ -0,0 +1,6 @@ +using System.Runtime.CompilerServices; + +#if KEEP_LINE_ABOVE +[assembly: Attribute] +#endif +[assembly: Attribute] \ No newline at end of file diff --git a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs index cfd1e4dd2..ed716cecf 100644 --- a/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs +++ b/Src/CSharpier/SyntaxPrinter/NamespaceLikePrinter.cs @@ -51,10 +51,13 @@ node is CompilationUnitSyntax compilationUnitSyntax } else { - docs.Add(Doc.HardLine, Doc.HardLine); + docs.Add(Doc.HardLine); } } - docs.Add(AttributeLists.Print(node, compilationUnitSyntax.AttributeLists)); + docs.Add( + Doc.HardLine, + AttributeLists.Print(node, compilationUnitSyntax.AttributeLists) + ); } if (members.Count <= 0) diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs index 342ec065f..c3c3f75e8 100644 --- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs +++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseTypeDeclaration.cs @@ -1,17 +1,5 @@ namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters; -// TODO these are all the edge cases left + any failing tests -/* -// adds an extra lines -using System.Runtime.CompilerServices; -#if STUFF -using System.Runtime.CompilerServices; -#endif - -[assembly: Attribute] - - */ - internal static class BaseTypeDeclaration { public static Doc Print(BaseTypeDeclarationSyntax node) From f7a784131cf568310694b1b72a43569194e0e8c3 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Mon, 3 Jan 2022 10:21:20 -0600 Subject: [PATCH 13/14] Fixing test --- .../TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst index 420715f91..801cd0117 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/CompilationUnitIfDirectiveEdgeCase5.cst @@ -3,4 +3,4 @@ using System.Runtime.CompilerServices; #if KEEP_LINE_ABOVE [assembly: Attribute] #endif -[assembly: Attribute] \ No newline at end of file +[assembly: Attribute] From 24fd5026eab70f77862a329e39e0abe78805d691 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Mon, 3 Jan 2022 10:55:31 -0600 Subject: [PATCH 14/14] Self code review --- Src/CSharpier.Benchmarks/Program.cs | 4 ++-- .../ClientApp/src/FormatCode.ts | 16 +++++++++------- .../FormattingTests/TestFiles/MemberLines.cst | 4 ++-- .../TestFiles/MemberLines.expected.cst | 4 ++-- Src/SyntaxFinder/Program.cs | 2 +- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Src/CSharpier.Benchmarks/Program.cs b/Src/CSharpier.Benchmarks/Program.cs index 46354eeb9..02ccf0340 100644 --- a/Src/CSharpier.Benchmarks/Program.cs +++ b/Src/CSharpier.Benchmarks/Program.cs @@ -369,8 +369,8 @@ private async Task FormatFile(string file, CancellationToken cancellationToken) if (!this.CommandLineOptions.Check && !this.CommandLineOptions.SkipWrite) { -// purposely avoid async here, that way the file completely writes if the process gets cancelled while running. -this.FileSystem.File.WriteAllText(file, result.Code, fileReaderResult.Encoding); + // purposely avoid async here, that way the file completely writes if the process gets cancelled while running. + this.FileSystem.File.WriteAllText(file, result.Code, fileReaderResult.Encoding); } } diff --git a/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts b/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts index 117253991..41e070d8f 100644 --- a/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts +++ b/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts @@ -3,7 +3,7 @@ let marks: any[] = []; let editor: any = undefined; export const formatCode = async (code: string) => { - const doStuff = async () => { + const makeRequest = async () => { const response = await fetch("/Format", { method: "POST", body: JSON.stringify(code), @@ -35,13 +35,15 @@ export const formatCode = async (code: string) => { } } - try { - return doStuff(); - } - catch { - await sleep(500); - return doStuff(); + for (let x = 0; x < 5; x++) { + try { + return makeRequest(); + } + catch { + await sleep(500); + } } + return makeRequest(); }; function sleep (milliseconds: number) { diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst index 7a46f464c..525510d86 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.cst @@ -72,9 +72,9 @@ public class ClassRules delegate void DelegatesAreLikeFields(); delegate void AndCanBeGrouped(); // comments force a line - private string DelegateWithComment; + delegate void DelegateWithComment(); [AttributesForceALine] - private string DelegateWithAttribute; + delegate void DelegateWithAttribute(); public void AlwaysLineBetweenMethods() { } private string FieldAfterMethodGetsLine; diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst index 6eced4cf4..af49adbfa 100644 --- a/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/MemberLines.expected.cst @@ -90,10 +90,10 @@ public class ClassRules delegate void AndCanBeGrouped(); // comments force a line - private string DelegateWithComment; + delegate void DelegateWithComment(); [AttributesForceALine] - private string DelegateWithAttribute; + delegate void DelegateWithAttribute(); public void AlwaysLineBetweenMethods() { } diff --git a/Src/SyntaxFinder/Program.cs b/Src/SyntaxFinder/Program.cs index 0b703fa2c..6e4ea0915 100644 --- a/Src/SyntaxFinder/Program.cs +++ b/Src/SyntaxFinder/Program.cs @@ -127,7 +127,7 @@ node.Parent is TypeDeclarationSyntax typeDeclarationSyntax } Interlocked.Increment(ref Total); - this.WriteCode(node.Parent); + this.WriteCode(node.Parent!); if (node.GetLeadingTrivia().Any(o => o.Kind() is SyntaxKind.EndOfLineTrivia)) {