fix: keep same-key siblings indexed when a duplicate _key is renamed - #3364
christianhg wants to merge 1 commit into
Conversation
`update value` syncs blocks by position, so removing a block above the caret replaces each later block with the content of the block after it. For a moment the replaced block and the not-yet-replaced block below it share a `_key`, and the duplicate-key normalizer renames the second one with a `set` on `_key`. `handleKeyChange` then pruned the map entries under the old keyed path, which the surviving sibling shares, and re-added only the renamed node. Every shifted block lost its `blockIndexMap` entry along with its spans' entries. A later collapsed `delete.backward` compared paths against the missing entry (`comparePathsInTree` reads it as -1), `before()` found no position, and `deleteCollapsed` fell back to the editor start: one Backspace deleted everything from the top of the document to the caret. After a rename, `handleKeyChange` now rebuilds the entries of every sibling carrying the old or the new key, first occurrence wins, which is what `buildIndexMaps` produces for the same value. This holds for root blocks, spans and container children, and for three or more siblings sharing a key. Pinned by map tests with full expected maps (red on the old transform), an `update value` plus Backspace test, and a remote patch batch that renames one of three same-key blocks and then unsets by key, which guards against restoring the siblings last-wins. Only renames are covered. Inserting, removing or replacing a node while its key is duplicated among its siblings can still leave the map different from a fresh build, as before.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 546b792 The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Bundle Stats✅ No significant changes. All scenario measurements (7)🗺️
Significant means at least 1.0 KB and 1% gzip, or at least 5 ms and 10% import time. |
Pressing Backspace after the editor receives a new value that removes a block above the caret no longer deletes everything from the start of the document to the caret. For example, with the blocks "foo", "bar", "baz" and "qux", syncing a value without "bar" and then pressing Backspace at the end of "baz" now leaves "foo", "ba" and "qux". Previously it left a single empty block followed by "qux".
The value sync replaces blocks by position, so removing "bar" briefly gives two blocks the same
_keyuntil the normalizer renames one. That rename dropped the other block's entries from the block index map, and with the caret's block missing from the map, a collapsed delete couldn't find the point before the caret and fell back to the document start. The rename now rebuilds the entries of every sibling sharing the old or new key, first occurrence wins, matching a fresh build. Other operations on duplicate-keyed siblings (insert, unset, full replace) are unchanged and still diverge from a fresh build, as they do onmain.Note
Medium Risk
Touches incremental block-index maintenance used for selection and delete resolution; scope is limited to
_keyrename handling but incorrect maps can still corrupt edits.Overview
Fixes incorrect block index map updates when a node's
_keyis renamed while another sibling still uses the old or new key—a transient state that can appear during remote value sync before normalization.handleKeyChangeno longer only re-adds the renamed node's subtree; it rebuilds index entries for every sibling sharing the old or new_key, with first occurrence winning, aligned with a freshbuildIndexMaps.SiblingContextnow carries akeyedPrefixto support that scoped rebuild across blocks, spans, and nested rows.User-visible: after an
update valueremoves a block above the caret, Backspace at the end of the next block deletes one character instead of wiping from document start to the caret. Patch flows with duplicate-key siblings before rename/unset are covered by new unit and integration tests; a patch changeset is included.Reviewed by Cursor Bugbot for commit 546b792. Bugbot is set up for automated code reviews on this repo. Configure here.