From 38774ddcf9dc5be822a07f3261fc8ee925c8af30 Mon Sep 17 00:00:00 2001 From: Morn Date: Thu, 26 Dec 2024 16:11:50 +0800 Subject: [PATCH] fix: error format of TextEditingDeltaInsertion --- .../service/ime/non_delta_input_service.dart | 20 ++-- .../ime/non_delta_input_service_test.dart | 96 +++++++++++++++++++ 2 files changed, 108 insertions(+), 8 deletions(-) diff --git a/lib/src/editor/editor_component/service/ime/non_delta_input_service.dart b/lib/src/editor/editor_component/service/ime/non_delta_input_service.dart index 24a929b96..2cf55ef1c 100644 --- a/lib/src/editor/editor_component/service/ime/non_delta_input_service.dart +++ b/lib/src/editor/editor_component/service/ime/non_delta_input_service.dart @@ -316,14 +316,18 @@ extension on TextEditingDelta { } } -extension on TextEditingDeltaInsertion { - TextEditingDeltaInsertion format() => TextEditingDeltaInsertion( - oldText: oldText << _len, - textInserted: textInserted, - insertionOffset: insertionOffset - _len, - selection: selection << _len, - composing: composing << _len, - ); +extension TextEditingDeltaInsertionExtension on TextEditingDeltaInsertion { + TextEditingDeltaInsertion format() { + final startWithSpace = oldText.startsWith(_whitespace); + return TextEditingDeltaInsertion( + oldText: startWithSpace ? oldText << _len : oldText, + textInserted: textInserted, + insertionOffset: + startWithSpace ? insertionOffset - _len : insertionOffset, + selection: startWithSpace ? selection << _len : selection, + composing: startWithSpace ? composing << _len : composing, + ); + } } extension on TextEditingDeltaDeletion { diff --git a/test/editor/editor_component/ime/non_delta_input_service_test.dart b/test/editor/editor_component/ime/non_delta_input_service_test.dart index 5f08273c4..095a83ee6 100644 --- a/test/editor/editor_component/ime/non_delta_input_service_test.dart +++ b/test/editor/editor_component/ime/non_delta_input_service_test.dart @@ -6,6 +6,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + group('NonDeltaTextInputService', () { test('actions', () { bool onInsert = false, @@ -103,5 +105,99 @@ void main() { expect(completer.future, completion(true)); }); + + test('Delta insertion format', () { + const insertion = TextEditingDeltaInsertion( + oldText: '', + textInserted: 'A', + insertionOffset: 0, + selection: TextSelection.collapsed(offset: 1), + composing: TextRange(start: 0, end: 1), + ); + final formatInsertion = insertion.format(); + assert(formatInsertion.selection == insertion.selection); + assert(formatInsertion.composing == insertion.composing); + + const insertion2 = TextEditingDeltaInsertion( + oldText: ' ', + textInserted: 'A', + insertionOffset: 1, + selection: TextSelection.collapsed(offset: 2), + composing: TextRange.empty, + ); + + final formatInsertion2 = insertion2.format(); + assert(formatInsertion2.insertionOffset == 0); + assert( + formatInsertion2.selection == const TextSelection.collapsed(offset: 1), + ); + + const insertion3 = TextEditingDeltaInsertion( + oldText: ' A', + textInserted: 'B', + insertionOffset: 2, + selection: TextSelection.collapsed(offset: 3), + composing: TextRange.empty, + ); + + final formatInsertion3 = insertion3.format(); + assert(formatInsertion3.insertionOffset == 1); + + assert( + formatInsertion3.selection == const TextSelection.collapsed(offset: 2), + ); + }); + }); + + testWidgets('Delta insertion format with deletion', (tester) async { + TextEditingValue value = + const TextEditingValue(selection: TextSelection.collapsed(offset: 0)); + const space = ' '; + final inputService = NonDeltaTextInputService( + onInsert: (v) async => value = v.apply(value), + onDelete: (v) async => value = v.apply(value), + onReplace: (v) async => value = v.apply(value), + onNonTextUpdate: (v) async => value = v.apply(value), + onPerformAction: (_) async {}, + ); + inputService.attach(value, const TextInputConfiguration()); + await inputService.apply( + const [ + TextEditingDeltaInsertion( + oldText: '', + textInserted: space, + insertionOffset: 0, + selection: TextSelection.collapsed(offset: 1), + composing: TextRange.empty, + ), + ], + ); + assert(value.text == space); + await inputService.apply( + [ + TextEditingDeltaDeletion( + oldText: value.text, + deletedRange: const TextRange(start: 0, end: 1), + selection: const TextSelection.collapsed(offset: 0), + composing: TextRange.empty, + ), + ], + ); + assert(value.text == ''); + await inputService.apply( + [ + TextEditingDeltaInsertion( + oldText: value.text, + textInserted: 'A' * 100, + insertionOffset: 0, + selection: const TextSelection.collapsed(offset: 100), + composing: TextRange.empty, + ), + ], + ); + assert(value.text == 'A' * 100); + inputService.currentTextEditingValue = value; + final currentSelection = inputService.currentTextEditingValue?.selection; + assert(currentSelection?.baseOffset == 100); }); }