copyAttributionRegion method
Returns of a copy of this AttributedSpans between startOffset
and endOffset
.
If no endOffset
is provided, a copy is made from startOffset
to the offset
of the last marker in this AttributedSpans.
Implementation
AttributedSpans copyAttributionRegion(int startOffset, [int? endOffset]) {
endOffset = endOffset ?? _markers.lastOrNull?.offset ?? 0;
_log.fine('start: $startOffset, end: $endOffset');
final List<SpanMarker> cutAttributions = [];
_log.fine('inspecting existing markers in full AttributedSpans');
final Map<Attribution, int> foundStartMarkers = {};
final Map<Attribution, int> foundEndMarkers = {};
// Analyze all markers that appear before the start of
// the copy range so that we can insert any appropriate
// `start` markers at the beginning of the copy range.
_markers //
.where((marker) => marker.offset < startOffset) //
.forEach((marker) {
_log.fine('marker before the copy region: $marker');
// Track any markers that begin before the `startOffset`
// and continue beyond `startOffset`.
if (marker.isStart) {
_log.fine('remembering this marker to insert in copied region');
foundStartMarkers.putIfAbsent(marker.attribution, () => 0);
foundStartMarkers[marker.attribution] = foundStartMarkers[marker.attribution]! + 1;
} else {
_log.fine(
'this marker counters an earlier one we found. We will not re-insert this marker in the copied region');
foundStartMarkers.putIfAbsent(marker.attribution, () => 0);
foundStartMarkers[marker.attribution] = foundStartMarkers[marker.attribution]! - 1;
}
});
// Insert any `start` markers at the start of the copy region
// so that we maintain attribution symmetry.
foundStartMarkers.forEach((markerAttribution, count) {
if (count == 1) {
// Found an unmatched `start` marker. Replace it.
_log.fine('inserting "$markerAttribution" marker at start of copy region to maintain symmetry.');
cutAttributions.add(SpanMarker(
attribution: markerAttribution,
offset: 0,
markerType: SpanMarkerType.start,
));
} else if (count < 0 || count > 1) {
throw Exception(
'Found an unbalanced number of `start` and `end` markers before offset: $startOffset - $_markers');
}
});
// Directly copy every marker that appears within the cut
// region.
_markers //
.where((marker) => startOffset <= marker.offset && marker.offset <= endOffset!) //
.forEach((marker) {
_log.fine('copying "${marker.attribution}" at ${marker.offset} from original AttributionSpans to copy region.');
cutAttributions.add(marker.copyWith(
offset: marker.offset - startOffset,
));
});
// Analyze all markers that appear after the end of
// the copy range so that we can insert any appropriate
// `end` markers at the end of the copy range.
_markers //
.reversed //
.where((marker) => marker.offset > endOffset!) //
.forEach((marker) {
_log.fine('marker after the copy region: $marker');
// Track any markers that end after the `endOffset`
// and start before `endOffset`.
if (marker.isEnd) {
_log.fine('remembering this marker to insert in copied region');
foundEndMarkers.putIfAbsent(marker.attribution, () => 0);
foundEndMarkers[marker.attribution] = foundEndMarkers[marker.attribution]! + 1;
} else {
_log.fine(
'this marker counters an earlier one we found. We will not re-insert this marker in the copied region');
foundEndMarkers.putIfAbsent(marker.attribution, () => 0);
foundEndMarkers[marker.attribution] = foundEndMarkers[marker.attribution]! - 1;
}
});
// Insert any `end` markers at the end of the copy region
// so that we maintain attribution symmetry.
foundEndMarkers.forEach((markerAttribution, count) {
if (count == 1) {
// Found an unmatched `end` marker. Replace it.
_log.fine('inserting "$markerAttribution" marker at end of copy region to maintain symmetry.');
cutAttributions.add(SpanMarker(
attribution: markerAttribution,
offset: endOffset! - startOffset,
markerType: SpanMarkerType.end,
));
} else if (count < 0 || count > 1) {
throw Exception('Found an unbalanced number of `start` and `end` markers after offset: $endOffset - $_markers');
}
});
_log.fine('copied attributions:');
for (final attribution in cutAttributions) {
_log.fine(' - $attribution');
}
return AttributedSpans(attributions: cutAttributions);
}