Skip to content

Commit d9ac67e

Browse files
authored
Merge pull request swiftlang#211 from dylansturg/no_trailing_spaces
Remove trailing spaces from comments and keep spaces after block comments.
2 parents cb4e621 + acf691e commit d9ac67e

File tree

3 files changed

+78
-7
lines changed

3 files changed

+78
-7
lines changed

Sources/SwiftFormatPrettyPrint/Comment.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@ import Foundation
1414
import SwiftFormatConfiguration
1515
import SwiftSyntax
1616

17+
extension StringProtocol {
18+
/// Trims whitespace from the end of a string, returning a new string with no trailing whitespace.
19+
///
20+
/// If the string is only whitespace, an empty string is returned.
21+
///
22+
/// - Returns: The string with trailing whitespace removed.
23+
func trimmingTrailingWhitespace() -> String {
24+
if isEmpty { return String() }
25+
let scalars = unicodeScalars
26+
var idx = scalars.index(before: scalars.endIndex)
27+
while scalars[idx].properties.isWhitespace {
28+
if idx == scalars.startIndex { return String() }
29+
idx = scalars.index(before: idx)
30+
}
31+
return String(String.UnicodeScalarView(scalars[...idx]))
32+
}
33+
}
34+
1735
struct Comment {
1836
enum Kind {
1937
case line, docLine, block, docBlock
@@ -47,17 +65,23 @@ struct Comment {
4765

4866
switch kind {
4967
case .line, .docLine:
50-
self.text = [text]
68+
self.text = [text.trimmingTrailingWhitespace()]
5169
self.text[0].removeFirst(kind.prefixLength)
5270
self.length = self.text.reduce(0, { $0 + $1.count + kind.prefixLength + 1 })
5371

5472
case .block, .docBlock:
5573
var fulltext: String = text
5674
fulltext.removeFirst(kind.prefixLength)
5775
fulltext.removeLast(2)
58-
self.text = fulltext.split(separator: "\n", omittingEmptySubsequences: false).map {
59-
String($0)
76+
let lines = fulltext.split(separator: "\n", omittingEmptySubsequences: false)
77+
78+
// The last line in a block style comment contains the "*/" pattern to end the comment. The
79+
// trailing space(s) need to be kept in that line to have space between text and "*/".
80+
var trimmedLines = lines.dropLast().map({ $0.trimmingTrailingWhitespace() })
81+
if let lastLine = lines.last {
82+
trimmedLines.append(String(lastLine))
6083
}
84+
self.text = trimmedLines
6185
self.length = self.text.reduce(0, { $0 + $1.count }) + kind.prefixLength + 3
6286
}
6387
}

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2730,10 +2730,15 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
27302730
case .blockComment(let text):
27312731
if index > 0 || isStartOfFile {
27322732
appendToken(.comment(Comment(kind: .block, text: text), wasEndOfLine: false))
2733-
// We place a size-0 break after the comment to allow a discretionary newline after the
2734-
// comment if the user places one here but the comment is otherwise adjacent to a text
2735-
// token.
2736-
appendToken(.break(.same, size: 0))
2733+
// There is always a break after the comment to allow a discretionary newline after it.
2734+
var breakSize = 0
2735+
if index + 1 < trivia.endIndex {
2736+
let nextPiece = trivia[index + 1]
2737+
// The original number of spaces is intentionally discarded, but 1 space is allowed in
2738+
// case the comment is followed by another token instead of a newline.
2739+
if case .spaces = nextPiece { breakSize = 1 }
2740+
}
2741+
appendToken(.break(.same, size: breakSize))
27372742
isStartOfFile = false
27382743
}
27392744
requiresNextNewline = false

Tests/SwiftFormatPrettyPrintTests/CommentTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,5 +639,47 @@ final class CommentTests: PrettyPrintTestCase {
639639
assertPrettyPrintEqual(input: input, expected: expected, linelength: 100)
640640
}
641641

642+
func testTrailingSpacesInComments() {
643+
// Xcode has a commonly enabled setting to delete trailing spaces, which also applies to
644+
// multi-line strings. The trailing spaces are intentionally written using a unicode escape
645+
// sequence to ensure they aren't deleted.
646+
let input = """
647+
/// This is a trailing space documentation comment.\u{0020}\u{0020}\u{0020}\u{0020}\u{0020}
648+
/// This is a leading space documentation comment.
649+
func foo() {
650+
// leading spaces are fine
651+
// trailing spaces should go\u{0020}\u{0020}\u{0020}\u{0020}\u{0020}
652+
let out = "123"
653+
}
642654
655+
/**
656+
* This is a leading space doc block comment
657+
* This is a trailing space doc block comment\u{0020}\u{0020}\u{0020}\u{0020}\u{0020}
658+
*/
659+
func foo() {
660+
/* block comment */ let out = "123"
661+
}
662+
"""
663+
664+
let expected = """
665+
/// This is a trailing space documentation comment.
666+
/// This is a leading space documentation comment.
667+
func foo() {
668+
// leading spaces are fine
669+
// trailing spaces should go
670+
let out = "123"
671+
}
672+
673+
/**
674+
* This is a leading space doc block comment
675+
* This is a trailing space doc block comment
676+
*/
677+
func foo() {
678+
/* block comment */ let out = "123"
679+
}
680+
681+
"""
682+
683+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 60)
684+
}
643685
}

0 commit comments

Comments
 (0)