Skip to content

Commit 16fcefb

Browse files
authored
Merge pull request swiftlang#161 from dabelknap/pretty-printer-doc
Elaborate on token generation
2 parents ff6f9ce + 1476058 commit 16fcefb

File tree

1 file changed

+60
-9
lines changed

1 file changed

+60
-9
lines changed

Diff for: Documentation/PrettyPrinter.md

+60-9
Original file line numberDiff line numberDiff line change
@@ -243,18 +243,69 @@ Tokens = [docBlock(" Doc Block comment\n * Second line *")]
243243

244244
Token generation begins with the abstract syntax tree (AST) of the Swift source
245245
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:
251259

252260
```
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)
255264
```
256265

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.
259310

260311
See: [`TokenStreamCreator.swift`](../Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift)

0 commit comments

Comments
 (0)