OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart_style.src.chunk; | 5 library dart_style.src.chunk; |
6 | 6 |
7 import 'fast_hash.dart'; | 7 import 'fast_hash.dart'; |
8 import 'nesting_level.dart'; | 8 import 'nesting_level.dart'; |
9 import 'rule/rule.dart'; | 9 import 'rule/rule.dart'; |
10 | 10 |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
85 /// line starts after this chunk. A single statement may be indented multiple | 85 /// line starts after this chunk. A single statement may be indented multiple |
86 /// times if the splits occur in more deeply nested expressions, for example: | 86 /// times if the splits occur in more deeply nested expressions, for example: |
87 /// | 87 /// |
88 /// // 40 columns | | 88 /// // 40 columns | |
89 /// someFunctionName(argument, argument, | 89 /// someFunctionName(argument, argument, |
90 /// argument, anotherFunction(argument, | 90 /// argument, anotherFunction(argument, |
91 /// argument)); | 91 /// argument)); |
92 NestingLevel get nesting => _nesting; | 92 NestingLevel get nesting => _nesting; |
93 NestingLevel _nesting; | 93 NestingLevel _nesting; |
94 | 94 |
95 /// If this chunk marks the beginning of a block, these are the chunks | 95 /// If this chunk marks the beginning of a block, this contains the child |
96 /// contained in the block. | 96 /// chunks and other data about that nested block. |
97 final blockChunks = <Chunk>[]; | 97 ChunkBlock get block => _block; |
| 98 ChunkBlock _block; |
| 99 |
| 100 /// Whether this chunk has a [block]. |
| 101 bool get isBlock => _block != null; |
98 | 102 |
99 /// Whether it's valid to add more text to this chunk or not. | 103 /// Whether it's valid to add more text to this chunk or not. |
100 /// | 104 /// |
101 /// Chunks are built up by adding text and then "capped off" by having their | 105 /// Chunks are built up by adding text and then "capped off" by having their |
102 /// split information set by calling [handleSplit]. Once the latter has been | 106 /// split information set by calling [handleSplit]. Once the latter has been |
103 /// called, no more text should be added to the chunk since it would appear | 107 /// called, no more text should be added to the chunk since it would appear |
104 /// *before* the split. | 108 /// *before* the split. |
105 bool get canAddText => _rule == null; | 109 bool get canAddText => _rule == null; |
106 | 110 |
107 /// The [Rule] that controls when a split should occur after this chunk. | 111 /// The [Rule] that controls when a split should occur after this chunk. |
108 /// | 112 /// |
109 /// Multiple splits may share a [Rule]. | 113 /// Multiple splits may share a [Rule]. |
110 Rule get rule => _rule; | 114 Rule get rule => _rule; |
111 Rule _rule; | 115 Rule _rule; |
112 | 116 |
113 /// Whether this chunk is always followed by a newline or whether the line | |
114 /// splitter may choose to keep the next chunk on the same line. | |
115 bool get isHardSplit => _rule is HardSplitRule; | |
116 | |
117 /// Whether or not an extra blank line should be output after this chunk if | 117 /// Whether or not an extra blank line should be output after this chunk if |
118 /// it's split. | 118 /// it's split. |
119 /// | 119 /// |
120 /// Internally, this can be either `true`, `false`, or `null`. The latter is | 120 /// Internally, this can be either `true`, `false`, or `null`. The latter is |
121 /// an indeterminate state that lets later modifications to the split decide | 121 /// an indeterminate state that lets later modifications to the split decide |
122 /// whether it should be double or not. | 122 /// whether it should be double or not. |
123 /// | 123 /// |
124 /// However, this getter does not expose that. It will return `false` if the | 124 /// However, this getter does not expose that. It will return `false` if the |
125 /// chunk is still indeterminate. | 125 /// chunk is still indeterminate. |
126 bool get isDouble => _isDouble != null ? _isDouble : false; | 126 bool get isDouble => _isDouble != null ? _isDouble : false; |
127 bool _isDouble; | 127 bool _isDouble; |
128 | 128 |
129 /// If `true`, then the line after this chunk should always be at column | 129 /// If `true`, then the line after this chunk should always be at column |
130 /// zero regardless of any indentation or expression nesting. | 130 /// zero regardless of any indentation or expression nesting. |
131 /// | 131 /// |
132 /// Used for multi-line strings and commented out code. | 132 /// Used for multi-line strings and commented out code. |
133 bool get flushLeft => _flushLeft; | 133 bool get flushLeft => _flushLeft; |
134 bool _flushLeft = false; | 134 bool _flushLeft = false; |
135 | 135 |
136 /// If `true`, then the line after this chunk and its contained block should | 136 /// If `true`, then the line after this chunk and its contained block should |
137 /// be flush left. | 137 /// be flush left. |
138 bool get flushLeftAfter { | 138 bool get flushLeftAfter { |
139 if (blockChunks.isEmpty) return _flushLeft; | 139 if (!isBlock) return _flushLeft; |
140 | 140 |
141 return blockChunks.last.flushLeftAfter; | 141 return _block.chunks.last.flushLeftAfter; |
142 } | 142 } |
143 | 143 |
144 /// Whether this chunk should append an extra space if it does not split. | 144 /// Whether this chunk should append an extra space if it does not split. |
145 /// | 145 /// |
146 /// This is `true`, for example, in a chunk that ends with a ",". | 146 /// This is `true`, for example, in a chunk that ends with a ",". |
147 bool get spaceWhenUnsplit => _spaceWhenUnsplit; | 147 bool get spaceWhenUnsplit => _spaceWhenUnsplit; |
148 bool _spaceWhenUnsplit = false; | 148 bool _spaceWhenUnsplit = false; |
149 | 149 |
150 /// Whether this chunk marks the end of a range of chunks that can be line | 150 /// Whether this chunk marks the end of a range of chunks that can be line |
151 /// split independently of the following chunks. | 151 /// split independently of the following chunks. |
152 bool get canDivide { | 152 bool get canDivide { |
153 // Have to call markDivide() before accessing this. | 153 // Have to call markDivide() before accessing this. |
154 assert(_canDivide != null); | 154 assert(_canDivide != null); |
155 return _canDivide; | 155 return _canDivide; |
156 } | 156 } |
157 | 157 |
158 bool _canDivide; | 158 bool _canDivide; |
159 | 159 |
160 /// The number of characters in this chunk when unsplit. | 160 /// The number of characters in this chunk when unsplit. |
161 int get length => _text.length + (spaceWhenUnsplit ? 1 : 0); | 161 int get length => _text.length + (spaceWhenUnsplit ? 1 : 0); |
162 | 162 |
163 /// The unsplit length of all of this chunk's block contents. | 163 /// The unsplit length of all of this chunk's block contents. |
164 /// | 164 /// |
165 /// Does not include this chunk's own length, just the length of its child | 165 /// Does not include this chunk's own length, just the length of its child |
166 /// block chunks (recursively). | 166 /// block chunks (recursively). |
167 int get unsplitBlockLength { | 167 int get unsplitBlockLength { |
| 168 if (_block == null) return 0; |
| 169 |
168 var length = 0; | 170 var length = 0; |
169 for (var chunk in blockChunks) { | 171 for (var chunk in _block.chunks) { |
170 length += chunk.length + chunk.unsplitBlockLength; | 172 length += chunk.length + chunk.unsplitBlockLength; |
171 } | 173 } |
172 | 174 |
173 return length; | 175 return length; |
174 } | 176 } |
175 | 177 |
176 /// The [Span]s that contain this chunk. | 178 /// The [Span]s that contain this chunk. |
177 final spans = <Span>[]; | 179 final spans = <Span>[]; |
178 | 180 |
179 /// Creates a new chunk starting with [_text]. | 181 /// Creates a new chunk starting with [_text]. |
180 Chunk(this._text); | 182 Chunk(this._text); |
181 | 183 |
182 /// Discard the split for the chunk and put it back into the state where more | 184 /// Discard the split for the chunk and put it back into the state where more |
183 /// text can be appended. | 185 /// text can be appended. |
184 void allowText() { | 186 void allowText() { |
185 _rule = null; | 187 _rule = null; |
186 } | 188 } |
187 | 189 |
188 /// Append [text] to the end of the split's text. | 190 /// Append [text] to the end of the split's text. |
189 void appendText(String text) { | 191 void appendText(String text) { |
190 assert(canAddText); | 192 assert(canAddText); |
191 _text += text; | 193 _text += text; |
192 } | 194 } |
193 | 195 |
194 /// Forces this soft split to become a hard split. | |
195 /// | |
196 /// This is called on the soft splits owned by a rule that decides to harden | |
197 /// when it finds out another hard split occurs within its chunks. | |
198 void harden() { | |
199 _rule = new HardSplitRule(); | |
200 spans.clear(); | |
201 } | |
202 | |
203 /// Finishes off this chunk with the given [rule] and split information. | 196 /// Finishes off this chunk with the given [rule] and split information. |
204 /// | 197 /// |
205 /// This may be called multiple times on the same split since the splits | 198 /// This may be called multiple times on the same split since the splits |
206 /// produced by walking the source and the splits coming from comments and | 199 /// produced by walking the source and the splits coming from comments and |
207 /// preserved whitespace often overlap. When that happens, this has logic to | 200 /// preserved whitespace often overlap. When that happens, this has logic to |
208 /// combine that information into a single split. | 201 /// combine that information into a single split. |
209 void applySplit(Rule rule, int indent, NestingLevel nesting, | 202 void applySplit(Rule rule, int indent, NestingLevel nesting, |
210 {bool flushLeft, bool spaceWhenUnsplit, bool isDouble}) { | 203 {bool flushLeft, bool isDouble, bool space}) { |
211 if (flushLeft == null) flushLeft = false; | 204 if (flushLeft == null) flushLeft = false; |
212 if (spaceWhenUnsplit == null) spaceWhenUnsplit = false; | 205 if (space == null) space = false; |
213 if (isHardSplit || rule is HardSplitRule) { | 206 if (rule.isHardened) { |
214 // A hard split always wins. | 207 // A hard split always wins. |
215 _rule = rule; | 208 _rule = rule; |
216 } else if (_rule == null) { | 209 } else if (_rule == null) { |
217 // If the chunk hasn't been initialized yet, just inherit the rule. | 210 // If the chunk hasn't been initialized yet, just inherit the rule. |
218 _rule = rule; | 211 _rule = rule; |
219 } | 212 } |
220 | 213 |
221 // Last split settings win. | 214 // Last split settings win. |
222 _flushLeft = flushLeft; | 215 _flushLeft = flushLeft; |
223 _nesting = nesting; | 216 _nesting = nesting; |
224 _indent = indent; | 217 _indent = indent; |
225 | 218 |
226 _spaceWhenUnsplit = spaceWhenUnsplit; | 219 _spaceWhenUnsplit = space; |
227 | 220 |
228 // Pin down the double state, if given and we haven't already. | 221 // Pin down the double state, if given and we haven't already. |
229 if (_isDouble == null) _isDouble = isDouble; | 222 if (_isDouble == null) _isDouble = isDouble; |
230 } | 223 } |
231 | 224 |
| 225 /// Turns this chunk into one that can contain a block of child chunks. |
| 226 void makeBlock(Chunk blockArgument) { |
| 227 assert(_block == null); |
| 228 _block = new ChunkBlock(blockArgument); |
| 229 } |
| 230 |
| 231 /// Returns `true` if the block body owned by this chunk should be expression |
| 232 /// indented given a set of rule values provided by [getValue]. |
| 233 bool indentBlock(int getValue(Rule rule)) { |
| 234 if (_block == null) return false; |
| 235 if (_block.argument == null) return false; |
| 236 |
| 237 return _block.argument.rule |
| 238 .isSplit(getValue(_block.argument.rule), _block.argument); |
| 239 } |
| 240 |
232 // Mark whether this chunk can divide the range of chunks. | 241 // Mark whether this chunk can divide the range of chunks. |
233 void markDivide(canDivide) { | 242 void markDivide(canDivide) { |
234 // Should only do this once. | 243 // Should only do this once. |
235 assert(_canDivide == null); | 244 assert(_canDivide == null); |
236 | 245 |
237 _canDivide = canDivide; | 246 _canDivide = canDivide; |
238 } | 247 } |
239 | 248 |
240 String toString() { | 249 String toString() { |
241 var parts = []; | 250 var parts = []; |
242 | 251 |
243 if (text.isNotEmpty) parts.add(text); | 252 if (text.isNotEmpty) parts.add(text); |
244 | 253 |
245 if (_indent != null) parts.add("indent:$_indent"); | 254 if (_indent != null) parts.add("indent:$_indent"); |
246 if (spaceWhenUnsplit) parts.add("space"); | 255 if (spaceWhenUnsplit == true) parts.add("space"); |
247 if (_isDouble) parts.add("double"); | 256 if (_isDouble == true) parts.add("double"); |
248 if (_flushLeft) parts.add("flush"); | 257 if (_flushLeft == true) parts.add("flush"); |
249 | 258 |
250 if (_rule == null) { | 259 if (_rule == null) { |
251 parts.add("(no split)"); | 260 parts.add("(no split)"); |
252 } else if (isHardSplit) { | |
253 parts.add("hard"); | |
254 } else { | 261 } else { |
255 parts.add(rule.toString()); | 262 parts.add(rule.toString()); |
| 263 if (rule.isHardened) parts.add("(hard)"); |
256 | 264 |
257 if (_rule.outerRules.isNotEmpty) { | 265 if (_rule.constrainedRules.isNotEmpty) { |
258 parts.add("-> ${_rule.outerRules.join(' ')}"); | 266 parts.add("-> ${_rule.constrainedRules.join(' ')}"); |
259 } | 267 } |
260 } | 268 } |
261 | 269 |
262 return parts.join(" "); | 270 return parts.join(" "); |
263 } | 271 } |
264 } | 272 } |
265 | 273 |
| 274 /// The child chunks owned by a chunk that begins a "block" -- an actual block |
| 275 /// statement, function expression, or collection literal. |
| 276 class ChunkBlock { |
| 277 /// If this block is for a collection literal in an argument list, this will |
| 278 /// be the chunk preceding this literal argument. |
| 279 /// |
| 280 /// That chunk is owned by the argument list and if it splits, this collection |
| 281 /// may need extra expression-level indentation. |
| 282 final Chunk argument; |
| 283 |
| 284 /// The child chunks in this block. |
| 285 final List<Chunk> chunks = []; |
| 286 |
| 287 ChunkBlock(this.argument); |
| 288 } |
| 289 |
266 /// Constants for the cost heuristics used to determine which set of splits is | 290 /// Constants for the cost heuristics used to determine which set of splits is |
267 /// most desirable. | 291 /// most desirable. |
268 class Cost { | 292 class Cost { |
269 /// The cost of splitting after the `=>` in a lambda or arrow-bodied member. | 293 /// The cost of splitting after the `=>` in a lambda or arrow-bodied member. |
270 /// | 294 /// |
271 /// We make this zero because there is already a span around the entire body | 295 /// We make this zero because there is already a span around the entire body |
272 /// and we generally do prefer splitting after the `=>` over other places. | 296 /// and we generally do prefer splitting after the `=>` over other places. |
273 static const arrow = 0; | 297 static const arrow = 0; |
274 | 298 |
275 /// The default cost. | 299 /// The default cost. |
276 /// | 300 /// |
277 /// This isn't zero because we want to ensure all splitting has *some* cost, | 301 /// This isn't zero because we want to ensure all splitting has *some* cost, |
278 /// otherwise, the formatter won't try to keep things on one line at all. | 302 /// otherwise, the formatter won't try to keep things on one line at all. |
279 /// Most splits and spans use this. Greater costs tend to come from a greater | 303 /// Most splits and spans use this. Greater costs tend to come from a greater |
280 /// number of nested spans. | 304 /// number of nested spans. |
281 static const normal = 1; | 305 static const normal = 1; |
282 | 306 |
283 /// Splitting after a "=" both for assignment and initialization. | 307 /// Splitting after a "=". |
284 static const assignment = 2; | 308 static const assign = 1; |
| 309 |
| 310 /// Splitting after a "=" when the right-hand side is a collection or cascade. |
| 311 static const assignBlock = 2; |
285 | 312 |
286 /// Splitting before the first argument when it happens to be a function | 313 /// Splitting before the first argument when it happens to be a function |
287 /// expression with a block body. | 314 /// expression with a block body. |
288 static const firstBlockArgument = 2; | 315 static const firstBlockArgument = 2; |
289 | 316 |
290 /// The series of positional arguments. | 317 /// The series of positional arguments. |
291 static const positionalArguments = 2; | 318 static const positionalArguments = 2; |
292 | 319 |
293 /// Splitting inside the brackets of a list with only one element. | 320 /// Splitting inside the brackets of a list with only one element. |
294 static const singleElementList = 2; | 321 static const singleElementList = 2; |
295 | 322 |
296 /// Splitting the internals of collection literal arguments. | 323 /// Splitting the internals of collection literal arguments. |
297 /// | 324 /// |
298 /// Used to prefer splitting at the argument boundary over splitting the | 325 /// Used to prefer splitting at the argument boundary over splitting the |
299 /// collection contents. | 326 /// collection contents. |
300 static const splitCollections = 2; | 327 static const splitCollections = 2; |
301 | 328 |
| 329 /// Splitting on the "." in a named constructor. |
| 330 static const constructorName = 3; |
| 331 |
302 /// Splitting before a type argument or type parameter. | 332 /// Splitting before a type argument or type parameter. |
303 static const typeArgument = 4; | 333 static const typeArgument = 4; |
304 } | 334 } |
305 | 335 |
306 /// The in-progress state for a [Span] that has been started but has not yet | 336 /// The in-progress state for a [Span] that has been started but has not yet |
307 /// been completed. | 337 /// been completed. |
308 class OpenSpan { | 338 class OpenSpan { |
309 /// Index of the first chunk contained in this span. | 339 /// Index of the first chunk contained in this span. |
310 int get start => _start; | 340 int get start => _start; |
311 int _start; | 341 int _start; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 /// output. This way, commented out chunks of code do not get erroneously | 389 /// output. This way, commented out chunks of code do not get erroneously |
360 /// re-indented. | 390 /// re-indented. |
361 final bool flushLeft; | 391 final bool flushLeft; |
362 | 392 |
363 /// Whether this comment is an inline block comment. | 393 /// Whether this comment is an inline block comment. |
364 bool get isInline => linesBefore == 0 && !isLineComment; | 394 bool get isInline => linesBefore == 0 && !isLineComment; |
365 | 395 |
366 SourceComment(this.text, this.linesBefore, | 396 SourceComment(this.text, this.linesBefore, |
367 {this.isLineComment, this.flushLeft}); | 397 {this.isLineComment, this.flushLeft}); |
368 } | 398 } |
OLD | NEW |