Skip to content

Commit 53efa73

Browse files
committed
Changing AttributedSpans.collapseSpans to treat end positions as exclusive
1 parent 37b1ab2 commit 53efa73

File tree

3 files changed

+28
-40
lines changed

3 files changed

+28
-40
lines changed

attributed_text/lib/src/attributed_spans.dart

+8-21
Original file line numberDiff line numberDiff line change
@@ -852,13 +852,13 @@ class AttributedSpans {
852852
return [];
853853
}
854854

855-
if (markers.isEmpty || markers.first.offset > contentLength - 1) {
855+
if (markers.isEmpty || markers.first.offset >= contentLength) {
856856
// There is content but no attributions that apply to it.
857-
return [MultiAttributionSpan(attributions: {}, start: 0, end: contentLength - 1)];
857+
return [MultiAttributionSpan(attributions: {}, start: 0, end: contentLength)];
858858
}
859859

860860
final collapsedSpans = <MultiAttributionSpan>[];
861-
var currentSpan = MultiAttributionSpan(attributions: {}, start: 0, end: contentLength - 1);
861+
var currentSpan = MultiAttributionSpan(attributions: {}, start: 0, end: contentLength);
862862

863863
_log.fine('walking list of markers to determine collapsed spans.');
864864
for (final marker in markers) {
@@ -869,31 +869,18 @@ class AttributedSpans {
869869
break;
870870
}
871871

872-
if ((marker.isStart && marker.offset > currentSpan.start) ||
873-
(marker.isEnd && marker.offset >= currentSpan.start)) {
872+
if (marker.offset > currentSpan.start) {
874873
// We reached the boundary between the current span and the next. Finalize the current span, commit it, and
875874
// prepare the next one.
876875
_log.fine(
877876
'encountered a span boundary with ${marker.isStart ? "a start" : "an end"} marker at offset ${marker.offset}.');
878877

879-
// Calculate the end of the current span.
880-
//
881-
// If the current marker is an end marker, then the current span at that marker. Otherwise, if the
882-
// marker is an start marker, the current span ends 1 unit before the marker.
883-
final currentEnd = marker.isEnd ? marker.offset : marker.offset - 1;
884-
885878
// Commit the completed span.
886-
collapsedSpans.add(currentSpan.copyWith(end: currentEnd));
879+
collapsedSpans.add(currentSpan.copyWith(end: marker.offset));
887880
_log.fine('committed span ${collapsedSpans.last}');
888881

889-
// Calculate the start of the next span.
890-
//
891-
// If the current marker is a start marker, then the next span begins at that marker. Otherwise, if the
892-
// marker is an end marker, the next span begins 1 unit after the marker.
893-
final nextStart = marker.isStart ? marker.offset : marker.offset + 1;
894-
895-
// Create the next span and continue consumeing markers
896-
currentSpan = currentSpan.copyWith(start: nextStart);
882+
// Create the next span and continue consuming markers.
883+
currentSpan = currentSpan.copyWith(start: marker.offset);
897884
_log.fine('new current span is $currentSpan');
898885
}
899886

@@ -910,7 +897,7 @@ class AttributedSpans {
910897
}
911898
}
912899

913-
if (collapsedSpans.last.end < contentLength - 1) {
900+
if (collapsedSpans.last.end < contentLength) {
914901
// The last span committed during the loop does not reach the end of the requested content range. We either ran
915902
// out of markers or the remaining markers are outside the content range. In both cases the value in currentSpan
916903
// should already have the correct start, end, and attributions values to cover the remaining content.

attributed_text/test/attributed_spans_test.dart

+19-18
Original file line numberDiff line numberDiff line change
@@ -474,14 +474,15 @@ void main() {
474474
test('empty spans', () {
475475
// Make sure no exceptions are thrown when collapsing
476476
// spans on an empty AttributedSpans.
477-
AttributedSpans().collapseSpans(contentLength: 0);
477+
final spans = AttributedSpans().collapseSpans(contentLength: 0);
478+
expect(spans, isEmpty);
478479
});
479480

480481
test('non-empty span with no attributions', () {
481482
final collapsedSpans = AttributedSpans().collapseSpans(contentLength: 10);
482483
expect(collapsedSpans, hasLength(1));
483484
expect(collapsedSpans.first.start, 0);
484-
expect(collapsedSpans.first.end, 9);
485+
expect(collapsedSpans.first.end, 10);
485486
expect(collapsedSpans.first.attributions, isEmpty);
486487
});
487488

@@ -491,7 +492,7 @@ void main() {
491492
const SpanMarker(attribution: ExpectedSpans.bold, offset: 0, markerType: SpanMarkerType.start),
492493
const SpanMarker(attribution: ExpectedSpans.bold, offset: 16, markerType: SpanMarkerType.end),
493494
],
494-
).collapseSpans(contentLength: 17);
495+
).collapseSpans(contentLength: 16);
495496

496497
expect(collapsedSpans.length, 1);
497498
expect(collapsedSpans.first.start, 0);
@@ -508,21 +509,21 @@ void main() {
508509
const SpanMarker(attribution: ExpectedSpans.bold, offset: 7, markerType: SpanMarkerType.start),
509510
const SpanMarker(attribution: ExpectedSpans.bold, offset: 10, markerType: SpanMarkerType.end),
510511
],
511-
).collapseSpans(contentLength: 17);
512+
).collapseSpans(contentLength: 16);
512513

513514
expect(collapsedSpans.length, 4);
514515
expect(collapsedSpans[0].start, 0);
515516
expect(collapsedSpans[0].end, 3);
516517
expect(collapsedSpans[0].attributions.length, 1);
517518
expect(collapsedSpans[0].attributions.first, ExpectedSpans.bold);
518-
expect(collapsedSpans[1].start, 4);
519-
expect(collapsedSpans[1].end, 6);
519+
expect(collapsedSpans[1].start, 3);
520+
expect(collapsedSpans[1].end, 7);
520521
expect(collapsedSpans[1].attributions.length, 0);
521522
expect(collapsedSpans[2].start, 7);
522523
expect(collapsedSpans[2].end, 10);
523524
expect(collapsedSpans[2].attributions.length, 1);
524525
expect(collapsedSpans[2].attributions.first, ExpectedSpans.bold);
525-
expect(collapsedSpans[3].start, 11);
526+
expect(collapsedSpans[3].start, 10);
526527
expect(collapsedSpans[3].end, 16);
527528
expect(collapsedSpans[3].attributions.length, 0);
528529
});
@@ -531,17 +532,17 @@ void main() {
531532
final collapsedSpans = AttributedSpans(
532533
attributions: [
533534
const SpanMarker(attribution: ExpectedSpans.bold, offset: 0, markerType: SpanMarkerType.start),
534-
const SpanMarker(attribution: ExpectedSpans.bold, offset: 4, markerType: SpanMarkerType.end),
535+
const SpanMarker(attribution: ExpectedSpans.bold, offset: 5, markerType: SpanMarkerType.end),
535536
const SpanMarker(attribution: ExpectedSpans.italics, offset: 5, markerType: SpanMarkerType.start),
536-
const SpanMarker(attribution: ExpectedSpans.italics, offset: 9, markerType: SpanMarkerType.end),
537+
const SpanMarker(attribution: ExpectedSpans.italics, offset: 10, markerType: SpanMarkerType.end),
537538
],
538539
).collapseSpans(contentLength: 10);
539540

540541
expect(collapsedSpans, hasLength(2));
541542
expect(collapsedSpans.first.start, 0);
542-
expect(collapsedSpans.first.end, 4);
543+
expect(collapsedSpans.first.end, 5);
543544
expect(collapsedSpans.last.start, 5);
544-
expect(collapsedSpans.last.end, 9);
545+
expect(collapsedSpans.last.end, 10);
545546
});
546547

547548
test('multiple non-overlapping attributions', () {
@@ -552,21 +553,21 @@ void main() {
552553
const SpanMarker(attribution: ExpectedSpans.italics, offset: 7, markerType: SpanMarkerType.start),
553554
const SpanMarker(attribution: ExpectedSpans.italics, offset: 10, markerType: SpanMarkerType.end),
554555
],
555-
).collapseSpans(contentLength: 17);
556+
).collapseSpans(contentLength: 16);
556557

557558
expect(collapsedSpans.length, 4);
558559
expect(collapsedSpans[0].start, 0);
559560
expect(collapsedSpans[0].end, 3);
560561
expect(collapsedSpans[0].attributions.length, 1);
561562
expect(collapsedSpans[0].attributions.first, ExpectedSpans.bold);
562-
expect(collapsedSpans[1].start, 4);
563-
expect(collapsedSpans[1].end, 6);
563+
expect(collapsedSpans[1].start, 3);
564+
expect(collapsedSpans[1].end, 7);
564565
expect(collapsedSpans[1].attributions.length, 0);
565566
expect(collapsedSpans[2].start, 7);
566567
expect(collapsedSpans[2].end, 10);
567568
expect(collapsedSpans[2].attributions.length, 1);
568569
expect(collapsedSpans[2].attributions.first, ExpectedSpans.italics);
569-
expect(collapsedSpans[3].start, 11);
570+
expect(collapsedSpans[3].start, 10);
570571
expect(collapsedSpans[3].end, 16);
571572
expect(collapsedSpans[3].attributions.length, 0);
572573
});
@@ -579,18 +580,18 @@ void main() {
579580
const SpanMarker(attribution: ExpectedSpans.italics, offset: 6, markerType: SpanMarkerType.start),
580581
const SpanMarker(attribution: ExpectedSpans.italics, offset: 16, markerType: SpanMarkerType.end),
581582
],
582-
).collapseSpans(contentLength: 17);
583+
).collapseSpans(contentLength: 16);
583584

584585
expect(collapsedSpans.length, 3);
585586
expect(collapsedSpans[0].start, 0);
586-
expect(collapsedSpans[0].end, 5);
587+
expect(collapsedSpans[0].end, 6);
587588
expect(collapsedSpans[0].attributions.length, 1);
588589
expect(collapsedSpans[0].attributions.first, ExpectedSpans.bold);
589590
expect(collapsedSpans[1].start, 6);
590591
expect(collapsedSpans[1].end, 8);
591592
expect(collapsedSpans[1].attributions.length, 2);
592593
expect(collapsedSpans[1].attributions, equals({ExpectedSpans.bold, ExpectedSpans.italics}));
593-
expect(collapsedSpans[2].start, 9);
594+
expect(collapsedSpans[2].start, 8);
594595
expect(collapsedSpans[2].end, 16);
595596
expect(collapsedSpans[2].attributions.length, 1);
596597
expect(collapsedSpans[2].attributions.first, ExpectedSpans.italics);

super_editor/lib/src/infrastructure/attributed_text_styles.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ extension ComputerTextSpan on AttributedText {
3333
final collapsedSpans = spans.collapseSpans(contentLength: text.length);
3434
final textSpans = collapsedSpans
3535
.map((attributedSpan) => TextSpan(
36-
text: text.substring(attributedSpan.start, attributedSpan.end + 1),
36+
text: text.substring(attributedSpan.start, attributedSpan.end),
3737
style: styleBuilder(attributedSpan.attributions),
3838
))
3939
.toList();

0 commit comments

Comments
 (0)