I was re-reading the classic Figma blog on this problem and thought more carefully about this section:
https://www.figma.com/blog/how-figmas-multiplayer-technology-works/#implementing-undo
Everytime I’ve read this in the past, I kind of skipped over this part thinking it was saying something I already understood. Also it's hard to read because the movie loops and you can't pause, so I'll repeat the key point here.
<aside> ⚠️ Important Invariant if the user presses undo n times, then redo n times, and nothing else has happened in between, then the document should end up how it was before pressing undo.
</aside>
Example:
In our current API, at step 5, the prio changes to 0, not 5.
Here’s a table to help keep this straight:
user | action | state | comment |
---|---|---|---|
⦰ | initial | cantaloupe | |
me | set | blue | |
collab | set | sausage | |
me | undo | cantaloupe | it was the state before the action i’m undoing |
collab | set | burrito | |
me | redo | sausage | it was the state before undo |
me | undo | burrito | it was the state before the action i’m undoing |
The Figma blog points out where we went wrong: we can't set the redo function when the undo entry is created. It has to be generated when undo happens.
Thinking more abstractly, if we have a history like:
t0 - t1 - t2
And the user undoes t1, we now have a history like:
t0 - t1 - t2 - t1'