anchor.diff:        }
       }
   
  -    return _hardenRules(rulesToHarden, length);
  -
  -    // TODO(bob): What if the rules are still in effect? Should we replace them
  -    // in _rules too?
  -  }
  -
  -  /// Hardens every chunk in the first [length] chunks that uses one of [rules].
  -  ///
  -  /// Returns the list of lengths within [length] that can now be split as
  -  /// separate lines.
  -  List<int> _hardenRules(Set<Rule> rules, int length) {
  -    var lengths = [];
  -    for (var i = 0; i < length; i++) {
  +    _hardenRules(rulesToHarden, start, end);
  +  }
  +
  +  /// Hardens every chunk in the range of chunks from [start] to [end]
  +  /// (half-inclusive) that uses one of [rules].
  +  void _hardenRules(Set<Rule> rules, int start, int end) {
  +    if (rules.isEmpty) return;
  +
  +    for (var i = start; i < end; i++) {
         var chunk = _chunks[i];
         if (rules.contains(chunk.rule)) {
           chunk.harden();
  -        if (_canEndLineAfter(i + 1)) lengths.add(i + 1);
  -      }
  -    }
  -
  -    // Remove implied references to hardened rules. If a rule implies one
  -    // that's already hardened, we don't need to track the implication anymore.
  -    for (var chunk in _chunks) {
  -      chunk.rule.implies.removeAll(rules);
  -    }
  -
  -    return lengths;
  -  }
  -
  -  /// Handles open multisplits and spans when a hard line occurs.
  -  ///
  -  /// Any active separable multisplits will get split in two at this point.
  -  /// Other multisplits are forced into the "hard" state. All of their previous
  -  /// splits are turned into explicit hard splits and any new splits for that
  -  /// multisplit become hard splits too.
  -  ///
  -  /// All open spans get discarded since they will never be satisfied.
  +      }
  +    }
  +
  +    // Note that other rules may still imply the now-discarded rules. We could
  +    // clean those out, but it takes time to do so and it's harmless to leave
  +    // them alone. Since removing them noticeably affects perf, we just ignore
  +    // them.
  +  }
  +
  +  /// Hardens any active rules that care when a hard split occurs within them.
     void _handleHardSplit() {
  -    // Discard all open spans. We don't remove them from the stack so that we
  -    // can still correctly count later calls to endSpan().
  -    for (var i = 0; i < _openSpans.length; i++) {
  -      _openSpans[i] = null;
  -    }
  -
       // Replace each ongoing rule with a hard split if it wants to split when
       // it contains an inner split.
       var hardenedRules = new Set();
  
positive.code:   /// bounce all the way back to no nesting, this goes through and renumbers
    /// the nesting levels of all of the preceding chunks.
    void _flattenNestingLevels(int length) {
      var nestingLevels = _chunks
          .take(length)
          .map((chunk) => chunk.nesting)
          .where((nesting) => nesting != -1)
          .toSet()
  
negative.code:       }
      }
  
      _hardenRules(hardenedRules, 0, _chunks.length);
    }
  }
  