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.source_writer; | 5 library dart_style.src.source_writer; |
6 | 6 |
7 import 'dart_formatter.dart'; | 7 import 'dart_formatter.dart'; |
8 import 'chunk.dart'; | 8 import 'chunk.dart'; |
9 import 'debug.dart'; | 9 import 'debug.dart'; |
10 import 'line_splitter.dart'; | 10 import 'line_splitter.dart'; |
(...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 /// It counts backwards from the end because this is called *after* the chunk | 465 /// It counts backwards from the end because this is called *after* the chunk |
466 /// of text containing the selection has been output. | 466 /// of text containing the selection has been output. |
467 void endSelectionFromEnd(int fromEnd) { | 467 void endSelectionFromEnd(int fromEnd) { |
468 assert(_chunks.isNotEmpty); | 468 assert(_chunks.isNotEmpty); |
469 _chunks.last.endSelectionFromEnd(fromEnd); | 469 _chunks.last.endSelectionFromEnd(fromEnd); |
470 } | 470 } |
471 | 471 |
472 /// Finishes writing and returns a [SourceCode] containing the final output | 472 /// Finishes writing and returns a [SourceCode] containing the final output |
473 /// and updated selection, if any. | 473 /// and updated selection, if any. |
474 SourceCode end() { | 474 SourceCode end() { |
475 if (_chunks.isNotEmpty) _completeLine(); | 475 if (_chunks.isNotEmpty) _completeLine(_chunks.length); |
476 | 476 |
477 // Be a good citizen, end with a newline. | 477 // Be a good citizen, end with a newline. |
478 if (_source.isCompilationUnit) _buffer.write(_formatter.lineEnding); | 478 if (_source.isCompilationUnit) _buffer.write(_formatter.lineEnding); |
479 | 479 |
480 // If we haven't hit the beginning and/or end of the selection yet, they | 480 // If we haven't hit the beginning and/or end of the selection yet, they |
481 // must be at the very end of the code. | 481 // must be at the very end of the code. |
482 if (_source.selectionStart != null) { | 482 if (_source.selectionStart != null) { |
483 if (_selectionStart == null) { | 483 if (_selectionStart == null) { |
484 _selectionStart = _buffer.length; | 484 _selectionStart = _buffer.length; |
485 } | 485 } |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
629 /// chunk is complete. | 629 /// chunk is complete. |
630 void _writeText(String text) { | 630 void _writeText(String text) { |
631 if (_chunks.isEmpty) { | 631 if (_chunks.isEmpty) { |
632 _chunks.add(new Chunk(text)); | 632 _chunks.add(new Chunk(text)); |
633 } else if (_chunks.last.canAddText) { | 633 } else if (_chunks.last.canAddText) { |
634 _chunks.last.appendText(text); | 634 _chunks.last.appendText(text); |
635 } else { | 635 } else { |
636 // Since we're about to write some text on the next line, we know the | 636 // Since we're about to write some text on the next line, we know the |
637 // previous one is fully done being tweaked and merged, so now we can see | 637 // previous one is fully done being tweaked and merged, so now we can see |
638 // if it can be split independently. | 638 // if it can be split independently. |
639 _checkForCompleteLine(); | 639 _checkForCompleteLine(_chunks.length); |
640 | 640 |
641 _chunks.add(new Chunk(text)); | 641 _chunks.add(new Chunk(text)); |
642 } | 642 } |
643 } | 643 } |
644 | 644 |
645 /// Checks to see if we are currently at a point where the existing chunks | 645 /// Checks to see if we the first [length] chunks can be processed as a |
646 /// can be processed as a single line and processes them if so. | 646 /// single line and processes them if so. |
647 /// | 647 /// |
648 /// We want to send small lists of chunks to [LineSplitter] for performance. | 648 /// We want to send small lists of chunks to [LineSplitter] for performance. |
649 /// We can do that when we know one set of chunks will absolutely not affect | 649 /// We can do that when we know one set of chunks will absolutely not affect |
650 /// anything following it. The rule for that is pretty simple: a hard newline | 650 /// anything following it. The rule for that is pretty simple: a hard newline |
651 /// that is not nested inside an expression. | 651 /// that is not nested inside an expression. |
652 void _checkForCompleteLine() { | 652 bool _checkForCompleteLine(int length) { |
653 if (_chunks.isEmpty) return; | 653 if (length == 0) return false; |
| 654 |
| 655 // Hang on to the split info so we can reset the writer to start with it. |
| 656 var split = _chunks[length - 1]; |
654 | 657 |
655 // Can only split on a hard line that is not nested in the middle of an | 658 // Can only split on a hard line that is not nested in the middle of an |
656 // expression. | 659 // expression. |
657 if (!_chunks.last.isHardSplit || _chunks.last.nesting >= 0) return; | 660 if (!split.isHardSplit || split.nesting >= 0) return false; |
658 | 661 |
659 // Hang on to the split info so we can reset the writer to start with it. | 662 _completeLine(length); |
660 var split = _chunks.last; | |
661 | 663 |
662 // Don't write any empty line, just discard it. | 664 // Discard the formatted chunks and any spans contained in them. |
663 if (_chunks.isNotEmpty) { | 665 _chunks.removeRange(0, length); |
664 _completeLine(); | 666 _spans.removeWhere((span) => span.shift(length)); |
665 _chunks.clear(); | |
666 } | |
667 | |
668 _spans.clear(); | |
669 | 667 |
670 // Get ready for the next line. | 668 // Get ready for the next line. |
671 _bufferedNewlines = split.isDouble ? 2 : 1; | 669 _bufferedNewlines = split.isDouble ? 2 : 1; |
672 _beginningIndent = split.indent; | 670 _beginningIndent = split.indent; |
| 671 |
| 672 return true; |
673 } | 673 } |
674 | 674 |
675 /// Hands off the current list of chunks to [LineSplitter] as a single logical | 675 /// Hands off the first [length] chunks to the [LineSplitter] as a single |
676 /// line. | 676 /// logical line to be split. |
677 void _completeLine() { | 677 void _completeLine(int length) { |
678 assert(_chunks.isNotEmpty); | 678 assert(_chunks.isNotEmpty); |
679 | 679 |
680 if (debugFormatter) { | 680 if (debugFormatter) { |
681 dumpChunks(_chunks); | 681 dumpChunks(_chunks.take(length).toList()); |
682 print(_spans.join("\n")); | 682 print(_spans.join("\n")); |
683 } | 683 } |
684 | 684 |
685 // Write the newlines required by the previous line. | 685 // Write the newlines required by the previous line. |
686 for (var i = 0; i < _bufferedNewlines; i++) { | 686 for (var i = 0; i < _bufferedNewlines; i++) { |
687 _buffer.write(_formatter.lineEnding); | 687 _buffer.write(_formatter.lineEnding); |
688 } | 688 } |
689 | 689 |
| 690 // If we aren't completing the entire set of chunks, get the subset that we |
| 691 // are completing. |
| 692 var chunks = _chunks; |
| 693 var spans = _spans; |
| 694 |
| 695 if (length < _chunks.length) { |
| 696 chunks = chunks.take(length).toList(); |
| 697 spans = spans.where((span) => span.start <= length).toList(); |
| 698 } |
| 699 |
690 var splitter = new LineSplitter(_formatter.lineEnding, _formatter.pageWidth, | 700 var splitter = new LineSplitter(_formatter.lineEnding, _formatter.pageWidth, |
691 _chunks, _spans, _beginningIndent); | 701 chunks, spans, _beginningIndent); |
692 var selection = splitter.apply(_buffer); | 702 var selection = splitter.apply(_buffer); |
693 | 703 |
694 if (selection[0] != null) _selectionStart = selection[0]; | 704 if (selection[0] != null) _selectionStart = selection[0]; |
695 if (selection[1] != null) _selectionLength = selection[1] - _selectionStart; | 705 if (selection[1] != null) _selectionLength = selection[1] - _selectionStart; |
696 } | 706 } |
697 | 707 |
698 /// Handles multisplits when a hard line occurs. | 708 /// Handles multisplits when a hard line occurs. |
699 /// | 709 /// |
700 /// Any active separable multisplits will get split in two at this point. | 710 /// Any active separable multisplits will get split in two at this point. |
701 /// Other multisplits are forced into the "hard" state. All of their previous | 711 /// Other multisplits are forced into the "hard" state. All of their previous |
(...skipping 16 matching lines...) Expand all Loading... |
718 for (var multisplit in _multisplits) { | 728 for (var multisplit in _multisplits) { |
719 // If this multisplit isn't separable or already split, we need to harden | 729 // If this multisplit isn't separable or already split, we need to harden |
720 // all of its previous splits now. | 730 // all of its previous splits now. |
721 var param = multisplit.harden(); | 731 var param = multisplit.harden(); |
722 if (param != null) traverseParams(param); | 732 if (param != null) traverseParams(param); |
723 } | 733 } |
724 | 734 |
725 if (splitParams.isEmpty) return; | 735 if (splitParams.isEmpty) return; |
726 | 736 |
727 // Take any existing splits for the multisplits and hard split them. | 737 // Take any existing splits for the multisplits and hard split them. |
728 for (var chunk in _chunks) { | 738 for (var i = 0; i < _chunks.length; i++) { |
| 739 var chunk = _chunks[i]; |
729 if (chunk.param == null) continue; | 740 if (chunk.param == null) continue; |
730 | 741 |
731 if (splitParams.contains(chunk.param)) { | 742 if (splitParams.contains(chunk.param)) { |
732 chunk.harden(); | 743 chunk.harden(); |
| 744 |
| 745 // Now that this chunk is a hard split, we may be able to format up to |
| 746 // it as its own line. If so, the chunks will get removed, so reset |
| 747 // the loop counter. |
| 748 if (_checkForCompleteLine(i + 1)) i = -1; |
733 } else { | 749 } else { |
734 // If the chunk isn't hardened, but implies something that is, we can | 750 // If the chunk isn't hardened, but implies something that is, we can |
735 // discard the implication since it is always satisfied now. | 751 // discard the implication since it is always satisfied now. |
736 chunk.param.implies.removeWhere(splitParams.contains); | 752 chunk.param.implies.removeWhere(splitParams.contains); |
737 } | 753 } |
738 } | 754 } |
739 } | 755 } |
740 } | 756 } |
OLD | NEW |