Skip to content

Commit 12eebf5

Browse files
committed
Fix indentation issue if trailing trivia contains a newline at the end of an indentation scope
We were always adding indentation to the trailing trivia of a token. This could cause issues if the token at the end of an indentation scope (in most cases a `}` token) contained a trailing newline. In that case we would add indentation after that final newline based on the indentation scope of `}`, which effectively indented the next token to the indentation scope inside the `}` and thus indenting it one level to far. To fix this, don’t add indentation for trailing newlines of tokens. Instead, add the indentation as the leading trivia of the next token, which will pick up the correct indentation scope.
1 parent 4dcc4ac commit 12eebf5

File tree

3 files changed

+75
-7
lines changed

3 files changed

+75
-7
lines changed

Sources/SwiftBasicFormat/BasicFormat.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,19 @@ open class BasicFormat: SyntaxRewriter {
594594
trailingTriviaIndentation = anchorPointIndentation
595595
}
596596

597-
leadingTrivia = leadingTrivia.indented(indentation: leadingTriviaIndentation, isOnNewline: previousTokenIsStringLiteralEndingInNewline)
598-
trailingTrivia = trailingTrivia.indented(indentation: trailingTriviaIndentation, isOnNewline: false)
597+
leadingTrivia = leadingTrivia.indented(
598+
indentation: leadingTriviaIndentation,
599+
isOnNewline: previousTokenIsStringLiteralEndingInNewline || previousTokenWillEndWithNewline
600+
)
601+
trailingTrivia = trailingTrivia.indented(
602+
indentation: trailingTriviaIndentation,
603+
isOnNewline: false,
604+
// Don't add indentation to the last newline.
605+
// Its indentation will be added to the next token's leading trivia, which
606+
// might have a different indentation scope than this token (in particular
607+
// if this token is a closing brace).
608+
addIndentationAfterLastNewline: false
609+
)
599610

600611
leadingTrivia = leadingTrivia.trimmingTrailingWhitespaceBeforeNewline(isBeforeNewline: leadingTriviaIsFollowedByNewline)
601612
trailingTrivia = trailingTrivia.trimmingTrailingWhitespaceBeforeNewline(isBeforeNewline: nextTokenWillStartWithNewline)

Sources/SwiftBasicFormat/Trivia+FormatExtensions.swift

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,20 @@ extension Trivia {
6262
}
6363

6464
/// Adds `indentation` after every newline in this trivia.
65-
func indented(indentation: Trivia, isOnNewline: Bool) -> Trivia {
65+
///
66+
/// - Parameters:
67+
/// - indentation: The amount of indentation to add
68+
/// - isOnNewline: Whether this token starts on a new line.
69+
/// This causes the indentation to get added at the start of the trivia.
70+
/// - addIndentationAfterLastNewline: Whether to add indentation after newline
71+
/// if the newline is the last piece of trivia. Not doing this makes sense
72+
/// if the indentation will get added to the next token's leading trivia
73+
/// via `isOnNewline`.
74+
func indented(
75+
indentation: Trivia,
76+
isOnNewline: Bool,
77+
addIndentationAfterLastNewline: Bool = true
78+
) -> Trivia {
6679
guard !isEmpty else {
6780
if isOnNewline {
6881
return indentation
@@ -72,13 +85,13 @@ extension Trivia {
7285

7386
var indentedPieces: [TriviaPiece] = []
7487
if isOnNewline {
75-
indentedPieces.append(contentsOf: indentation)
88+
indentedPieces += indentation
7689
}
7790

78-
for piece in pieces {
91+
for (index, piece) in pieces.enumerated() {
7992
indentedPieces.append(piece)
80-
if piece.isNewline {
81-
indentedPieces.append(contentsOf: indentation)
93+
if piece.isNewline && !(index == pieces.count - 1 && !addIndentationAfterLastNewline) {
94+
indentedPieces += indentation
8295
}
8396
}
8497

Tests/SwiftBasicFormatTest/BasicFormatTests.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,4 +566,48 @@ final class BasicFormatTest: XCTestCase {
566566
"""
567567
assertFormatted(source: source, expected: source)
568568
}
569+
570+
func testNewlineInTrailingTriviaAtEndOfIndentationScope() throws {
571+
assertFormatted(
572+
tree: try FunctionDeclSyntax("func test()") {
573+
CodeBlockItemSyntax("Task {\n}\n")
574+
},
575+
expected: """
576+
func test() {
577+
Task {
578+
}
579+
}
580+
"""
581+
)
582+
583+
assertFormatted(
584+
tree: try FunctionDeclSyntax("func test()") {
585+
CodeBlockItemSyntax("Task {\n}\n\n")
586+
},
587+
expected: """
588+
func test() {
589+
Task {
590+
}
591+
592+
}
593+
"""
594+
)
595+
596+
assertFormatted(
597+
tree: try FunctionDeclSyntax("func bar()") {
598+
try FunctionDeclSyntax("func test()") {
599+
CodeBlockItemSyntax("Task {\n}\n")
600+
}
601+
},
602+
expected: """
603+
func bar() {
604+
func test() {
605+
Task {
606+
}
607+
}
608+
}
609+
"""
610+
)
611+
}
612+
569613
}

0 commit comments

Comments
 (0)