Index: lib/src/chunk_builder.dart |
diff --git a/lib/src/chunk_builder.dart b/lib/src/chunk_builder.dart |
index bfe9b0c181383c3119d78e2c74e57669f7b83fd2..f23be5bb66429a42a3cdaad4a2ff3940bcba4206 100644 |
--- a/lib/src/chunk_builder.dart |
+++ b/lib/src/chunk_builder.dart |
@@ -7,9 +7,8 @@ library dart_style.src.chunk_builder; |
import 'chunk.dart'; |
import 'dart_formatter.dart'; |
import 'debug.dart' as debug; |
-import 'line_splitter.dart'; |
import 'line_writer.dart'; |
-import 'nesting.dart'; |
+import 'nesting_level.dart'; |
import 'nesting_builder.dart'; |
import 'rule/rule.dart'; |
import 'source_code.dart'; |
@@ -367,17 +366,11 @@ class ChunkBuilder { |
/// |
/// If omitted, defaults to a new [SimpleRule]. |
void startRule([Rule rule]) { |
- //assert(_pendingRule == null); |
- |
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); |
- |
- // Keep track of the rule's chunk range so we know how to calculate its |
- // length for preemption. |
- rule.start = _currentChunkIndex; |
} |
/// Starts a new [Rule] that comes into play *after* the next whitespace |
@@ -396,9 +389,7 @@ class ChunkBuilder { |
/// Ends the innermost rule. |
void endRule() { |
- // Keep track of the rule's chunk range so we know how to calculate its |
- // length for preemption. |
- _rules.removeLast().end = _currentChunkIndex; |
+ _rules.removeLast(); |
} |
/// Pre-emptively forces all of the current rules to become hard splits. |
@@ -723,7 +714,7 @@ class ChunkBuilder { |
bool _canDivideAt(int i) { |
var chunk = _chunks[i]; |
if (!chunk.isHardSplit) return false; |
- if (chunk.nesting.depth != 0) 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. |
@@ -741,27 +732,7 @@ class ChunkBuilder { |
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(_hardSplitRules); |
- |
- // For each independent set of chunks, see if there are any rules in them |
- // that we want to preemptively harden. This is basically to send smaller |
- // batches of chunks to LineSplitter in cases where the code is deeply |
- // nested or complex. |
- var preemptedRules = new Set(); |
- |
- var start = 0; |
- for (var i = 0; i < _chunks.length; i++) { |
- if (_canDivideAt(i)) { |
- _preemptRules(start, i, preemptedRules); |
- start = i; |
- } |
- } |
- |
- if (start < _chunks.length) { |
- _preemptRules(start, _chunks.length, preemptedRules); |
- } |
- |
- _hardenRules(preemptedRules); |
+ _hardenRules(); |
// Now that we know where all of the divided chunk sections are, mark the |
// chunks. |
@@ -770,96 +741,6 @@ class ChunkBuilder { |
} |
} |
- /// Removes any unused nesting levels from [chunks]. |
- /// |
- /// The line splitter considers every possible combination of mapping |
- /// indentation to nesting levels when trying to find the best solution. For |
- /// example, it may assign 4 spaces of indentation to level 1, 8 spaces to |
- /// level 3, etc. |
- /// |
- /// It's fairly common for a nesting level to not actually appear at the |
- /// boundary of a chunk. The source visitor may enter more than one level of |
- /// nesting at a point where a split cannot happen. In that case, there's no |
- /// point in trying to assign an indentation level to that nesting level. It |
- /// will never be used because no line will begin at that level of |
- /// indentation. |
- /// |
- /// Worse, if the splitter *does* consider these levels, it can dramatically |
- /// increase solving time. We can't determine which nesting levels will get |
- /// used eagerly since a level may not be used until later. Instead, when |
- /// we're building all of the chunks, this goes through and renumbers the |
- /// nesting levels. |
- void _flattenNestingLevels(List<Chunk> chunks) { |
- if (chunks.isEmpty) return; |
- |
- // Find all of the nesting levels that actually appear at the end of chunks. |
- var usedNestings = new Set(); |
- for (var chunk in chunks) { |
- if (chunk.nesting == null) continue; |
- usedNestings.add(chunk.nesting); |
- } |
- |
- // Now get rid of any others that didn't occur where a split happened. |
- for (var nesting in usedNestings) { |
- nesting.removeUnused(usedNestings); |
- } |
- } |
- |
- /// Adds some rules used by chunks within [start] and [end] to |
- /// [preemptedRules] to force them become hard splits if it looks like |
- /// there's no other option to get a solution in reasonable time. |
- /// |
- /// In most cases, the formatter can find an ideal solution to a set of rules |
- /// in reasonable time. Splitting chunks into short lists, nested blocks, and |
- /// the memoization and block caching that [LineSplitter] does all help. |
- /// |
- /// However, some code isn't helped by any of that. In particular, a very |
- /// large, deeply nested expression that contains no collection or function |
- /// literals has to be solved all at once by the line splitter. Memoization |
- /// helps, but some expressions just have too many permutations. |
- /// |
- /// In practice, this almost always occurs in machine-generated code where |
- /// the output quality doesn't matter as much. To avoid getting stuck there, |
- /// this finds rules that surround more than a page of code and forces them |
- /// to fully split. It only does this if it thinks it won't find a reasonable |
- /// solution otherwise. |
- /// |
- /// This may discard a more optimal solution in some cases. When a rule is |
- /// hardened, it is *fully* hardened. There may have been a solution where |
- /// only some of a rule's chunks needed to be split (for example, not fully |
- /// breaking an argument list). This won't consider those kinds of solution |
- /// To avoid this, pre-emption only kicks in for lines that look like they |
- /// will be hard to solve directly. |
- void _preemptRules(int start, int end, Set<Rule> preemptedRules) { |
- var chunks = _chunks.sublist(start, end); |
- _flattenNestingLevels(chunks); |
- |
- var rules = chunks |
- .map((chunk) => chunk.rule) |
- .where((rule) => rule != null && rule is! HardSplitRule) |
- .toSet(); |
- |
- var values = rules.fold(1, (value, rule) => value * rule.numValues); |
- |
- // TODO(rnystrom): We could do something more precise here by taking the |
- // rule types into account. |
- |
- // If the number of possible solutions is reasonable, don't preempt any. |
- if (values <= 32768) return; |
- |
- // Find the rules that contain too much. |
- for (var rule in rules) { |
- var length = 0; |
- for (var i = rule.start + 1; i <= rule.end; i++) { |
- length += _chunks[i].length; |
- if (length > pageWidth) { |
- preemptedRules.add(rule); |
- break; |
- } |
- } |
- } |
- } |
- |
/// Hardens the active rules when a hard split occurs within them. |
void _handleHardSplit() { |
if (_rules.isEmpty) return; |
@@ -873,12 +754,12 @@ class ChunkBuilder { |
_hardSplitRules.add(_rules.last); |
} |
- /// Replaces [rules] with hard splits, along with every rule that those |
- /// constrain to also split. |
+ /// 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(Set<Rule> rules) { |
- if (rules.isEmpty) return; |
+ void _hardenRules() { |
+ if (_hardSplitRules.isEmpty) return; |
// Harden all of the rules that are constrained by [rules] as well. |
var hardenedRules = new Set(); |
@@ -897,7 +778,7 @@ class ChunkBuilder { |
} |
} |
- for (var rule in rules) { |
+ for (var rule in _hardSplitRules) { |
walkConstraints(rule); |
} |