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 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 /// | 67 /// |
68 /// Collections can also contain function expressions, which have blocks which | 68 /// Collections can also contain function expressions, which have blocks which |
69 /// in turn force a newline in the middle of the collection. When that | 69 /// in turn force a newline in the middle of the collection. When that |
70 /// happens, we need to force all surrounding collections to be multi-line. | 70 /// happens, we need to force all surrounding collections to be multi-line. |
71 /// This tracks them so we can do that. | 71 /// This tracks them so we can do that. |
72 final _multisplits = <Multisplit>[]; | 72 final _multisplits = <Multisplit>[]; |
73 | 73 |
74 /// The nested stack of spans that are currently being written. | 74 /// The nested stack of spans that are currently being written. |
75 final _openSpans = <Span>[]; | 75 final _openSpans = <Span>[]; |
76 | 76 |
77 /// All of the spans that have been created, open and closed. | 77 /// All of the spans that have been created and closed. |
78 final _spans = <Span>[]; | 78 final _spans = <Span>[]; |
79 | 79 |
80 /// The current indentation and nesting levels. | 80 /// The current indentation and nesting levels. |
81 /// | 81 /// |
82 /// This is tracked as a stack of numbers. Each element in the stack | 82 /// This is tracked as a stack of numbers. Each element in the stack |
83 /// represents a level of statement indentation. The number of the element is | 83 /// represents a level of statement indentation. The number of the element is |
84 /// the current expression nesting depth for that statement. | 84 /// the current expression nesting depth for that statement. |
85 /// | 85 /// |
86 /// It's stored as a stack because expressions may contain statements which | 86 /// It's stored as a stack because expressions may contain statements which |
87 /// in turn contain other expressions. The nesting level of the inner | 87 /// in turn contain other expressions. The nesting level of the inner |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 /// Starts a new span with [cost]. | 364 /// Starts a new span with [cost]. |
365 /// | 365 /// |
366 /// Each call to this needs a later matching call to [endSpan]. | 366 /// Each call to this needs a later matching call to [endSpan]. |
367 void startSpan([int cost = Cost.normal]) { | 367 void startSpan([int cost = Cost.normal]) { |
368 _openSpans.add(new Span(_currentChunkIndex, cost)); | 368 _openSpans.add(new Span(_currentChunkIndex, cost)); |
369 } | 369 } |
370 | 370 |
371 /// Ends the innermost span. | 371 /// Ends the innermost span. |
372 void endSpan() { | 372 void endSpan() { |
373 var span = _openSpans.removeLast(); | 373 var span = _openSpans.removeLast(); |
| 374 |
| 375 // If the span was discarded while it was still open, just forget about it. |
| 376 if (span == null) return; |
| 377 |
374 span.close(_currentChunkIndex); | 378 span.close(_currentChunkIndex); |
375 | 379 |
376 // A span that just covers a single chunk can't be split anyway. | 380 // A span that just covers a single chunk can't be split anyway. |
377 if (span.start == span.end) return; | 381 if (span.start == span.end) return; |
378 _spans.add(span); | 382 _spans.add(span); |
379 } | 383 } |
380 | 384 |
381 /// Starts a new [Multisplit]. | 385 /// Starts a new [Multisplit]. |
382 /// | 386 /// |
383 /// Returns the [SplitParam] for the multisplit. | 387 /// Returns the [SplitParam] for the multisplit. |
384 SplitParam startMultisplit({bool separable}) { | 388 SplitParam startMultisplit({bool separable, int cost}) { |
385 var multisplit = new Multisplit(_currentChunkIndex, separable: separable); | 389 var multisplit = new Multisplit(_currentChunkIndex, |
| 390 separable: separable, cost: cost); |
386 _multisplits.add(multisplit); | 391 _multisplits.add(multisplit); |
387 | 392 |
388 return multisplit.param; | 393 return multisplit.param; |
389 } | 394 } |
390 | 395 |
391 /// Adds a new split point for the current innermost [Multisplit]. | 396 /// Adds a new split point for the current innermost [Multisplit]. |
392 /// | 397 /// |
393 /// If [space] is `true`, the chunk will include a space when unsplit. If | 398 /// If [space] is `true`, the chunk will include a space when unsplit. If |
394 /// [nest] is `true`, then this split will take into account expression | 399 /// [nest] is `true`, then this split will take into account expression |
395 /// nesting. Otherwise, it will not. Collections do not follow expression | 400 /// nesting. Otherwise, it will not. Collections do not follow expression |
(...skipping 20 matching lines...) Expand all Loading... |
416 /// Pre-emptively forces all of the multisplits to become hard splits. | 421 /// Pre-emptively forces all of the multisplits to become hard splits. |
417 /// | 422 /// |
418 /// This is called by [SourceVisitor] when it can determine that a multisplit | 423 /// This is called by [SourceVisitor] when it can determine that a multisplit |
419 /// will never be satisfied. Turning it into hard splits lets the writer | 424 /// will never be satisfied. Turning it into hard splits lets the writer |
420 /// break the output into smaller pieces for the line splitter, which helps | 425 /// break the output into smaller pieces for the line splitter, which helps |
421 /// performance and avoids failing on very large input. | 426 /// performance and avoids failing on very large input. |
422 /// | 427 /// |
423 /// In particular, it's easy for the visitor to know that collections with a | 428 /// In particular, it's easy for the visitor to know that collections with a |
424 /// large number of items must split. Doing that early avoids crashing the | 429 /// large number of items must split. Doing that early avoids crashing the |
425 /// splitter when it tries to recurse on huge collection literals. | 430 /// splitter when it tries to recurse on huge collection literals. |
426 void preemptMultisplits() => _splitMultisplits(); | 431 void preemptMultisplits() => _handleHardSplit(); |
427 | 432 |
428 /// Increases the level of expression nesting. | 433 /// Increases the level of expression nesting. |
429 /// | 434 /// |
430 /// Expressions that are more nested will get increased indentation when split | 435 /// Expressions that are more nested will get increased indentation when split |
431 /// if the previous line has a lower level of nesting. | 436 /// if the previous line has a lower level of nesting. |
432 void nestExpression() { | 437 void nestExpression() { |
433 if (_pendingNesting != null) { | 438 if (_pendingNesting != null) { |
434 _pendingNesting++; | 439 _pendingNesting++; |
435 } else { | 440 } else { |
436 _pendingNesting = _nesting + 1; | 441 _pendingNesting = _nesting + 1; |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
615 } | 620 } |
616 | 621 |
617 /// Ends the current chunk (if any) with the given split information. | 622 /// Ends the current chunk (if any) with the given split information. |
618 void _writeSplit(int indent, int nesting, SplitParam param, | 623 void _writeSplit(int indent, int nesting, SplitParam param, |
619 {bool isDouble, bool spaceWhenUnsplit}) { | 624 {bool isDouble, bool spaceWhenUnsplit}) { |
620 if (_chunks.isEmpty) return; | 625 if (_chunks.isEmpty) return; |
621 | 626 |
622 _chunks.last.applySplit(indent, nesting, param, | 627 _chunks.last.applySplit(indent, nesting, param, |
623 isDouble: isDouble, spaceWhenUnsplit: spaceWhenUnsplit); | 628 isDouble: isDouble, spaceWhenUnsplit: spaceWhenUnsplit); |
624 | 629 |
625 if (_chunks.last.isHardSplit) _splitMultisplits(); | 630 if (_chunks.last.isHardSplit) _handleHardSplit(); |
626 } | 631 } |
627 | 632 |
628 /// Writes [text] to either the current chunk or a new one if the current | 633 /// Writes [text] to either the current chunk or a new one if the current |
629 /// chunk is complete. | 634 /// chunk is complete. |
630 void _writeText(String text) { | 635 void _writeText(String text) { |
631 if (_chunks.isEmpty) { | 636 if (_chunks.isEmpty) { |
632 _chunks.add(new Chunk(text)); | 637 _chunks.add(new Chunk(text)); |
633 } else if (_chunks.last.canAddText) { | 638 } else if (_chunks.last.canAddText) { |
634 _chunks.last.appendText(text); | 639 _chunks.last.appendText(text); |
635 } else { | 640 } else { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
670 _beginningIndent = split.indent; | 675 _beginningIndent = split.indent; |
671 | 676 |
672 return true; | 677 return true; |
673 } | 678 } |
674 | 679 |
675 /// Hands off the first [length] chunks to the [LineSplitter] as a single | 680 /// Hands off the first [length] chunks to the [LineSplitter] as a single |
676 /// logical line to be split. | 681 /// logical line to be split. |
677 void _completeLine(int length) { | 682 void _completeLine(int length) { |
678 assert(_chunks.isNotEmpty); | 683 assert(_chunks.isNotEmpty); |
679 | 684 |
680 if (debugFormatter) { | |
681 dumpChunks(_chunks.take(length).toList()); | |
682 print(_spans.join("\n")); | |
683 } | |
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 | 690 // If we aren't completing the entire set of chunks, get the subset that we |
691 // are completing. | 691 // are completing. |
692 var chunks = _chunks; | 692 var chunks = _chunks; |
693 var spans = _spans; | 693 var spans = _spans; |
694 | 694 |
695 if (length < _chunks.length) { | 695 if (length < _chunks.length) { |
696 chunks = chunks.take(length).toList(); | 696 chunks = chunks.take(length).toList(); |
697 spans = spans.where((span) => span.start <= length).toList(); | 697 spans = spans.where((span) => span.start <= length).toList(); |
698 } | 698 } |
699 | 699 |
| 700 if (debugFormatter) { |
| 701 dumpChunks(chunks); |
| 702 print(spans.join("\n")); |
| 703 } |
| 704 |
700 var splitter = new LineSplitter(_formatter.lineEnding, _formatter.pageWidth, | 705 var splitter = new LineSplitter(_formatter.lineEnding, _formatter.pageWidth, |
701 chunks, spans, _beginningIndent); | 706 chunks, spans, _beginningIndent); |
702 var selection = splitter.apply(_buffer); | 707 var selection = splitter.apply(_buffer); |
703 | 708 |
704 if (selection[0] != null) _selectionStart = selection[0]; | 709 if (selection[0] != null) _selectionStart = selection[0]; |
705 if (selection[1] != null) _selectionLength = selection[1] - _selectionStart; | 710 if (selection[1] != null) _selectionLength = selection[1] - _selectionStart; |
706 } | 711 } |
707 | 712 |
708 /// Handles multisplits when a hard line occurs. | 713 /// Handles open multisplits and spans when a hard line occurs. |
709 /// | 714 /// |
710 /// Any active separable multisplits will get split in two at this point. | 715 /// Any active separable multisplits will get split in two at this point. |
711 /// Other multisplits are forced into the "hard" state. All of their previous | 716 /// Other multisplits are forced into the "hard" state. All of their previous |
712 /// splits are turned into explicit hard splits and any new splits for that | 717 /// splits are turned into explicit hard splits and any new splits for that |
713 /// multisplit become hard splits too. | 718 /// multisplit become hard splits too. |
714 void _splitMultisplits() { | 719 /// |
| 720 /// All open spans get discarded since they will never be satisfied. |
| 721 void _handleHardSplit() { |
| 722 // Discard all open spans. We don't remove them from the stack so that we |
| 723 // can still correctly count later calls to endSpan(). |
| 724 for (var i = 0; i < _openSpans.length; i++) { |
| 725 _openSpans[i] = null; |
| 726 } |
| 727 |
715 if (_multisplits.isEmpty) return; | 728 if (_multisplits.isEmpty) return; |
716 | 729 |
717 var splitParams = new Set(); | 730 var splitParams = new Set(); |
718 | 731 |
719 // Add [param] and the transitive closure of its implied params to | 732 // Add [param] and the transitive closure of its implied params to |
720 // [splitParams]. | 733 // [splitParams]. |
721 traverseParams(param) { | 734 traverseParams(param) { |
722 splitParams.add(param); | 735 splitParams.add(param); |
723 | 736 |
724 // Traverse the tree of implied params. | 737 // Traverse the tree of implied params. |
(...skipping 22 matching lines...) Expand all Loading... |
747 // the loop counter. | 760 // the loop counter. |
748 if (_checkForCompleteLine(i + 1)) i = -1; | 761 if (_checkForCompleteLine(i + 1)) i = -1; |
749 } else { | 762 } else { |
750 // If the chunk isn't hardened, but implies something that is, we can | 763 // If the chunk isn't hardened, but implies something that is, we can |
751 // discard the implication since it is always satisfied now. | 764 // discard the implication since it is always satisfied now. |
752 chunk.param.implies.removeWhere(splitParams.contains); | 765 chunk.param.implies.removeWhere(splitParams.contains); |
753 } | 766 } |
754 } | 767 } |
755 } | 768 } |
756 } | 769 } |
OLD | NEW |