facebook / lexical

Lexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.
https://lexical.dev
MIT License
19.62k stars 1.67k forks source link

Bug: incorrect "redo" behavior with nested editor #4401

Open montlebalm opened 1 year ago

montlebalm commented 1 year ago

I noticed a bug while trying to share a history stack across a parent and nested editor. I manually created a history stack and passed it the HistoryPlugin for both editors.

Unfortunately, the "redo" behavior isn't working as expected. When making edits in both editors, only the child's content is restored. There's a point in the "redo" stack where the parent's content should be restored, but only the selection changes.

Lexical version: 0.10.0

Steps To Reproduce

  1. Edit text in parent editor
  2. Edit text in image caption
  3. Undo caption edit
  4. Undo parent edit
  5. Redo parent edit (fails! Jumps to caption editor, but does not reinstate text)
  6. Redo caption edit

https://user-images.githubusercontent.com/739390/234023118-9a37672a-a6c2-49e2-939f-f968992016eb.mov

Link to code example: https://playground.lexical.dev/

The current behavior

The parent editor's selection is restored, but not its content.

The expected behavior

The parent editor's selection and content are restored.

DamianKuras commented 1 year ago

I also noticed this bug while using Playground editor with nested image caption editor.

Steps To Reproduce

  1. Edit text in parent editor
  2. Select image caption editor
  3. Undo parent text edit
  4. Redo parent edit (fails! Jumps to caption editor, but does not reinstate text)

Bug explanation

Upon investigating the issue, I discovered that changes made to the editor state before selecting a different editor are not persisted in the history. This occurs because the current HistoryStateEntry (containing changes to text in parent editor) is merged(overwritten) by selected caption editor and its state when the selection changes (changes to selection are always merged). Consequently, the changes made to the parent editor state before selecting a caption editor are not stored in the history and cannot be restored with the redo functionality. Only the caption editor (selected after the changes) and its state are stored in the current HistoryStateEntry and persisted in history, resulting in the observed behavior.

ivailop7 commented 1 year ago

There's an undo fix merged but not published to npm. Worth waiting for 0.12 and seeing if the issues persists.

9larsons commented 1 year ago

v0.12.0 did not resolve this issue.

dsdeur commented 8 months ago

I ran into the same issue.

As @DamianKuras describes the issue is that the pending change is overwritten when you change from one editor to another. This means that the last action before changing editors gets lost.

Initially I thought it might be possible to skip the new event after the selection is moved to the new editor, but that broke the history in different ways, so it seems that both actions are needed to correctly replay history.

I've prototyped a solution that stores actions from multiple editors in one history entry, and that seems to solve it, see changes here: https://github.com/dsdeur/lexical/pull/1/files (I am of course happy to make a PR for this if helpful).