@fluidframework/matrix 2.0.0-internal.4.3.0 → 2.0.0-internal.5.0.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/matrix.d.ts +3 -3
  3. package/dist/matrix.d.ts.map +1 -1
  4. package/dist/matrix.js +4 -26
  5. package/dist/matrix.js.map +1 -1
  6. package/dist/packageVersion.d.ts +1 -1
  7. package/dist/packageVersion.js +1 -1
  8. package/dist/packageVersion.js.map +1 -1
  9. package/dist/permutationvector.d.ts +5 -12
  10. package/dist/permutationvector.d.ts.map +1 -1
  11. package/dist/permutationvector.js +24 -40
  12. package/dist/permutationvector.js.map +1 -1
  13. package/dist/serialization.d.ts +1 -1
  14. package/dist/serialization.d.ts.map +1 -1
  15. package/dist/serialization.js +2 -2
  16. package/dist/serialization.js.map +1 -1
  17. package/dist/undoprovider.d.ts +6 -8
  18. package/dist/undoprovider.d.ts.map +1 -1
  19. package/dist/undoprovider.js +74 -62
  20. package/dist/undoprovider.js.map +1 -1
  21. package/lib/matrix.d.ts +3 -3
  22. package/lib/matrix.d.ts.map +1 -1
  23. package/lib/matrix.js +5 -27
  24. package/lib/matrix.js.map +1 -1
  25. package/lib/packageVersion.d.ts +1 -1
  26. package/lib/packageVersion.js +1 -1
  27. package/lib/packageVersion.js.map +1 -1
  28. package/lib/permutationvector.d.ts +5 -12
  29. package/lib/permutationvector.d.ts.map +1 -1
  30. package/lib/permutationvector.js +23 -40
  31. package/lib/permutationvector.js.map +1 -1
  32. package/lib/serialization.d.ts +1 -1
  33. package/lib/serialization.d.ts.map +1 -1
  34. package/lib/serialization.js +1 -1
  35. package/lib/serialization.js.map +1 -1
  36. package/lib/undoprovider.d.ts +6 -8
  37. package/lib/undoprovider.d.ts.map +1 -1
  38. package/lib/undoprovider.js +75 -63
  39. package/lib/undoprovider.js.map +1 -1
  40. package/package.json +12 -12
  41. package/src/matrix.ts +6 -38
  42. package/src/packageVersion.ts +1 -1
  43. package/src/permutationvector.ts +33 -46
  44. package/src/serialization.ts +1 -1
  45. package/src/undoprovider.ts +83 -74
@@ -5,10 +5,15 @@
5
5
 
6
6
  import { assert } from "@fluidframework/common-utils";
7
7
  import {
8
- TrackingGroup,
9
- MergeTreeDeltaOperationType,
10
8
  MergeTreeDeltaType,
11
- toRemovalInfo,
9
+ MergeTreeDeltaRevertible,
10
+ IMergeTreeDeltaCallbackArgs,
11
+ appendToMergeTreeDeltaRevertibles,
12
+ revertMergeTreeDeltaRevertibles,
13
+ MergeTreeRevertibleDriver,
14
+ discardMergeTreeDeltaRevertible,
15
+ TrackingGroup,
16
+ ITrackingGroup,
12
17
  } from "@fluidframework/merge-tree";
13
18
  import { MatrixItem, SharedMatrix } from "./matrix";
14
19
  import { Handle, isHandleValid } from "./handletable";
@@ -19,58 +24,55 @@ export class VectorUndoProvider {
19
24
  // 'currentGroup' and 'currentOp' are used while applying an IRevertable.revert() to coalesce
20
25
  // the recorded into a single IRevertable / tracking group as they move between the undo <->
21
26
  // redo stacks.
22
- private currentGroup?: TrackingGroup;
27
+ private currentGroup?: MergeTreeDeltaRevertible[];
23
28
  private currentOp?: MergeTreeDeltaType;
29
+ private currentRemoveTrackingGroup?: TrackingGroup;
24
30
 
25
31
  constructor(
26
32
  private readonly manager: IUndoConsumer,
27
- private readonly undoInsert: (segment: PermutationSegment) => void,
28
- private readonly undoRemove: (segment: PermutationSegment) => void,
33
+ private readonly driver: MergeTreeRevertibleDriver,
29
34
  ) {}
30
35
 
31
- public record(
32
- operation: MergeTreeDeltaOperationType,
33
- ranges: { segment: PermutationSegment }[],
34
- ) {
35
- if (ranges.length > 0) {
36
- // Link each segment to a new TrackingGroup. A TrackingGroup keeps track of the original
37
- // set of linked segments, including any fragmentatiton that occurs due to future splitting.
38
- //
39
- // A TrackingGroup also prevents removed segments from being unlinked from the tree during
40
- // Zamboni and guarantees segments will not be merged/coalesced with segments outside of the
41
- // current tracking group.
42
- //
43
- // These properties allow us to rely on MergeTree.getPosition() to find the locations/lengths
44
- // of all content contained within the tracking group in the future.
45
-
36
+ public record(deltaArgs: IMergeTreeDeltaCallbackArgs) {
37
+ if (deltaArgs.deltaSegments.length > 0) {
46
38
  // If we are in the process of reverting, the `IRevertible.revert()` will provide the tracking
47
39
  // group so that we can preserve the original segment ranges as a single op/group as we move
48
40
  // ops between the undo <-> redo stacks.
49
- const trackingGroup = this.currentGroup ?? new TrackingGroup();
50
- for (const range of ranges) {
51
- trackingGroup.link(range.segment);
52
- }
41
+ const revertibles: MergeTreeDeltaRevertible[] = this.currentGroup ?? [];
42
+ appendToMergeTreeDeltaRevertibles(deltaArgs, revertibles);
53
43
 
54
44
  // For SharedMatrix, each IRevertibles always holds a single row/col operation.
55
45
  // Therefore, 'currentOp' must either be undefined or equal to the current op.
56
46
  assert(
57
- this.currentOp === undefined || this.currentOp === operation,
47
+ this.currentOp === undefined || this.currentOp === deltaArgs.operation,
58
48
  0x02a /* "On vector undo, unexpected 'currentOp' type/state!" */,
59
49
  );
50
+ let removeTrackingGroup: TrackingGroup | undefined;
51
+ if (deltaArgs.operation === MergeTreeDeltaType.REMOVE) {
52
+ // for removed segment we need a tracking group.
53
+ // this is for a few reason:
54
+ // 1. the handle for the row/column on the removed segment is still allocated,
55
+ // and needs to be in order to process unacked ops sent before the remove.
56
+ // 2. handles are freed on unlink(zamboni), but that also clears the row/column data.
57
+ // which we don't want to happen, so we can re-insert the cells when the row/col comes back.
58
+ // the tracking group prevents unlink.
59
+ // 3. when we re-insert we need to find the old segment and clear their handles
60
+ // so the new segment takes them over. there is no efficient look-up for this.
61
+ // the tracking group provides one.
62
+ const trackingGroup = (removeTrackingGroup =
63
+ this.currentRemoveTrackingGroup ?? new TrackingGroup());
64
+ deltaArgs.deltaSegments.forEach((d) =>
65
+ d.segment.trackingCollection.link(trackingGroup),
66
+ );
67
+ }
60
68
 
61
- switch (operation) {
69
+ switch (deltaArgs.operation) {
70
+ case MergeTreeDeltaType.REMOVE:
62
71
  case MergeTreeDeltaType.INSERT:
63
- if (this.currentOp !== MergeTreeDeltaType.INSERT) {
64
- this.pushRevertible(trackingGroup, this.undoInsert);
65
- }
66
- break;
67
-
68
- case MergeTreeDeltaType.REMOVE: {
69
- if (this.currentOp !== MergeTreeDeltaType.REMOVE) {
70
- this.pushRevertible(trackingGroup, this.undoRemove);
72
+ if (this.currentOp !== deltaArgs.operation) {
73
+ this.pushRevertible(revertibles, removeTrackingGroup);
71
74
  }
72
75
  break;
73
- }
74
76
 
75
77
  default:
76
78
  throw new Error("operation type not revertible");
@@ -80,50 +82,59 @@ export class VectorUndoProvider {
80
82
  // another revertible until `IRevertable.revert()` finishes the current op and clears this
81
83
  // field.
82
84
  if (this.currentGroup !== undefined) {
83
- this.currentOp = operation;
85
+ this.currentOp ??= deltaArgs.operation;
86
+ this.currentRemoveTrackingGroup ??= removeTrackingGroup;
84
87
  }
85
88
  }
86
89
  }
87
90
 
88
91
  private pushRevertible(
89
- trackingGroup: TrackingGroup,
90
- callback: (segment: PermutationSegment) => void,
92
+ revertibles: MergeTreeDeltaRevertible[],
93
+ removedTrackingGroup: ITrackingGroup | undefined,
91
94
  ) {
92
- const revertible = {
95
+ const reverter = {
93
96
  revert: () => {
94
97
  assert(
95
98
  this.currentGroup === undefined && this.currentOp === undefined,
96
99
  0x02b /* "Must not nest calls to IRevertible.revert()" */,
97
100
  );
98
101
 
99
- this.currentGroup = new TrackingGroup();
102
+ this.currentGroup = [];
100
103
 
101
104
  try {
102
- while (trackingGroup.size > 0) {
103
- const segment = trackingGroup.segments[0] as PermutationSegment;
104
-
105
- // Unlink 'segment' from the current tracking group before invoking the callback
106
- // to exclude the current undo/redo segment from those copied to the replacement
107
- // segment (if any). (See 'PermutationSegment.transferToReplacement()')
108
- segment.trackingCollection.unlink(trackingGroup);
109
-
110
- callback(segment);
105
+ if (removedTrackingGroup !== undefined) {
106
+ while (removedTrackingGroup.size > 0) {
107
+ const tracked = removedTrackingGroup.tracked[0];
108
+ removedTrackingGroup.unlink(tracked);
109
+ // if there are groups tracked, this in a revert of a remove.
110
+ // this means we are about to re-insert the row/column
111
+ // with the same handle. We reuse the handle so the row/columns cells
112
+ // get re-inserted too.
113
+ // since a new segment will have the handle, we need to
114
+ // remove it from the removed segment which was tracked
115
+ (tracked as PermutationSegment).reset();
116
+ }
111
117
  }
118
+ revertMergeTreeDeltaRevertibles(this.driver, revertibles);
112
119
  } finally {
113
120
  this.currentOp = undefined;
114
121
  this.currentGroup = undefined;
122
+ this.currentRemoveTrackingGroup = undefined;
115
123
  }
116
124
  },
117
125
  discard: () => {
118
- while (trackingGroup.size > 0) {
119
- trackingGroup.unlink(trackingGroup.segments[0]);
126
+ if (removedTrackingGroup !== undefined) {
127
+ while (removedTrackingGroup.size > 0) {
128
+ removedTrackingGroup.unlink(removedTrackingGroup.tracked[0]);
129
+ }
120
130
  }
131
+ discardMergeTreeDeltaRevertible(revertibles);
121
132
  },
122
133
  };
123
134
 
124
- this.manager.pushToCurrentOperation(revertible);
135
+ this.manager.pushToCurrentOperation(reverter);
125
136
 
126
- return revertible;
137
+ return reverter;
127
138
  }
128
139
  }
129
140
 
@@ -134,30 +145,28 @@ export class MatrixUndoProvider<T> {
134
145
  private readonly rows: PermutationVector,
135
146
  private readonly cols: PermutationVector,
136
147
  ) {
137
- rows.undo = new VectorUndoProvider(
138
- consumer,
139
- /* undoInsert: */ (segment: PermutationSegment) => {
140
- if (toRemovalInfo(segment) === undefined) {
141
- const start = this.rows.getPosition(segment);
142
- this.matrix.removeRows(start, segment.cachedLength);
143
- }
148
+ rows.undo = new VectorUndoProvider(consumer, {
149
+ annotateRange() {
150
+ throw new Error("not implemented");
144
151
  },
145
- /* undoRemove: */ (segment: PermutationSegment) => {
146
- this.matrix._undoRemoveRows(segment);
152
+ insertFromSpec(pos, spec) {
153
+ matrix._undoRemoveRows(pos, spec);
147
154
  },
148
- );
149
- cols.undo = new VectorUndoProvider(
150
- consumer,
151
- /* undoInsert: */ (segment: PermutationSegment) => {
152
- if (toRemovalInfo(segment) === undefined) {
153
- const start = this.cols.getPosition(segment);
154
- this.matrix.removeCols(start, segment.cachedLength);
155
- }
155
+ removeRange(start, end) {
156
+ matrix.removeRows(start, end - start);
156
157
  },
157
- /* undoRemove: */ (segment: PermutationSegment) => {
158
- this.matrix._undoRemoveCols(segment);
158
+ });
159
+ cols.undo = new VectorUndoProvider(consumer, {
160
+ annotateRange() {
161
+ throw new Error("not implemented");
159
162
  },
160
- );
163
+ insertFromSpec(pos, spec) {
164
+ matrix._undoRemoveCols(pos, spec);
165
+ },
166
+ removeRange(start, end) {
167
+ matrix.removeCols(start, end - start);
168
+ },
169
+ });
161
170
  }
162
171
 
163
172
  cellSet(rowHandle: Handle, colHandle: Handle, oldValue: MatrixItem<T>) {