automerge / automerge-classic

A JSON-like data structure (a CRDT) that can be modified concurrently by different users, and merged again automatically.
http://automerge.org/
MIT License
14.75k stars 466 forks source link

Any advice for how to implement undo today, sans official support? #405

Closed nwpointer closed 3 years ago

nwpointer commented 3 years ago

I was considering manually creating an map of inverse operations for each valid operation in my app and then when the user requests undo, use the change message to look up and apply the corresponding inverse function. Kinda hacky and limited but pretty simple.

psudo code:

operations = {
    'add': (a,b)=>a+b
    'sub': (a,b)=>a-b
}

inverses = {
    'add': sub
    'sub': add
}

apply(operationName, args){
    Automerge.change(doc, `${operationName},${args.join.(',')}`, operations[operationName])
}

undo(){
    [lastop, ...args] = getLatestOp().split(',')
    Automerge.change(doc, `${lastop},${args.join.(',')}`, inverses[operationName])
}

getLatestOp(){
    // grabs the latest non undone operation from history, optionally filtering action by other users
}

if anybody else has ideas / solutions that have worked for them would love to discuss

ept commented 3 years ago

Hi @nwpointer, it depends a bit what operations you need to support. For example, if you only need to undo assignment of a key in a map, then you could record the old value of the key before you assign it, and then the undo resets the key to its previous value. If you need to undo the insertion of an element into a list, you can remember the index at which you inserted, and undo the insertion by deleting the element at that index. However, things get more complicated if other users may also be generating changes, and in that case you probably want to only undo the local user's changes, not changes made by other users (also you would have to account for list indexes changing due to other users' insertions/deletions).

The real solution is of course to add undo support to Automerge itself, but I'm afraid we don't have a timeline for doing that right now.