| 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 |