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

Unified Diff: dart_style/lib/src/chunk_builder.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « dart_style/lib/src/chunk.dart ('k') | dart_style/lib/src/dart_formatter.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: dart_style/lib/src/chunk_builder.dart
diff --git a/dart_style/lib/src/chunk_builder.dart b/dart_style/lib/src/chunk_builder.dart
deleted file mode 100644
index c7ff91bb200efe5ed2b961456910e6b34da1e193..0000000000000000000000000000000000000000
--- a/dart_style/lib/src/chunk_builder.dart
+++ /dev/null
@@ -1,796 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library dart_style.src.chunk_builder;
-
-import 'chunk.dart';
-import 'dart_formatter.dart';
-import 'debug.dart' as debug;
-import 'line_writer.dart';
-import 'nesting_level.dart';
-import 'nesting_builder.dart';
-import 'rule/rule.dart';
-import 'source_code.dart';
-import 'whitespace.dart';
-
-/// Takes the incremental serialized output of [SourceVisitor]--the source text
-/// along with any comments and preserved whitespace--and produces a coherent
-/// tree of [Chunk]s which can then be split into physical lines.
-///
-/// Keeps track of leading indentation, expression nesting, and all of the hairy
-/// code required to seamlessly integrate existing comments into the pure
-/// output produced by [SourceVisitor].
-class ChunkBuilder {
- final DartFormatter _formatter;
-
- /// The builder for the code surrounding the block that this writer is for, or
- /// `null` if this is writing the top-level code.
- final ChunkBuilder _parent;
-
- final SourceCode _source;
-
- final List<Chunk> _chunks;
-
- /// The whitespace that should be written to [_chunks] before the next
- /// non-whitespace token.
- ///
- /// This ensures that changes to indentation and nesting also apply to the
- /// most recent split, even if the visitor "creates" the split before changing
- /// indentation or nesting.
- Whitespace _pendingWhitespace = Whitespace.none;
-
- /// The nested stack of rules that are currently in use.
- ///
- /// New chunks are implicitly split by the innermost rule when the chunk is
- /// ended.
- final _rules = <Rule>[];
-
- /// The set of rules known to contain hard splits that will in turn force
- /// these rules to harden.
- ///
- /// This is accumulated lazily while chunks are being built. Then, once they
- /// are all done, the rules are all hardened. We do this later because some
- /// rules may not have all of their constraints fully wired up until after
- /// the hard split appears. For example, a hard split in a positional
- /// argument list needs to force the named arguments to split too, but we
- /// don't create that rule until after the positional arguments are done.
- final _hardSplitRules = new Set<Rule>();
-
- /// The list of rules that are waiting until the next whitespace has been
- /// written before they start.
- final _lazyRules = <Rule>[];
-
- /// The indexes of the chunks owned by each rule (except for hard splits).
- final _ruleChunks = <Rule, List<int>>{};
-
- /// The nested stack of spans that are currently being written.
- final _openSpans = <OpenSpan>[];
-
- /// The current state.
- final _nesting = new NestingBuilder();
-
- /// The stack of nesting levels where block arguments may start.
- ///
- /// A block argument's contents will nest at the last level in this stack.
- final _blockArgumentNesting = <NestingLevel>[];
-
- /// The index of the "current" chunk being written.
- ///
- /// If the last chunk is still being appended to, this is its index.
- /// Otherwise, it is the index of the next chunk which will be created.
- int get _currentChunkIndex {
- if (_chunks.isEmpty) return 0;
- if (_chunks.last.canAddText) return _chunks.length - 1;
- return _chunks.length;
- }
-
- /// Whether or not there was a leading comment that was flush left before any
- /// other content was written.
- ///
- /// This is used when writing child blocks to make the parent chunk have the
- /// right flush left value when a comment appears immediately inside the
- /// block.
- bool _firstFlushLeft = false;
-
- /// Whether there is pending whitespace that depends on the number of
- /// newlines in the source.
- ///
- /// This is used to avoid calculating the newlines between tokens unless
- /// actually needed since doing so is slow when done between every single
- /// token pair.
- bool get needsToPreserveNewlines =>
- _pendingWhitespace == Whitespace.oneOrTwoNewlines ||
- _pendingWhitespace == Whitespace.spaceOrNewline ||
- _pendingWhitespace == Whitespace.splitOrNewline;
-
- /// The number of characters of code that can fit in a single line.
- int get pageWidth => _formatter.pageWidth;
-
- /// The current innermost rule.
- Rule get rule => _rules.last;
-
- ChunkBuilder(this._formatter, this._source)
- : _parent = null,
- _chunks = [] {
- indent(_formatter.indent);
- startBlockArgumentNesting();
- }
-
- ChunkBuilder._(this._parent, this._formatter, this._source, this._chunks) {
- startBlockArgumentNesting();
- }
-
- /// Writes [string], the text for a single token, to the output.
- ///
- /// By default, this also implicitly adds one level of nesting if we aren't
- /// currently nested at all. We do this here so that if a comment appears
- /// after any token within a statement or top-level form and that comment
- /// leads to splitting, we correctly nest. Even pathological cases like:
- ///
- ///
- /// import // comment
- /// "this_gets_nested.dart";
- ///
- /// If we didn't do this here, we'd have to call [nestExpression] after the
- /// first token of practically every grammar production.
- void write(String string) {
- _emitPendingWhitespace();
- _writeText(string);
-
- _lazyRules.forEach(startRule);
- _lazyRules.clear();
-
- _nesting.commitNesting();
- }
-
- /// Writes a [WhitespaceChunk] of [type].
- void writeWhitespace(Whitespace type) {
- _pendingWhitespace = type;
- }
-
- /// Write a split owned by the current innermost rule.
- ///
- /// If [nesting] is given, uses that. Otherwise, uses the current nesting
- /// level. If unsplit, it expands to a space if [space] is `true`.
- ///
- /// If [flushLeft] is `true`, then forces the next line to start at column
- /// one regardless of any indentation or nesting.
- ///
- /// If [isDouble] is passed, forces the split to either be a single or double
- /// newline. Otherwise, leaves it indeterminate.
- Chunk split({bool space, bool isDouble, bool flushLeft}) =>
- _writeSplit(_rules.last, null,
- flushLeft: flushLeft, isDouble: isDouble, spaceWhenUnsplit: space);
-
- /// Write a split owned by the current innermost rule.
- ///
- /// Unlike [split()], this ignores any current expression nesting. It always
- /// indents the next line at the statement level.
- Chunk blockSplit({bool space, bool isDouble}) =>
- _writeSplit(_rules.last, _nesting.blockNesting,
- isDouble: isDouble, spaceWhenUnsplit: space);
-
- /// Outputs the series of [comments] and associated whitespace that appear
- /// before [token] (which is not written by this).
- ///
- /// The list contains each comment as it appeared in the source between the
- /// last token written and the next one that's about to be written.
- ///
- /// [linesBeforeToken] is the number of lines between the last comment (or
- /// previous token if there are no comments) and the next token.
- void writeComments(
- List<SourceComment> comments, int linesBeforeToken, String token) {
- // Corner case: if we require a blank line, but there exists one between
- // some of the comments, or after the last one, then we don't need to
- // enforce one before the first comment. Example:
- //
- // library foo;
- // // comment
- //
- // class Bar {}
- //
- // Normally, a blank line is required after `library`, but since there is
- // one after the comment, we don't need one before it. This is mainly so
- // that commented out directives stick with their preceding group.
- if (_pendingWhitespace == Whitespace.twoNewlines &&
- comments.first.linesBefore < 2) {
- if (linesBeforeToken > 1) {
- _pendingWhitespace = Whitespace.newline;
- } else {
- for (var i = 1; i < comments.length; i++) {
- if (comments[i].linesBefore > 1) {
- _pendingWhitespace = Whitespace.newline;
- break;
- }
- }
- }
- }
-
- // Corner case: if the comments are completely inline (i.e. just a series
- // of block comments with no newlines before, after, or between them), then
- // they will eat any pending newlines. Make sure that doesn't happen by
- // putting the pending whitespace before the first comment and moving them
- // to their own line. Turns this:
- //
- // library foo; /* a */ /* b */ import 'a.dart';
- //
- // into:
- //
- // library foo;
- //
- // /* a */ /* b */
- // import 'a.dart';
- if (linesBeforeToken == 0 &&
- comments.every((comment) => comment.isInline)) {
- if (_pendingWhitespace.minimumLines > 0) {
- comments.first.linesBefore = _pendingWhitespace.minimumLines;
- linesBeforeToken = 1;
- }
- }
-
- // Write each comment and the whitespace between them.
- for (var i = 0; i < comments.length; i++) {
- var comment = comments[i];
-
- preserveNewlines(comment.linesBefore);
-
- // Don't emit a space because we'll handle it below. If we emit it here,
- // we may get a trailing space if the comment needs a line before it.
- if (_pendingWhitespace == Whitespace.space) {
- _pendingWhitespace = Whitespace.none;
- }
- _emitPendingWhitespace();
-
- if (comment.linesBefore == 0) {
- // If we're sitting on a split, move the comment before it to adhere it
- // to the preceding text.
- if (_shouldMoveCommentBeforeSplit(comment.text)) {
- _chunks.last.allowText();
- }
-
- // The comment follows other text, so we need to decide if it gets a
- // space before it or not.
- if (_needsSpaceBeforeComment(isLineComment: comment.isLineComment)) {
- _writeText(" ");
- }
- } else {
- // The comment starts a line, so make sure it stays on its own line.
- _writeHardSplit(
- nest: true,
- flushLeft: comment.flushLeft,
- double: comment.linesBefore > 1);
- }
-
- _writeText(comment.text);
-
- if (comment.selectionStart != null) {
- startSelectionFromEnd(comment.text.length - comment.selectionStart);
- }
-
- if (comment.selectionEnd != null) {
- endSelectionFromEnd(comment.text.length - comment.selectionEnd);
- }
-
- // Make sure there is at least one newline after a line comment and allow
- // one or two after a block comment that has nothing after it.
- var linesAfter;
- if (i < comments.length - 1) {
- linesAfter = comments[i + 1].linesBefore;
- } else {
- linesAfter = linesBeforeToken;
-
- // Always force a newline after multi-line block comments. Prevents
- // mistakes like:
- //
- // /**
- // * Some doc comment.
- // */ someFunction() { ... }
- if (linesAfter == 0 && comments.last.text.contains("\n")) {
- linesAfter = 1;
- }
- }
-
- if (linesAfter > 0) _writeHardSplit(nest: true, double: linesAfter > 1);
- }
-
- // If the comment has text following it (aside from a grouping character),
- // it needs a trailing space.
- if (_needsSpaceAfterLastComment(comments, token)) {
- _pendingWhitespace = Whitespace.space;
- }
-
- preserveNewlines(linesBeforeToken);
- }
-
- /// If the current pending whitespace allows some source discretion, pins
- /// that down given that the source contains [numLines] newlines at that
- /// point.
- void preserveNewlines(int numLines) {
- // If we didn't know how many newlines the user authored between the last
- // token and this one, now we do.
- switch (_pendingWhitespace) {
- case Whitespace.spaceOrNewline:
- if (numLines > 0) {
- _pendingWhitespace = Whitespace.nestedNewline;
- } else {
- _pendingWhitespace = Whitespace.space;
- }
- break;
-
- case Whitespace.splitOrNewline:
- if (numLines > 0) {
- _pendingWhitespace = Whitespace.nestedNewline;
- } else {
- _pendingWhitespace = Whitespace.none;
- split(space: true);
- }
- break;
-
- case Whitespace.oneOrTwoNewlines:
- if (numLines > 1) {
- _pendingWhitespace = Whitespace.twoNewlines;
- } else {
- _pendingWhitespace = Whitespace.newline;
- }
- break;
- }
- }
-
- /// Creates a new indentation level [spaces] deeper than the current one.
- ///
- /// If omitted, [spaces] defaults to [Indent.block].
- void indent([int spaces]) {
- _nesting.indent(spaces);
- }
-
- /// Discards the most recent indentation level.
- void unindent() {
- _nesting.unindent();
- }
-
- /// Starts a new span with [cost].
- ///
- /// Each call to this needs a later matching call to [endSpan].
- void startSpan([int cost = Cost.normal]) {
- _openSpans.add(new OpenSpan(_currentChunkIndex, cost));
- }
-
- /// Ends the innermost span.
- void endSpan() {
- var openSpan = _openSpans.removeLast();
-
- // A span that just covers a single chunk can't be split anyway.
- var end = _currentChunkIndex;
- if (openSpan.start == end) return;
-
- // Add the span to every chunk that can split it.
- var span = new Span(openSpan.cost);
- for (var i = openSpan.start; i < end; i++) {
- var chunk = _chunks[i];
- if (!chunk.isHardSplit) chunk.spans.add(span);
- }
- }
-
- /// Starts a new [Rule].
- ///
- /// If omitted, defaults to a new [SimpleRule].
- void startRule([Rule rule]) {
- if (rule == null) rule = new SimpleRule();
-
- // See if any of the rules that contain this one care if it splits.
- _rules.forEach((outer) => outer.contain(rule));
- _rules.add(rule);
- }
-
- /// Starts a new [Rule] that comes into play *after* the next whitespace
- /// (including comments) is written.
- ///
- /// This is used for binary operators who want to start a rule before the
- /// first operand but not get forced to split if a comment appears before the
- /// entire expression.
- ///
- /// If [rule] is omitted, defaults to a new [SimpleRule].
- void startLazyRule([Rule rule]) {
- if (rule == null) rule = new SimpleRule();
-
- _lazyRules.add(rule);
- }
-
- /// Ends the innermost rule.
- void endRule() {
- _rules.removeLast();
- }
-
- /// Pre-emptively forces all of the current rules to become hard splits.
- ///
- /// This is called by [SourceVisitor] when it can determine that a rule will
- /// will always be split. Turning it (and the surrounding rules) into hard
- /// splits lets the writer break the output into smaller pieces for the line
- /// splitter, which helps performance and avoids failing on very large input.
- ///
- /// In particular, it's easy for the visitor to know that collections with a
- /// large number of items must split. Doing that early avoids crashing the
- /// splitter when it tries to recurse on huge collection literals.
- void forceRules() => _handleHardSplit();
-
- /// Begins a new expression nesting level [indent] spaces deeper than the
- /// current one if it splits.
- ///
- /// If [indent] is omitted, defaults to [Indent.expression]. If [now] is
- /// `true`, commits the nesting change immediately instead of waiting until
- /// after the next chunk of text is written.
- void nestExpression({int indent, bool now}) {
- if (now == null) now = false;
-
- _nesting.nest(indent);
- if (now) _nesting.commitNesting();
- }
-
- /// Discards the most recent level of expression nesting.
- ///
- /// Expressions that are more nested will get increased indentation when split
- /// if the previous line has a lower level of nesting.
- void unnest() {
- _nesting.unnest();
- }
-
- /// Marks the selection starting point as occurring [fromEnd] characters to
- /// the left of the end of what's currently been written.
- ///
- /// It counts backwards from the end because this is called *after* the chunk
- /// of text containing the selection has been output.
- void startSelectionFromEnd(int fromEnd) {
- assert(_chunks.isNotEmpty);
- _chunks.last.startSelectionFromEnd(fromEnd);
- }
-
- /// Marks the selection ending point as occurring [fromEnd] characters to the
- /// left of the end of what's currently been written.
- ///
- /// It counts backwards from the end because this is called *after* the chunk
- /// of text containing the selection has been output.
- void endSelectionFromEnd(int fromEnd) {
- assert(_chunks.isNotEmpty);
- _chunks.last.endSelectionFromEnd(fromEnd);
- }
-
- /// Captures the current nesting level as marking where subsequent block
- /// arguments should start.
- void startBlockArgumentNesting() {
- _blockArgumentNesting.add(_nesting.currentNesting);
- }
-
- /// Releases the last nesting level captured by [startBlockArgumentNesting].
- void endBlockArgumentNesting() {
- _blockArgumentNesting.removeLast();
- }
-
- /// Starts a new block as a child of the current chunk.
- ///
- /// Nested blocks are handled using their own independent [LineWriter].
- ChunkBuilder startBlock() {
- var builder =
- new ChunkBuilder._(this, _formatter, _source, _chunks.last.blockChunks);
-
- // A block always starts off indented one level.
- builder.indent();
-
- return builder;
- }
-
- /// Ends this [ChunkBuilder], which must have been created by [startBlock()].
- ///
- /// Forces the chunk that owns the block to split if it can tell that the
- /// block contents will always split. It does that by looking for hard splits
- /// in the block. If [ignoredSplit] is given, that rule will be ignored
- /// when determining if a block contains a hard split. If [forceSplit] is
- /// `true`, the block is considered to always split.
- ///
- /// Returns the previous writer for the surrounding block.
- ChunkBuilder endBlock(HardSplitRule ignoredSplit, {bool forceSplit}) {
- _divideChunks();
-
- // If we don't already know if the block is going to split, see if it
- // contains any hard splits or is longer than a page.
- if (!forceSplit) {
- var length = 0;
- for (var chunk in _chunks) {
- length += chunk.length + chunk.unsplitBlockLength;
- if (length > _formatter.pageWidth) {
- forceSplit = true;
- break;
- }
-
- if (chunk.isHardSplit && chunk.rule != ignoredSplit) {
- forceSplit = true;
- break;
- }
- }
- }
-
- // If there is a hard newline within the block, force the surrounding rule
- // for it so that we apply that constraint.
- if (forceSplit) _parent.forceRules();
-
- // Write the split for the block contents themselves.
- _parent._writeSplit(_parent._rules.last, _parent._blockArgumentNesting.last,
- flushLeft: _firstFlushLeft);
- return _parent;
- }
-
- /// Finishes writing and returns a [SourceCode] containing the final output
- /// and updated selection, if any.
- SourceCode end() {
- _writeHardSplit();
- _divideChunks();
-
- if (debug.traceChunkBuilder) {
- debug.log(debug.green("\nBuilt:"));
- debug.dumpChunks(0, _chunks);
- debug.log();
- }
-
- var writer = new LineWriter(_formatter, _chunks);
- var result = writer.writeLines(_formatter.indent,
- isCompilationUnit: _source.isCompilationUnit);
-
- var selectionStart;
- var selectionLength;
- if (_source.selectionStart != null) {
- selectionStart = result.selectionStart;
- var selectionEnd = result.selectionEnd;
-
- // If we haven't hit the beginning and/or end of the selection yet, they
- // must be at the very end of the code.
- if (selectionStart == null) selectionStart = writer.length;
- if (selectionEnd == null) selectionEnd = writer.length;
-
- selectionLength = selectionEnd - selectionStart;
- }
-
- return new SourceCode(result.text,
- uri: _source.uri,
- isCompilationUnit: _source.isCompilationUnit,
- selectionStart: selectionStart,
- selectionLength: selectionLength);
- }
-
- /// Writes the current pending [Whitespace] to the output, if any.
- ///
- /// This should only be called after source lines have been preserved to turn
- /// any ambiguous whitespace into a concrete choice.
- void _emitPendingWhitespace() {
- // Output any pending whitespace first now that we know it won't be
- // trailing.
- switch (_pendingWhitespace) {
- case Whitespace.space:
- _writeText(" ");
- break;
-
- case Whitespace.newline:
- _writeHardSplit();
- break;
-
- case Whitespace.nestedNewline:
- _writeHardSplit(nest: true);
- break;
-
- case Whitespace.newlineFlushLeft:
- _writeHardSplit(nest: true, flushLeft: true);
- break;
-
- case Whitespace.twoNewlines:
- _writeHardSplit(double: true);
- break;
-
- case Whitespace.spaceOrNewline:
- case Whitespace.splitOrNewline:
- case Whitespace.oneOrTwoNewlines:
- // We should have pinned these down before getting here.
- assert(false);
- break;
- }
-
- _pendingWhitespace = Whitespace.none;
- }
-
- /// Returns `true` if the last chunk is a split that should be moved after the
- /// comment that is about to be written.
- bool _shouldMoveCommentBeforeSplit(String comment) {
- // Not if there is nothing before it.
- if (_chunks.isEmpty) return false;
-
- // Multi-line comments are always pushed to the next line.
- if (comment.contains("\n")) return false;
-
- // If the text before the split is an open grouping character, we don't
- // want to adhere the comment to that.
- var text = _chunks.last.text;
- return !text.endsWith("(") && !text.endsWith("[") && !text.endsWith("{");
- }
-
- /// Returns `true` if a space should be output between the end of the current
- /// output and the subsequent comment which is about to be written.
- ///
- /// This is only called if the comment is trailing text in the unformatted
- /// source. In most cases, a space will be output to separate the comment
- /// from what precedes it. This returns false if:
- ///
- /// * This comment does begin the line in the output even if it didn't in
- /// the source.
- /// * The comment is a block comment immediately following a grouping
- /// character (`(`, `[`, or `{`). This is to allow `foo(/* comment */)`,
- /// et. al.
- bool _needsSpaceBeforeComment({bool isLineComment}) {
- // Not at the start of the file.
- if (_chunks.isEmpty) return false;
-
- // Not at the start of a line.
- if (!_chunks.last.canAddText) return false;
-
- var text = _chunks.last.text;
- if (text.endsWith("\n")) return false;
-
- // Always put a space before line comments.
- if (isLineComment) return true;
-
- // Block comments do not get a space if following a grouping character.
- return !text.endsWith("(") && !text.endsWith("[") && !text.endsWith("{");
- }
-
- /// Returns `true` if a space should be output after the last comment which
- /// was just written and the token that will be written.
- bool _needsSpaceAfterLastComment(List<SourceComment> comments, String token) {
- // Not if there are no comments.
- if (comments.isEmpty) return false;
-
- // Not at the beginning of a line.
- if (!_chunks.last.canAddText) return false;
-
- // Otherwise, it gets a space if the following token is not a delimiter or
- // the empty string, for EOF.
- return token != ")" &&
- token != "]" &&
- token != "}" &&
- token != "," &&
- token != ";" &&
- token != "";
- }
-
- /// Appends a hard split with the current indentation and nesting (the latter
- /// only if [nest] is `true`).
- ///
- /// If [double] is `true` or `false`, forces a since or double line to be
- /// output. Otherwise, it is left indeterminate.
- ///
- /// If [flushLeft] is `true`, then the split will always cause the next line
- /// to be at column zero. Otherwise, it uses the normal indentation and
- /// nesting behavior.
- void _writeHardSplit({bool nest: false, bool double, bool flushLeft}) {
- // A hard split overrides any other whitespace.
- _pendingWhitespace = null;
- _writeSplit(new HardSplitRule(), nest ? null : _nesting.blockNesting,
- flushLeft: flushLeft, isDouble: double);
- }
-
- /// Ends the current chunk (if any) with the given split information.
- ///
- /// Returns the chunk.
- Chunk _writeSplit(Rule rule, NestingLevel nesting,
- {bool flushLeft, bool isDouble, bool spaceWhenUnsplit}) {
- if (_chunks.isEmpty) {
- if (flushLeft != null) _firstFlushLeft = flushLeft;
-
- return null;
- }
-
- if (nesting == null) nesting = _nesting.nesting;
-
- var chunk = _chunks.last;
- chunk.applySplit(rule, _nesting.indentation, nesting,
- flushLeft: flushLeft,
- isDouble: isDouble,
- spaceWhenUnsplit: spaceWhenUnsplit);
-
- // Keep track of which chunks are owned by the rule.
- if (rule is! HardSplitRule) {
- _ruleChunks.putIfAbsent(rule, () => []).add(_chunks.length - 1);
- }
-
- if (chunk.isHardSplit) _handleHardSplit();
-
- return chunk;
- }
-
- /// Writes [text] to either the current chunk or a new one if the current
- /// chunk is complete.
- void _writeText(String text) {
- if (_chunks.isNotEmpty && _chunks.last.canAddText) {
- _chunks.last.appendText(text);
- } else {
- _chunks.add(new Chunk(text));
- }
- }
-
- /// Returns true if we can divide the chunks at [index] and line split the
- /// ones before and after that separately.
- bool _canDivideAt(int i) {
- var chunk = _chunks[i];
- if (!chunk.isHardSplit) return false;
- if (chunk.nesting.isNested) return false;
- if (chunk.blockChunks.isNotEmpty) return false;
-
- // Make sure we don't split the line in the middle of a rule.
- var chunks = _ruleChunks[chunk.rule];
- if (chunks != null && chunks.any((other) => other > i)) return false;
-
- return true;
- }
-
- /// Pre-processes the chunks after they are done being written by the visitor
- /// but before they are run through the line splitter.
- ///
- /// Marks ranges of chunks that can be line split independently to keep the
- /// batches we send to [LineSplitter] small.
- void _divideChunks() {
- // Harden all of the rules that we know get forced by containing hard
- // splits, along with all of the other rules they constrain.
- _hardenRules();
-
- // Now that we know where all of the divided chunk sections are, mark the
- // chunks.
- for (var i = 0; i < _chunks.length; i++) {
- _chunks[i].markDivide(_canDivideAt(i));
- }
- }
-
- /// Hardens the active rules when a hard split occurs within them.
- void _handleHardSplit() {
- if (_rules.isEmpty) return;
-
- // If the current rule doesn't care, it will "eat" the hard split and no
- // others will care either.
- if (!_rules.last.splitsOnInnerRules) return;
-
- // Start with the innermost rule. This will traverse the other rules it
- // constrains.
- _hardSplitRules.add(_rules.last);
- }
-
- /// Replaces all of the previously hardened rules with hard splits, along
- /// with every rule that those constrain to also split.
- ///
- /// This should only be called after all chunks have been written.
- void _hardenRules() {
- if (_hardSplitRules.isEmpty) return;
-
- // Harden all of the rules that are constrained by [rules] as well.
- var hardenedRules = new Set();
- walkConstraints(rule) {
- if (hardenedRules.contains(rule)) return;
- hardenedRules.add(rule);
-
- // Follow this rule's constraints, recursively.
- for (var other in _ruleChunks.keys) {
- if (other == rule) continue;
-
- if (rule.constrain(rule.fullySplitValue, other) ==
- other.fullySplitValue) {
- walkConstraints(other);
- }
- }
- }
-
- for (var rule in _hardSplitRules) {
- walkConstraints(rule);
- }
-
- // Harden every chunk that uses one of these rules.
- for (var chunk in _chunks) {
- if (hardenedRules.contains(chunk.rule)) {
- chunk.harden();
- }
- }
- }
-}
« no previous file with comments | « dart_style/lib/src/chunk.dart ('k') | dart_style/lib/src/dart_formatter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698