Skip to content

Commit 2e4761e

Browse files
committed
Minor fixes to BasicFormat that I found while using it to run CodeGeneration
1 parent 1a555cf commit 2e4761e

File tree

3 files changed

+48
-19
lines changed

3 files changed

+48
-19
lines changed

CodeGeneration/Sources/Utils/CodeGenerationFormat.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public class CodeGenerationFormat: BasicFormat {
106106
}
107107

108108
private func formatChildrenSeparatedByNewline<SyntaxType: SyntaxProtocol>(children: SyntaxChildren, elementType: SyntaxType.Type) -> [SyntaxType] {
109-
pushIndentationLevel(increasingIndentationBy: indentationWidth)
109+
increaseIndentationLevel()
110110
var formattedChildren = children.map {
111111
self.visit($0).as(SyntaxType.self)!
112112
}
@@ -117,7 +117,7 @@ public class CodeGenerationFormat: BasicFormat {
117117
return $0.with(\.leadingTrivia, indentedNewline + $0.leadingTrivia)
118118
}
119119
}
120-
popIndentationLevel()
120+
decreaseIndentationLevel()
121121
if !formattedChildren.isEmpty {
122122
formattedChildren[formattedChildren.count - 1] = formattedChildren[formattedChildren.count - 1].with(\.trailingTrivia, indentedNewline)
123123
}

Sources/SwiftBasicFormat/BasicFormat.swift

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ open class BasicFormat: SyntaxRewriter {
1818

1919
/// As we reach a new indendation level, its indentation will be added to the
2020
/// stack. As we exit that indentation level, the indendation will be popped.
21-
public var indentationStack: [Trivia]
21+
/// `isUserDefined` is `true` if the indentation was inferred from something
22+
/// the user provided manually instead of being inferred from the nesting
23+
/// level.
24+
public var indentationStack: [(indentation: Trivia, isUserDefined: Bool)]
2225

2326
/// The trivia by which tokens should currently be indented.
2427
public var currentIndentationLevel: Trivia {
25-
// `popIndentationLevel` guarantees that there is always one item on the stack.
26-
return indentationStack.last!
28+
// `decreaseIndentationLevel` guarantees that there is always one item on the stack.
29+
return indentationStack.last!.indentation
2730
}
2831

2932
/// For every token that is being put on a new line but did not have
@@ -39,37 +42,45 @@ open class BasicFormat: SyntaxRewriter {
3942

4043
public init(indentationWidth: Trivia = .spaces(4), initialIndentation: Trivia = []) {
4144
self.indentationWidth = indentationWidth
42-
self.indentationStack = [initialIndentation]
45+
self.indentationStack = [(indentation: initialIndentation, isUserDefined: false)]
4346
}
4447

4548
// MARK: - Updating indentation level
4649

47-
public func pushIndentationLevel(increasingIndentationBy: Trivia) {
48-
indentationStack.append(currentIndentationLevel + increasingIndentationBy)
50+
public func increaseIndentationLevel(to userDefinedIndentation: Trivia? = nil) {
51+
if let userDefinedIndentation = userDefinedIndentation {
52+
indentationStack.append((indentation: userDefinedIndentation, isUserDefined: false))
53+
} else {
54+
indentationStack.append((indentation: currentIndentationLevel + indentationWidth, isUserDefined: false))
55+
}
4956
}
5057

51-
public func popIndentationLevel() {
52-
precondition(indentationStack.count > 1, "Popping more indentation levels than have been pushed")
58+
public func decreaseIndentationLevel() {
59+
if indentationStack.count == 1 {
60+
assertionFailure("Popping more indentation levels than have been pushed")
61+
return
62+
}
5363
indentationStack.removeLast()
5464
}
5565

5666
open override func visitPre(_ node: Syntax) {
5767
if requiresIndent(node) {
5868
if let firstToken = node.firstToken(viewMode: .sourceAccurate),
5969
let tokenIndentation = firstToken.leadingTrivia.indentation(isOnNewline: false),
60-
!tokenIndentation.isEmpty
70+
!tokenIndentation.isEmpty,
71+
let lastNonUserDefinedIndentation = indentationStack.last(where: { !$0.isUserDefined })?.indentation
6172
{
6273
// If the first token in this block is indented, infer the indentation level from it.
63-
pushIndentationLevel(increasingIndentationBy: tokenIndentation)
74+
increaseIndentationLevel(to: lastNonUserDefinedIndentation + tokenIndentation)
6475
} else {
65-
pushIndentationLevel(increasingIndentationBy: indentationWidth)
76+
increaseIndentationLevel()
6677
}
6778
}
6879
}
6980

7081
open override func visitPost(_ node: Syntax) {
7182
if requiresIndent(node) {
72-
popIndentationLevel()
83+
decreaseIndentationLevel()
7384
}
7485
}
7586

@@ -187,7 +198,7 @@ open class BasicFormat: SyntaxRewriter {
187198
guard let previousToken = previousToken else {
188199
return false
189200
}
190-
return previousToken.trailingTrivia.pieces.last?.isWhitespace ?? false
201+
return previousToken.trailingTrivia.endsWithWhitespace
191202
|| requiresWhitespace(between: previousToken, and: token)
192203
}()
193204

@@ -197,7 +208,25 @@ open class BasicFormat: SyntaxRewriter {
197208
// don't add a leading newline to the file.
198209
return true
199210
}
200-
return previousToken.trailingTrivia.endsWithNewline
211+
if previousToken.trailingTrivia.endsWithNewline {
212+
return true
213+
}
214+
if case .stringSegment(let segment) = previousToken.tokenKind, segment.last?.isNewline ?? false {
215+
return true
216+
}
217+
return false
218+
}()
219+
220+
lazy var previousTokenIsStringLiteralEndingInNewline: Bool = {
221+
guard let previousToken = previousToken else {
222+
// Assume that the start of the tree is equivalent to a newline so we
223+
// don't add a leading newline to the file.
224+
return true
225+
}
226+
if case .stringSegment(let segment) = previousToken.tokenKind, segment.last?.isNewline ?? false {
227+
return true
228+
}
229+
return false
201230
}()
202231

203232
lazy var nextTokenWillStartWithNewline: Bool = {
@@ -270,7 +299,7 @@ open class BasicFormat: SyntaxRewriter {
270299
trailingTriviaIndentation = anchorPointIndentation
271300
}
272301

273-
leadingTrivia = leadingTrivia.indented(indentation: leadingTriviaIndentation, isOnNewline: false)
302+
leadingTrivia = leadingTrivia.indented(indentation: leadingTriviaIndentation, isOnNewline: previousTokenIsStringLiteralEndingInNewline)
274303
trailingTrivia = trailingTrivia.indented(indentation: trailingTriviaIndentation, isOnNewline: false)
275304

276305
leadingTrivia = leadingTrivia.trimmingTrailingWhitespaceBeforeNewline(isBeforeNewline: false)

Sources/_SwiftSyntaxTestSupport/SyntaxProtocol+Initializer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ private class InitializerExprFormat: BasicFormat {
2020
}
2121

2222
private func formatChildrenSeparatedByNewline<SyntaxType: SyntaxProtocol>(children: SyntaxChildren, elementType: SyntaxType.Type) -> [SyntaxType] {
23-
pushIndentationLevel(increasingIndentationBy: indentationWidth)
23+
increaseIndentationLevel()
2424
var formattedChildren = children.map {
2525
self.visit($0).as(SyntaxType.self)!
2626
}
@@ -31,7 +31,7 @@ private class InitializerExprFormat: BasicFormat {
3131
return $0.with(\.leadingTrivia, .newline + currentIndentationLevel + $0.leadingTrivia)
3232
}
3333
}
34-
popIndentationLevel()
34+
decreaseIndentationLevel()
3535
if !formattedChildren.isEmpty {
3636
formattedChildren[formattedChildren.count - 1] = formattedChildren[formattedChildren.count - 1].with(\.trailingTrivia, .newline + currentIndentationLevel)
3737
}

0 commit comments

Comments
 (0)