AppFlowy-IO / appflowy-editor

A highly customizable rich-text editor for Flutter. The AppFlowy Editor project for AppFlowy and beyond.
https://pub.dev/packages/appflowy_editor
Other
389 stars 165 forks source link

[Bug] `deleteBackward` and TextOperations that delete/modify delta that includes accent characters throw assertion errors #772

Open Xazin opened 2 months ago

Xazin commented 2 months ago

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: 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. (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. (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 sending notification was: Instance of 'PropertyValueNotifier' ════════════════════════════════════════════════════════════════════════════════ [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. (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. (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. (package:flutter/src/services/platform_channel.dart:564:34) #14 _DefaultBinaryMessenger.setMessageHandler. (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) ```
Xazin commented 2 months ago

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.