Skip to content

Commit c938faa

Browse files
committed
Fix multiline string indentation
1 parent 55881a9 commit c938faa

File tree

4 files changed

+72
-26
lines changed

4 files changed

+72
-26
lines changed

CodeGeneration/Sources/generate-swiftsyntax/templates/basicformat/BasicFormatFile.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,15 @@ let basicFormatFile = SourceFileSyntax(leadingTrivia: generateCopyrightHeader(fo
9393
trailingTrivia += .space
9494
}
9595
if let keyPath = getKeyPath(Syntax(node)), requiresLeadingNewline(keyPath), !(leadingTrivia.first?.isNewline ?? false) {
96-
leadingTrivia = .newline + leadingTrivia
96+
if !shouldOmitNewline(node) {
97+
leadingTrivia = .newline + leadingTrivia
98+
}
99+
}
100+
var isOnNewline: Bool = (lastRewrittenToken?.trailingTrivia.pieces.last?.isNewline == true)
101+
if case .stringSegment(let text) = lastRewrittenToken?.tokenKind {
102+
isOnNewline = isOnNewline || (text.last?.isNewline == true)
97103
}
98-
leadingTrivia = leadingTrivia.indented(indentation: indentation)
99-
trailingTrivia = trailingTrivia.indented(indentation: indentation)
104+
leadingTrivia = leadingTrivia.indented(indentation: indentation, isOnNewline: isOnNewline)
100105
let rewritten = TokenSyntax(
101106
node.tokenKind,
102107
leadingTrivia: leadingTrivia,
@@ -110,6 +115,22 @@ let basicFormatFile = SourceFileSyntax(leadingTrivia: generateCopyrightHeader(fo
110115
"""
111116
)
112117

118+
DeclSyntax(
119+
"""
120+
open func shouldOmitNewline(_ node: TokenSyntax) -> Bool {
121+
var ancestor: Syntax = Syntax(node)
122+
while let parent = ancestor.parent {
123+
ancestor = parent
124+
if ancestor.is(ExpressionSegmentSyntax.self) {
125+
return true
126+
}
127+
}
128+
129+
return false
130+
}
131+
"""
132+
)
133+
113134
try FunctionDeclSyntax("open func shouldIndent(_ keyPath: AnyKeyPath) -> Bool") {
114135
try SwitchExprSyntax("switch keyPath") {
115136
for node in SYNTAX_NODES where !node.isBase {

Sources/SwiftBasicFormat/Trivia+Indented.swift

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,33 @@
1313
import SwiftSyntax
1414

1515
extension Trivia {
16-
func indented(indentation: TriviaPiece) -> Trivia {
16+
func indented(indentation: TriviaPiece, isOnNewline: Bool = false) -> Trivia {
1717
var indentedPieces: [TriviaPiece] = []
1818
for (index, piece) in self.enumerated() {
19-
let nextPiece = index < pieces.count - 1 ? pieces[index + 1] : nil
20-
indentedPieces.append(piece)
21-
if piece.isNewline {
22-
switch (nextPiece, indentation) {
23-
case (.spaces(let nextPieceSpaces)?, .spaces(let indentationSpaces)):
19+
let previousPieceIsNewline: Bool
20+
if index == 0 {
21+
previousPieceIsNewline = isOnNewline
22+
} else {
23+
previousPieceIsNewline = pieces[index - 1].isNewline
24+
}
25+
if previousPieceIsNewline {
26+
switch (piece, indentation) {
27+
case (.spaces(let nextPieceSpaces), .spaces(let indentationSpaces)):
2428
if nextPieceSpaces < indentationSpaces {
2529
indentedPieces.append(.spaces(indentationSpaces - nextPieceSpaces))
2630
}
27-
case (.tabs(let nextPieceTabs)?, .tabs(let indentationTabs)):
31+
case (.tabs(let nextPieceTabs), .tabs(let indentationTabs)):
2832
if nextPieceTabs < indentationTabs {
2933
indentedPieces.append(.tabs(indentationTabs - nextPieceTabs))
3034
}
3135
default:
3236
indentedPieces.append(indentation)
3337
}
3438
}
39+
indentedPieces.append(piece)
40+
}
41+
if self.pieces.last?.isNewline == true {
42+
indentedPieces.append(indentation)
3543
}
3644
return Trivia(pieces: indentedPieces)
3745
}

Sources/SwiftBasicFormat/generated/BasicFormat.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,15 @@ open class BasicFormat: SyntaxRewriter {
5555
trailingTrivia += .space
5656
}
5757
if let keyPath = getKeyPath(Syntax(node)), requiresLeadingNewline(keyPath), !(leadingTrivia.first?.isNewline ?? false) {
58-
leadingTrivia = .newline + leadingTrivia
58+
if !shouldOmitNewline(node) {
59+
leadingTrivia = .newline + leadingTrivia
60+
}
5961
}
60-
leadingTrivia = leadingTrivia.indented(indentation: indentation)
61-
trailingTrivia = trailingTrivia.indented(indentation: indentation)
62+
var isOnNewline: Bool = (lastRewrittenToken?.trailingTrivia.pieces.last?.isNewline == true)
63+
if case .stringSegment(let text) = lastRewrittenToken?.tokenKind {
64+
isOnNewline = isOnNewline || (text.last?.isNewline == true)
65+
}
66+
leadingTrivia = leadingTrivia.indented(indentation: indentation, isOnNewline: isOnNewline)
6267
let rewritten = TokenSyntax(
6368
node.tokenKind,
6469
leadingTrivia: leadingTrivia,
@@ -71,6 +76,18 @@ open class BasicFormat: SyntaxRewriter {
7176
return rewritten
7277
}
7378

79+
open func shouldOmitNewline(_ node: TokenSyntax) -> Bool {
80+
var ancestor: Syntax = Syntax(node)
81+
while let parent = ancestor.parent {
82+
ancestor = parent
83+
if ancestor.is(ExpressionSegmentSyntax.self) {
84+
return true
85+
}
86+
}
87+
88+
return false
89+
}
90+
7491
open func shouldIndent(_ keyPath: AnyKeyPath) -> Bool {
7592
switch keyPath {
7693
case \AccessorBlockSyntax.accessors:

Tests/SwiftSyntaxBuilderTest/StringLiteralTests.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,11 @@ final class StringLiteralTests: XCTestCase {
282282
buildable,
283283
#"""
284284
assertionFailure("""
285-
Error validating child at index \(index) of \(nodeKind):
286-
Node did not satisfy any node choice requirement.
287-
Validation failures:
288-
\(nonNilErrors.map({ "- \($0.description)" }).joined(separator: "\n"))
289-
""", file: file, line: line)
285+
Error validating child at index \(index) of \(nodeKind):
286+
Node did not satisfy any node choice requirement.
287+
Validation failures:
288+
\(nonNilErrors.map({ "- \($0.description)" }).joined(separator: "\n"))
289+
""", file: file, line: line)
290290
"""#
291291
)
292292
}
@@ -309,10 +309,10 @@ final class StringLiteralTests: XCTestCase {
309309
#"""
310310
if true {
311311
assertionFailure("""
312-
Error validating child at index
313-
Node did not satisfy any node choice requirement.
314-
Validation failures:
315-
""")
312+
Error validating child at index
313+
Node did not satisfy any node choice requirement.
314+
Validation failures:
315+
""")
316316
}
317317
"""#
318318
)
@@ -339,10 +339,10 @@ final class StringLiteralTests: XCTestCase {
339339
if true {
340340
assertionFailure(
341341
"""
342-
Error validating child at index
343-
Node did not satisfy any node choice requirement.
344-
Validation failures:
345-
"""
342+
Error validating child at index
343+
Node did not satisfy any node choice requirement.
344+
Validation failures:
345+
"""
346346
)
347347
}
348348
"""#

0 commit comments

Comments
 (0)