fix: wrap history withMerging, withNewBatch, and withoutMerging in try/finally to ensure state cleanup on error#6063
Conversation
🦋 Changeset detectedLatest commit: 37b2a9d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
…y to ensure state cleanup on error
a04d27a to
0d02a9f
Compare
|
Findings
|
Tried addressing your findings can you please check again. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 37b2a9de91
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| fn() | ||
| } finally { | ||
| MERGING.set(editor, prev) | ||
| SPLITTING_ONCE.set(editor, prevSplitting) |
There was a problem hiding this comment.
Do not re-arm the outer split flag after nested batches
When withNewBatch is nested inside another withNewBatch before the outer callback has applied an operation, prevSplitting is true; if the inner callback applies an operation, with-history consumes that flag by setting SPLITTING_ONCE back to undefined, but this finally block restores it to true. The next operation in the outer callback is then forced into another new undo batch even though it is a subsequent operation and should merge as usual, regressing nested batch behavior introduced by this cleanup change.
Useful? React with 👍 / 👎.
Description
HistoryEditor.withMerging,HistoryEditor.withNewBatch, andHistoryEditor.withoutMergingset internal state flags (MERGING,SPLITTING_ONCE) on WeakMaps before calling the user-provided fn(), then restore the previous state afterward. However, if fn() throws an error, the state restoration code is never reached, leaving the editor permanently stuck in a corrupted internal state (e.g., all subsequent operations are merged into a single undo batch, or splitting is permanently enabled).This fix wraps the fn() call and state restoration in try/finally blocks, ensuring the editor's internal state is always correctly restored regardless of whether fn() succeeds or throws.
Issue
Issue N/A — Discovered via code inspection. (Follows up on PR #5837 which fixed the same bug for
withoutSaving).Context
The fix follows the exact same pattern already established by
withoutSavingin the same file (history-editor.ts), as well aswithoutNormalizingin the core slate package (without-normalizing.ts). Both of these methods wrap fn() in try/finally to protect against exceptions. This change simply applies the same defensive pattern to the three remaining methods that were missing it.The change is purely defensive — when fn() succeeds (the normal case), behaviour is identical to before.
Checks
yarn fix.)yarn start.)yarn changeset add.)