diff --git a/super_editor/lib/src/default_editor/text_tokenizing/action_tags.dart b/super_editor/lib/src/default_editor/text_tokenizing/action_tags.dart index 7156b52ad3..014c3c71a0 100644 --- a/super_editor/lib/src/default_editor/text_tokenizing/action_tags.dart +++ b/super_editor/lib/src/default_editor/text_tokenizing/action_tags.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:attributed_text/attributed_text.dart'; import 'package:characters/characters.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:super_editor/src/core/document.dart'; @@ -326,6 +327,17 @@ class ActionTagComposingReaction extends EditReaction { return; } + final hasComposingTagAttribution = textNode!.text + .getAttributionSpansInRange( + attributionFilter: (attribution) => attribution == actionTagComposingAttribution, + range: SpanRange(tagAroundPosition.indexedTag.startOffset, tagAroundPosition.indexedTag.endOffset), + ) + .isNotEmpty; + if (changeList.none((event) => event is DocumentEdit) && !hasComposingTagAttribution) { + // The user is neither typing nor moving the caret within an existing composing tag. + return; + } + _updateComposingTag(requestDispatcher, tagAroundPosition.indexedTag); editorContext.composingActionTag.value = tagAroundPosition.indexedTag; _onUpdateComposingActionTag(tagAroundPosition.indexedTag); diff --git a/super_editor/test/super_editor/text_entry/tagging/action_tags_test.dart b/super_editor/test/super_editor/text_entry/tagging/action_tags_test.dart index 61d1a178d3..9772843b5e 100644 --- a/super_editor/test/super_editor/text_entry/tagging/action_tags_test.dart +++ b/super_editor/test/super_editor/text_entry/tagging/action_tags_test.dart @@ -768,6 +768,63 @@ void main() { // Ensure that we received a notification when the tag was cancelled. expect(tagNotificationCount, 7); }); + + testWidgetsOnAllPlatforms("does not start composing when placing the caret at an existing tag pattern", + (tester) async { + await _pumpTestEditor( + tester, + MutableDocument( + nodes: [ + ParagraphNode( + id: "1", + text: AttributedText("This is origin/main branch"), + ), + ], + ), + ); + + // Place the caret at "mai|n". + await tester.placeCaretInParagraph("1", 18); + + // Ensure that we are not composing a tag. + final text = SuperEditorInspector.findTextInComponent("1"); + expect( + text.getAttributionSpansInRange( + attributionFilter: (attribution) => attribution == actionTagComposingAttribution, + range: const SpanRange(0, 26), + ), + isEmpty, + ); + }); + + testWidgetsOnAllPlatforms("updates composing when moving the caret within an existing composing tag", + (tester) async { + await _pumpTestEditor( + tester, + singleParagraphEmptyDoc(), + ); + await tester.placeCaretInParagraph("1", 0); + + // Compose an action tag. + await tester.typeImeText("/header"); + + // Ensure that the tag has a composing attribution. + final textBefore = SuperEditorInspector.findTextInComponent("1"); + expect( + textBefore.getAttributedRange({actionTagComposingAttribution}, 0), + const SpanRange(0, 6), + ); + + // Press the left arrow to move the caret within the tag. + await tester.pressLeftArrow(); + + // Ensure that the tag was updated. + final textAfter = SuperEditorInspector.findTextInComponent("1"); + expect( + textAfter.getAttributedRange({actionTagComposingAttribution}, 0), + const SpanRange(0, 5), + ); + }); }); group("submissions >", () {