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

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

Issue 1492683002: Change the way hard splits are handled. (Closed) Base URL: https://github.com/dart-lang/dart_style.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 | « lib/src/chunk.dart ('k') | lib/src/debug.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 343 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 var openSpan = _openSpans.removeLast(); 354 var openSpan = _openSpans.removeLast();
355 355
356 // A span that just covers a single chunk can't be split anyway. 356 // A span that just covers a single chunk can't be split anyway.
357 var end = _currentChunkIndex; 357 var end = _currentChunkIndex;
358 if (openSpan.start == end) return; 358 if (openSpan.start == end) return;
359 359
360 // Add the span to every chunk that can split it. 360 // Add the span to every chunk that can split it.
361 var span = new Span(openSpan.cost); 361 var span = new Span(openSpan.cost);
362 for (var i = openSpan.start; i < end; i++) { 362 for (var i = openSpan.start; i < end; i++) {
363 var chunk = _chunks[i]; 363 var chunk = _chunks[i];
364 if (!chunk.isHardSplit) chunk.spans.add(span); 364 if (!chunk.rule.isHardened) chunk.spans.add(span);
365 } 365 }
366 } 366 }
367 367
368 /// Starts a new [Rule]. 368 /// Starts a new [Rule].
369 /// 369 ///
370 /// If omitted, defaults to a new [SimpleRule]. 370 /// If omitted, defaults to a new [Rule].
371 void startRule([Rule rule]) { 371 void startRule([Rule rule]) {
372 if (rule == null) rule = new SimpleRule(); 372 if (rule == null) rule = new Rule();
373 373
374 // See if any of the rules that contain this one care if it splits. 374 // See if any of the rules that contain this one care if it splits.
375 _rules.forEach((outer) => outer.contain(rule)); 375 _rules.forEach((outer) => outer.contain(rule));
376 _rules.add(rule); 376 _rules.add(rule);
377 } 377 }
378 378
379 /// Starts a new [Rule] that comes into play *after* the next whitespace 379 /// Starts a new [Rule] that comes into play *after* the next whitespace
380 /// (including comments) is written. 380 /// (including comments) is written.
381 /// 381 ///
382 /// This is used for binary operators who want to start a rule before the 382 /// This is used for binary operators who want to start a rule before the
383 /// first operand but not get forced to split if a comment appears before the 383 /// first operand but not get forced to split if a comment appears before the
384 /// entire expression. 384 /// entire expression.
385 /// 385 ///
386 /// If [rule] is omitted, defaults to a new [SimpleRule]. 386 /// If [rule] is omitted, defaults to a new [Rule].
387 void startLazyRule([Rule rule]) { 387 void startLazyRule([Rule rule]) {
388 if (rule == null) rule = new SimpleRule(); 388 if (rule == null) rule = new Rule();
389 389
390 _lazyRules.add(rule); 390 _lazyRules.add(rule);
391 } 391 }
392 392
393 /// Ends the innermost rule. 393 /// Ends the innermost rule.
394 void endRule() { 394 void endRule() {
395 _rules.removeLast(); 395 _rules.removeLast();
396 } 396 }
397 397
398 /// Pre-emptively forces all of the current rules to become hard splits. 398 /// Pre-emptively forces all of the current rules to become hard splits.
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
483 483
484 /// Ends this [ChunkBuilder], which must have been created by [startBlock()]. 484 /// Ends this [ChunkBuilder], which must have been created by [startBlock()].
485 /// 485 ///
486 /// Forces the chunk that owns the block to split if it can tell that the 486 /// Forces the chunk that owns the block to split if it can tell that the
487 /// block contents will always split. It does that by looking for hard splits 487 /// block contents will always split. It does that by looking for hard splits
488 /// in the block. If [ignoredSplit] is given, that rule will be ignored 488 /// in the block. If [ignoredSplit] is given, that rule will be ignored
489 /// when determining if a block contains a hard split. If [forceSplit] is 489 /// when determining if a block contains a hard split. If [forceSplit] is
490 /// `true`, the block is considered to always split. 490 /// `true`, the block is considered to always split.
491 /// 491 ///
492 /// Returns the previous writer for the surrounding block. 492 /// Returns the previous writer for the surrounding block.
493 ChunkBuilder endBlock(HardSplitRule ignoredSplit, {bool forceSplit}) { 493 ChunkBuilder endBlock(Rule ignoredSplit, {bool forceSplit}) {
494 _divideChunks(); 494 _divideChunks();
495 495
496 // If we don't already know if the block is going to split, see if it 496 // If we don't already know if the block is going to split, see if it
497 // contains any hard splits or is longer than a page. 497 // contains any hard splits or is longer than a page.
498 if (!forceSplit) { 498 if (!forceSplit) {
499 var length = 0; 499 var length = 0;
500 for (var chunk in _chunks) { 500 for (var chunk in _chunks) {
501 length += chunk.length + chunk.unsplitBlockLength; 501 length += chunk.length + chunk.unsplitBlockLength;
502 if (length > _formatter.pageWidth) { 502 if (length > _formatter.pageWidth) {
503 forceSplit = true; 503 forceSplit = true;
504 break; 504 break;
505 } 505 }
506 506
507 if (chunk.isHardSplit && chunk.rule != ignoredSplit) { 507 if (chunk.rule != null &&
508 chunk.rule.isHardened &&
509 chunk.rule != ignoredSplit) {
508 forceSplit = true; 510 forceSplit = true;
509 break; 511 break;
510 } 512 }
511 } 513 }
512 } 514 }
513 515
514 _parent._endChildBlock( 516 _parent._endChildBlock(
515 firstFlushLeft: _firstFlushLeft, forceSplit: forceSplit); 517 firstFlushLeft: _firstFlushLeft, forceSplit: forceSplit);
516 518
517 return _parent; 519 return _parent;
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
675 /// 677 ///
676 /// If [double] is `true` or `false`, forces a single or double line to be 678 /// If [double] is `true` or `false`, forces a single or double line to be
677 /// output. Otherwise, it is left indeterminate. 679 /// output. Otherwise, it is left indeterminate.
678 /// 680 ///
679 /// If [flushLeft] is `true`, then the split will always cause the next line 681 /// If [flushLeft] is `true`, then the split will always cause the next line
680 /// to be at column zero. Otherwise, it uses the normal indentation and 682 /// to be at column zero. Otherwise, it uses the normal indentation and
681 /// nesting behavior. 683 /// nesting behavior.
682 void _writeHardSplit({bool isDouble, bool flushLeft, bool nest: false}) { 684 void _writeHardSplit({bool isDouble, bool flushLeft, bool nest: false}) {
683 // A hard split overrides any other whitespace. 685 // A hard split overrides any other whitespace.
684 _pendingWhitespace = null; 686 _pendingWhitespace = null;
685 _writeSplit(new HardSplitRule(), 687 _writeSplit(new Rule.hard(),
686 flushLeft: flushLeft, isDouble: isDouble, nest: nest); 688 flushLeft: flushLeft, isDouble: isDouble, nest: nest);
687 } 689 }
688 690
689 /// Ends the current chunk (if any) with the given split information. 691 /// Ends the current chunk (if any) with the given split information.
690 /// 692 ///
691 /// Returns the chunk. 693 /// Returns the chunk.
692 Chunk _writeSplit(Rule rule, 694 Chunk _writeSplit(Rule rule,
693 {bool flushLeft, bool isDouble, bool nest, bool space}) { 695 {bool flushLeft, bool isDouble, bool nest, bool space}) {
694 if (nest == null) nest = true; 696 if (nest == null) nest = true;
695 697
696 if (_chunks.isEmpty) { 698 if (_chunks.isEmpty) {
697 if (flushLeft != null) _firstFlushLeft = flushLeft; 699 if (flushLeft != null) _firstFlushLeft = flushLeft;
698 700
699 return null; 701 return null;
700 } 702 }
701 703
702 _chunks.last.applySplit(rule, _nesting.indentation, 704 _chunks.last.applySplit(rule, _nesting.indentation,
703 nest ? _nesting.nesting : new NestingLevel(), 705 nest ? _nesting.nesting : new NestingLevel(),
704 flushLeft: flushLeft, isDouble: isDouble, space: space); 706 flushLeft: flushLeft, isDouble: isDouble, space: space);
705 707
706 return _afterSplit(); 708 return _afterSplit();
707 } 709 }
708 710
709 /// Keep tracks of which chunks are owned by which rules and handles hard 711 /// Keep tracks of which chunks are owned by which rules and handles hard
710 /// splits after a chunk has been completed. 712 /// splits after a chunk has been completed.
711 Chunk _afterSplit() { 713 Chunk _afterSplit() {
712 var chunk = _chunks.last; 714 var chunk = _chunks.last;
713 715
714 if (chunk.rule is! HardSplitRule) { 716 if (_rules.isNotEmpty) {
715 _ruleChunks.putIfAbsent(rule, () => []).add(_chunks.length - 1); 717 _ruleChunks.putIfAbsent(rule, () => []).add(_chunks.length - 1);
716 } 718 }
717 719
718 if (chunk.isHardSplit) _handleHardSplit(); 720 if (chunk.rule.isHardened) _handleHardSplit();
719 721
720 return chunk; 722 return chunk;
721 } 723 }
722 724
723 /// Writes [text] to either the current chunk or a new one if the current 725 /// Writes [text] to either the current chunk or a new one if the current
724 /// chunk is complete. 726 /// chunk is complete.
725 void _writeText(String text) { 727 void _writeText(String text) {
726 if (_chunks.isNotEmpty && _chunks.last.canAddText) { 728 if (_chunks.isNotEmpty && _chunks.last.canAddText) {
727 _chunks.last.appendText(text); 729 _chunks.last.appendText(text);
728 } else { 730 } else {
729 _chunks.add(new Chunk(text)); 731 _chunks.add(new Chunk(text));
730 } 732 }
731 } 733 }
732 734
733 /// Returns true if we can divide the chunks at [index] and line split the 735 /// Returns true if we can divide the chunks at [index] and line split the
734 /// ones before and after that separately. 736 /// ones before and after that separately.
735 bool _canDivideAt(int i) { 737 bool _canDivideAt(int i) {
738 // Don't divide after the last chunk.
739 if (i == _chunks.length - 1) return false;
740
736 var chunk = _chunks[i]; 741 var chunk = _chunks[i];
737 if (!chunk.isHardSplit) return false; 742 if (!chunk.rule.isHardened) return false;
738 if (chunk.nesting.isNested) return false; 743 if (chunk.nesting.isNested) return false;
739 if (chunk.isBlock) return false; 744 if (chunk.isBlock) return false;
740 745
741 // Make sure we don't split the line in the middle of a rule. 746 // Make sure we don't split the line in the middle of a rule.
742 var chunks = _ruleChunks[chunk.rule]; 747 var chunks = _ruleChunks[chunk.rule];
743 if (chunks != null && chunks.any((other) => other > i)) return false; 748 if (chunks != null && chunks.any((other) => other > i)) return false;
744 749
745 return true; 750 return true;
746 } 751 }
747 752
(...skipping 27 matching lines...) Expand all
775 _hardSplitRules.add(_rules.last); 780 _hardSplitRules.add(_rules.last);
776 } 781 }
777 782
778 /// Replaces all of the previously hardened rules with hard splits, along 783 /// Replaces all of the previously hardened rules with hard splits, along
779 /// with every rule that those constrain to also split. 784 /// with every rule that those constrain to also split.
780 /// 785 ///
781 /// This should only be called after all chunks have been written. 786 /// This should only be called after all chunks have been written.
782 void _hardenRules() { 787 void _hardenRules() {
783 if (_hardSplitRules.isEmpty) return; 788 if (_hardSplitRules.isEmpty) return;
784 789
785 // Harden all of the rules that are constrained by [rules] as well.
786 var hardenedRules = new Set();
787 walkConstraints(rule) { 790 walkConstraints(rule) {
788 if (hardenedRules.contains(rule)) return; 791 rule.harden();
789 hardenedRules.add(rule);
790 792
791 // Follow this rule's constraints, recursively. 793 // Follow this rule's constraints, recursively.
792 for (var other in _ruleChunks.keys) { 794 for (var other in _ruleChunks.keys) {
793 if (other == rule) continue; 795 if (other == rule) continue;
794 796
795 if (rule.constrain(rule.fullySplitValue, other) == 797 if (!other.isHardened &&
796 other.fullySplitValue) { 798 rule.constrain(rule.fullySplitValue, other) ==
799 other.fullySplitValue) {
797 walkConstraints(other); 800 walkConstraints(other);
798 } 801 }
799 } 802 }
800 } 803 }
801 804
802 for (var rule in _hardSplitRules) { 805 for (var rule in _hardSplitRules) {
803 walkConstraints(rule); 806 walkConstraints(rule);
804 } 807 }
805 808
806 // Harden every chunk that uses one of these rules. 809 // Discard spans in hardened chunks since we know for certain they will
810 // split anyway.
807 for (var chunk in _chunks) { 811 for (var chunk in _chunks) {
808 if (hardenedRules.contains(chunk.rule)) { 812 if (chunk.rule != null && chunk.rule.isHardened) {
809 chunk.harden(); 813 chunk.spans.clear();
810 } 814 }
811 } 815 }
812 } 816 }
813 } 817 }
OLDNEW
« no previous file with comments | « lib/src/chunk.dart ('k') | lib/src/debug.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698