From 6cf4ea0257feef10b12a6990f2d7c704b34b2db4 Mon Sep 17 00:00:00 2001 From: Shawn Hyam Date: Tue, 7 May 2024 08:51:12 -0400 Subject: [PATCH 1/2] Merge adjacent .line and .docLine comments into a single element. --- Sources/SwiftFormat/PrettyPrint/Comment.swift | 7 +++-- .../PrettyPrint/TokenStreamCreator.swift | 31 +++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Sources/SwiftFormat/PrettyPrint/Comment.swift b/Sources/SwiftFormat/PrettyPrint/Comment.swift index 8e119b819..60d63af10 100644 --- a/Sources/SwiftFormat/PrettyPrint/Comment.swift +++ b/Sources/SwiftFormat/PrettyPrint/Comment.swift @@ -64,7 +64,7 @@ struct Comment { switch kind { case .line, .docLine: - self.text = [text.trimmingTrailingWhitespace()] + self.text = [text] self.text[0].removeFirst(kind.prefixLength) self.length = self.text.reduce(0, { $0 + $1.count + kind.prefixLength + 1 }) @@ -88,8 +88,9 @@ struct Comment { func print(indent: [Indent]) -> String { switch self.kind { case .line, .docLine: - let separator = "\n" + kind.prefix - return kind.prefix + self.text.joined(separator: separator) + let separator = "\n" + indent.indentation() + kind.prefix + let trimmedLines = self.text.map { $0.trimmingTrailingWhitespace() } + return kind.prefix + trimmedLines.joined(separator: separator) case .block, .docBlock: let separator = "\n" return kind.prefix + self.text.joined(separator: separator) + "*/" diff --git a/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift index cf02ce209..81fb56707 100644 --- a/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift @@ -3386,12 +3386,31 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { private func appendToken(_ token: Token) { if let last = tokens.last { switch (last, token) { - case (.comment(let c1, _), .comment(let c2, _)) - where c1.kind == .docLine && c2.kind == .docLine: - var newComment = c1 - newComment.addText(c2.text) - tokens[tokens.count - 1] = .comment(newComment, wasEndOfLine: false) - return + case (.break(.same, _, .soft(let count, _)), .comment(let c2, _)) + where count == 1 && (c2.kind == .docLine || c2.kind == .line): + // we are search for the pattern of [line comment] - [soft break 1] - [line comment] + // where the comment type is the same; these can be merged into a single comment + if let nextToLast = tokens.dropLast().last, + case let .comment(c1, false) = nextToLast, + c1.kind == c2.kind + { + var mergedComment = c1 + mergedComment.addText(c2.text) + tokens.removeLast() // remove the soft break + // replace the original comment with the merged one + tokens[tokens.count - 1] = .comment(mergedComment, wasEndOfLine: false) + + // need to fix lastBreakIndex because we just removed the last break + lastBreakIndex = tokens.lastIndex(where: { + switch $0 { + case .break: return true + default: return false + } + }) + canMergeNewlinesIntoLastBreak = false + + return + } // If we see a pair of spaces where one or both are flexible, combine them into a new token // with the maximum of their counts. From b065fa319e616110ac03f16ca647188475b3c5f7 Mon Sep 17 00:00:00 2001 From: Shawn Hyam Date: Wed, 8 May 2024 14:40:51 -0400 Subject: [PATCH 2/2] Improvements based on feedback and further testing. --- .../PrettyPrint/TokenStreamCreator.swift | 8 ++- .../PrettyPrint/CommentTests.swift | 72 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift index 81fb56707..08f1aa4f0 100644 --- a/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift @@ -3384,10 +3384,14 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { /// This function also handles collapsing neighboring tokens in situations where that is /// desired, like merging adjacent comments and newlines. private func appendToken(_ token: Token) { + func breakAllowsCommentMerge(_ breakKind: BreakKind) -> Bool { + return breakKind == .same || breakKind == .continue || breakKind == .contextual + } + if let last = tokens.last { switch (last, token) { - case (.break(.same, _, .soft(let count, _)), .comment(let c2, _)) - where count == 1 && (c2.kind == .docLine || c2.kind == .line): + case (.break(let breakKind, _, .soft(1, _)), .comment(let c2, _)) + where breakAllowsCommentMerge(breakKind) && (c2.kind == .docLine || c2.kind == .line): // we are search for the pattern of [line comment] - [soft break 1] - [line comment] // where the comment type is the same; these can be merged into a single comment if let nextToLast = tokens.dropLast().last, diff --git a/Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift b/Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift index 6525cdf60..84403f64d 100644 --- a/Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift +++ b/Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift @@ -83,6 +83,8 @@ final class CommentTests: PrettyPrintTestCase { func testLineComments() { let input = """ + // Line Comment0 + // Line Comment1 // Line Comment2 let a = 123 @@ -93,6 +95,7 @@ final class CommentTests: PrettyPrintTestCase { // Comment 4 let reallyLongVariableName = 123 // This comment should not wrap + // and should not combine with this comment func MyFun() { // just a comment @@ -135,6 +138,8 @@ final class CommentTests: PrettyPrintTestCase { let expected = """ + // Line Comment0 + // Line Comment1 // Line Comment2 let a = 123 @@ -145,6 +150,7 @@ final class CommentTests: PrettyPrintTestCase { // Comment 4 let reallyLongVariableName = 123 // This comment should not wrap + // and should not combine with this comment func MyFun() { // just a comment @@ -208,6 +214,13 @@ final class CommentTests: PrettyPrintTestCase { 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] @@ -236,6 +249,14 @@ final class CommentTests: PrettyPrintTestCase { 123, 456, // small comment ] + // Multiline comment + let d = [ + 123, + // comment line 1 + // comment line 2 + 456, + ] + /* Array comment */ let a = [ 456, /* small comment */ @@ -760,4 +781,55 @@ final class CommentTests: PrettyPrintTestCase { ] ) } + + func testLineWithDocLineComment() { + // none of these should be merged if/when there is comment formatting + let input = + """ + /// Doc line comment + // Line comment + /// Doc line comment + // Line comment + + // Another line comment + + """ + assertPrettyPrintEqual(input: input, expected: input, linelength: 80) + } + + func testNonmergeableComments() { + // none of these should be merged if/when there is comment formatting + let input = + """ + let x = 1 // end of line comment + // + + let y = // eol comment + 1 // another + + 2 // and another + + """ + + assertPrettyPrintEqual(input: input, expected: input, linelength: 80) + } + + func testMergeableComments() { + // these examples should be merged and formatted if/when there is comment formatting + let input = + """ + let z = + // one comment + // and another comment + 1 + 2 + + let w = [1, 2, 3] + .foo() + // this comment + // could be merged with this one + .bar() + + """ + + assertPrettyPrintEqual(input: input, expected: input, linelength: 80) + } }