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

Side by Side Diff: packages/dart_style/lib/src/line_splitting/solve_state.dart

Issue 1521693002: Roll Observatory deps (charted -> ^0.3.0) (Closed) Base URL: https://chromium.googlesource.com/external/github.com/dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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.line_splitting.solve_state; 5 library dart_style.src.line_splitting.solve_state;
6 6
7 import '../debug.dart' as debug; 7 import '../debug.dart' as debug;
8 import '../rule/rule.dart'; 8 import '../rule/rule.dart';
9 import '../whitespace.dart';
9 import 'line_splitter.dart'; 10 import 'line_splitter.dart';
10 import 'rule_set.dart'; 11 import 'rule_set.dart';
11 12
12 /// A possibly incomplete solution in the line splitting search space. 13 /// A possibly incomplete solution in the line splitting search space.
13 /// 14 ///
14 /// A single [SolveState] binds some subset of the rules to values while 15 /// A single [SolveState] binds some subset of the rules to values while
15 /// leaving the rest unbound. If every rule is bound, the solve state describes 16 /// leaving the rest unbound. If every rule is bound, the solve state describes
16 /// a complete solution to the line splitting problem. Even if rules are 17 /// a complete solution to the line splitting problem. Even if rules are
17 /// unbound, a state can also usually be used as a solution by treating all 18 /// unbound, a state can also usually be used as a solution by treating all
18 /// unbound rules as unsplit. (The usually is because a state that constrains 19 /// unbound rules as unsplit. (The usually is because a state that constrains
19 /// an unbound rule to split can't be used with that rule unsplit.) 20 /// an unbound rule to split can't be used with that rule unsplit.)
20 /// 21 ///
21 /// From a given solve state, we can explore the search tree to more refined 22 /// From a given solve state, we can explore the search tree to more refined
22 /// solve states by producing new ones that add more bound rules to the current 23 /// solve states by producing new ones that add more bound rules to the current
23 /// state. 24 /// state.
24 class SolveState { 25 class SolveState {
25 final LineSplitter _splitter; 26 final LineSplitter _splitter;
26 final RuleSet _ruleValues; 27 final RuleSet _ruleValues;
27 28
29 /// The set of [Rule]s that are bound by [_ruleValues].
30 ///
31 /// Cached by [_ensureConstraints] for use by [_ensureUnboundConstraints].
32 Set<Rule> _boundRules;
33
34 /// The set of [Rule]s that are not bound by [_ruleValues].
35 ///
36 /// Cached by [_ensureConstraints] for use by [_ensureUnboundConstraints].
37 Set<Rule> _unboundRules;
38
28 /// The unbound rules in this state that can be bound to produce new more 39 /// The unbound rules in this state that can be bound to produce new more
29 /// refined states. 40 /// refined states.
30 /// 41 ///
31 /// Keeping this set small is the key to make the entire line splitter 42 /// Keeping this set small is the key to make the entire line splitter
32 /// perform well. If we consider too make rules at each state, our 43 /// perform well. If we consider too many rules at each state, our
33 /// exploration of the solution space is too branchy and we waste time on 44 /// exploration of the solution space is too branchy and we waste time on
34 /// dead end solutions. 45 /// dead end solutions.
35 /// 46 ///
36 /// Here is the key insight. The line splitter treats any unbound rule as 47 /// Here is the key insight. The line splitter treats any unbound rule as
37 /// being unsplit. This means refining a solution always means taking a rule 48 /// being unsplit. This means refining a solution always means taking a rule
38 /// that is unsplit and making it split. That monotonically increases the 49 /// that is unsplit and making it split. That monotonically increases the
39 /// cost, but may help fit the solution inside the page. 50 /// cost, but may help fit the solution inside the page.
40 /// 51 ///
41 /// We want to keep the cost low, so the only reason to consider making a 52 /// We want to keep the cost low, so the only reason to consider making a
42 /// rule split is if it reduces an overflowing line. It's also the case that 53 /// rule split is if it reduces an overflowing line. It's also the case that
(...skipping 25 matching lines...) Expand all
68 /// This is generally true but will be false if the state contains any 79 /// This is generally true but will be false if the state contains any
69 /// unbound rules that are constrained to not be zero by other bound rules. 80 /// unbound rules that are constrained to not be zero by other bound rules.
70 /// This avoids picking a solution that leaves those rules at zero when they 81 /// This avoids picking a solution that leaves those rules at zero when they
71 /// aren't allowed to be. 82 /// aren't allowed to be.
72 bool _isComplete = true; 83 bool _isComplete = true;
73 84
74 /// The constraints the bound rules in this state have on the remaining 85 /// The constraints the bound rules in this state have on the remaining
75 /// unbound rules. 86 /// unbound rules.
76 Map<Rule, int> _constraints; 87 Map<Rule, int> _constraints;
77 88
89 /// The unbound rule values that are disallowed because they would place
90 /// invalid constraints on the currently bound values.
91 ///
92 /// For example, say rule A is bound to 1 and B is unbound. If B has value
93 /// 1, it constrains A to be 1. Likewise, if B is 2, it constrains A to be
94 /// 2 as well. Since we already know A is 1, that means we know B cannot be
95 /// bound to value 2. This means B is more limited in this state than it
96 /// might be in another state that binds A to a different value.
97 ///
98 /// It's important to track this, because we can't allow to states to overlap
99 /// if one permits more values for some unbound rule than the other does.
100 Map<Rule, Set<int>> _unboundConstraints;
101
78 /// The bound rules that appear inside lines also containing unbound rules. 102 /// The bound rules that appear inside lines also containing unbound rules.
79 /// 103 ///
80 /// By appearing in the same line, it means these bound rules may affect the 104 /// By appearing in the same line, it means these bound rules may affect the
81 /// results of binding those unbound rules. This is used to tell if two 105 /// results of binding those unbound rules. This is used to tell if two
82 /// states may diverge by binding unbound rules or not. 106 /// states may diverge by binding unbound rules or not.
83 Set<Rule> _boundRulesInUnboundLines; 107 Set<Rule> _boundRulesInUnboundLines;
84 108
85 SolveState(this._splitter, this._ruleValues) { 109 SolveState(this._splitter, this._ruleValues) {
86 _calculateSplits(); 110 _calculateSplits();
87 _calculateCost(); 111 _calculateCost();
88 } 112 }
89 113
90 /// Gets the value to use for [rule], either the bound value or `0` if it 114 /// Gets the value to use for [rule], either the bound value or
91 /// isn't bound. 115 /// [Rule.unsplit] if it isn't bound.
92 int getValue(Rule rule) { 116 int getValue(Rule rule) => _ruleValues.getValue(rule);
93 if (rule is HardSplitRule) return 0;
94
95 return _ruleValues.getValue(rule);
96 }
97 117
98 /// Returns `true` if this state is a better solution to use as the final 118 /// Returns `true` if this state is a better solution to use as the final
99 /// result than [other]. 119 /// result than [other].
100 bool isBetterThan(SolveState other) { 120 bool isBetterThan(SolveState other) {
101 // If this state contains an unbound rule that we know can't be left 121 // If this state contains an unbound rule that we know can't be left
102 // unsplit, we can't pick this as a solution. 122 // unsplit, we can't pick this as a solution.
103 if (!_isComplete) return false; 123 if (!_isComplete) return false;
104 124
105 // Anything is better than nothing. 125 // Anything is better than nothing.
106 if (other == null) return true; 126 if (other == null) return true;
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 // have, that rule would already be bound to some other value.) 225 // have, that rule would already be bound to some other value.)
206 if (!unsplitRules.tryBind(_splitter.rules, rule, 0, (_) {})) { 226 if (!unsplitRules.tryBind(_splitter.rules, rule, 0, (_) {})) {
207 break; 227 break;
208 } 228 }
209 } 229 }
210 } 230 }
211 } 231 }
212 232
213 /// Returns `true` if [other] overlaps this state. 233 /// Returns `true` if [other] overlaps this state.
214 bool _isOverlapping(SolveState other) { 234 bool _isOverlapping(SolveState other) {
215 _ensureOverlapFields();
216 other._ensureOverlapFields();
217
218 // Lines that contain both bound and unbound rules must have the same 235 // Lines that contain both bound and unbound rules must have the same
219 // bound values. 236 // bound values.
237 _ensureBoundRulesInUnboundLines();
238 other._ensureBoundRulesInUnboundLines();
220 if (_boundRulesInUnboundLines.length != 239 if (_boundRulesInUnboundLines.length !=
221 other._boundRulesInUnboundLines.length) { 240 other._boundRulesInUnboundLines.length) {
222 return false; 241 return false;
223 } 242 }
224 243
225 for (var rule in _boundRulesInUnboundLines) { 244 for (var rule in _boundRulesInUnboundLines) {
226 if (!other._boundRulesInUnboundLines.contains(rule)) return false; 245 if (!other._boundRulesInUnboundLines.contains(rule)) return false;
227 if (_ruleValues.getValue(rule) != other._ruleValues.getValue(rule)) { 246 if (_ruleValues.getValue(rule) != other._ruleValues.getValue(rule)) {
228 return false; 247 return false;
229 } 248 }
230 } 249 }
231 250
251 _ensureConstraints();
252 other._ensureConstraints();
232 if (_constraints.length != other._constraints.length) return false; 253 if (_constraints.length != other._constraints.length) return false;
233 254
234 for (var rule in _constraints.keys) { 255 for (var rule in _constraints.keys) {
235 if (_constraints[rule] != other._constraints[rule]) return false; 256 if (_constraints[rule] != other._constraints[rule]) return false;
236 } 257 }
237 258
259 _ensureUnboundConstraints();
260 other._ensureUnboundConstraints();
261 if (_unboundConstraints.length != other._unboundConstraints.length) {
262 return false;
263 }
264
265 for (var rule in _unboundConstraints.keys) {
266 var disallowed = _unboundConstraints[rule];
267 var otherDisallowed = other._unboundConstraints[rule];
268
269 if (disallowed.length != otherDisallowed.length) return false;
270 for (var value in disallowed) {
271 if (!otherDisallowed.contains(value)) return false;
272 }
273 }
274
238 return true; 275 return true;
239 } 276 }
240 277
241 /// Calculates the [SplitSet] for this solve state, assuming any unbound 278 /// Calculates the [SplitSet] for this solve state, assuming any unbound
242 /// rules are set to zero. 279 /// rules are set to zero.
243 void _calculateSplits() { 280 void _calculateSplits() {
244 // Figure out which expression nesting levels got split and need to be 281 // Figure out which expression nesting levels got split and need to be
245 // assigned columns. 282 // assigned columns.
246 var usedNestingLevels = new Set(); 283 var usedNestingLevels = new Set();
247 for (var i = 0; i < _splitter.chunks.length - 1; i++) { 284 for (var i = 0; i < _splitter.chunks.length - 1; i++) {
(...skipping 12 matching lines...) Expand all
260 for (var i = 0; i < _splitter.chunks.length - 1; i++) { 297 for (var i = 0; i < _splitter.chunks.length - 1; i++) {
261 var chunk = _splitter.chunks[i]; 298 var chunk = _splitter.chunks[i];
262 if (chunk.rule.isSplit(getValue(chunk.rule), chunk)) { 299 if (chunk.rule.isSplit(getValue(chunk.rule), chunk)) {
263 var indent = 0; 300 var indent = 0;
264 if (!chunk.flushLeftAfter) { 301 if (!chunk.flushLeftAfter) {
265 // Add in the chunk's indent. 302 // Add in the chunk's indent.
266 indent = _splitter.blockIndentation + chunk.indent; 303 indent = _splitter.blockIndentation + chunk.indent;
267 304
268 // And any expression nesting. 305 // And any expression nesting.
269 indent += chunk.nesting.totalUsedIndent; 306 indent += chunk.nesting.totalUsedIndent;
307
308 if (chunk.indentBlock(getValue)) indent += Indent.expression;
270 } 309 }
271 310
272 _splits.add(i, indent); 311 _splits.add(i, indent);
273 } 312 }
274 } 313 }
275 } 314 }
276 315
277 /// Evaluates the cost (i.e. the relative "badness") of splitting the line 316 /// Evaluates the cost (i.e. the relative "badness") of splitting the line
278 /// into [lines] physical lines based on the current set of rules. 317 /// into [lines] physical lines based on the current set of rules.
279 void _calculateCost() { 318 void _calculateCost() {
280 assert(_splits != null); 319 assert(_splits != null);
281 320
282 // Calculate the length of each line and apply the cost of any spans that 321 // Calculate the length of each line and apply the cost of any spans that
283 // get split. 322 // get split.
284 var cost = 0; 323 var cost = 0;
285 _overflowChars = 0; 324 _overflowChars = 0;
286 325
287 var length = _splitter.firstLineIndent; 326 var length = _splitter.firstLineIndent;
288 327
289 // The unbound rules in use by the current line. This will be null after 328 // The unbound rules in use by the current line. This will be null after
290 // the first long line has completed. 329 // the first long line has completed.
291 var currentLineRules = []; 330 var foundOverflowRules = false;
331 var start = 0;
292 332
293 endLine(int end) { 333 endLine(int end) {
294 // Track lines that went over the length. It is only rules contained in 334 // Track lines that went over the length. It is only rules contained in
295 // long lines that we may want to split. 335 // long lines that we may want to split.
296 if (length > _splitter.writer.pageWidth) { 336 if (length > _splitter.writer.pageWidth) {
297 _overflowChars += length - _splitter.writer.pageWidth; 337 _overflowChars += length - _splitter.writer.pageWidth;
298 338
299 // Only try rules that are in the first long line, since we know at 339 // Only try rules that are in the first long line, since we know at
300 // least one of them *will* be split. 340 // least one of them *will* be split.
301 if (currentLineRules != null && currentLineRules.isNotEmpty) { 341 if (!foundOverflowRules) {
302 _liveRules.addAll(currentLineRules); 342 for (var i = start; i < end; i++) {
303 currentLineRules = null; 343 if (_addLiveRules(_splitter.chunks[i].rule)) {
304 } 344 foundOverflowRules = true;
305 } else { 345 }
306 // The line fit, so don't keep track of its rules. 346 }
307 if (currentLineRules != null) {
308 currentLineRules.clear();
309 } 347 }
310 } 348 }
349
350 start = end;
311 } 351 }
312 352
313 // The set of spans that contain chunks that ended up splitting. We store 353 // The set of spans that contain chunks that ended up splitting. We store
314 // these in a set so a span's cost doesn't get double-counted if more than 354 // these in a set so a span's cost doesn't get double-counted if more than
315 // one split occurs in it. 355 // one split occurs in it.
316 var splitSpans = new Set(); 356 var splitSpans = new Set();
317 357
358 // The nesting level of the chunk that ended the previous line.
359 var previousNesting;
360
318 for (var i = 0; i < _splitter.chunks.length; i++) { 361 for (var i = 0; i < _splitter.chunks.length; i++) {
319 var chunk = _splitter.chunks[i]; 362 var chunk = _splitter.chunks[i];
320 363
321 length += chunk.text.length; 364 length += chunk.text.length;
322 365
323 // Ignore the split after the last chunk. 366 // Ignore the split after the last chunk.
324 if (i == _splitter.chunks.length - 1) break; 367 if (i == _splitter.chunks.length - 1) break;
325 368
326 if (_splits.shouldSplitAt(i)) { 369 if (_splits.shouldSplitAt(i)) {
327 endLine(i); 370 endLine(i);
328 371
329 splitSpans.addAll(chunk.spans); 372 splitSpans.addAll(chunk.spans);
330 373
331 // Include the cost of the nested block. 374 // Include the cost of the nested block.
332 if (chunk.blockChunks.isNotEmpty) { 375 if (chunk.isBlock) {
333 cost += 376 cost +=
334 _splitter.writer.formatBlock(chunk, _splits.getColumn(i)).cost; 377 _splitter.writer.formatBlock(chunk, _splits.getColumn(i)).cost;
335 } 378 }
336 379
380 // Do not allow sequential lines to have the same indentation but for
381 // different reasons. In other words, don't allow different expressions
382 // to claim the same nesting level on subsequent lines.
383 //
384 // A contrived example would be:
385 //
386 // function(inner(
387 // argument), second(
388 // another);
389 //
390 // For the most part, we prevent this by the constraints on splits.
391 // For example, the above can't happen because the split before
392 // "argument", forces the split before "second".
393 //
394 // But there are a couple of squirrely cases where it's hard to prevent
395 // by construction. Instead, this outlaws it by penalizing it very
396 // heavily if it happens to get this far.
397 if (previousNesting != null &&
398 chunk.nesting.totalUsedIndent != 0 &&
399 chunk.nesting.totalUsedIndent == previousNesting.totalUsedIndent &&
400 !identical(chunk.nesting, previousNesting)) {
401 _overflowChars += 10000;
402 }
403
404 previousNesting = chunk.nesting;
405
337 // Start the new line. 406 // Start the new line.
338 length = _splits.getColumn(i); 407 length = _splits.getColumn(i);
339 } else { 408 } else {
340 if (chunk.spaceWhenUnsplit) length++; 409 if (chunk.spaceWhenUnsplit) length++;
341 410
342 // Include the nested block inline, if any. 411 // Include the nested block inline, if any.
343 length += chunk.unsplitBlockLength; 412 length += chunk.unsplitBlockLength;
344
345 // If we might be in the first overly long line, keep track of any
346 // unbound rules we encounter. These are ones that we'll want to try to
347 // bind to shorten the long line.
348 if (currentLineRules != null &&
349 chunk.rule != null &&
350 !chunk.isHardSplit &&
351 !_ruleValues.contains(chunk.rule)) {
352 currentLineRules.add(chunk.rule);
353 }
354 } 413 }
355 } 414 }
356 415
357 // Add the costs for the rules that split. 416 // Add the costs for the rules that have any splits.
358 _ruleValues.forEach(_splitter.rules, (rule, value) { 417 _ruleValues.forEach(_splitter.rules, (rule, value) {
359 // A rule may be bound to zero if another rule constrains it to not split. 418 if (value != Rule.unsplit) cost += rule.cost;
360 if (value != 0) cost += rule.cost;
361 }); 419 });
362 420
363 // Add the costs for the spans containing splits. 421 // Add the costs for the spans containing splits.
364 for (var span in splitSpans) cost += span.cost; 422 for (var span in splitSpans) cost += span.cost;
365 423
366 // Finish the last line. 424 // Finish the last line.
367 endLine(_splitter.chunks.length); 425 endLine(_splitter.chunks.length);
368 426
369 _splits.setCost(cost); 427 _splits.setCost(cost);
370 } 428 }
371 429
372 /// Lazily initializes the fields needed to compare two states for overlap. 430 /// Adds [rule] and all of the rules it constrains to the set of [_liveRules].
431 ///
432 /// Only does this if [rule] is a valid soft rule. Returns `true` if any new
433 /// live rules were added.
434 bool _addLiveRules(Rule rule) {
435 if (rule == null) return false;
436
437 var added = false;
438 for (var constrained in rule.allConstrainedRules) {
439 if (_ruleValues.contains(constrained)) continue;
440
441 _liveRules.add(constrained);
442 added = true;
443 }
444
445 return added;
446 }
447
448 /// Lazily initializes the [_boundInUnboundLines], which is needed to compare
449 /// two states for overlap.
373 /// 450 ///
374 /// We do this lazily because the calculation is a bit slow, and is only 451 /// We do this lazily because the calculation is a bit slow, and is only
375 /// needed when we have two states with the same score. 452 /// needed when we have two states with the same score.
376 void _ensureOverlapFields() { 453 void _ensureBoundRulesInUnboundLines() {
377 if (_constraints != null) return; 454 if (_boundRulesInUnboundLines != null) return;
378 455
379 _calculateConstraints();
380 _calculateBoundRulesInUnboundLines();
381 }
382
383 /// Initializes [_constraints] with any constraints the bound rules place on
384 /// the unbound rules.
385 void _calculateConstraints() {
386 _constraints = {};
387
388 var unboundRules = [];
389 var boundRules = [];
390
391 for (var rule in _splitter.rules) {
392 if (_ruleValues.contains(rule)) {
393 boundRules.add(rule);
394 } else {
395 unboundRules.add(rule);
396 }
397 }
398
399 for (var bound in boundRules) {
400 var value = _ruleValues.getValue(bound);
401
402 for (var unbound in unboundRules) {
403 var constraint = bound.constrain(value, unbound);
404 if (constraint != null) {
405 _constraints[unbound] = constraint;
406 }
407 }
408 }
409 }
410
411 void _calculateBoundRulesInUnboundLines() {
412 _boundRulesInUnboundLines = new Set(); 456 _boundRulesInUnboundLines = new Set();
413 457
414 var boundInLine = new Set(); 458 var boundInLine = new Set();
415 var hasUnbound = false; 459 var hasUnbound = false;
416 460
417 for (var i = 0; i < _splitter.chunks.length - 1; i++) { 461 for (var i = 0; i < _splitter.chunks.length - 1; i++) {
418 if (splits.shouldSplitAt(i)) { 462 if (splits.shouldSplitAt(i)) {
419 if (hasUnbound) _boundRulesInUnboundLines.addAll(boundInLine); 463 if (hasUnbound) _boundRulesInUnboundLines.addAll(boundInLine);
420 464
421 boundInLine.clear(); 465 boundInLine.clear();
422 hasUnbound = false; 466 hasUnbound = false;
423 } 467 }
424 468
425 var rule = _splitter.chunks[i].rule; 469 var rule = _splitter.chunks[i].rule;
426 if (rule != null && rule is! HardSplitRule) { 470 if (rule != null) {
427 if (_ruleValues.contains(rule)) { 471 if (_ruleValues.contains(rule)) {
428 boundInLine.add(rule); 472 boundInLine.add(rule);
429 } else { 473 } else {
430 hasUnbound = true; 474 hasUnbound = true;
431 } 475 }
432 } 476 }
433 } 477 }
434 478
435 if (hasUnbound) _boundRulesInUnboundLines.addAll(boundInLine); 479 if (hasUnbound) _boundRulesInUnboundLines.addAll(boundInLine);
436 } 480 }
437 481
482 /// Lazily initializes the [_constraints], which is needed to compare two
483 /// states for overlap.
484 ///
485 /// We do this lazily because the calculation is a bit slow, and is only
486 /// needed when we have two states with the same score.
487 void _ensureConstraints() {
488 if (_constraints != null) return;
489
490 _unboundRules = new Set();
491 _boundRules = new Set();
492
493 for (var rule in _splitter.rules) {
494 if (_ruleValues.contains(rule)) {
495 _boundRules.add(rule);
496 } else {
497 _unboundRules.add(rule);
498 }
499 }
500
501 _constraints = {};
502
503 for (var bound in _boundRules) {
504 for (var unbound in bound.constrainedRules) {
505 if (!_unboundRules.contains(unbound)) continue;
506
507 var value = _ruleValues.getValue(bound);
508 var constraint = bound.constrain(value, unbound);
509 if (constraint != null) {
510 _constraints[unbound] = constraint;
511 }
512 }
513 }
514 }
515
516 /// Lazily initializes the [_unboundConstraints], which is needed to compare
517 /// two states for overlap.
518 ///
519 /// We do this lazily because the calculation is a bit slow, and is only
520 /// needed when we have two states with the same score.
521 void _ensureUnboundConstraints() {
522 if (_unboundConstraints != null) return;
523
524 // _ensureConstraints should be called first which initializes these.
525 assert(_boundRules != null);
526 assert(_unboundRules != null);
527
528 _unboundConstraints = {};
529
530 for (var unbound in _unboundRules) {
531 var disallowedValues;
532
533 for (var bound in unbound.constrainedRules) {
534 if (!_boundRules.contains(bound)) continue;
535
536 var boundValue = _ruleValues.getValue(bound);
537
538 for (var value = 0; value < unbound.numValues; value++) {
539 var constraint = unbound.constrain(value, bound);
540
541 // If the unbound rule doesn't place any constraint on this bound
542 // rule, we're fine.
543 if (constraint == null) continue;
544
545 // If the bound rule's value already meets the constraint it applies,
546 // we don't need to track it. This way, two states that have the
547 // same bound value, one of which has a satisfied constraint, are
548 // still allowed to overlap.
549 if (constraint == boundValue) continue;
550 if (constraint == Rule.mustSplit && boundValue != Rule.unsplit) {
551 continue;
552 }
553
554 if (disallowedValues == null) {
555 disallowedValues = new Set();
556 _unboundConstraints[unbound] = disallowedValues;
557 }
558
559 disallowedValues.add(value);
560 }
561 }
562 }
563 }
564
438 String toString() { 565 String toString() {
439 var buffer = new StringBuffer(); 566 var buffer = new StringBuffer();
440 567
441 buffer.writeAll(_splitter.rules.map((rule) { 568 buffer.writeAll(_splitter.rules.map((rule) {
442 var valueLength = "${rule.fullySplitValue}".length; 569 var valueLength = "${rule.fullySplitValue}".length;
443 570
444 var value = "?"; 571 var value = "?";
445 if (_ruleValues.contains(rule)) { 572 if (_ruleValues.contains(rule)) {
446 value = "${_ruleValues.getValue(rule)}"; 573 value = "${_ruleValues.getValue(rule)}";
447 } 574 }
(...skipping 10 matching lines...) Expand all
458 585
459 buffer.write(" \$${splits.cost}"); 586 buffer.write(" \$${splits.cost}");
460 587
461 if (overflowChars > 0) buffer.write(" (${overflowChars} over)"); 588 if (overflowChars > 0) buffer.write(" (${overflowChars} over)");
462 if (!_isComplete) buffer.write(" (incomplete)"); 589 if (!_isComplete) buffer.write(" (incomplete)");
463 if (splits == null) buffer.write(" invalid"); 590 if (splits == null) buffer.write(" invalid");
464 591
465 return buffer.toString(); 592 return buffer.toString();
466 } 593 }
467 } 594 }
OLDNEW
« no previous file with comments | « packages/dart_style/lib/src/line_splitting/rule_set.dart ('k') | packages/dart_style/lib/src/line_writer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698