Skip to content

[diffs/edit] Add undo-traversal seals and delete-direction stops to coalescing#988

Closed
fat wants to merge 3 commits into
beta-1.3from
fat/coalescing-stops
Closed

[diffs/edit] Add undo-traversal seals and delete-direction stops to coalescing#988
fat wants to merge 3 commits into
beta-1.3from
fat/coalescing-stops

Conversation

@fat

@fat fat commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What

Fixes the three pinned EditStack coalescing bugs (stacked on #987, which it builds on):

  1. Typing after undo fused into pre-undo history — one undo then erased both the old and new text.
  2. An undo-boundary entry stopped shielding once undone — paste boundaries evaporated from the redo stack.
  3. Backspace then forward-delete at the same pivot merged into a single undo step instead of getting a stop when the delete direction flips.

How

  • Traversal seals (bugs 1+2, one mechanism): every popUndoToRedo/popRedoToUndo stamps the newly exposed top undo entry with a durable sealed flag; shouldCoalesceEditStackEntry rejects a sealed previous entry. Traversal is an undo stop in both directions — typing after undo, after redo, or at any depth of ping-pong starts a fresh step. undoBoundary's paste/cut semantics are untouched.
  • Delete-direction stops (bug 3): pure-delete entries whose caret selections pair 1:1 with edit edges get a deleteDirection label (backspace/delete); coalescing rejects only when both entries' labels are known and opposite. Labels propagate through merges, so a whole backspace run still stops a forward delete. Anything ambiguous (range selections, multi-caret partial eligibility, untracked edits) stays on today's geometry-only rules.

The judgment call reviewers should look at

The both-known rule exists because a single-sided version regressed consecutive outdent grouping — outdent deletes coincidentally place carets on edit edges, and two Shift+Tabs started costing two undo steps. Caught by adversarial review probes, fixed, and pinned by three new regression tests. One documented residual: an outdent pair with the caret mid-indent is byte-identical to backspace-then-forward-delete, so it keeps getting two steps — no classifier can split those without breaking the pinned contract. Threading explicit direction from the command layer (delete handlers, indent dispatch) is the noted follow-up if we want editor-level precision.

Verification

  • 3 pins flipped with assertions unchanged (audit: removed-expect set byte-identical to added-expect set); 3 new regression tests for the outdent interaction.
  • 55/55 independent review probes: ping-pong typing, same-direction runs (single/multi-char/multi-caret), forward→backspace symmetry, boundary shields, maxEntries, seal-vs-run-continuation.
  • Full diffs suite 1281 pass / 0 fail (twice, identical); typecheck + oxlint + oxfmt clean.
  • Known-bug pins remaining: 20.

Third PR of the burn-down (#986, #987).

🤖 Generated with Claude Code

fat and others added 3 commits July 14, 2026 18:11
Design decision: edits applied through applyEdits are not a separate
semantic class — a mixed programmatic/local sequence must behave exactly
like the all-local sequence. Previously updateHistory=false skipped the
EditStack entirely, so history entries carried stale offsets after any
programmatic edit: undo could corrupt text, redo replayed at wrong
positions, and exhaustion never restored the original document.

TextDocument.applyResolvedEdits now always creates/coalesces/pushes an
entry; the updateHistory flag only gates caller-supplied selection and
undo-boundary metadata. Editor.applyEdits always tracks (its flag is
kept but deprecated). Undo/redo replay is untouched — it always wrote
through the private buffer path, so no recursion. Composition previews
never touched the document and are unaffected.

Flips the seven history-equivalence pins to permanent regression tests
(assertions unchanged — they encode the all-tracked reference), rewrites
the two old-model pins, and updates the one bypass-model test elsewhere
in the suite plus the public docs. Pinned known-bug count: 32 -> 25.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pre-commit formatter globs cover js/ts but not .mdx, so the History
section edit landed unformatted and failed root:format-check in CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oalescing

Fixes the three pinned coalescing bugs:

1. Typing after undo fused into the newly exposed pre-undo entry.
2. An undo-boundary entry stopped shielding once undone.
3. Backspace then forward-delete at the same pivot merged into one step.

Every undo/redo pop now stamps the exposed top entry with a durable
`sealed` flag that rejects forward coalescing — traversal is an undo
stop in both directions, covering bugs 1 and 2 with one mechanism.
Delete entries additionally carry a `deleteDirection` classified from
caret geometry; coalescing rejects only when both entries' directions
are known and opposite, so single-labeled and unlabeled (range-based,
untracked, multi-caret-partial) deletes keep today's geometry rules.
Direction labels propagate through merges so a whole backspace run
still stops a forward delete.

The both-known rule matters: a single-sided check regressed consecutive
outdent grouping (outdent deletes coincide with caret edges), caught in
adversarial review and pinned by three new regression tests. One
documented residual: a caret mid-indent outdent pair is byte-identical
to backspace-then-forward-delete and keeps getting two undo steps;
threading explicit direction from the command layer is the follow-up.

Pins flipped with assertions unchanged; suite 1281 pass / 0 fail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pierre-docs-diffs Ready Ready Preview Jul 15, 2026 2:09am
pierre-docs-diffshub Ready Ready Preview Jul 15, 2026 2:09am
pierre-docs-trees Ready Ready Preview Jul 15, 2026 2:09am
pierrejs-diff-demo Ready Ready Preview Jul 15, 2026 2:09am

Request Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3adadf45a8

ℹ️ 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".

Comment on lines +241 to +245
if (
selection.start.line !== selection.end.line ||
selection.start.character !== selection.end.character
) {
return undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve delete direction on the command path

When Backspace/Delete is executed through the real helper, applyDeleteCharacterToSelections builds non-collapsed deleteSelections and applyTextReplaceToSelections passes those ranges to applyResolvedEdits as selectionsBefore (selection.ts lines 1203-1234 and 787-792). Because those selections are the deleted ranges rather than the original carets, this collapsed-selection check returns undefined, so no deleteDirection is recorded and a real Backspace followed by forward Delete at the same pivot still coalesces into one undo step. The new regression test bypasses this path by calling TextDocument.applyEdits with caret selections directly.

Useful? React with 👍 / 👎.

@necolas
necolas requested a review from ije July 15, 2026 16:09
Base automatically changed from fat/history-equivalence to beta-1.3 July 15, 2026 17:27
@fat fat closed this Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant