@@ -18,12 +18,15 @@ open class BasicFormat: SyntaxRewriter {
18
18
19
19
/// As we reach a new indendation level, its indentation will be added to the
20
20
/// 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 ) ]
22
25
23
26
/// The trivia by which tokens should currently be indented.
24
27
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
27
30
}
28
31
29
32
/// For every token that is being put on a new line but did not have
@@ -39,37 +42,45 @@ open class BasicFormat: SyntaxRewriter {
39
42
40
43
public init ( indentationWidth: Trivia = . spaces( 4 ) , initialIndentation: Trivia = [ ] ) {
41
44
self . indentationWidth = indentationWidth
42
- self . indentationStack = [ initialIndentation]
45
+ self . indentationStack = [ ( indentation : initialIndentation, isUserDefined : false ) ]
43
46
}
44
47
45
48
// MARK: - Updating indentation level
46
49
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
+ }
49
56
}
50
57
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
+ }
53
63
indentationStack. removeLast ( )
54
64
}
55
65
56
66
open override func visitPre( _ node: Syntax ) {
57
67
if requiresIndent ( node) {
58
68
if let firstToken = node. firstToken ( viewMode: . sourceAccurate) ,
59
69
let tokenIndentation = firstToken. leadingTrivia. indentation ( isOnNewline: false ) ,
60
- !tokenIndentation. isEmpty
70
+ !tokenIndentation. isEmpty,
71
+ let lastNonUserDefinedIndentation = indentationStack. last ( where: { !$0. isUserDefined } ) ? . indentation
61
72
{
62
73
// If the first token in this block is indented, infer the indentation level from it.
63
- pushIndentationLevel ( increasingIndentationBy : tokenIndentation)
74
+ increaseIndentationLevel ( to : lastNonUserDefinedIndentation + tokenIndentation)
64
75
} else {
65
- pushIndentationLevel ( increasingIndentationBy : indentationWidth )
76
+ increaseIndentationLevel ( )
66
77
}
67
78
}
68
79
}
69
80
70
81
open override func visitPost( _ node: Syntax ) {
71
82
if requiresIndent ( node) {
72
- popIndentationLevel ( )
83
+ decreaseIndentationLevel ( )
73
84
}
74
85
}
75
86
@@ -187,7 +198,7 @@ open class BasicFormat: SyntaxRewriter {
187
198
guard let previousToken = previousToken else {
188
199
return false
189
200
}
190
- return previousToken. trailingTrivia. pieces . last ? . isWhitespace ?? false
201
+ return previousToken. trailingTrivia. endsWithWhitespace
191
202
|| requiresWhitespace ( between: previousToken, and: token)
192
203
} ( )
193
204
@@ -197,7 +208,25 @@ open class BasicFormat: SyntaxRewriter {
197
208
// don't add a leading newline to the file.
198
209
return true
199
210
}
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
201
230
} ( )
202
231
203
232
lazy var nextTokenWillStartWithNewline : Bool = {
@@ -270,7 +299,7 @@ open class BasicFormat: SyntaxRewriter {
270
299
trailingTriviaIndentation = anchorPointIndentation
271
300
}
272
301
273
- leadingTrivia = leadingTrivia. indented ( indentation: leadingTriviaIndentation, isOnNewline: false )
302
+ leadingTrivia = leadingTrivia. indented ( indentation: leadingTriviaIndentation, isOnNewline: previousTokenIsStringLiteralEndingInNewline )
274
303
trailingTrivia = trailingTrivia. indented ( indentation: trailingTriviaIndentation, isOnNewline: false )
275
304
276
305
leadingTrivia = leadingTrivia. trimmingTrailingWhitespaceBeforeNewline ( isBeforeNewline: false )
0 commit comments