@@ -35,21 +35,28 @@ open class BasicFormat: SyntaxRewriter {
3535 /// This is used as a reference-point to indent user-indented code.
3636 private var anchorPoints : [ TokenSyntax : Trivia ] = [ : ]
3737
38+ public let viewMode : SyntaxTreeViewMode
39+
3840 /// The previously visited token. This is faster than accessing
3941 /// `token.previousToken` inside `visit(_:TokenSyntax)`. `nil` if no token has
4042 /// been visited yet.
4143 private var previousToken : TokenSyntax ? = nil
4244
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+ ) {
4450 self . indentationWidth = indentationWidth
4551 self . indentationStack = [ ( indentation: initialIndentation, isUserDefined: false ) ]
52+ self . viewMode = viewMode
4653 }
4754
4855 // MARK: - Updating indentation level
4956
5057 public func increaseIndentationLevel( to userDefinedIndentation: Trivia ? = nil ) {
5158 if let userDefinedIndentation = userDefinedIndentation {
52- indentationStack. append ( ( indentation: userDefinedIndentation, isUserDefined: false ) )
59+ indentationStack. append ( ( indentation: userDefinedIndentation, isUserDefined: true ) )
5360 } else {
5461 indentationStack. append ( ( indentation: currentIndentationLevel + indentationWidth, isUserDefined: false ) )
5562 }
@@ -65,7 +72,7 @@ open class BasicFormat: SyntaxRewriter {
6572
6673 open override func visitPre( _ node: Syntax ) {
6774 if requiresIndent ( node) {
68- if let firstToken = node. firstToken ( viewMode: . sourceAccurate ) ,
75+ if let firstToken = node. firstToken ( viewMode: viewMode ) ,
6976 let tokenIndentation = firstToken. leadingTrivia. indentation ( isOnNewline: false ) ,
7077 !tokenIndentation. isEmpty,
7178 let lastNonUserDefinedIndentation = indentationStack. last ( where: { !$0. isUserDefined } ) ? . indentation
@@ -103,6 +110,8 @@ open class BasicFormat: SyntaxRewriter {
103110 return true
104111 case . codeBlockItemList:
105112 return true
113+ case . ifConfigClauseList:
114+ return true
106115 case . memberDeclList:
107116 return true
108117 case . switchCaseList:
@@ -118,7 +127,7 @@ open class BasicFormat: SyntaxRewriter {
118127 var ancestor : Syntax = Syntax ( token)
119128 while let parent = ancestor. parent {
120129 ancestor = parent
121- if let firstToken = parent. firstToken ( viewMode: . sourceAccurate ) ,
130+ if let firstToken = parent. firstToken ( viewMode: viewMode ) ,
122131 let anchorPointIndentation = anchorPoints [ firstToken]
123132 {
124133 return anchorPointIndentation
@@ -148,41 +157,113 @@ open class BasicFormat: SyntaxRewriter {
148157 var ancestor : Syntax = Syntax ( token)
149158 while let parent = ancestor. parent {
150159 ancestor = parent
151- if ancestor. position != token. position {
160+ if ancestor. position != token. position || ancestor . firstToken ( viewMode : viewMode ) != token {
152161 break
153162 }
154163 if let ancestorsParent = ancestor. parent, childrenSeparatedByNewline ( ancestorsParent) {
155164 return true
156165 }
166+ switch ancestor. keyPathInParent {
167+ case \IfConfigClauseSyntax . elements:
168+ return true
169+ default :
170+ break
171+ }
157172 }
158173
159174 return false
160175 }
161176
162177 open func requiresWhitespace( between first: TokenSyntax ? , and second: TokenSyntax ? ) -> Bool {
163178 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
174242 return false
175243 default :
176244 break
177245 }
178246
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
184256 }
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
186267 }
187268
188269 // MARK: - Formatting a token
@@ -191,15 +272,15 @@ open class BasicFormat: SyntaxRewriter {
191272 defer {
192273 self . previousToken = token
193274 }
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 )
196277
197278 lazy var previousTokenWillEndWithWhitespace : Bool = {
198279 guard let previousToken = previousToken else {
199280 return false
200281 }
201282 return previousToken. trailingTrivia. endsWithWhitespace
202- || requiresWhitespace ( between: previousToken, and: token)
283+ || ( requiresWhitespace ( between: previousToken, and: token) && isMutable ( previousToken ) )
203284 } ( )
204285
205286 lazy var previousTokenWillEndWithNewline : Bool = {
@@ -234,7 +315,7 @@ open class BasicFormat: SyntaxRewriter {
234315 return false
235316 }
236317 return nextToken. leadingTrivia. startsWithNewline
237- || requiresLeadingNewline ( nextToken)
318+ || ( requiresLeadingNewline ( nextToken) && isMutable ( nextToken ) )
238319 } ( )
239320
240321 /// This token's trailing trivia + any spaces or tabs at the start of the
0 commit comments