@@ -35,21 +35,28 @@ open class BasicFormat: SyntaxRewriter {
35
35
/// This is used as a reference-point to indent user-indented code.
36
36
private var anchorPoints : [ TokenSyntax : Trivia ] = [ : ]
37
37
38
+ public let viewMode : SyntaxTreeViewMode
39
+
38
40
/// The previously visited token. This is faster than accessing
39
41
/// `token.previousToken` inside `visit(_:TokenSyntax)`. `nil` if no token has
40
42
/// been visited yet.
41
43
private var previousToken : TokenSyntax ? = nil
42
44
43
- public init ( indentationWidth: Trivia = . spaces( 4 ) , initialIndentation: Trivia = [ ] ) {
45
+ public init (
46
+ indentationWidth: Trivia = . spaces( 4 ) ,
47
+ initialIndentation: Trivia = [ ] ,
48
+ viewMode: SyntaxTreeViewMode = . sourceAccurate
49
+ ) {
44
50
self . indentationWidth = indentationWidth
45
51
self . indentationStack = [ ( indentation: initialIndentation, isUserDefined: false ) ]
52
+ self . viewMode = viewMode
46
53
}
47
54
48
55
// MARK: - Updating indentation level
49
56
50
57
public func increaseIndentationLevel( to userDefinedIndentation: Trivia ? = nil ) {
51
58
if let userDefinedIndentation = userDefinedIndentation {
52
- indentationStack. append ( ( indentation: userDefinedIndentation, isUserDefined: false ) )
59
+ indentationStack. append ( ( indentation: userDefinedIndentation, isUserDefined: true ) )
53
60
} else {
54
61
indentationStack. append ( ( indentation: currentIndentationLevel + indentationWidth, isUserDefined: false ) )
55
62
}
@@ -65,7 +72,7 @@ open class BasicFormat: SyntaxRewriter {
65
72
66
73
open override func visitPre( _ node: Syntax ) {
67
74
if requiresIndent ( node) {
68
- if let firstToken = node. firstToken ( viewMode: . sourceAccurate ) ,
75
+ if let firstToken = node. firstToken ( viewMode: viewMode ) ,
69
76
let tokenIndentation = firstToken. leadingTrivia. indentation ( isOnNewline: false ) ,
70
77
!tokenIndentation. isEmpty,
71
78
let lastNonUserDefinedIndentation = indentationStack. last ( where: { !$0. isUserDefined } ) ? . indentation
@@ -103,6 +110,8 @@ open class BasicFormat: SyntaxRewriter {
103
110
return true
104
111
case . codeBlockItemList:
105
112
return true
113
+ case . ifConfigClauseList:
114
+ return true
106
115
case . memberDeclList:
107
116
return true
108
117
case . switchCaseList:
@@ -118,7 +127,7 @@ open class BasicFormat: SyntaxRewriter {
118
127
var ancestor : Syntax = Syntax ( token)
119
128
while let parent = ancestor. parent {
120
129
ancestor = parent
121
- if let firstToken = parent. firstToken ( viewMode: . sourceAccurate ) ,
130
+ if let firstToken = parent. firstToken ( viewMode: viewMode ) ,
122
131
let anchorPointIndentation = anchorPoints [ firstToken]
123
132
{
124
133
return anchorPointIndentation
@@ -148,41 +157,113 @@ open class BasicFormat: SyntaxRewriter {
148
157
var ancestor : Syntax = Syntax ( token)
149
158
while let parent = ancestor. parent {
150
159
ancestor = parent
151
- if ancestor. position != token. position {
160
+ if ancestor. position != token. position || ancestor . firstToken ( viewMode : viewMode ) != token {
152
161
break
153
162
}
154
163
if let ancestorsParent = ancestor. parent, childrenSeparatedByNewline ( ancestorsParent) {
155
164
return true
156
165
}
166
+ switch ancestor. keyPathInParent {
167
+ case \IfConfigClauseSyntax . elements:
168
+ return true
169
+ default :
170
+ break
171
+ }
157
172
}
158
173
159
174
return false
160
175
}
161
176
162
177
open func requiresWhitespace( between first: TokenSyntax ? , and second: TokenSyntax ? ) -> Bool {
163
178
switch ( first? . tokenKind, second? . tokenKind) {
164
- case ( . leftParen, . leftBrace) , // Ensures there is not a space in `.map({ $0.foo })`
165
- ( . exclamationMark, . leftParen) , // Ensures there is not a space in `myOptionalClosure!()`
166
- ( . exclamationMark, . period) , // Ensures there is not a space in `myOptionalBar!.foo()`
167
- ( . keyword( . as) , . exclamationMark) , // Ensures there is not a space in `as!`
168
- ( . keyword( . as) , . postfixQuestionMark) , // Ensures there is not a space in `as?`
169
- ( . keyword( . try ) , . exclamationMark) , // Ensures there is not a space in `try!`
170
- ( . keyword( . try ) , . postfixQuestionMark) , // Ensures there is not a space in `try?`:
171
- ( . postfixQuestionMark, . leftParen) , // Ensures there is not a space in `init?()` or `myOptionalClosure?()`s
172
- ( . postfixQuestionMark, . rightAngle) , // Ensures there is not a space in `ContiguousArray<RawSyntax?>`
173
- ( . postfixQuestionMark, . rightParen) : // Ensures there is not a space in `myOptionalClosure?()`
179
+ case ( . atSign, _) ,
180
+ ( . backslash, _) ,
181
+ ( . backtick, _) ,
182
+ ( . dollarIdentifier, . period) , // a.b
183
+ ( . eof, _) ,
184
+ ( . exclamationMark, . leftParen) , // myOptionalClosure!()
185
+ ( . exclamationMark, . period) , // myOptionalBar!.foo()
186
+ ( . extendedRegexDelimiter, . leftParen) , // opening extended regex delimiter should never be separate by a space
187
+ ( . extendedRegexDelimiter, . regexSlash) , // opening extended regex delimiter should never be separate by a space
188
+ ( . identifier, . leftAngle) , // MyType<Int>
189
+ ( . identifier, . leftParen) , // foo()
190
+ ( . identifier, . leftSquareBracket) , // myArray[1]
191
+ ( . identifier, . period) , // a.b
192
+ ( . integerLiteral, . period) , // macOS 11.2.1
193
+ ( . keyword( . `init`) , . leftAngle) , // init<T>()
194
+ ( . keyword( . `init`) , . leftParen) , // init()
195
+ ( . keyword( . self ) , . period) , // self.someProperty
196
+ ( . keyword( . Self) , . period) , // self.someProperty
197
+ ( . keyword( . set) , . leftParen) , // var mYar: Int { set(value) {} }
198
+ ( . keyword( . subscript) , . leftParen) , // subscript(x: Int)
199
+ ( . keyword( . super) , . period) , // super.someProperty
200
+ ( . leftBrace, _) ,
201
+ ( . leftParen, _) ,
202
+ ( . leftSquareBracket, _) ,
203
+ ( . multilineStringQuote, . rawStringDelimiter) , // closing raw string delimiter should never be separate by a space
204
+ ( . period, _) ,
205
+ ( . postfixQuestionMark, . leftAngle) , // init?<T>()
206
+ ( . postfixQuestionMark, . leftParen) , // init?() or myOptionalClosure?()
207
+ ( . postfixQuestionMark, . period) , // someOptional?.someProperty
208
+ ( . pound, _) ,
209
+ ( . poundUnavailableKeyword, . leftParen) , // #unavailable(...)
210
+ ( . prefixAmpersand, _) ,
211
+ ( . prefixOperator, _) ,
212
+ ( . rawStringDelimiter, . leftParen) , // opening raw string delimiter should never be separate by a space
213
+ ( . rawStringDelimiter, . multilineStringQuote) , // opening raw string delimiter should never be separate by a space
214
+ ( . rawStringDelimiter, . singleQuote) , // opening raw string delimiter should never be separate by a space
215
+ ( . rawStringDelimiter, . stringQuote) , // opening raw string delimiter should never be separate by a space
216
+ ( . regexLiteralPattern, _) ,
217
+ ( . regexSlash, . extendedRegexDelimiter) , // closing extended regex delimiter should never be separate by a space
218
+ ( . rightAngle, . leftParen) , // func foo<T>(x: T)
219
+ ( . rightParen, . leftParen) , // returnsClosure()()
220
+ ( . rightParen, . period) , // foo().bar
221
+ ( . rightSquareBracket, . period) , // myArray[1].someProperty
222
+ ( . singleQuote, . rawStringDelimiter) , // closing raw string delimiter should never be separate by a space
223
+ ( . stringQuote, . rawStringDelimiter) , // closing raw string delimiter should never be separate by a space
224
+ ( . stringSegment, _) ,
225
+ ( _, . colon) ,
226
+ ( _, . comma) ,
227
+ ( _, . ellipsis) ,
228
+ ( _, . eof) ,
229
+ ( _, . exclamationMark) ,
230
+ ( _, . postfixOperator) ,
231
+ ( _, . postfixQuestionMark) ,
232
+ ( _, . rightBrace) ,
233
+ ( _, . rightParen) ,
234
+ ( _, . rightSquareBracket) ,
235
+ ( _, . semicolon) ,
236
+ ( _, nil ) ,
237
+ ( nil , _) :
238
+ return false
239
+ case ( . leftAngle, _) where second? . tokenKind != . rightAngle: // `<` and `>` need to be separated by a space because otherwise they become an operator
240
+ return false
241
+ case ( _, . rightAngle) where first? . tokenKind != . leftAngle: // `<` and `>` need to be separated by a space because otherwise they become an operator
174
242
return false
175
243
default :
176
244
break
177
245
}
178
246
179
- if first? . requiresTrailingSpace ?? false {
180
- return true
181
- }
182
- if second? . requiresLeadingSpace ?? false {
183
- return true
247
+ switch first? . keyPathInParent {
248
+ case \ExpressionSegmentSyntax . backslash,
249
+ \ExpressionSegmentSyntax . rightParen,
250
+ \DeclNameArgumentSyntax . colon,
251
+ \StringLiteralExprSyntax . openQuote,
252
+ \RegexLiteralExprSyntax . openSlash:
253
+ return false
254
+ default :
255
+ break
184
256
}
185
- return false
257
+
258
+ return true
259
+ }
260
+
261
+ /// Whether the formatter should consider this token as being mutable.
262
+ /// This allows the diagnostic generator to only assume that missing nodes
263
+ /// will be mutated. Thus, if two tokens need to be separated by a space, it
264
+ /// will not be assumed that the space is added to an immutable previous node.
265
+ open func isMutable( _ token: TokenSyntax ) -> Bool {
266
+ return true
186
267
}
187
268
188
269
// MARK: - Formatting a token
@@ -191,15 +272,15 @@ open class BasicFormat: SyntaxRewriter {
191
272
defer {
192
273
self . previousToken = token
193
274
}
194
- let previousToken = self . previousToken ?? token. previousToken ( viewMode: . sourceAccurate )
195
- let nextToken = token. nextToken ( viewMode: . sourceAccurate )
275
+ let previousToken = self . previousToken ?? token. previousToken ( viewMode: viewMode )
276
+ let nextToken = token. nextToken ( viewMode: viewMode )
196
277
197
278
lazy var previousTokenWillEndWithWhitespace : Bool = {
198
279
guard let previousToken = previousToken else {
199
280
return false
200
281
}
201
282
return previousToken. trailingTrivia. endsWithWhitespace
202
- || requiresWhitespace ( between: previousToken, and: token)
283
+ || ( requiresWhitespace ( between: previousToken, and: token) && isMutable ( previousToken ) )
203
284
} ( )
204
285
205
286
lazy var previousTokenWillEndWithNewline : Bool = {
@@ -234,7 +315,7 @@ open class BasicFormat: SyntaxRewriter {
234
315
return false
235
316
}
236
317
return nextToken. leadingTrivia. startsWithNewline
237
- || requiresLeadingNewline ( nextToken)
318
+ || ( requiresLeadingNewline ( nextToken) && isMutable ( nextToken ) )
238
319
} ( )
239
320
240
321
/// This token's trailing trivia + any spaces or tabs at the start of the
0 commit comments