@@ -26,7 +26,7 @@ import { RedoOp } from './RedoOp';
2626 /* The diagram above shows the situations where an UndoOp may be necessary. Here
2727 * InvAfterOp on the top left is invalidating Op1, and transitively Op2 that is
2828 * causally dependant on Op1. However, InvAfterOp is itself being undone, then
29- * redone, then undone again, and those are also cascaded to Op1 and Op2.
29+ * redone, then undone again, and those actions are also cascaded to Op1 and Op2.
3030 *
3131 * There are 4 possible cases, marked above:
3232 *
@@ -36,15 +36,13 @@ import { RedoOp } from './RedoOp';
3636 * (2) is a cascade of (1) to Op2, that is dependent on Op1.
3737 *
3838 * (3) is a cascade of a RedoOp on the original InvAfterOp, that triggers a new
39- * undo for Op1. Notice in this case that te RedoOp is cascaded as an undo.
39+ * undo for Op1. Notice in this case that the RedoOp is cascaded as an undo.
4040 *
4141 * (4) is similar to (2), but is undoing a RedoOp for Op2 instead of Op2 itself.
4242 *
4343 * It is important to notice that in all cases but (1),
4444 *
45- * undo.target.causal == undo.causal.target
46- *
47- * i.e. the squares in the diagram above commute.
45+ * undo.causal.target \in undo.target.causalOps
4846 *
4947 */
5048
@@ -54,8 +52,8 @@ class UndoOp extends MutationOp {
5452
5553 targetOp ?: MutationOp ; // the op that will be undone
5654
57- constructor ( targetOp ?: MutationOp , reason ?: InvalidateAfterOp | UndoOp | RedoOp ) {
58- super ( targetOp ?. targetObject ) ;
55+ constructor ( targetOp ?: MutationOp , causalOp ?: InvalidateAfterOp | UndoOp | RedoOp ) {
56+ super ( targetOp ?. targetObject , causalOp === undefined ? undefined : [ causalOp ] . values ( ) ) ;
5957
6058 if ( targetOp !== undefined ) {
6159 this . targetOp = targetOp ;
@@ -64,74 +62,51 @@ class UndoOp extends MutationOp {
6462 throw new Error ( "An undo op can't be undone this way, please see RedoOp." ) ;
6563 }
6664
67- const targetOpCausalOps = this . targetOp . causalOps ;
65+ const undoneOp = targetOp ;
6866
69- if ( targetOpCausalOps === undefined ) {
67+ if ( undoneOp . causalOps === undefined ) {
7068 throw new Error ( "Can't undo an op that has no causal assumptions." ) ;
7169 }
7270
73- if ( reason === undefined ) {
74- throw new Error ( 'Creating an undo op, but no reason was provided.' ) ;
71+ if ( causalOp === undefined ) {
72+ throw new Error ( 'Creating an undo op, but no causal op was provided.' ) ;
7573 }
7674
77- this . reasonOp = reason . createReference ( ) ;
78-
79- if ( reason instanceof InvalidateAfterOp ) {
75+ if ( causalOp instanceof InvalidateAfterOp ) {
8076
81- const invalidateAfterOp = reason as InvalidateAfterOp ;
77+ const invAfterOp = causalOp ;
8278
83- const targetOp = invalidateAfterOp . getTargetOp ( ) ;
79+ if ( ! undoneOp . causalOps . has ( ( invAfterOp . targetOp as MutationOp ) . createReference ( ) ) ) {
8480
85- if ( ! targetOpCausalOps . has ( targetOp . createReference ( ) ) ) {
86- throw new Error ( 'Wrong undo: the target op is not a consequence of the invalidated op.' ) ;
81+ throw new Error ( 'Creating undo because of an InvalidateAfterOp, but the op being undone does not depend on the invalidated one.' ) ;
8782 }
8883
89- // here we could check that targetOp is really outside of invalidateAfterOp.terminalOps,
84+ // here we could also check that targetOp is really outside of invalidateAfterOp.terminalOps,
9085 // but that's costly, and constructor checks aim only to aid debugging, so we'll not.
9186
92- if ( ! this . getTargetObject ( ) . equals ( invalidateAfterOp . getTargetObject ( ) ) ) {
87+ if ( ! this . getTargetObject ( ) . equals ( invAfterOp . getTargetObject ( ) ) ) {
9388 throw new Error ( 'Trying to undo an op in a different mutable object than the invalidation op.' ) ;
9489 }
9590
9691
9792 } else {
9893
99- // the op that is being undone (this.targetOp) has the op
100- // that was undone/redone by reason (this.reasonOp.targetOp)
101- // in its causalOps (i.e. this.reasonOp.targetOp \in this.targetOp.causalOps)
102-
103- // this diagram commutes:
104-
105- /* this.reasonOp.targetOp
106- * === < --------- this.targetOp
107- * this.targetOp.reasonOp ^
108- * ^ |
109- * | targetOp | targetOp
110- * | reasonOp |
111- * this.reasonOp <--------------------- this
112- */
113-
114-
115-
116-
117-
118- if ( this . targetOp . causalOps === undefined ) {
119- throw new Error ( 'The targetOp of an UndoOp must have a non-empty causalOps set.' ) ;
120- }
94+ // Must be one of the cases 2-4 mentioned in the note above, hence:
95+ // undo.causal.target \in undo.target.causalOps
12196
122- if ( ! this . targetOp . causalOps . has ( ( reason . targetOp as HashedObject ) . createReference ( ) ) ) {
123- throw new Error ( 'The targetOp of an UndoOp must have a non-empty causalOps set .' ) ;
97+ if ( ! undoneOp . causalOps . has ( ( causalOp . targetOp as MutationOp ) . createReference ( ) ) ) {
98+ throw new Error ( 'Attempting to create a cascaded UndoOp, but the received causal op does not trigger a valid cascade for the op being undone .' )
12499 }
125100
126- if ( reason instanceof RedoOp ) {
101+ if ( causalOp instanceof RedoOp ) {
127102
128103 if ( ! ( this . targetOp instanceof RedoOp ) ) {
129104 throw new Error ( 'If the reason for an UndoOp is a RedoOp, its target must be a RedoOp too.' ) ;
130105 }
131106
132- } else if ( reason instanceof UndoOp ) {
107+ } else if ( causalOp instanceof UndoOp ) {
133108
134- const cascadedUndoOp = reason as UndoOp ;
109+ const cascadedUndoOp = causalOp as UndoOp ;
135110
136111 const undoneDepOp = cascadedUndoOp . getTargetOp ( ) ;
137112
0 commit comments