facebook / lexical

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

Bug: Key Combination Breaks Text Editor Within List Items #5631

Open FacuToso opened 9 months ago

FacuToso commented 9 months ago

Brief Description of the Problem:

A fatal error has been identified in the Lexical text editor that occurs when manipulating multi-level lists. The error is specifically triggered by a sequence of actions that includes merging external text into the list, creating new list items, and then attempting to delete them. This behavior results in a critical malfunction of the editor, rendering it completely unusable. The only way to restore the editor's functionality is to completely reload the page. This error represents a serious issue as it prevents the continuous use of the editor and jeopardizes the integrity of the user's data.

Lexical version: Latest

Steps to reproduce the error and the relevant code snippet are detailed in the next section. The problem can be reproduced in the official Lexical playground

Steps To Reproduce

  1. Create a multi-level list in the Lexical text editor, like this:
      1. Tier 1
         A. Tier 2
            a. Tier 3
               I. Tier 4
  2. Add text outside the list and place the cursor (Represented by |) in this position:

      1. Tier 1
         A. Tier 2
            a. Tier 3
               I. Tier 4
    
      *|*ExternalText
  3. Move the cursor to the start of the external text and use the Backspace key to merge this text with the last item of the list:
  4. Tier 1 A. Tier 2 a. Tier 3 I. Tier 4|ExternalText
  5. Press Enter to create a new item in the list:
  6. Tier 1 A. Tier 2 a. Tier 3 I. Tier 4 II. |ExternalText
  7. Press Enter again to add another item:
  8. Tier 1 A. Tier 2 a. Tier 3 I. Tier 4 II. III. |ExternalText
    6.Use the Up Arrow key to move the cursor up in the list:
  9. Tier 1 A. Tier 2 a. Tier 3 I. Tier 4 II. | III. ExternalText
  10. Press Enter, which causes a misalignment in the list structure:
  11. Tier 1 A. Tier 2 a. Tier 3 I. Tier 4 b. | I. ExternalText
    
    **8.Press the Delete key, which results in the failure of the editor.**

The problem can be reproduced in the official Lexical playground

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

The current behavior The editor fails and crashes when deleting a list item under specific circumstances. This issue appears to be related to a problem in the code where anchorKey and focusKey do not find an existing key in the pendingNodeMap. The relevant code snippet is:

if ($isRangeSelection(pendingSelection)) {
  const pendingNodeMap = pendingEditorState._nodeMap;
  const anchorKey = pendingSelection.anchor.key;
  const focusKey = pendingSelection.focus.key;
  if (pendingNodeMap.get(anchorKey) === void 0 || pendingNodeMap.get(focusKey) === void 0) {
    throw Error(`updateEditor: selection has been lost because the previously selected nodes have been removed and selection wasn't moved to another node. Ensure selection changes after removing/replacing a selected node.`);
  }
}

The expected behavior

The editor should handle the deletion of list items without crashing, maintaining the integrity of the list structure and the editor's state.

Exported DOM from Lexical Playground: LexicalError.json

Tree view:

 root
  └ (50) list  
    ├ (49) listitem  
    | └ (48) text  "Tier1"
    └ (52) listitem  
      └ (53) list  
        ├ (51) listitem  
        | └ (54) text  "Tier2"
        └ (56) listitem  
          └ (57) list  
            ├ (55) listitem  
            | └ (58) text  "Tier3"
            ├ (61) listitem  
            | └ (62) list  
            |   └ (59) listitem  
            |     └ (63) text  "Tier4"
            ├ (77) listitem  
            └ (79) listitem  
              └ (78) list  
                └ (76) listitem  
                  └ (74) text  "ExternalText"

 selection: range  
  ├ anchor { key: 77, offset: 0, type: element }
  └ focus { key: 77, offset: 0, type: element }

 commands:
  └ { type: UNKNOWN, payload: true }
  └ { type: UNKNOWN, payload: undefined }
  └ { type: UNKNOWN, payload: KeyboardEvent }
  └ { type: UNKNOWN, payload: undefined }
  └ { type: UNKNOWN, payload: KeyboardEvent }
  └ { type: UNKNOWN, payload: undefined }
  └ { type: UNKNOWN, payload: true }
  └ { type: UNKNOWN, payload: undefined }
  └ { type: UNKNOWN, payload: KeyboardEvent }
  └ { type: UNKNOWN, payload: false }

 editor:
  └ namespace Playground
  └ editable true

Dom view:

<ol class="PlaygroundEditorTheme__ol1">
  <li value="1" class="PlaygroundEditorTheme__listItem">
    <span style="white-space: pre-wrap;">Tier1</span>
  </li>
  <li value="2" class="PlaygroundEditorTheme__listItem PlaygroundEditorTheme__nestedListItem">
    <ol class="PlaygroundEditorTheme__ol2">
      <li value="1" class="PlaygroundEditorTheme__listItem">
        <span style="white-space: pre-wrap;">Tier2</span>
      </li>
      <li value="2" class="PlaygroundEditorTheme__listItem PlaygroundEditorTheme__nestedListItem">
        <ol class="PlaygroundEditorTheme__ol3">
          <li value="1" class="PlaygroundEditorTheme__listItem">
            <span style="white-space: pre-wrap;">Tier3</span>
          </li>
          <li value="2" class="PlaygroundEditorTheme__listItem PlaygroundEditorTheme__nestedListItem">
            <ol class="PlaygroundEditorTheme__ol4">
              <li value="1" class="PlaygroundEditorTheme__listItem">
                <span style="white-space: pre-wrap;">Tier4</span>
              </li>
            </ol>
          </li>
          <li value="2" class="PlaygroundEditorTheme__listItem"></li>
          <li value="3" class="PlaygroundEditorTheme__listItem PlaygroundEditorTheme__nestedListItem">
            <ol class="PlaygroundEditorTheme__ol4">
              <li value="1" class="PlaygroundEditorTheme__listItem">
                <span style="white-space: pre-wrap;">ExternalText</span>
              </li>
            </ol>
          </li>
        </ol>
      </li>
    </ol>
  </li>
</ol>
Sahejkm commented 8 months ago

Hi @FacuToso , we are not able to replicate the issue, could you help upload a video recording for the issue ? Thanks!