Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(132)

Side by Side Diff: packages/dart_style/lib/src/chunk_builder.dart

Issue 1521693002: Roll Observatory deps (charted -> ^0.3.0) (Closed) Base URL: https://chromium.googlesource.com/external/github.com/dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « packages/dart_style/lib/src/chunk.dart ('k') | packages/dart_style/lib/src/dart_formatter.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 'debug.dart' as debug; 9 import 'debug.dart' as debug;
10 import 'line_writer.dart'; 10 import 'line_writer.dart';
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 /// rules may not have all of their constraints fully wired up until after 54 /// rules may not have all of their constraints fully wired up until after
55 /// the hard split appears. For example, a hard split in a positional 55 /// the hard split appears. For example, a hard split in a positional
56 /// argument list needs to force the named arguments to split too, but we 56 /// argument list needs to force the named arguments to split too, but we
57 /// don't create that rule until after the positional arguments are done. 57 /// don't create that rule until after the positional arguments are done.
58 final _hardSplitRules = new Set<Rule>(); 58 final _hardSplitRules = new Set<Rule>();
59 59
60 /// The list of rules that are waiting until the next whitespace has been 60 /// The list of rules that are waiting until the next whitespace has been
61 /// written before they start. 61 /// written before they start.
62 final _lazyRules = <Rule>[]; 62 final _lazyRules = <Rule>[];
63 63
64 /// The indexes of the chunks owned by each rule (except for hard splits).
65 final _ruleChunks = <Rule, List<int>>{};
66
67 /// The nested stack of spans that are currently being written. 64 /// The nested stack of spans that are currently being written.
68 final _openSpans = <OpenSpan>[]; 65 final _openSpans = <OpenSpan>[];
69 66
70 /// The current state. 67 /// The current state.
71 final _nesting = new NestingBuilder(); 68 final _nesting = new NestingBuilder();
72 69
73 /// The stack of nesting levels where block arguments may start. 70 /// The stack of nesting levels where block arguments may start.
74 /// 71 ///
75 /// A block argument's contents will nest at the last level in this stack. 72 /// A block argument's contents will nest at the last level in this stack.
76 final _blockArgumentNesting = <NestingLevel>[]; 73 final _blockArgumentNesting = <NestingLevel>[];
(...skipping 17 matching lines...) Expand all
94 bool _firstFlushLeft = false; 91 bool _firstFlushLeft = false;
95 92
96 /// Whether there is pending whitespace that depends on the number of 93 /// Whether there is pending whitespace that depends on the number of
97 /// newlines in the source. 94 /// newlines in the source.
98 /// 95 ///
99 /// This is used to avoid calculating the newlines between tokens unless 96 /// This is used to avoid calculating the newlines between tokens unless
100 /// actually needed since doing so is slow when done between every single 97 /// actually needed since doing so is slow when done between every single
101 /// token pair. 98 /// token pair.
102 bool get needsToPreserveNewlines => 99 bool get needsToPreserveNewlines =>
103 _pendingWhitespace == Whitespace.oneOrTwoNewlines || 100 _pendingWhitespace == Whitespace.oneOrTwoNewlines ||
104 _pendingWhitespace == Whitespace.spaceOrNewline || 101 _pendingWhitespace == Whitespace.spaceOrNewline ||
105 _pendingWhitespace == Whitespace.splitOrNewline; 102 _pendingWhitespace == Whitespace.splitOrNewline;
106 103
107 /// The number of characters of code that can fit in a single line. 104 /// The number of characters of code that can fit in a single line.
108 int get pageWidth => _formatter.pageWidth; 105 int get pageWidth => _formatter.pageWidth;
109 106
110 /// The current innermost rule. 107 /// The current innermost rule.
111 Rule get rule => _rules.last; 108 Rule get rule => _rules.last;
112 109
113 ChunkBuilder(this._formatter, this._source) 110 ChunkBuilder(this._formatter, this._source)
114 : _parent = null, 111 : _parent = null,
115 _chunks = [] { 112 _chunks = [] {
(...skipping 28 matching lines...) Expand all
144 _nesting.commitNesting(); 141 _nesting.commitNesting();
145 } 142 }
146 143
147 /// Writes a [WhitespaceChunk] of [type]. 144 /// Writes a [WhitespaceChunk] of [type].
148 void writeWhitespace(Whitespace type) { 145 void writeWhitespace(Whitespace type) {
149 _pendingWhitespace = type; 146 _pendingWhitespace = type;
150 } 147 }
151 148
152 /// Write a split owned by the current innermost rule. 149 /// Write a split owned by the current innermost rule.
153 /// 150 ///
154 /// If [nesting] is given, uses that. Otherwise, uses the current nesting
155 /// level. If unsplit, it expands to a space if [space] is `true`.
156 ///
157 /// If [flushLeft] is `true`, then forces the next line to start at column 151 /// If [flushLeft] is `true`, then forces the next line to start at column
158 /// one regardless of any indentation or nesting. 152 /// one regardless of any indentation or nesting.
159 /// 153 ///
160 /// If [isDouble] is passed, forces the split to either be a single or double 154 /// If [isDouble] is passed, forces the split to either be a single or double
161 /// newline. Otherwise, leaves it indeterminate. 155 /// newline. Otherwise, leaves it indeterminate.
162 Chunk split({bool space, bool isDouble, bool flushLeft}) =>
163 _writeSplit(_rules.last, null,
164 flushLeft: flushLeft, isDouble: isDouble, spaceWhenUnsplit: space);
165
166 /// Write a split owned by the current innermost rule.
167 /// 156 ///
168 /// Unlike [split()], this ignores any current expression nesting. It always 157 /// If [nest] is `false`, ignores any current expression nesting. Otherwise,
169 /// indents the next line at the statement level. 158 /// uses the current nesting level. If unsplit, it expands to a space if
170 Chunk blockSplit({bool space, bool isDouble}) => 159 /// [space] is `true`.
171 _writeSplit(_rules.last, _nesting.blockNesting, 160 Chunk split({bool flushLeft, bool isDouble, bool nest, bool space}) =>
172 isDouble: isDouble, spaceWhenUnsplit: space); 161 _writeSplit(_rules.last,
162 flushLeft: flushLeft, isDouble: isDouble, nest: nest, space: space);
173 163
174 /// Outputs the series of [comments] and associated whitespace that appear 164 /// Outputs the series of [comments] and associated whitespace that appear
175 /// before [token] (which is not written by this). 165 /// before [token] (which is not written by this).
176 /// 166 ///
177 /// The list contains each comment as it appeared in the source between the 167 /// The list contains each comment as it appeared in the source between the
178 /// last token written and the next one that's about to be written. 168 /// last token written and the next one that's about to be written.
179 /// 169 ///
180 /// [linesBeforeToken] is the number of lines between the last comment (or 170 /// [linesBeforeToken] is the number of lines between the last comment (or
181 /// previous token if there are no comments) and the next token. 171 /// previous token if there are no comments) and the next token.
182 void writeComments( 172 void writeComments(
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
250 } 240 }
251 241
252 // The comment follows other text, so we need to decide if it gets a 242 // The comment follows other text, so we need to decide if it gets a
253 // space before it or not. 243 // space before it or not.
254 if (_needsSpaceBeforeComment(isLineComment: comment.isLineComment)) { 244 if (_needsSpaceBeforeComment(isLineComment: comment.isLineComment)) {
255 _writeText(" "); 245 _writeText(" ");
256 } 246 }
257 } else { 247 } else {
258 // The comment starts a line, so make sure it stays on its own line. 248 // The comment starts a line, so make sure it stays on its own line.
259 _writeHardSplit( 249 _writeHardSplit(
260 nest: true,
261 flushLeft: comment.flushLeft, 250 flushLeft: comment.flushLeft,
262 double: comment.linesBefore > 1); 251 isDouble: comment.linesBefore > 1,
252 nest: true);
263 } 253 }
264 254
265 _writeText(comment.text); 255 _writeText(comment.text);
266 256
267 if (comment.selectionStart != null) { 257 if (comment.selectionStart != null) {
268 startSelectionFromEnd(comment.text.length - comment.selectionStart); 258 startSelectionFromEnd(comment.text.length - comment.selectionStart);
269 } 259 }
270 260
271 if (comment.selectionEnd != null) { 261 if (comment.selectionEnd != null) {
272 endSelectionFromEnd(comment.text.length - comment.selectionEnd); 262 endSelectionFromEnd(comment.text.length - comment.selectionEnd);
(...skipping 11 matching lines...) Expand all
284 // mistakes like: 274 // mistakes like:
285 // 275 //
286 // /** 276 // /**
287 // * Some doc comment. 277 // * Some doc comment.
288 // */ someFunction() { ... } 278 // */ someFunction() { ... }
289 if (linesAfter == 0 && comments.last.text.contains("\n")) { 279 if (linesAfter == 0 && comments.last.text.contains("\n")) {
290 linesAfter = 1; 280 linesAfter = 1;
291 } 281 }
292 } 282 }
293 283
294 if (linesAfter > 0) _writeHardSplit(nest: true, double: linesAfter > 1); 284 if (linesAfter > 0) _writeHardSplit(isDouble: linesAfter > 1, nest: true);
295 } 285 }
296 286
297 // If the comment has text following it (aside from a grouping character), 287 // If the comment has text following it (aside from a grouping character),
298 // it needs a trailing space. 288 // it needs a trailing space.
299 if (_needsSpaceAfterLastComment(comments, token)) { 289 if (_needsSpaceAfterLastComment(comments, token)) {
300 _pendingWhitespace = Whitespace.space; 290 _pendingWhitespace = Whitespace.space;
301 } 291 }
302 292
303 preserveNewlines(linesBeforeToken); 293 preserveNewlines(linesBeforeToken);
304 } 294 }
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 var openSpan = _openSpans.removeLast(); 351 var openSpan = _openSpans.removeLast();
362 352
363 // A span that just covers a single chunk can't be split anyway. 353 // A span that just covers a single chunk can't be split anyway.
364 var end = _currentChunkIndex; 354 var end = _currentChunkIndex;
365 if (openSpan.start == end) return; 355 if (openSpan.start == end) return;
366 356
367 // Add the span to every chunk that can split it. 357 // Add the span to every chunk that can split it.
368 var span = new Span(openSpan.cost); 358 var span = new Span(openSpan.cost);
369 for (var i = openSpan.start; i < end; i++) { 359 for (var i = openSpan.start; i < end; i++) {
370 var chunk = _chunks[i]; 360 var chunk = _chunks[i];
371 if (!chunk.isHardSplit) chunk.spans.add(span); 361 if (!chunk.rule.isHardened) chunk.spans.add(span);
372 } 362 }
373 } 363 }
374 364
375 /// Starts a new [Rule]. 365 /// Starts a new [Rule].
376 /// 366 ///
377 /// If omitted, defaults to a new [SimpleRule]. 367 /// If omitted, defaults to a new [Rule].
378 void startRule([Rule rule]) { 368 void startRule([Rule rule]) {
379 if (rule == null) rule = new SimpleRule(); 369 if (rule == null) rule = new Rule();
380 370
381 // See if any of the rules that contain this one care if it splits. 371 // See if any of the rules that contain this one care if it splits.
382 _rules.forEach((outer) => outer.contain(rule)); 372 _rules.forEach((outer) => outer.contain(rule));
383 _rules.add(rule); 373 _rules.add(rule);
384 } 374 }
385 375
386 /// Starts a new [Rule] that comes into play *after* the next whitespace 376 /// Starts a new [Rule] that comes into play *after* the next whitespace
387 /// (including comments) is written. 377 /// (including comments) is written.
388 /// 378 ///
389 /// This is used for binary operators who want to start a rule before the 379 /// This is used for binary operators who want to start a rule before the
390 /// first operand but not get forced to split if a comment appears before the 380 /// first operand but not get forced to split if a comment appears before the
391 /// entire expression. 381 /// entire expression.
392 /// 382 ///
393 /// If [rule] is omitted, defaults to a new [SimpleRule]. 383 /// If [rule] is omitted, defaults to a new [Rule].
394 void startLazyRule([Rule rule]) { 384 void startLazyRule([Rule rule]) {
395 if (rule == null) rule = new SimpleRule(); 385 if (rule == null) rule = new Rule();
396 386
397 _lazyRules.add(rule); 387 _lazyRules.add(rule);
398 } 388 }
399 389
400 /// Ends the innermost rule. 390 /// Ends the innermost rule.
401 void endRule() { 391 void endRule() {
402 _rules.removeLast(); 392 _rules.removeLast();
403 } 393 }
404 394
405 /// Pre-emptively forces all of the current rules to become hard splits. 395 /// Pre-emptively forces all of the current rules to become hard splits.
(...skipping 18 matching lines...) Expand all
424 if (now == null) now = false; 414 if (now == null) now = false;
425 415
426 _nesting.nest(indent); 416 _nesting.nest(indent);
427 if (now) _nesting.commitNesting(); 417 if (now) _nesting.commitNesting();
428 } 418 }
429 419
430 /// Discards the most recent level of expression nesting. 420 /// Discards the most recent level of expression nesting.
431 /// 421 ///
432 /// Expressions that are more nested will get increased indentation when split 422 /// Expressions that are more nested will get increased indentation when split
433 /// if the previous line has a lower level of nesting. 423 /// if the previous line has a lower level of nesting.
434 void unnest() { 424 ///
425 /// If [now] is `false`, does not commit the nesting change until after the
426 /// next chunk of text is written.
427 void unnest({bool now}) {
428 if (now == null) now = true;
429
435 _nesting.unnest(); 430 _nesting.unnest();
431 if (now) _nesting.commitNesting();
436 } 432 }
437 433
438 /// Marks the selection starting point as occurring [fromEnd] characters to 434 /// Marks the selection starting point as occurring [fromEnd] characters to
439 /// the left of the end of what's currently been written. 435 /// the left of the end of what's currently been written.
440 /// 436 ///
441 /// It counts backwards from the end because this is called *after* the chunk 437 /// It counts backwards from the end because this is called *after* the chunk
442 /// of text containing the selection has been output. 438 /// of text containing the selection has been output.
443 void startSelectionFromEnd(int fromEnd) { 439 void startSelectionFromEnd(int fromEnd) {
444 assert(_chunks.isNotEmpty); 440 assert(_chunks.isNotEmpty);
445 _chunks.last.startSelectionFromEnd(fromEnd); 441 _chunks.last.startSelectionFromEnd(fromEnd);
(...skipping 16 matching lines...) Expand all
462 } 458 }
463 459
464 /// Releases the last nesting level captured by [startBlockArgumentNesting]. 460 /// Releases the last nesting level captured by [startBlockArgumentNesting].
465 void endBlockArgumentNesting() { 461 void endBlockArgumentNesting() {
466 _blockArgumentNesting.removeLast(); 462 _blockArgumentNesting.removeLast();
467 } 463 }
468 464
469 /// Starts a new block as a child of the current chunk. 465 /// Starts a new block as a child of the current chunk.
470 /// 466 ///
471 /// Nested blocks are handled using their own independent [LineWriter]. 467 /// Nested blocks are handled using their own independent [LineWriter].
472 ChunkBuilder startBlock() { 468 ChunkBuilder startBlock(Chunk argumentChunk) {
469 var chunk = _chunks.last;
470 chunk.makeBlock(argumentChunk);
471
473 var builder = 472 var builder =
474 new ChunkBuilder._(this, _formatter, _source, _chunks.last.blockChunks); 473 new ChunkBuilder._(this, _formatter, _source, chunk.block.chunks);
475 474
476 // A block always starts off indented one level. 475 // A block always starts off indented one level.
477 builder.indent(); 476 builder.indent();
478 477
479 return builder; 478 return builder;
480 } 479 }
481 480
482 /// Ends this [ChunkBuilder], which must have been created by [startBlock()]. 481 /// Ends this [ChunkBuilder], which must have been created by [startBlock()].
483 /// 482 ///
484 /// Forces the chunk that owns the block to split if it can tell that the 483 /// Forces the chunk that owns the block to split if it can tell that the
485 /// block contents will always split. It does that by looking for hard splits 484 /// block contents will always split. It does that by looking for hard splits
486 /// in the block. If [ignoredSplit] is given, that rule will be ignored 485 /// in the block. If [ignoredSplit] is given, that rule will be ignored
487 /// when determining if a block contains a hard split. If [forceSplit] is 486 /// when determining if a block contains a hard split. If [forceSplit] is
488 /// `true`, the block is considered to always split. 487 /// `true`, the block is considered to always split.
489 /// 488 ///
490 /// Returns the previous writer for the surrounding block. 489 /// Returns the previous writer for the surrounding block.
491 ChunkBuilder endBlock(HardSplitRule ignoredSplit, {bool forceSplit}) { 490 ChunkBuilder endBlock(Rule ignoredSplit, {bool forceSplit}) {
492 _divideChunks(); 491 _divideChunks();
493 492
494 // If we don't already know if the block is going to split, see if it 493 // If we don't already know if the block is going to split, see if it
495 // contains any hard splits or is longer than a page. 494 // contains any hard splits or is longer than a page.
496 if (!forceSplit) { 495 if (!forceSplit) {
497 var length = 0; 496 var length = 0;
498 for (var chunk in _chunks) { 497 for (var chunk in _chunks) {
499 length += chunk.length + chunk.unsplitBlockLength; 498 length += chunk.length + chunk.unsplitBlockLength;
500 if (length > _formatter.pageWidth) { 499 if (length > _formatter.pageWidth) {
501 forceSplit = true; 500 forceSplit = true;
502 break; 501 break;
503 } 502 }
504 503
505 if (chunk.isHardSplit && chunk.rule != ignoredSplit) { 504 if (chunk.rule != null &&
505 chunk.rule.isHardened &&
506 chunk.rule != ignoredSplit) {
506 forceSplit = true; 507 forceSplit = true;
507 break; 508 break;
508 } 509 }
509 } 510 }
510 } 511 }
511 512
512 // If there is a hard newline within the block, force the surrounding rule 513 _parent._endChildBlock(
513 // for it so that we apply that constraint. 514 firstFlushLeft: _firstFlushLeft, forceSplit: forceSplit);
514 if (forceSplit) _parent.forceRules();
515 515
516 // Write the split for the block contents themselves.
517 _parent._writeSplit(_parent._rules.last, _parent._blockArgumentNesting.last,
518 flushLeft: _firstFlushLeft);
519 return _parent; 516 return _parent;
520 } 517 }
521 518
519 /// Finishes off the last chunk in a child block of this parent.
520 void _endChildBlock({bool firstFlushLeft, bool forceSplit}) {
521 // If there is a hard newline within the block, force the surrounding rule
522 // for it so that we apply that constraint.
523 if (forceSplit) forceRules();
524
525 // Write the split for the block contents themselves.
526 var chunk = _chunks.last;
527 chunk.applySplit(rule, _nesting.indentation, _blockArgumentNesting.last,
528 flushLeft: firstFlushLeft);
529
530 if (chunk.rule.isHardened) _handleHardSplit();
531 }
532
522 /// Finishes writing and returns a [SourceCode] containing the final output 533 /// Finishes writing and returns a [SourceCode] containing the final output
523 /// and updated selection, if any. 534 /// and updated selection, if any.
524 SourceCode end() { 535 SourceCode end() {
525 _writeHardSplit(); 536 _writeHardSplit();
526 _divideChunks(); 537 _divideChunks();
527 538
528 if (debug.traceChunkBuilder) { 539 if (debug.traceChunkBuilder) {
529 debug.log(debug.green("\nBuilt:")); 540 debug.log(debug.green("\nBuilt:"));
530 debug.dumpChunks(0, _chunks); 541 debug.dumpChunks(0, _chunks);
531 debug.log(); 542 debug.log();
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
570 581
571 case Whitespace.newline: 582 case Whitespace.newline:
572 _writeHardSplit(); 583 _writeHardSplit();
573 break; 584 break;
574 585
575 case Whitespace.nestedNewline: 586 case Whitespace.nestedNewline:
576 _writeHardSplit(nest: true); 587 _writeHardSplit(nest: true);
577 break; 588 break;
578 589
579 case Whitespace.newlineFlushLeft: 590 case Whitespace.newlineFlushLeft:
580 _writeHardSplit(nest: true, flushLeft: true); 591 _writeHardSplit(flushLeft: true, nest: true);
581 break; 592 break;
582 593
583 case Whitespace.twoNewlines: 594 case Whitespace.twoNewlines:
584 _writeHardSplit(double: true); 595 _writeHardSplit(isDouble: true);
585 break; 596 break;
586 597
587 case Whitespace.spaceOrNewline: 598 case Whitespace.spaceOrNewline:
588 case Whitespace.splitOrNewline: 599 case Whitespace.splitOrNewline:
589 case Whitespace.oneOrTwoNewlines: 600 case Whitespace.oneOrTwoNewlines:
590 // We should have pinned these down before getting here. 601 // We should have pinned these down before getting here.
591 assert(false); 602 assert(false);
592 break; 603 break;
593 } 604 }
594 605
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
654 token != "]" && 665 token != "]" &&
655 token != "}" && 666 token != "}" &&
656 token != "," && 667 token != "," &&
657 token != ";" && 668 token != ";" &&
658 token != ""; 669 token != "";
659 } 670 }
660 671
661 /// Appends a hard split with the current indentation and nesting (the latter 672 /// Appends a hard split with the current indentation and nesting (the latter
662 /// only if [nest] is `true`). 673 /// only if [nest] is `true`).
663 /// 674 ///
664 /// If [double] is `true` or `false`, forces a since or double line to be 675 /// If [double] is `true` or `false`, forces a single or double line to be
665 /// output. Otherwise, it is left indeterminate. 676 /// output. Otherwise, it is left indeterminate.
666 /// 677 ///
667 /// If [flushLeft] is `true`, then the split will always cause the next line 678 /// If [flushLeft] is `true`, then the split will always cause the next line
668 /// to be at column zero. Otherwise, it uses the normal indentation and 679 /// to be at column zero. Otherwise, it uses the normal indentation and
669 /// nesting behavior. 680 /// nesting behavior.
670 void _writeHardSplit({bool nest: false, bool double, bool flushLeft}) { 681 void _writeHardSplit({bool isDouble, bool flushLeft, bool nest: false}) {
671 // A hard split overrides any other whitespace. 682 // A hard split overrides any other whitespace.
672 _pendingWhitespace = null; 683 _pendingWhitespace = null;
673 _writeSplit(new HardSplitRule(), nest ? null : _nesting.blockNesting, 684 _writeSplit(new Rule.hard(),
674 flushLeft: flushLeft, isDouble: double); 685 flushLeft: flushLeft, isDouble: isDouble, nest: nest);
675 } 686 }
676 687
677 /// Ends the current chunk (if any) with the given split information. 688 /// Ends the current chunk (if any) with the given split information.
678 /// 689 ///
679 /// Returns the chunk. 690 /// Returns the chunk.
680 Chunk _writeSplit(Rule rule, NestingLevel nesting, 691 Chunk _writeSplit(Rule rule,
681 {bool flushLeft, bool isDouble, bool spaceWhenUnsplit}) { 692 {bool flushLeft, bool isDouble, bool nest, bool space}) {
693 if (nest == null) nest = true;
694
682 if (_chunks.isEmpty) { 695 if (_chunks.isEmpty) {
683 if (flushLeft != null) _firstFlushLeft = flushLeft; 696 if (flushLeft != null) _firstFlushLeft = flushLeft;
684 697
685 return null; 698 return null;
686 } 699 }
687 700
688 if (nesting == null) nesting = _nesting.nesting; 701 _chunks.last.applySplit(rule, _nesting.indentation,
702 nest ? _nesting.nesting : new NestingLevel(),
703 flushLeft: flushLeft, isDouble: isDouble, space: space);
689 704
690 var chunk = _chunks.last; 705 if (_chunks.last.rule.isHardened) _handleHardSplit();
691 chunk.applySplit(rule, _nesting.indentation, nesting, 706 return _chunks.last;
692 flushLeft: flushLeft,
693 isDouble: isDouble,
694 spaceWhenUnsplit: spaceWhenUnsplit);
695
696 // Keep track of which chunks are owned by the rule.
697 if (rule is! HardSplitRule) {
698 _ruleChunks.putIfAbsent(rule, () => []).add(_chunks.length - 1);
699 }
700
701 if (chunk.isHardSplit) _handleHardSplit();
702
703 return chunk;
704 } 707 }
705 708
706 /// Writes [text] to either the current chunk or a new one if the current 709 /// Writes [text] to either the current chunk or a new one if the current
707 /// chunk is complete. 710 /// chunk is complete.
708 void _writeText(String text) { 711 void _writeText(String text) {
709 if (_chunks.isNotEmpty && _chunks.last.canAddText) { 712 if (_chunks.isNotEmpty && _chunks.last.canAddText) {
710 _chunks.last.appendText(text); 713 _chunks.last.appendText(text);
711 } else { 714 } else {
712 _chunks.add(new Chunk(text)); 715 _chunks.add(new Chunk(text));
713 } 716 }
714 } 717 }
715 718
716 /// Returns true if we can divide the chunks at [index] and line split the 719 /// Returns true if we can divide the chunks at [index] and line split the
717 /// ones before and after that separately. 720 /// ones before and after that separately.
718 bool _canDivideAt(int i) { 721 bool _canDivideAt(int i) {
722 // Don't divide after the last chunk.
723 if (i == _chunks.length - 1) return false;
724
719 var chunk = _chunks[i]; 725 var chunk = _chunks[i];
720 if (!chunk.isHardSplit) return false; 726 if (!chunk.rule.isHardened) return false;
721 if (chunk.nesting.isNested) return false; 727 if (chunk.nesting.isNested) return false;
722 if (chunk.blockChunks.isNotEmpty) return false; 728 if (chunk.isBlock) return false;
723
724 // Make sure we don't split the line in the middle of a rule.
725 var chunks = _ruleChunks[chunk.rule];
726 if (chunks != null && chunks.any((other) => other > i)) return false;
727 729
728 return true; 730 return true;
729 } 731 }
730 732
731 /// Pre-processes the chunks after they are done being written by the visitor 733 /// Pre-processes the chunks after they are done being written by the visitor
732 /// but before they are run through the line splitter. 734 /// but before they are run through the line splitter.
733 /// 735 ///
734 /// Marks ranges of chunks that can be line split independently to keep the 736 /// Marks ranges of chunks that can be line split independently to keep the
735 /// batches we send to [LineSplitter] small. 737 /// batches we send to [LineSplitter] small.
736 void _divideChunks() { 738 void _divideChunks() {
(...skipping 21 matching lines...) Expand all
758 _hardSplitRules.add(_rules.last); 760 _hardSplitRules.add(_rules.last);
759 } 761 }
760 762
761 /// Replaces all of the previously hardened rules with hard splits, along 763 /// Replaces all of the previously hardened rules with hard splits, along
762 /// with every rule that those constrain to also split. 764 /// with every rule that those constrain to also split.
763 /// 765 ///
764 /// This should only be called after all chunks have been written. 766 /// This should only be called after all chunks have been written.
765 void _hardenRules() { 767 void _hardenRules() {
766 if (_hardSplitRules.isEmpty) return; 768 if (_hardSplitRules.isEmpty) return;
767 769
768 // Harden all of the rules that are constrained by [rules] as well.
769 var hardenedRules = new Set();
770 walkConstraints(rule) { 770 walkConstraints(rule) {
771 if (hardenedRules.contains(rule)) return; 771 rule.harden();
772 hardenedRules.add(rule);
773 772
774 // Follow this rule's constraints, recursively. 773 // Follow this rule's constraints, recursively.
775 for (var other in _ruleChunks.keys) { 774 for (var other in rule.constrainedRules) {
776 if (other == rule) continue; 775 if (other == rule) continue;
777 776
778 if (rule.constrain(rule.fullySplitValue, other) == 777 if (!other.isHardened &&
779 other.fullySplitValue) { 778 rule.constrain(rule.fullySplitValue, other) ==
779 other.fullySplitValue) {
780 walkConstraints(other); 780 walkConstraints(other);
781 } 781 }
782 } 782 }
783 } 783 }
784 784
785 for (var rule in _hardSplitRules) { 785 for (var rule in _hardSplitRules) {
786 walkConstraints(rule); 786 walkConstraints(rule);
787 } 787 }
788 788
789 // Harden every chunk that uses one of these rules. 789 // Discard spans in hardened chunks since we know for certain they will
790 // split anyway.
790 for (var chunk in _chunks) { 791 for (var chunk in _chunks) {
791 if (hardenedRules.contains(chunk.rule)) { 792 if (chunk.rule != null && chunk.rule.isHardened) {
792 chunk.harden(); 793 chunk.spans.clear();
793 } 794 }
794 } 795 }
795 } 796 }
796 } 797 }
OLDNEW
« no previous file with comments | « packages/dart_style/lib/src/chunk.dart ('k') | packages/dart_style/lib/src/dart_formatter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698