memspace / zefyr

Soft and gentle rich text editing for Flutter applications.
https://zefyr-editor.gitbook.io
2.22k stars 550 forks source link

Weird behavior when removing and adding the same text by characters #552

Closed Amir-P closed 2 years ago

Amir-P commented 3 years ago

Steps to Reproduce

  1. Use the latest version on 1.0-dev with Flutter 2.5.3 (Stable) (Note: It's not happening on Flutter 2.2)
  2. Write a text (e.g. "Test")
  3. Remove the text (Remove by characters from the end)
  4. Try to write the same text again
  5. You can see that document is not being updated while you're typing until you reach the last character when the whole text will be added to the document.

Nov-13-2021 09-29-20

Logs

I tried the word "Hey" and here is the logs I gathered from functions updateEditingValue and updateRemoteValueIfNeeded from RawEditorStateTextInputClientMixin. The issue comes from adding the remaining parts of text after removing a character from it which then prevents updating document when you're typing that text again.

// After typing "H"
updateEditingValue
textEditingValue: TextEditingValue(text: ┤H
├, selection: TextSelection(baseOffset: 1, extentOffset: 1, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]
updateRemoteValueIfNeeded
textEditingValue: TextEditingValue(text: ┤H
├, selection: TextSelection(baseOffset: 1, extentOffset: 1, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))

// After typing "e"
updateEditingValue
textEditingValue: TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]
updateRemoteValueIfNeeded
textEditingValue: TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))

// After typing "y"
updateEditingValue
textEditingValue: TextEditingValue(text: ┤Hey
├, selection: TextSelection(baseOffset: 3, extentOffset: 3, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]
updateRemoteValueIfNeeded
textEditingValue: TextEditingValue(text: ┤Hey
├, selection: TextSelection(baseOffset: 3, extentOffset: 3, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))

// After removing "y"
updateRemoteValueIfNeeded
textEditingValue: TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]

// After removing "e"
updateRemoteValueIfNeeded
textEditingValue: TextEditingValue(text: ┤H
├, selection: TextSelection(baseOffset: 1, extentOffset: 1, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤H
├, selection: TextSelection(baseOffset: 1, extentOffset: 1, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]

// After removing "H"
updateRemoteValueIfNeeded
textEditingValue: TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤H
├, selection: TextSelection(baseOffset: 1, extentOffset: 1, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]

// After typing "H" (Not updated in controller since the value is present in _sentRemoteValues)
updateEditingValue
textEditingValue: TextEditingValue(text: ┤H
├, selection: TextSelection(baseOffset: 1, extentOffset: 1, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤H
├, selection: TextSelection(baseOffset: 1, extentOffset: 1, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]

// After typing "e" (Not updated in controller since the value is present in _sentRemoteValues)
updateEditingValue
textEditingValue: TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤He
├, selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]

// After typing "y" (Now updated in controller)
updateEditingValue
textEditingValue: TextEditingValue(text: ┤Hey
├, selection: TextSelection(baseOffset: 3, extentOffset: 3, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
_sentRemoteValues: [TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1)), TextEditingValue(text: ┤
├, selection: TextSelection(baseOffset: 0, extentOffset: 0, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))]
updateRemoteValueIfNeeded
textEditingValue: TextEditingValue(text: ┤Hey
├, selection: TextSelection(baseOffset: 3, extentOffset: 3, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: -1, end: -1))
[✓] Flutter (Channel stable, 2.5.3, on macOS 12.0.1 21A559 darwin-arm, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 2020.3)
[✓] IntelliJ IDEA Community Edition (version 2021.1.3)
[✓] VS Code (version 1.62.1)
[✓] Connected device (2 available)
Amir-P commented 3 years ago

Seems like this issue is not happening on real devices and on simulators, it's only happening if you use physical keyboard not the keyboard on simulator. I'm not sure if this should be closed or not. WDYT? @pulyaevskiy

pulyaevskiy commented 3 years ago

Thanks for reporting.

After a quick test it looks like the issue occurs when a character is deleted, the _sentRemoteValues list keeps growing, which should never happen. It practically should be empty most of the time.

I'll try to debug this a bit more to find the root cause.

pulyaevskiy commented 2 years ago

Fixed by #563