Skip to content

Commit d8324b1

Browse files
committed
wip - op invalidation
1 parent 826c4fd commit d8324b1

File tree

2 files changed

+50
-47
lines changed

2 files changed

+50
-47
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { InvalidateAfterOp } from "./InvalidateAfterOp";
2+
import { MutationOp } from "./MutationOp";
3+
4+
5+
class CascadedInvalidateOp extends MutationOp {
6+
7+
targetOp?: MutationOp;
8+
undo?: boolean;
9+
10+
constructor(undo?: boolean, targetOp?: MutationOp, causalOp?: InvalidateAfterOp|CascadedInvalidateOp) {
11+
super(targetOp?.targetObject, causalOp === undefined? undefined : [causalOp].values());
12+
13+
if (undo !== undefined) {
14+
this.undo = undo;
15+
}
16+
17+
}
18+
19+
getClassName(): string {
20+
throw new Error("Method not implemented.");
21+
}
22+
init(): void {
23+
throw new Error("Method not implemented.");
24+
}
25+
26+
}
27+
28+
export { CascadedInvalidateOp };

src/data/model/UndoOp.ts

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)