VisualEditor/Undo and Redo

Desired behavior edit

Undo edit

Intuitively, undoes the last thing you did. Does not undo, for instance, a user-initiated deletion & insertion. This must be distinguished from a delete/insert sequence that we recorded because that was the simple way to accomplish text that, when pasted, replaced the selection.

Undo never depends on the current selection, only the last action you took.

Selection at Start Action After Undo Selection at end
Insertion point Enter text Text removed Insertion point, where we started
Selected text Enter text Original text replaced Text selected
Insertion point Backspace to delete text Text replaced Insertion point where we started
Selected text Delete text Text replaced Text selected

Redo edit

Redoes something you just did.

Comparison to other editors edit

Implementation edit

The Visual Editor currently has a model of transactions, each of which has a number of operations. Typical single-character insert transaction contains "retain" which locates the cursor, "insert" one character, then "retain" again to go to the end of the document (uncertain what that does for us).

Question, shouldn't insert/delete text be made to be a single transaction, not split among many? Or is it that way already?

These transactions are grouped into "states", which is a bit of a misnomer as they don't describe the state of the editor, they just bundle transactions together.

When recording a transaction, can also add 'combine' flag to indicate it ... (?should be combined) with other transactions in state. For instance, when typing individual characters you want them combined into the same "state" so they will all be undone together.

Original implementation inserted select events into state, which neatly divided bursts of typing. Seemed that we did not need that since we could infer previous selection state from transactions. But now I am starting to see why Trevor added this, it's just easier to keep that history of selections rather than reconstruct it. (?)

What we really want edit

It seems clear that selections and transactions/states should not be in the same data structure. Selections really just define where and how the next insertion is going to happen. Transactions are rolled forward and back. It should be possible to reconstruct the last selection just from what the undone/redone transaction was.

So, the ideal thing would be that we have just one data structure which consists of undo/redoable transactions, which themselves contain multiple operations. Some transactions might be fairly complicated, corresponding to replacements or unwrappings/rewrappings of content.

Hack for December deadline edit

We can't do this right now because the "tree sync" of the UI occurs at the end of every simple transaction. Trevor believes it's too hard to fix that code before informal deadline of December 9th.

So rather than rewrite all of that we will work with a model where we continue to store human-requested selections in the same data structure as transactions, in order to provide obvious points where to stop undoing / redoing. Then we can redo that selection.

This also means that when new human-entered selections are entered after another selection, it replaces that selection. They don't pile up in the transaction log (states).