Cursor-style inline diff with rollbacks: snapshot-based architecture
In Skycode (my AI-agent VS Code fork) I needed inline diff for AI-proposed changes, with per-block accept/reject and rollback to any previous chat message.
Problem
Cursor does this beautifully but proprietary. Open-source alternatives (Continue, Cline) either lack this or work via workspace.applyEdit() — meaning changes write to disk immediately, no rollback.
I wanted:
- Inline diff display without writing to disk
- Per-block accept/reject
- Full rollback to any message snapshot
- Zero data loss for the user
Architecture
Core idea: per-message snapshots. Each AI message creates a rollback point holding the contents of all changed files BEFORE the change was applied.
type MessageSnapshot = {
messageId: string;
timestamp: number;
files: Map<string, FileSnapshot>;
};
When AI wants to change a file, the engine:
- Creates snapshot of original content (if not yet in current message)
- Computes diff and splits into blocks
- Renders inline decorations in the editor
- Waits for user action
Inline decorations
VS Code has no native API for "virtual" diff inside the active document. There is DecorationProvider — but it can only highlight lines. The diff with strikethrough and added lines is drawn by a custom system on top of existing text: added lines are inserted as real lines in TextDocument with a "pending" mark, removed ones are highlighted as a red overlay with line-through.
On accept the pending mark is removed and the original is replaced. On reject the pending lines are deleted and the original is restored from the snapshot.
Rollback
The most interesting part — rollback to an arbitrary message. The UI shows a "Restore" button on each AI message. On click the engine:
- Takes the snapshot of that message
- Restores all files from the snapshot
- Removes all messages after
- Clears current inline diffs
This allows the user to experiment fearlessly: if AI went the wrong way — one click and you are where you were.
Tests
217 unit tests cover:
- Snapshot creation on first file change in a message
- No snapshot duplication on repeated change within the same message
- Correctness of per-block accept/reject
- Restoration on rollback
- Edge cases with concurrent changes
What's next
Currently works on one file at a time. The plan is multi-file diffs with group accept/reject. And a tree visualization of messages (instead of a flat list).