appflowy-editor
appflowy-editor copied to clipboard
[Bug] `deleteBackward` and TextOperations that delete/modify delta that includes accent characters throw assertion errors
Bug Description
Seems the editor does not handle accent characters like ^¨~ very well when it comes to deleting or replacing.
How to Reproduce
Type an accent character eg. ~ and hit the button.
import 'package:flutter/material.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
void main() {
runApp(const MaterialApp(home: SamplePage()));
}
class SamplePage extends StatefulWidget {
const SamplePage({super.key});
@override
State<SamplePage> createState() => _SamplePageState();
}
class _SamplePageState extends State<SamplePage> {
late final EditorState editorState;
@override
void initState() {
super.initState();
editorState = EditorState.blank();
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
editorState.deleteBackward();
},
label: const Text('Delete backwards 1'),
),
body: AppFlowyEditor(
editorState: editorState,
),
);
}
}
Expected Behavior
Similar to how you can escape the assertion errors by pressing space or selecting some text and then performing the operation, I'd expect the accent character to be first transformed to a non-transitional character and then operations to be applied.
deleteBackward is one case, but it also happens with deleteText. I'm assuming there could be similar behavior with other operations eg. insertion.
Operating System
MacOS
AppFlowy Editor Version(s)
Main
Screenshots
https://github.com/AppFlowy-IO/appflowy-editor/assets/42929161/ffb7ff31-3eb3-45e2-b5f4-94820c650b3e
Additional Context
Stacktrace
════════ Exception caught by foundation library ════════════════════════════════
The following assertion was thrown while dispatching notifications for PropertyValueNotifier<Selection?>:
Range end 2 is out of text of length 1
'package:flutter/src/services/text_input.dart':
Failed assertion: line 1011 pos 12: 'range.end >= 0 && range.end <= text.length'
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=2_bug.yml
When the exception was thrown, this was the stack:
#2 TextEditingValue._textRangeIsValid (package:flutter/src/services/text_input.dart:1011:12)
#3 TextEditingValue.toJSON (package:flutter/src/services/text_input.dart:964:12)
#4 _PlatformTextInputControl.setEditingState (package:flutter/src/services/text_input.dart:2366:13)
#5 TextInput._setEditingState (package:flutter/src/services/text_input.dart:2041:15)
#6 TextInputConnection.setEditingState (package:flutter/src/services/text_input.dart:1425:25)
#7 NonDeltaTextInputService.attach (package:appflowy_editor/src/editor/editor_component/service/ime/non_delta_input_service.dart:83:9)
#8 KeyboardServiceWidgetState._attachTextInputService (package:appflowy_editor/src/editor/editor_component/service/keyboard_service_widget.dart:232:24)
#9 KeyboardServiceWidgetState._onSelectionChanged (package:appflowy_editor/src/editor/editor_component/service/keyboard_service_widget.dart:217:7)
#10 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:433:24)
#11 PropertyValueNotifier.value= (package:appflowy_editor/src/editor/util/property_notifier.dart:25:5)
#12 EditorState.selection= (package:appflowy_editor/src/editor_state.dart:119:23)
#13 EditorState.apply (package:appflowy_editor/src/editor_state.dart:318:9)
#14 SelectionTransform.deleteBackward (package:appflowy_editor/src/editor/command/selection_commands.dart:30:11)
#15 _SamplePageState.build.<anonymous closure> (package:test_app/main.dart:30:23)
#16 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1183:21)
#17 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:315:24)
#18 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:652:11)
#19 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:309:5)
#20 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:242:7)
#21 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:670:9)
#22 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:98:12)
#23 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:143:9)
#24 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:633:13)
#25 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18)
#26 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:127:7)
#27 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:495:19)
#28 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:475:22)
#29 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:430:11)
#30 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:420:7)
#31 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:383:5)
#32 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:330:7)
#33 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:299:9)
#34 _invoke1 (dart:ui/hooks.dart:328:13)
#35 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:429:7)
#36 _dispatchPointerDataPacket (dart:ui/hooks.dart:262:31)
(elided 2 frames from class _AssertionError)
The PropertyValueNotifier<Selection?> sending notification was: Instance of 'PropertyValueNotifier<Selection?>'
════════════════════════════════════════════════════════════════════════════════
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: 'package:appflowy_editor/src/core/transform/transaction.dart': Failed assertion: line 270 pos 7: 'index + length <= delta.length && index >= 0 && length >= 0': The index(0) or length(1) is out of range or negative.
#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2 TextTransaction.deleteText (package:appflowy_editor/src/core/transform/transaction.dart:270:7)
#3 onDelete (package:appflowy_editor/src/editor/editor_component/service/ime/delta_input_on_delete_impl.dart:25:19)
#4 KeyboardServiceWidgetState.initState.<anonymous closure> (package:appflowy_editor/src/editor/editor_component/service/keyboard_service_widget.dart:70:43)
#5 NonDeltaTextInputService.apply (package:appflowy_editor/src/editor/editor_component/service/ime/non_delta_input_service.dart:52:23)
#6 NonDeltaTextInputService.updateEditingValue.<anonymous closure> (package:appflowy_editor/src/editor/editor_component/service/ime/non_delta_input_service.dart:109:9)
#7 Debounce.debounce (package:appflowy_editor/src/editor/util/debounce.dart:15:15)
#8 NonDeltaTextInputService.updateEditingValue (package:appflowy_editor/src/editor/editor_component/service/ime/non_delta_input_service.dart:102:14)
#9 TextInput._updateEditingValue (package:flutter/src/services/text_input.dart:2116:43)
#10 TextInput._handleTextInputInvocation (package:flutter/src/services/text_input.dart:1938:29)
#11 TextInput._loudlyHandleTextInputInvocation (package:flutter/src/services/text_input.dart:1826:20)
#12 MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:571:55)
#13 MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:564:34)
#14 _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:603:35)
#15 _invoke2 (dart:ui/hooks.dart:344:13)
#16 _ChannelCallbackRecord.invoke (dart:ui/channel_buffers.dart:45:5)
#17 _Channel.push (dart:ui/channel_buffers.dart:135:31)
#18 ChannelBuffers.push (dart:ui/channel_buffers.dart:343:17)
#19 PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:737:22)
#20 _dispatchPlatformMessage (dart:ui/hooks.dart:257:31)
When inputting an accent character like ~ or ^, I expect to be able to also delete it with just one backspace.
The other way around, I expect two spaces to be able to add one space, as the next input might be a character that the accent can be used with.
Normally an underscore is added to an accent character when it's still in it's transitional state. Worth taking note of.