fix(undo): drop a document's undo stack when its file is rewritten while switched away#78
Merged
Conversation
…ile switched away Per-document undo (#77) makes a document's undo stack survive switching away and back. But the stack records absolute text ranges, so if the document's file is rewritten externally while backgrounded — e.g. renaming a node rewrites the [[label]] in every file that links it — switch-back reloads the changed text and Cmd+Z replays stale ranges, corrupting the text (or silently no-op'ing when a range is out of bounds). Snapshot each document's content (storage form) on switch-away and, on switch-back, drop its undo stack when the reloaded text differs. Snapshots are pruned alongside the per-document undo managers. Also corrects the now-stale `documentId` doc comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Per-document undo (#77) makes a document's undo stack survive switching away and back. But AppKit's text undo records absolute character ranges, so if a document's file is rewritten externally while it's switched away, switch-back reloads the changed text and Cmd+Z replays stale ranges against it.
Concrete repro in the embedder (wiki-link notes): edit note A (which links
[[B]]) → switch to B → rename B (after the debounce the rename rewrites A's file on disk, changing the[[label]]length) → switch back to A → Cmd+Z corrupts A's text (misplaced edit), or silently no-ops when the stale range is now out of bounds.Before #77 this couldn't happen — undo was wiped on every switch, so switch-back Cmd+Z was an empty no-op. Surviving undo is the feature; this PR makes it safe.
Fix
undoContentSnapshots, fromlastSyncedText).invalidateUndoIfContentDiverged(for:incomingText:)drops that document's undo stack when the reloaded text differs from the snapshot (timing-independent; the SwiftUI two-phase switch is covered by the existinglastSyncedText == textearly-return, so the switch branch runs once with the real reloaded text).documentIddoc comment ("Changing this invalidates undo history" → undo now survives switches).Net: +/- across 3 source files + a focused test. No public API change.
Test
swift test— 131 pass (128 existing + 3 new inPerDocumentUndoTests): stable manager per document / distinct across / original returned on switch-back; undo dropped only on diverged content; no snapshot never clears. Also verified end-to-end in a Debug build (rename-linked-node repro no longer corrupts; normal Cmd+Z-survives-switch still works).🤖 Generated with Claude Code