-
Notifications
You must be signed in to change notification settings - Fork 244
/
Copy pathToken.swift
234 lines (198 loc) · 10 KB
/
Token.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftSyntax
enum GroupBreakStyle {
/// A consistent break indicates that the break will always be finalized as a newline
/// if wrapping occurs.
case consistent
/// Inconsistent breaks will only be expressed as a newline if they're required to be wrapped as
/// their addition to the line would go past the line length limit.
case inconsistent
}
enum OpenBreakKind: Equatable {
/// An open break that applies a block indent to its scope and is allowed to apply a continuation
/// indent if and only if the line on which the open break occurs is a continuation line.
case block
/// An open break that always applies up to one continuation indent to its scope. A continuation
/// indent is applied if either the line on which this break is encountered is a continuation or
/// if this break fires. A continuation open break never applies a block indent to its scope.
case continuation
}
enum BreakKind: Equatable {
/// If line wrapping occurs at an `open` break, then the base indentation level increases by one
/// indentation unit until the corresponding `close` break is encountered.
case open(kind: OpenBreakKind)
/// If line wrapping occurs at a `close` break, then the base indentation level returns to the
/// value it had before the corresponding `open` break.
///
/// If `mustBreak` is true, then this break will always produce a line break when it occurs on a
/// different line than its corresponding `open` break. This is the behavior one typically wants
/// when laying out curly-brace delimited blocks or array/dictionary literals. If `mustBreak` is
/// false, then this break will only produce a line break when absolutely necessary (i.e., if the
/// rest of the line's length required it). This behavior is desirable for the parentheses around
/// function calls, where there is not typically a need for a line break before the closing
/// parenthesis.
///
/// In either case above, the base indentation level of subsequent tokens is still adjusted.
case close(mustBreak: Bool)
/// If line wrapping occurs at a `continue` break, then the following line will be treated as a
/// continuation line (indented one unit further than the base level) without changing the base
/// level.
///
/// An example use of a `continue` break is when an expression is split across multiple lines;
/// the break before each operator is a continuation:
///
/// ```swift
/// let someLongVariable = someLongExpression
/// + anotherLongExpression - aThirdLongExpression
/// + somethingElse
/// ```
case `continue`
/// If line wrapping occurs at a `same` break, then the following line will be indented at the
/// base indentation level instead of an increased continuation level.
///
/// An example use of a `same` break is to align the first element on each line in a
/// comma-delimited list:
///
/// ```swift
/// let array = [
/// 1, 2, 3,
/// 4, 5, 6,
/// 7, 8, 9,
/// ]
/// ```
case same
/// A `reset` break that occurs on a continuation line forces a line break that ends the
/// continuation and causes the tokens on the next line to be indented at the base indentation
/// level.
///
/// These breaks are used, for example, to force an open curly brace onto a new line if it would
/// otherwise fit on a continuation line, so that the body of the braced block can be
/// distinguished from the continuation line(s) above it:
///
/// ```swift
/// func foo(_ x: Int) {
/// // This is allowed because the line above is not a continuation.
/// }
///
/// func foo(
/// _ x: Int
/// ) {
/// // This is also allowed, for the same reason.
/// }
///
/// func foo(_ x: Int)
/// throws -> Int
/// {
/// // Here we must force the brace down or the block contents would
/// // collide with the "throws" line.
/// }
/// ```
case reset
/// A `contextual` break acts as either a `continue` break or maintains the existing level of
/// indentation when it fires. The contextual breaking behavior of a given contextual breaking
/// scope (i.e. inside a `contextualBreakingStart`/`contextualBreakingEnd` region) is set by the
/// first child `contextualBreakingStart`/`contextualBreakingEnd` pair. When the first child is
/// multiline the contextual breaks maintain indentation and they are continuations otherwise.
///
/// These are used when multiple related breaks need to exhibit the same behavior based the
/// context in which they appear. For example, when breaks exist between expressions that are
/// chained together (e.g. member access) and indenting the line *after* a closing paren/brace
/// looks better indented when the previous expr was 1 line but not indented when the expr was
/// multiline.
case contextual
/// A `close` break that defaults to forced breaking behavior.
static let close = BreakKind.close(mustBreak: true)
/// An `open` break that defaults to applying a block indent to its scope.
static let open = BreakKind.open(kind: .block)
}
/// Behaviors for creating newlines as part of a break, i.e. where breaking onto a newline is
/// allowed.
enum NewlineBehavior {
/// Breaking onto a newline is allowed if necessary, but is not required. `ignoresDiscretionary`
/// specifies whether a user-entered discretionary newline should be respected.
case elective(ignoresDiscretionary: Bool)
/// Breaking onto a newline `count` times is required, unless it would create more blank lines
/// than are allowed by the current configuration. Any blank lines over the configured limit are
/// discarded. `discretionary` tracks whether these newlines were created based on user-entered
/// discretionary newlines, from the source, or were inserted by the formatter.
case soft(count: Int, discretionary: Bool)
/// Breaking onto a newline `count` times is required and any limits on blank lines are
/// **ignored**. Exactly `count` newlines are always printed, regardless of existing consecutive
/// newlines and the configured maximum number of blank lines.
case hard(count: Int)
/// Break onto a new line is allowed if neccessary. If a line break is emitted, it will be escaped with a '\', and this breaks whitespace will be printed prior to the
/// escaped line break. This is useful in multiline strings where we don't want newlines printed in syntax to appear in the literal.
case escaped
/// An elective newline that respects discretionary newlines from the user-entered text.
static let elective = NewlineBehavior.elective(ignoresDiscretionary: false)
/// A single soft newline that is created by the formatter, i.e. *not* discretionary.
static let soft = NewlineBehavior.soft(count: 1, discretionary: false)
/// A single hard newline.
static let hard = NewlineBehavior.hard(count: 1)
}
/// Kinds of printer control tokens that can be used to customize the pretty printer's behavior in
/// a subsection of a token stream.
enum PrinterControlKind {
/// A signal that break tokens shouldn't be allowed to fire until a corresponding enable breaking
/// control token is encountered.
///
/// It's valid to nest `disableBreaking` and `enableBreaking` tokens. Breaks will be suppressed
/// long as there is at least 1 unmatched disable token. If `allowDiscretionary` is `true`, then
/// discretionary breaks aren't effected. An `allowDiscretionary` value of true never overrides a
/// value of false. Hard breaks are always inserted no matter what.
case disableBreaking(allowDiscretionary: Bool)
/// A signal that break tokens should be allowed to fire following this token, as long as there
/// are no other unmatched disable tokens.
case enableBreaking
}
enum Token {
case syntax(String)
case open(GroupBreakStyle)
case close
case `break`(BreakKind, size: Int, newlines: NewlineBehavior)
case space(size: Int, flexible: Bool)
case comment(Comment, wasEndOfLine: Bool)
case verbatim(Verbatim)
case printerControl(kind: PrinterControlKind)
/// Marks the beginning of a comma delimited collection, where a trailing comma should be inserted
/// at `commaDelimitedRegionEnd` if and only if the collection spans multiple lines.
case commaDelimitedRegionStart
/// Marks the end of a comma delimited collection, where a trailing comma should be inserted
/// if and only if the collection spans multiple lines and has multiple elements.
case commaDelimitedRegionEnd(hasTrailingComma: Bool, isSingleElement: Bool)
/// Starts a scope where `contextual` breaks have consistent behavior.
case contextualBreakingStart
/// Ends a scope where `contextual` breaks have consistent behavior.
case contextualBreakingEnd
/// Turn formatting back on at the given position in the original file
/// nil is used to indicate the rest of the file should be output
case enableFormatting(AbsolutePosition?)
/// Turn formatting off at the given position in the original file.
case disableFormatting(AbsolutePosition)
// Convenience overloads for the enum types
static let open = Token.open(.inconsistent, 0)
static func open(_ breakStyle: GroupBreakStyle, _ offset: Int) -> Token {
return Token.open(breakStyle)
}
static let space = Token.space(size: 1, flexible: false)
static func space(size: Int) -> Token {
return .space(size: size, flexible: false)
}
static let `break` = Token.break(.continue, size: 1, newlines: .elective)
static func `break`(_ kind: BreakKind, size: Int = 1) -> Token {
return .break(kind, size: size, newlines: .elective)
}
static func `break`(_ kind: BreakKind, newlines: NewlineBehavior) -> Token {
return .break(kind, size: 1, newlines: newlines)
}
}