| 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 '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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |