| 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_builder; | 5 library dart_style.src.chunk_builder; |
| 6 | 6 |
| 7 import 'chunk.dart'; | 7 import 'chunk.dart'; |
| 8 import 'dart_formatter.dart'; | 8 import 'dart_formatter.dart'; |
| 9 import 'line_splitter.dart'; | 9 import 'line_splitter.dart'; |
| 10 import 'line_writer.dart'; | 10 import 'line_writer.dart'; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 | 25 |
| 26 /// The builder for the code surrounding the block that this writer is for, or | 26 /// The builder for the code surrounding the block that this writer is for, or |
| 27 /// `null` if this is writing the top-level code. | 27 /// `null` if this is writing the top-level code. |
| 28 final ChunkBuilder _parent; | 28 final ChunkBuilder _parent; |
| 29 | 29 |
| 30 final SourceCode _source; | 30 final SourceCode _source; |
| 31 | 31 |
| 32 final List<Chunk> _chunks; | 32 final List<Chunk> _chunks; |
| 33 | 33 |
| 34 /// The whitespace that should be written to [_chunks] before the next | 34 /// The whitespace that should be written to [_chunks] before the next |
| 35 /// non-whitespace token or `null` if no whitespace is pending. | 35 /// non-whitespace token. |
| 36 /// | 36 /// |
| 37 /// This ensures that changes to indentation and nesting also apply to the | 37 /// This ensures that changes to indentation and nesting also apply to the |
| 38 /// most recent split, even if the visitor "creates" the split before changing | 38 /// most recent split, even if the visitor "creates" the split before changing |
| 39 /// indentation or nesting. | 39 /// indentation or nesting. |
| 40 Whitespace _pendingWhitespace; | 40 Whitespace _pendingWhitespace = Whitespace.none; |
| 41 | 41 |
| 42 /// The nested stack of rules that are currently in use. | 42 /// The nested stack of rules that are currently in use. |
| 43 /// | 43 /// |
| 44 /// New chunks are implicitly split by the innermost rule when the chunk is | 44 /// New chunks are implicitly split by the innermost rule when the chunk is |
| 45 /// ended. | 45 /// ended. |
| 46 final _rules = <Rule>[]; | 46 final _rules = <Rule>[]; |
| 47 | 47 |
| 48 /// The list of rules that are waiting until the next whitespace has been | 48 /// The list of rules that are waiting until the next whitespace has been |
| 49 /// written before they start. | 49 /// written before they start. |
| 50 final _lazyRules = <Rule>[]; | 50 final _lazyRules = <Rule>[]; |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 Chunk split({int nesting, bool space, bool flushLeft}) => | 138 Chunk split({int nesting, bool space, bool flushLeft}) => |
| 139 _writeSplit(_rules.last, | 139 _writeSplit(_rules.last, |
| 140 nesting: nesting, flushLeft: flushLeft, spaceWhenUnsplit: space); | 140 nesting: nesting, flushLeft: flushLeft, spaceWhenUnsplit: space); |
| 141 | 141 |
| 142 /// Outputs the series of [comments] and associated whitespace that appear | 142 /// Outputs the series of [comments] and associated whitespace that appear |
| 143 /// before [token] (which is not written by this). | 143 /// before [token] (which is not written by this). |
| 144 /// | 144 /// |
| 145 /// The list contains each comment as it appeared in the source between the | 145 /// The list contains each comment as it appeared in the source between the |
| 146 /// last token written and the next one that's about to be written. | 146 /// last token written and the next one that's about to be written. |
| 147 /// | 147 /// |
| 148 /// [linesBeforeToken] is number of lines between the last comment (or | 148 /// [linesBeforeToken] is the number of lines between the last comment (or |
| 149 /// previous token if there are no comments) and the next token. | 149 /// previous token if there are no comments) and the next token. |
| 150 void writeComments(List<SourceComment> comments, int linesBeforeToken, | 150 void writeComments(List<SourceComment> comments, int linesBeforeToken, |
| 151 String token) { | 151 String token) { |
| 152 // Corner case: if we require a blank line, but there exists one between | 152 // Corner case: if we require a blank line, but there exists one between |
| 153 // some of the comments, or after the last one, then we don't need to | 153 // some of the comments, or after the last one, then we don't need to |
| 154 // enforce one before the first comment. Example: | 154 // enforce one before the first comment. Example: |
| 155 // | 155 // |
| 156 // library foo; | 156 // library foo; |
| 157 // // comment | 157 // // comment |
| 158 // | 158 // |
| (...skipping 10 matching lines...) Expand all Loading... |
| 169 } else { | 169 } else { |
| 170 for (var i = 1; i < comments.length; i++) { | 170 for (var i = 1; i < comments.length; i++) { |
| 171 if (comments[i].linesBefore > 1) { | 171 if (comments[i].linesBefore > 1) { |
| 172 _pendingWhitespace = Whitespace.newline; | 172 _pendingWhitespace = Whitespace.newline; |
| 173 break; | 173 break; |
| 174 } | 174 } |
| 175 } | 175 } |
| 176 } | 176 } |
| 177 } | 177 } |
| 178 | 178 |
| 179 // Corner case: if the comments are completely inline (i.e. just a series |
| 180 // of block comments with no newlines before, after, or between them), then |
| 181 // they will eat any pending newlines. Make sure that doesn't happen by |
| 182 // putting the pending whitespace before the first comment and moving them |
| 183 // to their own line. Turns this: |
| 184 // |
| 185 // library foo; /* a */ /* b */ import 'a.dart'; |
| 186 // |
| 187 // into: |
| 188 // |
| 189 // library foo; |
| 190 // |
| 191 // /* a */ /* b */ |
| 192 // import 'a.dart'; |
| 193 if (linesBeforeToken == 0 && |
| 194 comments.every((comment) => comment.isInline)) { |
| 195 if (_pendingWhitespace.minimumLines > 0) { |
| 196 comments.first.linesBefore = _pendingWhitespace.minimumLines; |
| 197 linesBeforeToken = 1; |
| 198 } |
| 199 } |
| 200 |
| 179 // Write each comment and the whitespace between them. | 201 // Write each comment and the whitespace between them. |
| 180 for (var i = 0; i < comments.length; i++) { | 202 for (var i = 0; i < comments.length; i++) { |
| 181 var comment = comments[i]; | 203 var comment = comments[i]; |
| 182 | 204 |
| 183 preserveNewlines(comment.linesBefore); | 205 preserveNewlines(comment.linesBefore); |
| 184 | 206 |
| 185 // Don't emit a space because we'll handle it below. If we emit it here, | 207 // Don't emit a space because we'll handle it below. If we emit it here, |
| 186 // we may get a trailing space if the comment needs a line before it. | 208 // we may get a trailing space if the comment needs a line before it. |
| 187 if (_pendingWhitespace == Whitespace.space) _pendingWhitespace = null; | 209 if (_pendingWhitespace == Whitespace.space) { |
| 210 _pendingWhitespace = Whitespace.none; |
| 211 } |
| 188 _emitPendingWhitespace(); | 212 _emitPendingWhitespace(); |
| 189 | 213 |
| 190 if (comment.linesBefore == 0) { | 214 if (comment.linesBefore == 0) { |
| 191 // If we're sitting on a split, move the comment before it to adhere it | 215 // If we're sitting on a split, move the comment before it to adhere it |
| 192 // to the preceding text. | 216 // to the preceding text. |
| 193 if (_shouldMoveCommentBeforeSplit()) { | 217 if (_shouldMoveCommentBeforeSplit()) { |
| 194 _chunks.last.allowText(); | 218 _chunks.last.allowText(); |
| 195 } | 219 } |
| 196 | 220 |
| 197 // The comment follows other text, so we need to decide if it gets a | 221 // The comment follows other text, so we need to decide if it gets a |
| (...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 485 isCompilationUnit: _source.isCompilationUnit, | 509 isCompilationUnit: _source.isCompilationUnit, |
| 486 selectionStart: selectionStart, | 510 selectionStart: selectionStart, |
| 487 selectionLength: selectionLength); | 511 selectionLength: selectionLength); |
| 488 } | 512 } |
| 489 | 513 |
| 490 /// Writes the current pending [Whitespace] to the output, if any. | 514 /// Writes the current pending [Whitespace] to the output, if any. |
| 491 /// | 515 /// |
| 492 /// This should only be called after source lines have been preserved to turn | 516 /// This should only be called after source lines have been preserved to turn |
| 493 /// any ambiguous whitespace into a concrete choice. | 517 /// any ambiguous whitespace into a concrete choice. |
| 494 void _emitPendingWhitespace() { | 518 void _emitPendingWhitespace() { |
| 495 if (_pendingWhitespace == null) return; | |
| 496 // Output any pending whitespace first now that we know it won't be | 519 // Output any pending whitespace first now that we know it won't be |
| 497 // trailing. | 520 // trailing. |
| 498 switch (_pendingWhitespace) { | 521 switch (_pendingWhitespace) { |
| 499 case Whitespace.space: | 522 case Whitespace.space: |
| 500 _writeText(" "); | 523 _writeText(" "); |
| 501 break; | 524 break; |
| 502 | 525 |
| 503 case Whitespace.newline: | 526 case Whitespace.newline: |
| 504 _writeHardSplit(); | 527 _writeHardSplit(); |
| 505 break; | 528 break; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 516 _writeHardSplit(double: true); | 539 _writeHardSplit(double: true); |
| 517 break; | 540 break; |
| 518 | 541 |
| 519 case Whitespace.spaceOrNewline: | 542 case Whitespace.spaceOrNewline: |
| 520 case Whitespace.oneOrTwoNewlines: | 543 case Whitespace.oneOrTwoNewlines: |
| 521 // We should have pinned these down before getting here. | 544 // We should have pinned these down before getting here. |
| 522 assert(false); | 545 assert(false); |
| 523 break; | 546 break; |
| 524 } | 547 } |
| 525 | 548 |
| 526 _pendingWhitespace = null; | 549 _pendingWhitespace = Whitespace.none; |
| 527 } | 550 } |
| 528 | 551 |
| 529 /// Returns `true` if the last chunk is a split that should be move after the | 552 /// Returns `true` if the last chunk is a split that should be move after the |
| 530 /// comment that is about to be written. | 553 /// comment that is about to be written. |
| 531 bool _shouldMoveCommentBeforeSplit() { | 554 bool _shouldMoveCommentBeforeSplit() { |
| 532 // Not if there is nothing before it. | 555 // Not if there is nothing before it. |
| 533 if (_chunks.isEmpty) return false; | 556 if (_chunks.isEmpty) return false; |
| 534 | 557 |
| 535 // If the text before the split is an open grouping character, we don't | 558 // If the text before the split is an open grouping character, we don't |
| 536 // want to adhere the comment to that. | 559 // want to adhere the comment to that. |
| (...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 820 if (rule.constrain(rule.fullySplitValue, other) == | 843 if (rule.constrain(rule.fullySplitValue, other) == |
| 821 other.fullySplitValue) { | 844 other.fullySplitValue) { |
| 822 harden(other); | 845 harden(other); |
| 823 } | 846 } |
| 824 } | 847 } |
| 825 } | 848 } |
| 826 | 849 |
| 827 harden(rule); | 850 harden(rule); |
| 828 } | 851 } |
| 829 } | 852 } |
| OLD | NEW |