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

Unified Diff: lib/src/chunk_builder.dart

Issue 1255643002: New, simpler and faster line splitter. (Closed) Base URL: https://github.com/dart-lang/dart_style.git@master
Patch Set: Optimize nesting. Reformat. Created 5 years, 5 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 | « lib/src/chunk.dart ('k') | lib/src/debug.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
« no previous file with comments | « lib/src/chunk.dart ('k') | lib/src/debug.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698