|
|
@@ -10,18 +10,18 @@ import Button from '../ui/button/button.js';
|
|
|
import ButtonView from '../ui/button/buttonview.js';
|
|
|
|
|
|
/**
|
|
|
- * Undo feature. Introduces the "Undo" and "Redo" buttons to the editor.
|
|
|
+ * The undo feature. It introduces the Undo and Redo buttons to the editor.
|
|
|
*
|
|
|
- * Below is the explanation of undo mechanism working together with {@link engine.model.CompressedHistory CompressedHistory}:
|
|
|
+ * Below is the explanation of the undo mechanism working together with {@link engine.model.CompressedHistory CompressedHistory}:
|
|
|
*
|
|
|
* Whenever a {@link engine.model.Delta delta} is applied to the {@link engine.model.Document document}, it is saved to
|
|
|
* `CompressedHistory` as is. The {@link engine.model.Batch batch} that owns that delta is also saved, in {@link undo.UndoCommand},
|
|
|
- * together with selection that was on the document before the delta was applied. Batch is saved instead of delta because
|
|
|
- * changes are undone batch-by-batch, not delta-by-delta and batch is seen as one undo step.
|
|
|
+ * together with the selection that was present in the document before the delta was applied. A batch is saved instead of the delta
|
|
|
+ * because changes are undone batch-by-batch, not delta-by-delta and a batch is seen as one undo step.
|
|
|
*
|
|
|
- * After some changes happen to the document, we can represent `CompressedHistory` and `UndoCommand` stack as follows:
|
|
|
+ * After some changes happen to the document, the `CompressedHistory` and `UndoCommand` stack can be represented as follows:
|
|
|
*
|
|
|
- * history undo stack
|
|
|
+ * History Undo stack
|
|
|
* =========== ==================================
|
|
|
* [delta A1] [batch A with selection before A1]
|
|
|
* [delta B1] [batch B with selection before B1]
|
|
|
@@ -31,18 +31,18 @@ import ButtonView from '../ui/button/buttonview.js';
|
|
|
* [delta B3]
|
|
|
* [delta C3]
|
|
|
*
|
|
|
- * Where deltas starting by the same letter are from same batch.
|
|
|
+ * Where deltas starting with the same letter are from same batch.
|
|
|
*
|
|
|
- * Undoing a batch means that we need to generate set of deltas which will reverse effects of that batch. I.e. if batch
|
|
|
- * added several letters, undoing batch should remove them. It is important to apply undoing deltas in reversed order,
|
|
|
- * so if batch has delta `X`, `Y`, `Z` we should apply reversed deltas `Zr`, `Yr` and `Xr`. In other case, reversed delta
|
|
|
- * `Xr` would operate on wrong document state, because delta `X` does not know that delta `Y` and `Z` happened.
|
|
|
+ * Undoing a batch means that a set of deltas which will reverse the effects of that batch needs to be generated. For example, if a batch
|
|
|
+ * added several letters, undoing the batch should remove them. It is important to apply undoing deltas in the reversed order,
|
|
|
+ * so if a batch has delta `X`, `Y`, `Z`, reversed deltas `Zr`, `Yr` and `Xr` need to be applied. Otherwise reversed delta
|
|
|
+ * `Xr` would operate on a wrong document state, because delta `X` does not know that deltas `Y` and `Z` happened.
|
|
|
*
|
|
|
- * After deltas from undone batch got {@link engine.model.Delta#getReversed reversed} we need to make sure if they are
|
|
|
- * ready to be applied. In our scenario, delta `C3` is the last delta so `C3r` bases on up-to-date document state so
|
|
|
+ * After deltas from an undone batch got {@link engine.model.Delta#getReversed reversed}, one needs to make sure if they are
|
|
|
+ * ready to be applied. In the scenario above, delta `C3` is the last delta and `C3r` bases on up-to-date document state, so
|
|
|
* it can be applied to the document.
|
|
|
*
|
|
|
- * history undo stack
|
|
|
+ * History Undo stack
|
|
|
* =========== ==================================
|
|
|
* [delta A1 ] [batch A with selection before A1]
|
|
|
* [delta B1 ] [batch B with selection before B1]
|
|
|
@@ -53,18 +53,23 @@ import ButtonView from '../ui/button/buttonview.js';
|
|
|
* [delta C3 ]
|
|
|
* [delta C3r]
|
|
|
*
|
|
|
- * Next is delta `C2`, reversed to `C2r`. `C2r` bases on `C2`, so it bases on wrong document state. It needs to be
|
|
|
- * transformed by deltas from history that happened after it, so it "knows" about them. Let `C2' = C2r * B3 * C3 * C3r`,
|
|
|
+ * Next is delta `C2`, reversed to `C2r`. `C2r` bases on `C2`, so it bases on the wrong document state. It needs to be
|
|
|
+ * transformed by deltas from history that happened after it, so it "knows" about them. Let us assume that `C2' = C2r * B3 * C3 * C3r`,
|
|
|
* where `*` means "transformed by". As can be seen, `C2r` is transformed by a delta which is undone afterwards anyway.
|
|
|
* This brings two problems: lower effectiveness (obvious) and incorrect results. Bad results come from the fact that
|
|
|
* operational transformation algorithms assume there is no connection between two transformed operations when resolving
|
|
|
- * conflicts, which is true for, i.e. collaborative editing, but is not true for undo algorithm.
|
|
|
+ * conflicts, which is true for example for collaborative editing, but is not true for the undo algorithm.
|
|
|
*
|
|
|
* To prevent both problems, `CompressedHistory` introduces an API to {@link engine.model.CompressedDelta#removeDelta remove}
|
|
|
- * deltas from history. It is used to remove undone and undoing deltas after they are applied. It feels right - delta is
|
|
|
- * undone/reversed = "removed", there should be no sign of it in history (fig. 1). `---` symbolizes removed delta.
|
|
|
+ * deltas from history. It is used to remove undone and undoing deltas after they are applied. It feels right — since when a
|
|
|
+ * delta is undone or reversed, it is "removed" and there should be no sign of it in the history (fig. 1).
|
|
|
*
|
|
|
- * history (fig. 1) history (fig. 2) history (fig. 3)
|
|
|
+ * Notes:
|
|
|
+ *
|
|
|
+ * * `---` symbolizes a removed delta.
|
|
|
+ * * `'` symbolizes a reversed delta that was later transformed.
|
|
|
+ *
|
|
|
+ * History (fig. 1) History (fig. 2) History (fig. 3)
|
|
|
* ================ ================ ================
|
|
|
* [delta A1] [delta A1] [delta A1]
|
|
|
* [delta B1] [delta B1] [delta B1]
|
|
|
@@ -77,26 +82,25 @@ import ButtonView from '../ui/button/buttonview.js';
|
|
|
* [---C2'--] [---C2'--]
|
|
|
* [---C1'--]
|
|
|
*
|
|
|
- * Now we can transform `C2r` only by `B3` and remove both it and `C2` (fig. 2). Same with `C1` (fig. 3). `'` symbolizes
|
|
|
- * reversed delta that was later transformed.
|
|
|
+ * `C2r` can now be transformed only by `B3` and both `C2'` and `C2` can be removed (fig. 2). Same with `C1` (fig. 3).
|
|
|
*
|
|
|
- * But what about that selection? For batch `C`, undo feature remembers selection just before `C1` was applied. It can be
|
|
|
+ * But what about that selection? For batch `C`, undo feature remembers the selection just before `C1` was applied. It can be
|
|
|
* visualized between delta `B2` and `B3` (see fig. 3). As can be seen, some operations were applied to the document since the selection
|
|
|
- * state was remembered. Setting document selection as it was remembered would be incorrect. It feels natural that
|
|
|
- * selection state should also be transformed by deltas from history. Same pattern applies as with transforming deltas - ranges
|
|
|
- * should not be transformed by undone and undoing deltas. Thankfully, those deltas are already removed from history.
|
|
|
+ * state was remembered. Setting the document selection as it was remembered would be incorrect. It feels natural that
|
|
|
+ * the selection state should also be transformed by deltas from history. The same pattern applies as with transforming deltas —
|
|
|
+ * ranges should not be transformed by undone and undoing deltas. Thankfully, those deltas are already removed from history.
|
|
|
*
|
|
|
- * Unfortunately, a problem appears with delta `B3`. It still remembers context of deltas `C2` and `C1` on which it bases.
|
|
|
- * It is an obvious error: i.e. transforming by that delta would lead to wrong results or "repeating" history would
|
|
|
- * produce different document than actual.
|
|
|
+ * Unfortunately, a problem appears with delta `B3`. It still remembers the context of deltas `C2` and `C1` on which it bases.
|
|
|
+ * It is an obvious error — transforming by that delta would lead to incorrect results or "repeating" history would
|
|
|
+ * produce a different document than the actual one.
|
|
|
*
|
|
|
- * To prevent this situation, we have to also {@link engine.model.CompressedHistory#updateDelta update} `B3` in history.
|
|
|
- * It should be kept in a state that "does not remember" deltas that has been removed from the history. It is easily
|
|
|
- * achieved while transforming reversed delta. I.e., when `C2r` is transformed by `B3`, at the same time we transform
|
|
|
- * `B3` by `C2r`. Transforming `B3` that remembers `C2` by delta reversing `C2` effectively makes `B3` "forget" about `C2`.
|
|
|
- * By doing those transformation we effectively make `B3` base on `B2` which is a correct state of history (fig. 4).
|
|
|
+ * To prevent this situation, `B3` needs to also be {@link engine.model.CompressedHistory#updateDelta updated} in history.
|
|
|
+ * It should be kept in a state that "does not remember" deltas that were removed from history. It is easily
|
|
|
+ * achieved while transforming the reversed delta. For example, when `C2r` is transformed by `B3`, at the same time `B3` is
|
|
|
+ * transformed by `C2r`. Transforming `B3` that remembers `C2` by a delta reversing `C2` effectively makes `B3` "forget" about `C2`.
|
|
|
+ * By doing these transformation you effectively make `B3` base on `B2` which is the correct state of history (fig. 4).
|
|
|
*
|
|
|
- * history (fig. 4) history (fig. 5)
|
|
|
+ * History (fig. 4) History (fig. 5)
|
|
|
* =========================== ===============================
|
|
|
* [delta A1] [---A1---]
|
|
|
* [delta B1] [delta B1 "without A1"]
|
|
|
@@ -110,8 +114,8 @@ import ButtonView from '../ui/button/buttonview.js';
|
|
|
* [---C1'--] [---C1'--]
|
|
|
* [---A1'--]
|
|
|
*
|
|
|
- * Selective undo works on the same basis, however instead of undoing the last batch in undo stack, any batch can be undone.
|
|
|
- * Same algorithm applies: deltas from batch (i.e. `A1`) are reversed and then transformed by deltas stored in history,
|
|
|
+ * Selective undo works on the same basis, however, instead of undoing the last batch in the undo stack, any batch can be undone.
|
|
|
+ * The same algorithm applies: deltas from a batch (i.e. `A1`) are reversed and then transformed by deltas stored in history,
|
|
|
* simultaneously updating them. Then deltas are applied to the document and removed from history (fig. 5).
|
|
|
*
|
|
|
* @memberOf undo
|