Skip to content

Standalone editor: selectPhrase can't match a phrase spanning two TextNodes #591

Description

@kcarnold

EditorControls.selectPhrase (frontend/src/editor/editor.tsx) collects the paragraph's TextNodes and searches each one independently:

for (const tier of MATCH_TIERS) {
  for (const node of textNodes) {
    const hit = firstMatch(tier, node.getTextContent(), phrase);
    
  }
}

Lexical splits a paragraph into several TextNodes wherever formatting changes, so a phrase crossing one of those boundaries — the **bold** word — exists in no single node and is found at no tier. The writer sees the partner "look for" a phrase that is plainly in front of them and fail. The highlight tool reports a miss, and applyEditOp's reveal silently skips the selection.

This is independent of the fold ladder: it was equally true of the previous toLowerCase().indexOf() implementation.

Fix

Match once against the paragraph's full text, then map the resulting source offsets back to (node, offset) for the selection endpoints — the same map-back discipline the folds already use for dropped and merged characters. Roughly:

  1. Concatenate the node texts, recording each node's start offset.
  2. Run the tier ladder on the concatenation.
  3. Walk the offsets back to the node containing start and the node containing end, with the remainder as the in-node offset.

$createRangeSelection already supports anchor and focus in different nodes, so the selection itself needs no change.

Test with a paragraph built as three text nodes (plain / bold / plain) and a needle spanning all three.

Found while making text matching typography-tolerant (#587). Dissolved for free if #589 happens, since matching would move to the document's plain text and hosts would resolve ranges instead.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions