@@ -243,18 +243,69 @@ Tokens = [docBlock(" Doc Block comment\n * Second line *")]
243
243
244
244
Token generation begins with the abstract syntax tree (AST) of the Swift source
245
245
file, provided by the [ SwiftSyntax] ( https://github.com/apple/swift-syntax )
246
- library. We have a ` visit ` method for each of the different syntax node types
247
- (e.g. ` FunctionDeclSyntax ` , ` GenericWhereClause ` , etc.). Within each of these
248
- visit methods, we can attach pretty-printer ` Token ` objects before and after
249
- syntax tokens from the AST. For example, if we wanted a group after the opening
250
- brace of a function declaration, it might look like:
246
+ library. We have overloaded a ` visit ` method for each of the different kinds of
247
+ syntax nodes. Most of these nodes are higher-level, and are composed of other
248
+ nodes. For example, ` FunctionDeclSyntax ` contains
249
+ ` GenericParameterClauseSyntax ` , ` FunctionSignatureSyntax ` nodes among others.
250
+ These member nodes are called via a call to ` super.visit ` at the end of the
251
+ function. That being said, we visit the higher level nodes before the lower
252
+ level nodes.
253
+
254
+ Within the visit methods, you can attach pretty-printing tokens at different
255
+ points within the syntax structures. For example, if you wanted to place an
256
+ indenting group around the body of a function declaration with consistent
257
+ breaking, and you want the trailing brace forced to the next line, it might look
258
+ like:
251
259
252
260
```
253
- # node: FunctionDeclSyntax
254
- after(node.body?.leftBrace, tokens: .break(size: 1, offset: 2), .open(.consistent, 0))
261
+ // In visit(_ node: FunctionDeclSyntax)
262
+ after(node.body?.leftBrace, tokens: .break(offset: 2), .open(.consistent, 0))
263
+ before(node.body?.rightBrace, tokens: .break(offset: -2), .close)
255
264
```
256
265
257
- All of the tokens are placed into an array, which are then passed on to the
258
- * scan* phase of the pretty printer.
266
+ Two dictionaries are maintained to keep track of the pretty-printing tokens
267
+ attached to the syntax tokens: ` beforeMap ` , and ` afterMap ` . Calls to ` before `
268
+ and ` after ` populate these dictionaries. In the above example, ` node.body? ` may
269
+ return ` nil ` , in which case ` before ` and ` after ` gracefully do nothing.
270
+
271
+ The lowest level in the AST is ` TokenSyntax ` , and it is at this point that we
272
+ actually add the syntax token and its attached pretty-printer tokens to the
273
+ output array. This is done in ` visit(_ token: TokenSyntax) ` . We first check the
274
+ syntax token's leading trivia for the presence of newlines and comments
275
+ (excluding end-of-line comments), and add corresponding printing tokens to the
276
+ output array. Next, we look at the token's entry in the ` beforeMap ` dictionary
277
+ and add any accumulated ` before ` tokens to the output array. Next, we add the
278
+ syntax token itself to the array. We look ahead to the leading trivia of the
279
+ next syntax token to check for an end-of-line comment, and we add it to the
280
+ array if needed. Finally, we add the ` after ` tokens. The ordering of the ` after `
281
+ tokens is adjusted such that the token attached by lower level ` visit ` method
282
+ are added to the array before the higher level ` visit ` methods.
283
+
284
+ The only types of trivia we are interested in are newlines and comments. Since
285
+ these only appear as leading trivia, we don't need to look at trailing trivia.
286
+ It is important to note that ` SwiftSyntax ` always attaches comments as the
287
+ leading trivia on the following token. Spaces are handled directly by inserting
288
+ ` break ` and ` space ` tokens, and backticks are handled in the * scan* and * print*
289
+ phases of the algorithm, after token generation.
290
+
291
+ When examining trivia for comments, a distinction is made for end-of-line
292
+ comments:
293
+
294
+ ```
295
+ // not end-of-line
296
+ let a = 123 // end-of-line comment
297
+ let b = "abc"
298
+
299
+ // In the above example, "not end-of-line" is part of the leading trivia of
300
+ // "let" for "let a", and "end-of-line comment" is leading trivia for "let" of
301
+ // "let b".
302
+ ```
303
+
304
+ A comment is determined to be end-of-line when it appears as the first item in a
305
+ token's leading trivia (it is not preceded by a newline, and we are not at the
306
+ beginning of a source file).
307
+
308
+ When we have visited all nodes in the AST, the array of printing tokens is then
309
+ passed on to the * scan* phase of the pretty-printer.
259
310
260
311
See: [ ` TokenStreamCreator.swift ` ] ( ../Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift )
0 commit comments