Skip to content

Commit acf691e

Browse files
committed
Remove trailing spaces from comments.
This removes trailing spaces at the end of each line in all types of comments. While Xcode supports automatically deleting these spaces, not all users enable it or even use Xcode for all development.
1 parent cb4e621 commit acf691e

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)