Hi everybody, I've been considering the following thought experiment for undo/redo: 1. Doc state = A 2. Op A is performed by Mandy, Doc state = B 3. Op B is performed by Fred, Doc state = C 4. Mandy hits undo on her local editor 5. Doc state = ?? What does Mandy expect to have happen? Does this undo Op A or Op B? Etherepad's timeline view is a global undo stack, i.e., you can undo changes from any person to revert the document back to a previous state. In contrast, when typing on a collaborative document, does the user expect undo to revert *their* most recent change, or the *document's* most recent change? This leads directly into the next question. Does undo either: a) revert the document to a previous state, resulting in a document in an identical state as before the operation was originally performed e.g., 1. Doc state = A 2. Op A is performed, Doc state = B 3. Op A is undone via magic, Doc state = A OR b) undo a previous action, resulting in a document that is *similar* but not identical to before the operation was performed 1. Doc state = A 2. Op A is performed, Doc state = B 3. Op A is undone via magic, Doc state = C What is the difference you ask? Undo for direct formatting, range removal, etc. is really tricky because in the current implementations data is irretreviably lost as part of execution. The interactions are also far more complex than most other operations. E.g., What should happen if Fred inserts new text into the middle of a block Mandy is removing? What should happen if Fred inserts new text into the middle of a block Mandy is formatting? To implement undo option A with these types of operations the ops must be re-written to do less, pushing the complexity out into what generates the op. E.g., OpRemoveText is only allowed to remove text nodes, and SessionController (or a helper) is responsible for coming up with the exact list of text nodes to be removed, and generating an OpRemoveText for each of these. If however Option B is acceptable (i.e., the document should be visually indistinguishable, but is allowed to be different at the DOM level) there is another potential solution. Assuming option B and local undo stack: * Each client will generate their own local undo stack as each local operation is executed. * This local undo stack is rebased on every external change using the standard OT behaviours, and is not shared. * At the point the user undoes their recent change, the undo operations are forwarded from their local client as if it was a normal change. Thoughts, criticisms, concerns or shouting are all welcome :) Philip
Hi Philip, no time for detailed reply today, but definitely interesting topic, which can be seen already to sit & wait directly on our way into the future :) Am Mittwoch, 31. Juli 2013, 04:28:23 schrieb Philip Peitsch:
Hi everybody,
I've been considering the following thought experiment for undo/redo:
1. Doc state = A 2. Op A is performed by Mandy, Doc state = B 3. Op B is performed by Fred, Doc state = C 4. Mandy hits undo on her local editor 5. Doc state = ??
What does Mandy expect to have happen? Does this undo Op A or Op B?
Another option would be to just have Undo to be another new operation which just is the invert of the op that should be undone. Which means the original op will stay part of the op stack, and "just" a new one is added on top. Then no special handling of ops is needed, and transformation can be done as before. It might be also possible to undo any op in the stack (given the inverted op can be transformed through the rest of the op stack). So the UI could offer to just undo your own last ops, or undo ops/actions from everyone. Would also mean no need for the complexity of a dedicated undo/redo manager. But also means that your accidental paste of a private message to your better half will also after "undo!!!" stay in the op history, uh :) (though doing deletion surgery on the op stack could be another future feature to have) Of course this requires that all info needed to invert an operation is still available, like the properties of removed styles, overwritten style properties, removed text, etc. Which means quite some changes to our current opspecs and op creation code, but this should not stop us, at least in the long run. In any case we should see to keep clients ODTs as convergent as possible, ideally 100 %. Anything else might only mean troubles later and hard to reproduce errors, my gut tells. Cheers Friedrich -- Friedrich W. H. Kossebau // KO GmbH http://kogmbh.com/legal/
Hi everybody,
I've been considering the following thought experiment for undo/redo:
1. Doc state = A 2. Op A is performed by Mandy, Doc state = B 3. Op B is performed by Fred, Doc state = C 4. Mandy hits undo on her local editor 5. Doc state = ??
What does Mandy expect to have happen? Does this undo Op A or Op B?
Another option would be to just have Undo to be another new operation which just is the invert of the op that should be undone. Which means the original [...]
You just missed my proposal mere paragraphs away that suggests the same thing :)
[...] It might be also possible to undo any op in the stack (given the inverted op can be transformed through the rest of the op stack). So the UI could offer to just undo your own last ops, or undo ops/actions from everyone.
Correct. This should also be possible (though, how desirable? I'm unsure)
Would also mean no need for the complexity of a dedicated undo/redo manager.
I disagree. You will still (always) require a dedicated undo-redo manager. For example, in LibreOffice (and many editors), typing consecutive letters without changing the cursor position in between is undone as a single block. This grouping happens after each individual operation is fired away. (If you look at the current undo manager it is actually rather straightforward... not as much complexity in there as you fear I assure you! :) )
Of course this requires that all info needed to invert an operation is still available, like the properties of removed styles, overwritten style properties, removed text, etc. Which means quite some changes to our current opspecs and op creation code, but this should not stop us, at least in the long run.
This is the main reason I was raising this. Rather than having the undo op be immediately available when the original op is created, it will likely be easier to literally generate the undo op *whilst* applying the original. This means less traffic for everyone, and (theoretically) allows all the benefits one would expect from an undo stack.
In any case we should see to keep clients ODTs as convergent as possible, ideally 100 %. Anything else might only mean troubles later and hard to reproduce errors, my gut tells.
Correct. I agree with this fully! But, nowhere have you said that undo needs to result in exactly the same document as before the (now undo) operation was performed. So... is it safe to say that "visually identical" is acceptable, even if the document is "changed"? :) --- Philip Peitsch Mob: 0439 810 260
participants (3)
-
Friedrich W. H. Kossebau
-
Philip Peitsch
-
Philip Peitsch