removeAttribution method

void removeAttribution({
  1. required Attribution attributionToRemove,
  2. required int start,
  3. required int end,
})

Removes attributionToRemove between start and end, inclusive.

Implementation

void removeAttribution({
  required Attribution attributionToRemove,
  required int start,
  required int end,
}) {
  _log.info('Removing attribution $attributionToRemove from $start to $end');
  if (start < 0 || start > end) {
    throw Exception('removeAttribution() did not satisfy start < 0 and start > end, start: $start, end: $end');
  }

  if (!hasAttributionsWithin(attributions: {attributionToRemove}, start: start, end: end)) {
    _log.fine('No such attribution exists in the given span range');
    return;
  }

  // It's possible that a span we want to remove was started before the
  // removal region and/or ended after the removal region. Therefore,
  // the first thing we do is cut off those outer spans one unit before
  // and/or after the removal region.
  //
  // Example:
  //    Starting spans + removal region:
  //    ---[xxxxx]---[yyyyyy]----
  //          |-remove-|
  //
  //    Spans after end cap adjustment:
  //    ---[xx]|xxx]---[yy|[yyy]----
  //
  //    Notice that the above marker structure is illegal.
  //    That's OK because the illegal configuration is only
  //    temporary. By the end of this method it will look
  //    like the following:
  //
  //    Spans after all inner markers are removed:
  //    ---[xx]--------[yyy]----
  final endCapMarkersToInsert = <SpanMarker>{};

  // Determine if we need to insert a new end-cap before
  // the removal region.
  if (hasAttributionAt(start - 1, attribution: attributionToRemove)) {
    final markersAtStart = _getMarkerAt(attributionToRemove, start - 1, SpanMarkerType.end);
    if (markersAtStart.isEmpty) {
      _log.finer('Creating a new "end" marker to appear before the removal range at ${start - 1}');
      endCapMarkersToInsert.add(SpanMarker(
        attribution: attributionToRemove,
        offset: start - 1,
        markerType: SpanMarkerType.end,
      ));
    }
  }

  // Determine if we need to insert a new end-cap after the
  // removal region.
  if (hasAttributionAt(end + 1, attribution: attributionToRemove)) {
    final markersAtEnd = _getMarkerAt(attributionToRemove, end + 1, SpanMarkerType.start);
    if (markersAtEnd.isEmpty) {
      _log.finer('Creating a new "start" marker to appear after the removal range at ${end + 1}');
      endCapMarkersToInsert.add(SpanMarker(
        attribution: attributionToRemove,
        offset: end + 1,
        markerType: SpanMarkerType.start,
      ));
    }
  }

  // Insert new span end-caps immediately before and after
  // the removal region, if needed.
  for (final endCapMarker in endCapMarkersToInsert) {
    _log.finer('Inserting new cap marker: $endCapMarker');
    _insertMarker(endCapMarker);
  }

  // Now that the end caps have been handled, remove all
  // relevant attribution markers between [start, end].
  final markersToDelete = _markers
      .where((attribution) => attribution.attribution == attributionToRemove)
      .where((attribution) => attribution.offset >= start)
      .where((attribution) => attribution.offset <= end)
      .toList();
  _log.finer('removing ${markersToDelete.length} markers between $start and $end');
  _markers.removeWhere((element) => markersToDelete.contains(element));

  _log.finer('all attributions after:');
  _markers.where((element) => element.attribution == attributionToRemove).forEach((element) {
    _log.finer(' - $element');
  });
}