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