@@ -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 )
0 commit comments