Repository: superlistapp/super_editor (dart)

Issue:
[attributed_text] - Make it easy to query all desired attributions
I'd like to easily query all the user tags or hash tags in an `AttributedText`.

Today this looks like:

```dart
final hashTags = text.getAttributionSpansInRange(
  attributionFilter: (attribution) => attribution is HashTagAttribution,
  range: SpanRange(start: 0, end: node.text.text.length),
);
```

A method could be added to make it look like this:

```dart
final hashTags = text.getAllAttributionSpans((a) => a is HashTagAttribution);
```

Pull request description:
Add easier attribution span query methods (Resolves #1193)

Patch:
diff --git a/attributed_text/lib/src/attributed_text.dart b/attributed_text/lib/src/attributed_text.dart
index f167d39bd2..e3ceb2117c 100644
--- a/attributed_text/lib/src/attributed_text.dart
+++ b/attributed_text/lib/src/attributed_text.dart
@@ -123,6 +123,19 @@ class AttributedText {
     return attributionsThroughout;
   }
 
+  /// Returns all spans in this [AttributedText] for the given [attributions].
+  Set<AttributionSpan> getAttributionSpans(Set<Attribution> attributions) => getAttributionSpansInRange(
+        attributionFilter: (a) => attributions.contains(a),
+        range: SpanRange(start: 0, end: text.length),
+      );
+
+  /// Returns all spans in this [AttributedText], for attributions that are
+  /// selected by the given [filter].
+  Set<AttributionSpan> getAttributionSpansByFilter(AttributionFilter filter) => getAttributionSpansInRange(
+        attributionFilter: filter,
+        range: SpanRange(start: 0, end: text.length),
+      );
+
   /// Returns spans for each attribution that (at least partially) appear
   /// within the given [range], as selected by [attributionFilter].
   ///
@@ -132,7 +145,7 @@ class AttributedText {
   /// returned, including the area that sits outside the given [range].
   ///
   /// To obtain attribution spans that are cut down and limited to the
-  /// given [range], pass [true] for [resizeSpansToFitInRange]. This setting
+  /// given [range], pass `true` for [resizeSpansToFitInRange]. This setting
   /// only effects the returned spans, it does not alter the attributions
   /// within this [AttributedText].
   Set<AttributionSpan> getAttributionSpansInRange({


Test patch:
diff --git a/attributed_text/test/attributed_text_test.dart b/attributed_text/test/attributed_text_test.dart
index baedf8565f..fd1cabf65c 100644
--- a/attributed_text/test/attributed_text_test.dart
+++ b/attributed_text/test/attributed_text_test.dart
@@ -298,6 +298,81 @@ void main() {
     });
 
     group('attribution queries', () {
+      test('finds all spans for single attribution throughout text', () {
+        final attributedText = AttributedText(
+          'Hello world',
+          AttributedSpans(
+            attributions: [
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 2, markerType: SpanMarkerType.start),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 3, markerType: SpanMarkerType.end),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 6, markerType: SpanMarkerType.start),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 7, markerType: SpanMarkerType.end),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 9, markerType: SpanMarkerType.start),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 10, markerType: SpanMarkerType.end),
+            ],
+          ),
+        );
+
+        final ranges = attributedText.getAttributionSpans({ExpectedSpans.bold});
+
+        expect(ranges.length, 3);
+        expect(
+          ranges,
+          [
+            AttributionSpan(attribution: ExpectedSpans.bold, start: 2, end: 3),
+            AttributionSpan(attribution: ExpectedSpans.bold, start: 6, end: 7),
+            AttributionSpan(attribution: ExpectedSpans.bold, start: 9, end: 10),
+          ],
+        );
+      });
+
+      test('finds all spans for multiple attributions throughout text', () {
+        final attributedText = AttributedText(
+          'Hello world',
+          AttributedSpans(
+            attributions: [
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 2, markerType: SpanMarkerType.start),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 3, markerType: SpanMarkerType.end),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 6, markerType: SpanMarkerType.start),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 7, markerType: SpanMarkerType.end),
+              SpanMarker(attribution: ExpectedSpans.italics, offset: 5, markerType: SpanMarkerType.start),
+              SpanMarker(attribution: ExpectedSpans.italics, offset: 7, markerType: SpanMarkerType.end),
+              SpanMarker(attribution: ExpectedSpans.italics, offset: 9, markerType: SpanMarkerType.start),
+              SpanMarker(attribution: ExpectedSpans.italics, offset: 10, markerType: SpanMarkerType.end),
+            ],
+          ),
+        );
+
+        final ranges = attributedText.getAttributionSpans({ExpectedSpans.bold, ExpectedSpans.italics});
+
+        expect(ranges.length, 4);
+        expect(
+          ranges,
+          [
+            AttributionSpan(attribution: ExpectedSpans.bold, start: 2, end: 3),
+            AttributionSpan(attribution: ExpectedSpans.italics, start: 5, end: 7),
+            AttributionSpan(attribution: ExpectedSpans.bold, start: 6, end: 7),
+            AttributionSpan(attribution: ExpectedSpans.italics, start: 9, end: 10),
+          ],
+        );
+      });
+
+      test('returns empty list when searching for non-existent attribution spans', () {
+        final attributedText = AttributedText(
+          'Hello world',
+          AttributedSpans(
+            attributions: [
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 2, markerType: SpanMarkerType.start),
+              SpanMarker(attribution: ExpectedSpans.bold, offset: 3, markerType: SpanMarkerType.end),
+            ],
+          ),
+        );
+
+        final ranges = attributedText.getAttributionSpans({ExpectedSpans.italics});
+
+        expect(ranges.length, 0);
+      });
+
       test('finds all bold text around a character', () {
         final attributedText = AttributedText(
           'Hello world',