@fluidframework/matrix 2.0.0-internal.3.0.0 → 2.0.0-internal.3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +20 -21
- package/.mocharc.js +2 -2
- package/README.md +21 -21
- package/api-extractor.json +2 -2
- package/bench/bsp-set-optimizations.md +5 -5
- package/bench/src/index.ts +38 -20
- package/bench/src/read/map.ts +16 -10
- package/bench/src/read/nativearray.ts +16 -10
- package/bench/src/read/test.ts +17 -19
- package/bench/src/read/tiled.ts +240 -181
- package/bench/src/util.ts +19 -18
- package/bench/tsconfig.json +8 -13
- package/dist/bspSet.d.ts.map +1 -1
- package/dist/bspSet.js.map +1 -1
- package/dist/handlecache.d.ts.map +1 -1
- package/dist/handlecache.js +1 -3
- package/dist/handlecache.js.map +1 -1
- package/dist/handletable.d.ts.map +1 -1
- package/dist/handletable.js +7 -3
- package/dist/handletable.js.map +1 -1
- package/dist/matrix.d.ts +1 -1
- package/dist/matrix.d.ts.map +1 -1
- package/dist/matrix.js +28 -19
- package/dist/matrix.js.map +1 -1
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/permutationvector.d.ts.map +1 -1
- package/dist/permutationvector.js +15 -8
- package/dist/permutationvector.js.map +1 -1
- package/dist/productSet.d.ts.map +1 -1
- package/dist/productSet.js +6 -3
- package/dist/productSet.js.map +1 -1
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js.map +1 -1
- package/dist/sparsearray2d.d.ts.map +1 -1
- package/dist/sparsearray2d.js +12 -12
- package/dist/sparsearray2d.js.map +1 -1
- package/dist/split.d.ts.map +1 -1
- package/dist/split.js +5 -3
- package/dist/split.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/undoprovider.d.ts.map +1 -1
- package/dist/undoprovider.js.map +1 -1
- package/lib/bspSet.d.ts.map +1 -1
- package/lib/bspSet.js.map +1 -1
- package/lib/handlecache.d.ts.map +1 -1
- package/lib/handlecache.js +1 -3
- package/lib/handlecache.js.map +1 -1
- package/lib/handletable.d.ts.map +1 -1
- package/lib/handletable.js +7 -3
- package/lib/handletable.js.map +1 -1
- package/lib/matrix.d.ts +1 -1
- package/lib/matrix.d.ts.map +1 -1
- package/lib/matrix.js +29 -20
- package/lib/matrix.js.map +1 -1
- package/lib/ops.d.ts.map +1 -1
- package/lib/ops.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/permutationvector.d.ts.map +1 -1
- package/lib/permutationvector.js +15 -8
- package/lib/permutationvector.js.map +1 -1
- package/lib/productSet.d.ts.map +1 -1
- package/lib/productSet.js +6 -3
- package/lib/productSet.js.map +1 -1
- package/lib/range.d.ts.map +1 -1
- package/lib/range.js.map +1 -1
- package/lib/runtime.d.ts.map +1 -1
- package/lib/runtime.js.map +1 -1
- package/lib/serialization.d.ts.map +1 -1
- package/lib/serialization.js.map +1 -1
- package/lib/sparsearray2d.d.ts.map +1 -1
- package/lib/sparsearray2d.js +12 -12
- package/lib/sparsearray2d.js.map +1 -1
- package/lib/split.d.ts.map +1 -1
- package/lib/split.js +5 -3
- package/lib/split.js.map +1 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/undoprovider.d.ts.map +1 -1
- package/lib/undoprovider.js +1 -1
- package/lib/undoprovider.js.map +1 -1
- package/package.json +117 -116
- package/prettier.config.cjs +1 -1
- package/src/bspSet.ts +507 -434
- package/src/handlecache.ts +114 -112
- package/src/handletable.ts +66 -62
- package/src/matrix.ts +781 -710
- package/src/ops.ts +11 -11
- package/src/packageVersion.ts +1 -1
- package/src/permutationvector.ts +425 -368
- package/src/productSet.ts +852 -788
- package/src/range.ts +8 -8
- package/src/runtime.ts +35 -35
- package/src/serialization.ts +13 -9
- package/src/sparsearray2d.ts +196 -192
- package/src/split.ts +111 -90
- package/src/types.ts +3 -3
- package/src/undoprovider.ts +161 -144
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +8 -12
package/src/split.ts
CHANGED
|
@@ -9,59 +9,75 @@ import { SetOperations, Pair } from "./bspSet";
|
|
|
9
9
|
export type Ivl<Index extends number = number> = Pair<Index>;
|
|
10
10
|
|
|
11
11
|
/** A much faster version of `Math.max` specialized to two numeric arguments. */
|
|
12
|
-
const fastMax = <Index extends number>(x1: Index, x2: Index): Index => x1 < x2 ? x2 : x1;
|
|
12
|
+
const fastMax = <Index extends number>(x1: Index, x2: Index): Index => (x1 < x2 ? x2 : x1);
|
|
13
13
|
|
|
14
14
|
/** A much faster version of `Math.min` specialized to two numeric arguments. */
|
|
15
|
-
const fastMin = <Index extends number>(x1: Index, x2: Index): Index => x1 < x2 ? x1 : x2;
|
|
15
|
+
const fastMin = <Index extends number>(x1: Index, x2: Index): Index => (x1 < x2 ? x1 : x2);
|
|
16
16
|
|
|
17
17
|
export function ivlJoin<Index extends number>(ivl1: Ivl<Index>, ivl2: Ivl<Index>): Ivl<Index> {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const [x1a, x1b] = ivl1;
|
|
19
|
+
const [x2a, x2b] = ivl2;
|
|
20
|
+
return [fastMin(x1a, x2a), fastMax(x1b, x2b)];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function ivlMeets(ivl1: Ivl, ivl2: Ivl): boolean {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const [x1a, x1b] = ivl1;
|
|
25
|
+
const [x2a, x2b] = ivl2;
|
|
26
|
+
return fastMax(x1a, x2a) < fastMin(x1b, x2b);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export function ivlMeetsOrTouches(ivl1: Ivl, ivl2: Ivl): boolean {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
const [x1a, x1b] = ivl1;
|
|
31
|
+
const [x2a, x2b] = ivl2;
|
|
32
|
+
return fastMax(x1a, x2a) <= fastMin(x1b, x2b);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/** computes the set difference on intervals. Precondition: they meet */
|
|
36
|
-
export function ivlExcept<Index extends number>(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
export function ivlExcept<Index extends number>(
|
|
37
|
+
ivl1: Ivl<Index>,
|
|
38
|
+
ivl2: Ivl<Index>,
|
|
39
|
+
): Ivl<Index> | undefined {
|
|
40
|
+
const [x1a, x1b] = ivl1;
|
|
41
|
+
const [x2a, x2b] = ivl2;
|
|
42
|
+
if (x1a < x2a && x2b >= x1b) {
|
|
43
|
+
return [x1a, x2a];
|
|
44
|
+
}
|
|
45
|
+
if (x1a >= x2a && x2b < x1b) {
|
|
46
|
+
return [x2b, x1b];
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
function ivlMeet<Index extends number>(ivl1: Ivl<Index>, ivl2: Ivl<Index>): Ivl<Index> {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
const [x1a, x1b] = ivl1;
|
|
53
|
+
const [x2a, x2b] = ivl2;
|
|
54
|
+
return [fastMax(x1a, x2a), fastMin(x1b, x2b)];
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
export function ivlCompare<Index extends number>(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
export function ivlCompare<Index extends number>(
|
|
58
|
+
ivl1: Ivl<Index>,
|
|
59
|
+
ivl2: Ivl<Index>,
|
|
60
|
+
): -1 | 0 | 1 | undefined {
|
|
61
|
+
const [x1a, x1b] = ivl1;
|
|
62
|
+
const [x2a, x2b] = ivl2;
|
|
63
|
+
if (x1a === x2a && x1b === x2b) {
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
if (x1a >= x2a && x1b <= x2b) {
|
|
67
|
+
return -1;
|
|
68
|
+
}
|
|
69
|
+
if (x1a <= x2a && x1b >= x2b) {
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
export interface Distribution {
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
/** The cummulative distribution function. This is used to compute the probabilty mass of a given interval. */
|
|
77
|
+
readonly cdf: (x: number) => number;
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
/** The inverse cummulative distribution function. This is used to estimate a point given a quantile. */
|
|
80
|
+
readonly invCdf: (x: number) => number;
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
/** This is a bounded pareto distribution with a shape parameter `alpha`, a lower bound `L` and an upper bound `H`.
|
|
@@ -75,76 +91,81 @@ export interface Distribution {
|
|
|
75
91
|
* to cut next.
|
|
76
92
|
*/
|
|
77
93
|
export function boundedPareto(alpha: number, L: number, H: number): Distribution {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
const lAlpha = L ** alpha;
|
|
95
|
+
const cdfDenom = 1 - (L / H) ** alpha;
|
|
96
|
+
const hAlpha = H ** alpha;
|
|
97
|
+
const hlAlpha = hAlpha * lAlpha;
|
|
98
|
+
const hAlphaSubLAlpha = hAlpha - lAlpha;
|
|
99
|
+
const negAlphaInv = -1 / alpha;
|
|
100
|
+
|
|
101
|
+
const cdf =
|
|
102
|
+
alpha === 1
|
|
103
|
+
? (x: number) => (1 - lAlpha / x) / cdfDenom
|
|
104
|
+
: (x: number) => (1 - lAlpha * x ** -alpha) / cdfDenom;
|
|
105
|
+
const invCdf =
|
|
106
|
+
alpha === 1
|
|
107
|
+
? (y: number) => 1 / ((hAlpha - y * hAlphaSubLAlpha) / hlAlpha)
|
|
108
|
+
: (y: number) => ((hAlpha - y * hAlphaSubLAlpha) / hlAlpha) ** negAlphaInv;
|
|
109
|
+
|
|
110
|
+
return { cdf, invCdf };
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
/** Creates a dimension splitter that operates on integer interval values and is based on a bounded Pareto
|
|
96
114
|
* distribution. */
|
|
97
115
|
export function boundedParetoSplitter<Index extends number>(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
116
|
+
alpha: number,
|
|
117
|
+
L: number,
|
|
118
|
+
H: number,
|
|
101
119
|
): DimensionSplitter<Pair<Index>> {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
const distribution = boundedPareto(alpha, L, H);
|
|
121
|
+
return {
|
|
122
|
+
canSplit: ([keyLb, keyUb]) => keyUb - keyLb > 1,
|
|
123
|
+
split([keyLb, keyUb]: Pair<Index>) {
|
|
124
|
+
const ubCdf = distribution.cdf(keyUb);
|
|
125
|
+
const lbCdf = distribution.cdf(keyLb + 1);
|
|
126
|
+
const cuttingPoint = distribution.invCdf((ubCdf + lbCdf) / 2);
|
|
127
|
+
// pick a cutting point, but making sure either side has at least one element in it.
|
|
128
|
+
const discreteCuttingPoint = Math.min(
|
|
129
|
+
Math.max(Math.round(cuttingPoint), keyLb + 1),
|
|
130
|
+
keyUb - 1,
|
|
131
|
+
) as Index;
|
|
132
|
+
|
|
133
|
+
const leftProb = distribution.cdf(discreteCuttingPoint) - distribution.cdf(keyLb + 1);
|
|
134
|
+
const rightProb = distribution.cdf(keyUb) - distribution.cdf(discreteCuttingPoint);
|
|
135
|
+
|
|
136
|
+
const result: Pair<Pair<Pair<Index>, number>> = [
|
|
137
|
+
[[keyLb, discreteCuttingPoint], leftProb],
|
|
138
|
+
[[discreteCuttingPoint, keyUb], rightProb],
|
|
139
|
+
];
|
|
140
|
+
return result;
|
|
141
|
+
},
|
|
142
|
+
};
|
|
122
143
|
}
|
|
123
144
|
|
|
124
145
|
export function boundedParetoSetOperations<Index extends number, Id>(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
146
|
+
alpha: number,
|
|
147
|
+
L: number,
|
|
148
|
+
H: number,
|
|
149
|
+
top: Pair<Index>,
|
|
150
|
+
id: Id,
|
|
130
151
|
): SetOperations<Pair<Index>, Id> {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
152
|
+
const splitter = boundedParetoSplitter<Index>(alpha, L, H);
|
|
153
|
+
return {
|
|
154
|
+
id,
|
|
155
|
+
split: (key) => splitter.split(key),
|
|
156
|
+
canSplit: (key) => splitter.canSplit(key),
|
|
157
|
+
meets: ivlMeets,
|
|
158
|
+
intersect: ivlMeet,
|
|
159
|
+
union: (x, y) => (ivlMeetsOrTouches(x, y) ? ivlJoin(x, y) : undefined),
|
|
160
|
+
except: ivlExcept,
|
|
161
|
+
compare: ivlCompare,
|
|
162
|
+
top,
|
|
163
|
+
};
|
|
143
164
|
}
|
|
144
165
|
|
|
145
166
|
export interface DimensionSplitter<Key> {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
/** For a given key, returns if the key can be further sub-divided. */
|
|
168
|
+
canSplit(key: Key): boolean;
|
|
169
|
+
/** Splits a key and returns the probability mass for either half. */
|
|
170
|
+
split(key: Key): Pair<Pair<Key, number>>;
|
|
150
171
|
}
|
package/src/types.ts
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
// of SharedMatrix undo while we decide on the correct layering for undo.
|
|
8
8
|
|
|
9
9
|
export interface IRevertible {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
revert();
|
|
11
|
+
discard();
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export interface IUndoConsumer {
|
|
15
|
-
|
|
15
|
+
pushToCurrentOperation(revertible: IRevertible);
|
|
16
16
|
}
|
package/src/undoprovider.ts
CHANGED
|
@@ -4,157 +4,174 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/common-utils";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
TrackingGroup,
|
|
9
|
+
MergeTreeDeltaOperationType,
|
|
10
|
+
MergeTreeDeltaType,
|
|
11
|
+
} from "@fluidframework/merge-tree";
|
|
8
12
|
import { MatrixItem, SharedMatrix } from "./matrix";
|
|
9
13
|
import { Handle, isHandleValid } from "./handletable";
|
|
10
14
|
import { PermutationSegment, PermutationVector } from "./permutationvector";
|
|
11
15
|
import { IUndoConsumer } from "./types";
|
|
12
16
|
|
|
13
17
|
export class VectorUndoProvider {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
18
|
+
// 'currentGroup' and 'currentOp' are used while applying an IRevertable.revert() to coalesce
|
|
19
|
+
// the recorded into a single IRevertable / tracking group as they move between the undo <->
|
|
20
|
+
// redo stacks.
|
|
21
|
+
private currentGroup?: TrackingGroup;
|
|
22
|
+
private currentOp?: MergeTreeDeltaType;
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
private readonly manager: IUndoConsumer,
|
|
26
|
+
private readonly undoInsert: (segment: PermutationSegment) => void,
|
|
27
|
+
private readonly undoRemove: (segment: PermutationSegment) => void,
|
|
28
|
+
) {}
|
|
29
|
+
|
|
30
|
+
public record(
|
|
31
|
+
operation: MergeTreeDeltaOperationType,
|
|
32
|
+
ranges: { segment: PermutationSegment }[],
|
|
33
|
+
) {
|
|
34
|
+
if (ranges.length > 0) {
|
|
35
|
+
// Link each segment to a new TrackingGroup. A TrackingGroup keeps track of the original
|
|
36
|
+
// set of linked segments, including any fragmentatiton that occurs due to future splitting.
|
|
37
|
+
//
|
|
38
|
+
// A TrackingGroup also prevents removed segments from being unlinked from the tree during
|
|
39
|
+
// Zamboni and guarantees segments will not be merged/coalesced with segments outside of the
|
|
40
|
+
// current tracking group.
|
|
41
|
+
//
|
|
42
|
+
// These properties allow us to rely on MergeTree.getPosition() to find the locations/lengths
|
|
43
|
+
// of all content contained within the tracking group in the future.
|
|
44
|
+
|
|
45
|
+
// If we are in the process of reverting, the `IRevertible.revert()` will provide the tracking
|
|
46
|
+
// group so that we can preserve the original segment ranges as a single op/group as we move
|
|
47
|
+
// ops between the undo <-> redo stacks.
|
|
48
|
+
const trackingGroup = this.currentGroup ?? new TrackingGroup();
|
|
49
|
+
for (const range of ranges) {
|
|
50
|
+
trackingGroup.link(range.segment);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// For SharedMatrix, each IRevertibles always holds a single row/col operation.
|
|
54
|
+
// Therefore, 'currentOp' must either be undefined or equal to the current op.
|
|
55
|
+
assert(
|
|
56
|
+
this.currentOp === undefined || this.currentOp === operation,
|
|
57
|
+
0x02a /* "On vector undo, unexpected 'currentOp' type/state!" */,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
switch (operation) {
|
|
61
|
+
case MergeTreeDeltaType.INSERT:
|
|
62
|
+
if (this.currentOp !== MergeTreeDeltaType.INSERT) {
|
|
63
|
+
this.pushRevertible(trackingGroup, this.undoInsert);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
|
|
67
|
+
case MergeTreeDeltaType.REMOVE: {
|
|
68
|
+
if (this.currentOp !== MergeTreeDeltaType.REMOVE) {
|
|
69
|
+
this.pushRevertible(trackingGroup, this.undoRemove);
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
default:
|
|
75
|
+
throw new Error("operation type not revertible");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// If we are in the process of reverting, set 'currentOp' to remind ourselves not to push
|
|
79
|
+
// another revertible until `IRevertable.revert()` finishes the current op and clears this
|
|
80
|
+
// field.
|
|
81
|
+
if (this.currentGroup !== undefined) {
|
|
82
|
+
this.currentOp = operation;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private pushRevertible(
|
|
88
|
+
trackingGroup: TrackingGroup,
|
|
89
|
+
callback: (segment: PermutationSegment) => void,
|
|
90
|
+
) {
|
|
91
|
+
const revertible = {
|
|
92
|
+
revert: () => {
|
|
93
|
+
assert(
|
|
94
|
+
this.currentGroup === undefined && this.currentOp === undefined,
|
|
95
|
+
0x02b /* "Must not nest calls to IRevertible.revert()" */,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
this.currentGroup = new TrackingGroup();
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
while (trackingGroup.size > 0) {
|
|
102
|
+
const segment = trackingGroup.segments[0] as PermutationSegment;
|
|
103
|
+
|
|
104
|
+
// Unlink 'segment' from the current tracking group before invoking the callback
|
|
105
|
+
// to exclude the current undo/redo segment from those copied to the replacement
|
|
106
|
+
// segment (if any). (See 'PermutationSegment.transferToReplacement()')
|
|
107
|
+
segment.trackingCollection.unlink(trackingGroup);
|
|
108
|
+
|
|
109
|
+
callback(segment);
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
this.currentOp = undefined;
|
|
113
|
+
this.currentGroup = undefined;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
discard: () => {
|
|
117
|
+
while (trackingGroup.size > 0) {
|
|
118
|
+
trackingGroup.unlink(trackingGroup.segments[0]);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
this.manager.pushToCurrentOperation(revertible);
|
|
124
|
+
|
|
125
|
+
return revertible;
|
|
126
|
+
}
|
|
113
127
|
}
|
|
114
128
|
|
|
115
129
|
export class MatrixUndoProvider<T> {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
130
|
+
constructor(
|
|
131
|
+
private readonly consumer: IUndoConsumer,
|
|
132
|
+
private readonly matrix: SharedMatrix<T>,
|
|
133
|
+
private readonly rows: PermutationVector,
|
|
134
|
+
private readonly cols: PermutationVector,
|
|
135
|
+
) {
|
|
136
|
+
rows.undo = new VectorUndoProvider(
|
|
137
|
+
consumer,
|
|
138
|
+
/* undoInsert: */ (segment: PermutationSegment) => {
|
|
139
|
+
const start = this.rows.getPosition(segment);
|
|
140
|
+
this.matrix.removeRows(start, segment.cachedLength);
|
|
141
|
+
},
|
|
142
|
+
/* undoRemove: */ (segment: PermutationSegment) => {
|
|
143
|
+
this.matrix._undoRemoveRows(segment);
|
|
144
|
+
},
|
|
145
|
+
);
|
|
146
|
+
cols.undo = new VectorUndoProvider(
|
|
147
|
+
consumer,
|
|
148
|
+
/* undoInsert: */ (segment: PermutationSegment) => {
|
|
149
|
+
const start = this.cols.getPosition(segment);
|
|
150
|
+
this.matrix.removeCols(start, segment.cachedLength);
|
|
151
|
+
},
|
|
152
|
+
/* undoRemove: */ (segment: PermutationSegment) => {
|
|
153
|
+
this.matrix._undoRemoveCols(segment);
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
cellSet(rowHandle: Handle, colHandle: Handle, oldValue: MatrixItem<T>) {
|
|
159
|
+
assert(
|
|
160
|
+
isHandleValid(rowHandle) && isHandleValid(colHandle),
|
|
161
|
+
0x02c /* "On cellSet(), invalid row and/or column handles!" */,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (this.consumer !== undefined) {
|
|
165
|
+
this.consumer.pushToCurrentOperation({
|
|
166
|
+
revert: () => {
|
|
167
|
+
this.matrix.setCell(
|
|
168
|
+
this.rows.handleToPosition(rowHandle),
|
|
169
|
+
this.cols.handleToPosition(colHandle),
|
|
170
|
+
oldValue,
|
|
171
|
+
);
|
|
172
|
+
},
|
|
173
|
+
discard: () => {},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
160
177
|
}
|
package/tsconfig.esnext.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./lib",
|
|
5
|
+
"module": "esnext",
|
|
6
|
+
},
|
|
7
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
],
|
|
11
|
-
"exclude": [
|
|
12
|
-
"src/test/**/*"
|
|
13
|
-
]
|
|
2
|
+
"extends": "@fluidframework/build-common/ts-common-config.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "./src",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"composite": true,
|
|
7
|
+
},
|
|
8
|
+
"include": ["src/**/*"],
|
|
9
|
+
"exclude": ["src/test/**/*"],
|
|
14
10
|
}
|