diff --git a/Documentation/Configuration.md b/Documentation/Configuration.md index 1035286ce..84d7ae9a7 100644 --- a/Documentation/Configuration.md +++ b/Documentation/Configuration.md @@ -31,6 +31,9 @@ top-level keys and values: lines that are allowed to be present in a source file. Any number larger than this will be collapsed down to the maximum. +* `spacesBeforeEndOfLineComments` _(number)_: The number of spaces between + the last token on a non-empty line and a line comment starting with `//`. + * `respectsExistingLineBreaks` _(boolean)_: Indicates whether or not existing line breaks in the source code should be honored (if they are valid according to the style guidelines being enforced). If this settings is diff --git a/Sources/SwiftFormat/API/Configuration+Default.swift b/Sources/SwiftFormat/API/Configuration+Default.swift index 3f0123fb4..52a91873e 100644 --- a/Sources/SwiftFormat/API/Configuration+Default.swift +++ b/Sources/SwiftFormat/API/Configuration+Default.swift @@ -26,6 +26,7 @@ extension Configuration { self.lineLength = 100 self.tabWidth = 8 self.indentation = .spaces(2) + self.spacesBeforeEndOfLineComments = 2 self.respectsExistingLineBreaks = true self.lineBreakBeforeControlFlowKeywords = false self.lineBreakBeforeEachArgument = false diff --git a/Sources/SwiftFormat/API/Configuration.swift b/Sources/SwiftFormat/API/Configuration.swift index ab8a3e952..7669a1ed6 100644 --- a/Sources/SwiftFormat/API/Configuration.swift +++ b/Sources/SwiftFormat/API/Configuration.swift @@ -28,6 +28,7 @@ public struct Configuration: Codable, Equatable { case version case maximumBlankLines case lineLength + case spacesBeforeEndOfLineComments case tabWidth case indentation case respectsExistingLineBreaks @@ -66,6 +67,9 @@ public struct Configuration: Codable, Equatable { /// The maximum length of a line of source code, after which the formatter will break lines. public var lineLength: Int + /// Number of spaces that precede line comments. + public var spacesBeforeEndOfLineComments: Int + /// The width of the horizontal tab in spaces. /// /// This value is used when converting indentation types (for example, from tabs into spaces). @@ -225,6 +229,9 @@ public struct Configuration: Codable, Equatable { self.lineLength = try container.decodeIfPresent(Int.self, forKey: .lineLength) ?? defaults.lineLength + self.spacesBeforeEndOfLineComments = + try container.decodeIfPresent(Int.self, forKey: .spacesBeforeEndOfLineComments) + ?? defaults.spacesBeforeEndOfLineComments self.tabWidth = try container.decodeIfPresent(Int.self, forKey: .tabWidth) ?? defaults.tabWidth @@ -288,6 +295,7 @@ public struct Configuration: Codable, Equatable { try container.encode(version, forKey: .version) try container.encode(maximumBlankLines, forKey: .maximumBlankLines) try container.encode(lineLength, forKey: .lineLength) + try container.encode(spacesBeforeEndOfLineComments, forKey: .spacesBeforeEndOfLineComments) try container.encode(tabWidth, forKey: .tabWidth) try container.encode(indentation, forKey: .indentation) try container.encode(respectsExistingLineBreaks, forKey: .respectsExistingLineBreaks) diff --git a/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift index 8e7dc617b..468dc519c 100644 --- a/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift @@ -3171,7 +3171,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { return ( true, [ - .space(size: 2, flexible: true), + .space(size: config.spacesBeforeEndOfLineComments, flexible: true), .comment(Comment(kind: .line, text: text), wasEndOfLine: true), // There must be a break with a soft newline after the comment, but it's impossible to // know which kind of break must be used. Adding this newline is deferred until the diff --git a/Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift b/Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift index 84403f64d..64f9aa039 100644 --- a/Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift +++ b/Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift @@ -1,4 +1,5 @@ import _SwiftFormatTestSupport +import SwiftFormat final class CommentTests: PrettyPrintTestCase { func testDocumentationComments() { @@ -199,6 +200,152 @@ final class CommentTests: PrettyPrintTestCase { assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) } + func testLineCommentsWithCustomLeadingSpaces() { + let pairs: [(String, String)] = [ + ( + """ + // Line Comment0 + + // Line Comment1 + // Line Comment2 + let a = 123 + let b = "456" // End of line comment + let c = "More content" + + """, + """ + // Line Comment0 + + // Line Comment1 + // Line Comment2 + let a = 123 + let b = "456" // End of line comment + let c = "More content" + + """ + ), + ( + """ + // Comment 3 + // Comment 4 + + let reallyLongVariableName = 123 // This comment should not wrap + // and should not combine with this comment + + func MyFun() { + // just a comment + } + """, + """ + // Comment 3 + // Comment 4 + + let reallyLongVariableName = 123 // This comment should not wrap + // and should not combine with this comment + + func MyFun() { + // just a comment + } + + """ + ), + ( + """ + func MyFun() { + // Comment 1 + // Comment 2 + let a = 123 + + let b = 456 // Comment 3 + } + + func MyFun() { + let c = 789 // Comment 4 + // Comment 5 + } + """, + """ + func MyFun() { + // Comment 1 + // Comment 2 + let a = 123 + + let b = 456 // Comment 3 + } + + func MyFun() { + let c = 789 // Comment 4 + // Comment 5 + } + + """ + ), + ( + """ + let a = myfun(123 // Cmt 7 + ) + let a = myfun(var1: 123 // Cmt 7 + ) + + guard condition else { return // Cmt 6 + } + + switch myvar { + case .one, .two, // three + .four: + dostuff() + default: () + } + + """, + """ + let a = myfun( + 123 // Cmt 7 + ) + let a = myfun( + var1: 123 // Cmt 7 + ) + + guard condition else { + return // Cmt 6 + } + + switch myvar { + case .one, .two, // three + .four: + dostuff() + default: () + } + + """ + ), + ( + """ + let a = 123 + // comment + b + c + + let d = 123 + // Trailing Comment + """, + """ + let a = + 123 // comment + + b + c + + let d = 123 + // Trailing Comment + + """ + ), + ] + + var config = Configuration.forTesting + config.spacesBeforeEndOfLineComments = 3 + for (input, expected) in pairs { + assertPrettyPrintEqual(input: input, expected: expected, linelength: 45, configuration: config) + } + } + func testContainerLineComments() { let input = """ @@ -274,6 +421,82 @@ final class CommentTests: PrettyPrintTestCase { assertPrettyPrintEqual(input: input, expected: expected, linelength: 80) } + func testContainerLineCommentsWithCustomLeadingSpaces() { + let input = + """ + // Array comment + let a = [456, // small comment + 789] + + // Dictionary comment + let b = ["abc": 456, // small comment + "def": 789] + + // Trailing comment + let c = [123, 456 // small comment + ] + + // Multiline comment + let d = [123, + // comment line 1 + // comment line 2 + 456 + ] + + /* Array comment */ + let a = [456, /* small comment */ + 789] + + /* Dictionary comment */ + let b = ["abc": 456, /* small comment */ + "def": 789] + """ + + let expected = + """ + // Array comment + let a = [ + 456, // small comment + 789, + ] + + // Dictionary comment + let b = [ + "abc": 456, // small comment + "def": 789, + ] + + // Trailing comment + let c = [ + 123, 456, // small comment + ] + + // Multiline comment + let d = [ + 123, + // comment line 1 + // comment line 2 + 456, + ] + + /* Array comment */ + let a = [ + 456, /* small comment */ + 789, + ] + + /* Dictionary comment */ + let b = [ + "abc": 456, /* small comment */ + "def": 789, + ] + + """ + var config = Configuration.forTesting + config.spacesBeforeEndOfLineComments = 1 + assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config) + } + func testDocumentationBlockComments() { let input = """