@fluidframework/merge-tree 2.3.0-288113 → 2.4.0-294316
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/CHANGELOG.md +4 -0
- package/api-report/merge-tree.legacy.alpha.api.md +20 -2
- package/dist/attributionCollection.d.ts.map +1 -1
- package/dist/attributionCollection.js +1 -29
- package/dist/attributionCollection.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -5
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +1 -0
- package/dist/localReference.d.ts.map +1 -1
- package/dist/localReference.js +0 -2
- package/dist/localReference.js.map +1 -1
- package/dist/mergeTree.d.ts +2 -32
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +137 -199
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +16 -10
- package/dist/mergeTreeNodes.d.ts.map +1 -1
- package/dist/mergeTreeNodes.js +5 -2
- package/dist/mergeTreeNodes.js.map +1 -1
- package/dist/partialLengths.d.ts.map +1 -1
- package/dist/partialLengths.js +8 -54
- package/dist/partialLengths.js.map +1 -1
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +0 -2
- package/dist/properties.js.map +1 -1
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +0 -14
- package/dist/revertibles.js.map +1 -1
- package/dist/segmentGroupCollection.d.ts.map +1 -1
- package/dist/segmentGroupCollection.js +0 -2
- package/dist/segmentGroupCollection.js.map +1 -1
- package/dist/segmentPropertiesManager.d.ts.map +1 -1
- package/dist/segmentPropertiesManager.js +1 -3
- package/dist/segmentPropertiesManager.js.map +1 -1
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js +1 -4
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/snapshotV1.d.ts.map +1 -1
- package/dist/snapshotV1.js +1 -11
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.d.ts.map +1 -1
- package/dist/snapshotlegacy.js +0 -1
- package/dist/snapshotlegacy.js.map +1 -1
- package/dist/sortedSegmentSet.d.ts +0 -1
- package/dist/sortedSegmentSet.d.ts.map +1 -1
- package/dist/sortedSegmentSet.js +1 -9
- package/dist/sortedSegmentSet.js.map +1 -1
- package/dist/sortedSet.d.ts.map +1 -1
- package/dist/sortedSet.js +0 -4
- package/dist/sortedSet.js.map +1 -1
- package/dist/test/client.conflictFarm.spec.d.ts.map +1 -1
- package/dist/test/client.conflictFarm.spec.js +36 -27
- package/dist/test/client.conflictFarm.spec.js.map +1 -1
- package/dist/test/client.replay.spec.js +1 -1
- package/dist/test/client.replay.spec.js.map +1 -1
- package/dist/test/mergeTreeOperationRunner.d.ts +2 -1
- package/dist/test/mergeTreeOperationRunner.d.ts.map +1 -1
- package/dist/test/mergeTreeOperationRunner.js +29 -11
- package/dist/test/mergeTreeOperationRunner.js.map +1 -1
- package/dist/test/obliterate.partialLength.spec.js +8 -4
- package/dist/test/obliterate.partialLength.spec.js.map +1 -1
- package/dist/test/obliterate.spec.js +66 -5
- package/dist/test/obliterate.spec.js.map +1 -1
- package/dist/test/reconnectHelper.d.ts +0 -1
- package/dist/test/reconnectHelper.d.ts.map +1 -1
- package/dist/test/reconnectHelper.js +1 -1
- package/dist/test/reconnectHelper.js.map +1 -1
- package/dist/test/testClient.d.ts +1 -11
- package/dist/test/testClient.d.ts.map +1 -1
- package/dist/test/testClient.js +0 -3
- package/dist/test/testClient.js.map +1 -1
- package/dist/test/testClientLogger.d.ts.map +1 -1
- package/dist/test/testClientLogger.js +17 -7
- package/dist/test/testClientLogger.js.map +1 -1
- package/dist/test/testUtils.d.ts +10 -0
- package/dist/test/testUtils.d.ts.map +1 -1
- package/dist/test/testUtils.js +5 -1
- package/dist/test/testUtils.js.map +1 -1
- package/dist/zamboni.d.ts.map +1 -1
- package/dist/zamboni.js +0 -4
- package/dist/zamboni.js.map +1 -1
- package/lib/attributionCollection.d.ts.map +1 -1
- package/lib/attributionCollection.js +1 -29
- package/lib/attributionCollection.js.map +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +3 -5
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +1 -0
- package/lib/localReference.d.ts.map +1 -1
- package/lib/localReference.js +0 -2
- package/lib/localReference.js.map +1 -1
- package/lib/mergeTree.d.ts +2 -32
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +138 -200
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +16 -10
- package/lib/mergeTreeNodes.d.ts.map +1 -1
- package/lib/mergeTreeNodes.js +5 -2
- package/lib/mergeTreeNodes.js.map +1 -1
- package/lib/partialLengths.d.ts.map +1 -1
- package/lib/partialLengths.js +8 -54
- package/lib/partialLengths.js.map +1 -1
- package/lib/properties.d.ts.map +1 -1
- package/lib/properties.js +0 -2
- package/lib/properties.js.map +1 -1
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +0 -14
- package/lib/revertibles.js.map +1 -1
- package/lib/segmentGroupCollection.d.ts.map +1 -1
- package/lib/segmentGroupCollection.js +0 -2
- package/lib/segmentGroupCollection.js.map +1 -1
- package/lib/segmentPropertiesManager.d.ts.map +1 -1
- package/lib/segmentPropertiesManager.js +1 -3
- package/lib/segmentPropertiesManager.js.map +1 -1
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js +1 -4
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/snapshotV1.d.ts.map +1 -1
- package/lib/snapshotV1.js +1 -11
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.d.ts.map +1 -1
- package/lib/snapshotlegacy.js +0 -1
- package/lib/snapshotlegacy.js.map +1 -1
- package/lib/sortedSegmentSet.d.ts +0 -1
- package/lib/sortedSegmentSet.d.ts.map +1 -1
- package/lib/sortedSegmentSet.js +1 -9
- package/lib/sortedSegmentSet.js.map +1 -1
- package/lib/sortedSet.d.ts.map +1 -1
- package/lib/sortedSet.js +0 -4
- package/lib/sortedSet.js.map +1 -1
- package/lib/test/client.conflictFarm.spec.d.ts.map +1 -1
- package/lib/test/client.conflictFarm.spec.js +37 -28
- package/lib/test/client.conflictFarm.spec.js.map +1 -1
- package/lib/test/client.replay.spec.js +1 -1
- package/lib/test/client.replay.spec.js.map +1 -1
- package/lib/test/mergeTreeOperationRunner.d.ts +2 -1
- package/lib/test/mergeTreeOperationRunner.d.ts.map +1 -1
- package/lib/test/mergeTreeOperationRunner.js +30 -12
- package/lib/test/mergeTreeOperationRunner.js.map +1 -1
- package/lib/test/obliterate.partialLength.spec.js +9 -5
- package/lib/test/obliterate.partialLength.spec.js.map +1 -1
- package/lib/test/obliterate.spec.js +67 -6
- package/lib/test/obliterate.spec.js.map +1 -1
- package/lib/test/reconnectHelper.d.ts +0 -1
- package/lib/test/reconnectHelper.d.ts.map +1 -1
- package/lib/test/reconnectHelper.js +1 -1
- package/lib/test/reconnectHelper.js.map +1 -1
- package/lib/test/testClient.d.ts +1 -11
- package/lib/test/testClient.d.ts.map +1 -1
- package/lib/test/testClient.js +0 -3
- package/lib/test/testClient.js.map +1 -1
- package/lib/test/testClientLogger.d.ts.map +1 -1
- package/lib/test/testClientLogger.js +18 -8
- package/lib/test/testClientLogger.js.map +1 -1
- package/lib/test/testUtils.d.ts +10 -0
- package/lib/test/testUtils.d.ts.map +1 -1
- package/lib/test/testUtils.js +3 -0
- package/lib/test/testUtils.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/lib/zamboni.d.ts.map +1 -1
- package/lib/zamboni.js +0 -4
- package/lib/zamboni.js.map +1 -1
- package/package.json +22 -21
- package/src/attributionCollection.ts +14 -42
- package/src/client.ts +13 -11
- package/src/index.ts +1 -0
- package/src/localReference.ts +1 -3
- package/src/mergeTree.ts +188 -258
- package/src/mergeTreeNodes.ts +22 -12
- package/src/partialLengths.ts +23 -68
- package/src/properties.ts +1 -3
- package/src/revertibles.ts +7 -21
- package/src/segmentGroupCollection.ts +1 -3
- package/src/segmentPropertiesManager.ts +0 -1
- package/src/snapshotLoader.ts +2 -4
- package/src/snapshotV1.ts +5 -15
- package/src/snapshotlegacy.ts +1 -2
- package/src/sortedSegmentSet.ts +3 -10
- package/src/sortedSet.ts +2 -6
- package/src/zamboni.ts +4 -8
- package/tsconfig.json +1 -0
package/dist/mergeTree.js
CHANGED
|
@@ -24,12 +24,8 @@ const properties_js_1 = require("./properties.js");
|
|
|
24
24
|
const referencePositions_js_1 = require("./referencePositions.js");
|
|
25
25
|
// eslint-disable-next-line import/no-deprecated
|
|
26
26
|
const segmentPropertiesManager_js_1 = require("./segmentPropertiesManager.js");
|
|
27
|
-
const
|
|
27
|
+
const sortedSegmentSet_js_1 = require("./sortedSegmentSet.js");
|
|
28
28
|
const zamboni_js_1 = require("./zamboni.js");
|
|
29
|
-
function wasRemovedAfter(seg, seq) {
|
|
30
|
-
return (seg.removedSeq !== constants_js_1.UnassignedSequenceNumber &&
|
|
31
|
-
(seg.removedSeq === undefined || seg.removedSeq > seq));
|
|
32
|
-
}
|
|
33
29
|
function markSegmentMoved(seg, moveInfo) {
|
|
34
30
|
seg.moveDst = moveInfo.moveDst;
|
|
35
31
|
seg.movedClientIds = [...moveInfo.movedClientIds];
|
|
@@ -180,6 +176,67 @@ function getSlideToSegoff(segoff, slidingPreference = localReference_js_1.Slidin
|
|
|
180
176
|
exports.getSlideToSegoff = getSlideToSegoff;
|
|
181
177
|
const forwardPred = (ref) => ref.slidingPreference !== localReference_js_1.SlidingPreference.BACKWARD;
|
|
182
178
|
const backwardPred = (ref) => ref.slidingPreference === localReference_js_1.SlidingPreference.BACKWARD;
|
|
179
|
+
class Obliterates {
|
|
180
|
+
constructor(mergeTree) {
|
|
181
|
+
this.mergeTree = mergeTree;
|
|
182
|
+
/**
|
|
183
|
+
* Array containing the all move operations within the
|
|
184
|
+
* collab window.
|
|
185
|
+
*
|
|
186
|
+
* The moves are stored in sequence order which accelerates clean up in setMinSeq
|
|
187
|
+
*
|
|
188
|
+
* See https://github.com/microsoft/FluidFramework/blob/main/packages/dds/merge-tree/docs/Obliterate.md#remote-perspective
|
|
189
|
+
* for additional context
|
|
190
|
+
*/
|
|
191
|
+
// eslint-disable-next-line import/no-deprecated
|
|
192
|
+
this.seqOrdered = new index_js_1.DoublyLinkedList();
|
|
193
|
+
/**
|
|
194
|
+
* This contains a sorted lists of all obliterate starts
|
|
195
|
+
* and is used to accelerate finding overlapping obliterates
|
|
196
|
+
* as well as determining if there are any obliterates at all.
|
|
197
|
+
*/
|
|
198
|
+
this.startOrdered = new sortedSegmentSet_js_1.SortedSegmentSet();
|
|
199
|
+
}
|
|
200
|
+
setMinSeq(minSeq) {
|
|
201
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
|
202
|
+
while (!this.seqOrdered.empty && this.seqOrdered.first?.data.seq <= minSeq) {
|
|
203
|
+
const ob = this.seqOrdered.shift();
|
|
204
|
+
this.startOrdered.remove(ob.data.start);
|
|
205
|
+
this.mergeTree.removeLocalReferencePosition(ob.data.start);
|
|
206
|
+
this.mergeTree.removeLocalReferencePosition(ob.data.end);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// eslint-disable-next-line import/no-deprecated
|
|
210
|
+
addOrUpdate(obliterateInfo) {
|
|
211
|
+
const { seq, start } = obliterateInfo;
|
|
212
|
+
if (seq !== constants_js_1.UnassignedSequenceNumber) {
|
|
213
|
+
this.seqOrdered.push(obliterateInfo);
|
|
214
|
+
}
|
|
215
|
+
this.startOrdered.addOrUpdate(start);
|
|
216
|
+
}
|
|
217
|
+
empty() {
|
|
218
|
+
return this.startOrdered.size === 0;
|
|
219
|
+
}
|
|
220
|
+
// eslint-disable-next-line import/no-deprecated
|
|
221
|
+
findOverlapping(seg) {
|
|
222
|
+
// eslint-disable-next-line import/no-deprecated
|
|
223
|
+
const overlapping = [];
|
|
224
|
+
for (const start of this.startOrdered.items) {
|
|
225
|
+
if (start.getSegment().ordinal <= seg.ordinal) {
|
|
226
|
+
// eslint-disable-next-line import/no-deprecated
|
|
227
|
+
const ob = start.properties?.obliterate;
|
|
228
|
+
if (ob.end.getSegment().ordinal >= seg.ordinal) {
|
|
229
|
+
overlapping.push(ob);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
// the start is past the seg, so exit
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return overlapping;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
183
240
|
/**
|
|
184
241
|
* @internal
|
|
185
242
|
*/
|
|
@@ -200,36 +257,7 @@ class MergeTree {
|
|
|
200
257
|
// for now assume only markers have ids and so point directly at the Segment
|
|
201
258
|
// if we need to have pointers to non-markers, we can change to point at local refs
|
|
202
259
|
this.idToMarker = new Map();
|
|
203
|
-
|
|
204
|
-
* Array containing the sequence number of all move operations within the
|
|
205
|
-
* collab window
|
|
206
|
-
*
|
|
207
|
-
* When a segment is inserted, we must traverse to the left and right of it
|
|
208
|
-
* to determine whether the segment was inserted into an obliterated range.
|
|
209
|
-
* By keeping track of all move seqs, we can significantly reduce the search
|
|
210
|
-
* space we must traverse.
|
|
211
|
-
*
|
|
212
|
-
* Sequence numbers in `moveSeqs` are sorted to accelerate bookkeeping.
|
|
213
|
-
*
|
|
214
|
-
* See https://github.com/microsoft/FluidFramework/blob/main/packages/dds/merge-tree/docs/Obliterate.md#remote-perspective
|
|
215
|
-
* for additional context
|
|
216
|
-
*/
|
|
217
|
-
this.moveSeqs = [];
|
|
218
|
-
/**
|
|
219
|
-
* Similar to moveSeqs, but tracks local moves. These are not the move
|
|
220
|
-
* operations within the collab window, but rather local moves that have
|
|
221
|
-
* not been acked.
|
|
222
|
-
*/
|
|
223
|
-
this.localMoveSeqs = new Set();
|
|
224
|
-
/**
|
|
225
|
-
* Groups of segments moved by local moves/obliterates
|
|
226
|
-
*
|
|
227
|
-
* When a local obliterate is acked, we must also ack segments that were
|
|
228
|
-
* concurrently obliterated on insert. We check this segment group to find
|
|
229
|
-
* such segments
|
|
230
|
-
*/
|
|
231
|
-
// eslint-disable-next-line import/no-deprecated
|
|
232
|
-
this.locallyMovedSegments = new Map();
|
|
260
|
+
this.obliterates = new Obliterates(this);
|
|
233
261
|
this.splitLeafSegment = (segment, pos) => {
|
|
234
262
|
if (!(pos > 0 && segment)) {
|
|
235
263
|
return {};
|
|
@@ -345,7 +373,6 @@ class MergeTree {
|
|
|
345
373
|
childIndex++, nodeIndex++ // Advance to next child & node
|
|
346
374
|
) {
|
|
347
375
|
// Insert the next node into the current block
|
|
348
|
-
// TODO Non null asserting, why is this not null?
|
|
349
376
|
this.addNode(block, nodes[nodeIndex]);
|
|
350
377
|
}
|
|
351
378
|
// Calculate this block's info. Previously this was inlined into the above loop as a micro-optimization,
|
|
@@ -354,8 +381,7 @@ class MergeTree {
|
|
|
354
381
|
this.blockUpdate(block);
|
|
355
382
|
}
|
|
356
383
|
return blocks.length === 1 // If there is only one block at this layer...
|
|
357
|
-
? //
|
|
358
|
-
blocks[0] // ...then we're done. Return the root.
|
|
384
|
+
? blocks[0] // ...then we're done. Return the root.
|
|
359
385
|
: buildMergeBlock(blocks); // ...otherwise recursively build the next layer above blocks.
|
|
360
386
|
};
|
|
361
387
|
if (segments.length > 0) {
|
|
@@ -403,7 +429,6 @@ class MergeTree {
|
|
|
403
429
|
while (parent) {
|
|
404
430
|
const children = parent.children;
|
|
405
431
|
for (let childIndex = 0; childIndex < parent.childCount; childIndex++) {
|
|
406
|
-
// TODO Non null asserting, why is this not null?
|
|
407
432
|
const child = children[childIndex];
|
|
408
433
|
if ((!!prevParent && child === prevParent) || child === node) {
|
|
409
434
|
break;
|
|
@@ -618,8 +643,7 @@ class MergeTree {
|
|
|
618
643
|
(0, internal_1.assert)(this.collabWindow.minSeq <= minSeq, 0x04f /* "minSeq of collab window > target minSeq!" */);
|
|
619
644
|
if (minSeq > this.collabWindow.minSeq) {
|
|
620
645
|
this.collabWindow.minSeq = minSeq;
|
|
621
|
-
|
|
622
|
-
this.moveSeqs = firstMoveSeqIdx === -1 ? [] : this.moveSeqs.slice(firstMoveSeqIdx);
|
|
646
|
+
this.obliterates.setMinSeq(minSeq);
|
|
623
647
|
if (MergeTree.options.zamboniSegments) {
|
|
624
648
|
(0, zamboni_js_1.zamboniSegments)(this);
|
|
625
649
|
}
|
|
@@ -727,34 +751,8 @@ class MergeTree {
|
|
|
727
751
|
const deltaSegments = [];
|
|
728
752
|
const overlappingRemoves = [];
|
|
729
753
|
pendingSegmentGroup.segments.map((pendingSegment) => {
|
|
730
|
-
const localMovedSeq = pendingSegment.localMovedSeq;
|
|
731
754
|
const overlappingRemove = !pendingSegment.ack(pendingSegmentGroup, opArgs);
|
|
732
|
-
if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE && localMovedSeq !== undefined) {
|
|
733
|
-
const locallyMovedSegments = this.locallyMovedSegments.get(localMovedSeq);
|
|
734
|
-
if (locallyMovedSegments) {
|
|
735
|
-
// Disabling because a for of loop causes the type of segment to be ISegment, which does not have parent information stored
|
|
736
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
737
|
-
locallyMovedSegments.segments.forEach((segment) => {
|
|
738
|
-
segment.localMovedSeq = undefined;
|
|
739
|
-
if (!nodesToUpdate.includes(segment.parent)) {
|
|
740
|
-
nodesToUpdate.push(segment.parent);
|
|
741
|
-
}
|
|
742
|
-
if (segment.movedSeq === constants_js_1.UnassignedSequenceNumber) {
|
|
743
|
-
segment.movedSeq = seq;
|
|
744
|
-
}
|
|
745
|
-
});
|
|
746
|
-
this.locallyMovedSegments.delete(localMovedSeq);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
755
|
overwrite = overlappingRemove || overwrite;
|
|
750
|
-
if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE) {
|
|
751
|
-
if (seq !== this.moveSeqs[this.moveSeqs.length - 1]) {
|
|
752
|
-
this.moveSeqs.push(seq);
|
|
753
|
-
}
|
|
754
|
-
if (localMovedSeq !== undefined) {
|
|
755
|
-
this.localMoveSeqs.delete(localMovedSeq);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
756
|
overlappingRemoves.push(overlappingRemove);
|
|
759
757
|
if (MergeTree.options.zamboniSegments) {
|
|
760
758
|
this.addToLRUSet(pendingSegment, seq);
|
|
@@ -766,6 +764,9 @@ class MergeTree {
|
|
|
766
764
|
segment: pendingSegment,
|
|
767
765
|
});
|
|
768
766
|
});
|
|
767
|
+
if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE) {
|
|
768
|
+
this.obliterates.addOrUpdate(pendingSegmentGroup.obliterateInfo);
|
|
769
|
+
}
|
|
769
770
|
// Perform slides after all segments have been acked, so that
|
|
770
771
|
// positions after slide are final
|
|
771
772
|
if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.REMOVE ||
|
|
@@ -971,90 +972,54 @@ class MergeTree {
|
|
|
971
972
|
this.updateRoot(splitNode);
|
|
972
973
|
saveIfLocal(newSegment);
|
|
973
974
|
insertPos += newSegment.cachedLength;
|
|
974
|
-
if (!this.options?.mergeTreeEnableObliterate) {
|
|
975
|
-
continue;
|
|
976
|
-
}
|
|
977
|
-
let moveUpperBound = Number.POSITIVE_INFINITY;
|
|
978
|
-
const smallestSeqMoveOp = this.getSmallestSeqMoveOp();
|
|
979
|
-
if (smallestSeqMoveOp === undefined) {
|
|
975
|
+
if (!this.options?.mergeTreeEnableObliterate || this.obliterates.empty()) {
|
|
980
976
|
continue;
|
|
981
977
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
let
|
|
985
|
-
|
|
986
|
-
let
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
return moveUpperBound >= smallestSeqMoveOp;
|
|
1005
|
-
};
|
|
1006
|
-
const findRightMovedSegment = (seg) => {
|
|
1007
|
-
const movedSeqs = seg.movedSeqs?.filter((movedSeq) => movedSeq >= refSeq) ?? [];
|
|
1008
|
-
const localMovedSeqs = seg.localMovedSeq ? [seg.localMovedSeq] : [];
|
|
1009
|
-
for (const movedSeq of movedSeqs) {
|
|
1010
|
-
const left = leftAckedSegments[movedSeq];
|
|
1011
|
-
if (left) {
|
|
1012
|
-
_movedSeq = movedSeq;
|
|
1013
|
-
const clientIdIdx = left.movedSeqs?.indexOf(movedSeq) ?? -1;
|
|
1014
|
-
const movedClientId = left.movedClientIds?.[clientIdIdx];
|
|
1015
|
-
(0, internal_1.assert)(movedClientId !== undefined, 0x869 /* expected client id to exist */);
|
|
1016
|
-
movedClientIds = [movedClientId];
|
|
1017
|
-
return false;
|
|
978
|
+
// eslint-disable-next-line import/no-deprecated
|
|
979
|
+
let oldest;
|
|
980
|
+
let normalizedOldestSeq = 0;
|
|
981
|
+
// eslint-disable-next-line import/no-deprecated
|
|
982
|
+
let newest;
|
|
983
|
+
let normalizedNewestSeq = 0;
|
|
984
|
+
const movedClientIds = [];
|
|
985
|
+
const movedSeqs = [];
|
|
986
|
+
for (const ob of this.obliterates.findOverlapping(newSegment)) {
|
|
987
|
+
// compute a normalized seq that takes into account local seqs
|
|
988
|
+
// but is still comparable to remote seqs to keep the checks below easy
|
|
989
|
+
// REMOTE SEQUENCE NUMBERS LOCAL SEQUENCE NUMBERS
|
|
990
|
+
// [0, 1, 2, 3, ..., 100, ..., 1000, ..., (MAX - MaxLocalSeq), L1, L2, L3, L4, ..., L100, ..., L1000, ...(MAX)]
|
|
991
|
+
const normalizedObSeq = ob.seq === constants_js_1.UnassignedSequenceNumber
|
|
992
|
+
? Number.MAX_SAFE_INTEGER - this.collabWindow.localSeq + ob.localSeq
|
|
993
|
+
: ob.seq;
|
|
994
|
+
if (normalizedObSeq > refSeq) {
|
|
995
|
+
if (oldest === undefined || normalizedOldestSeq > normalizedObSeq) {
|
|
996
|
+
normalizedOldestSeq = normalizedObSeq;
|
|
997
|
+
oldest = ob;
|
|
998
|
+
movedClientIds.unshift(ob.clientId);
|
|
999
|
+
movedSeqs.unshift(ob.seq);
|
|
1018
1000
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
(0, internal_1.assert)(movedClientId !== undefined, 0x86a /* expected client id to exist */);
|
|
1027
|
-
movedClientIds = [movedClientId];
|
|
1028
|
-
return false;
|
|
1001
|
+
else {
|
|
1002
|
+
if (newest === undefined || normalizedNewestSeq < normalizedObSeq) {
|
|
1003
|
+
normalizedNewestSeq = normalizedObSeq;
|
|
1004
|
+
newest = ob;
|
|
1005
|
+
}
|
|
1006
|
+
movedClientIds.push(ob.clientId);
|
|
1007
|
+
movedSeqs.push(ob.seq);
|
|
1029
1008
|
}
|
|
1030
1009
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
}
|
|
1034
|
-
if (!isRemoved(seg) || wasRemovedAfter(seg, moveUpperBound)) {
|
|
1035
|
-
moveUpperBound = Math.min(moveUpperBound, seg.seq ?? Number.POSITIVE_INFINITY);
|
|
1036
|
-
}
|
|
1037
|
-
// If we've reached a segment that existed before any of our in-collab-window move ops
|
|
1038
|
-
// happened, no need to continue.
|
|
1039
|
-
return moveUpperBound >= smallestSeqMoveOp;
|
|
1040
|
-
};
|
|
1041
|
-
(0, mergeTreeNodeWalk_js_1.backwardExcursion)(newSegment, findLeftMovedSegment);
|
|
1042
|
-
moveUpperBound = Number.POSITIVE_INFINITY;
|
|
1043
|
-
(0, mergeTreeNodeWalk_js_1.forwardExcursion)(newSegment, findRightMovedSegment);
|
|
1044
|
-
if (_localMovedSeq !== undefined || _movedSeq !== undefined) {
|
|
1045
|
-
(0, internal_1.assert)(movedClientIds !== undefined, 0x86b /* movedClientIds should be set if local/moved seq is set */);
|
|
1010
|
+
}
|
|
1011
|
+
if (oldest && newest?.clientId !== clientId) {
|
|
1046
1012
|
const moveInfo = {
|
|
1047
1013
|
movedClientIds,
|
|
1048
|
-
movedSeq:
|
|
1049
|
-
movedSeqs
|
|
1050
|
-
localMovedSeq:
|
|
1051
|
-
wasMovedOnInsert:
|
|
1014
|
+
movedSeq: oldest.seq,
|
|
1015
|
+
movedSeqs,
|
|
1016
|
+
localMovedSeq: oldest.localSeq,
|
|
1017
|
+
wasMovedOnInsert: oldest.seq !== constants_js_1.UnassignedSequenceNumber,
|
|
1052
1018
|
};
|
|
1053
1019
|
markSegmentMoved(newSegment, moveInfo);
|
|
1054
1020
|
if (moveInfo.localMovedSeq !== undefined) {
|
|
1055
|
-
|
|
1056
|
-
(
|
|
1057
|
-
this.addToPendingList(newSegment, movedSegmentGroup, localSeq);
|
|
1021
|
+
(0, internal_1.assert)(oldest.segmentGroup !== undefined, 0x86c /* expected segment group to exist */);
|
|
1022
|
+
this.addToPendingList(newSegment, oldest.segmentGroup);
|
|
1058
1023
|
}
|
|
1059
1024
|
if (newSegment.parent) {
|
|
1060
1025
|
this.blockUpdatePathLengths(newSegment.parent, seq, clientId);
|
|
@@ -1092,27 +1057,14 @@ class MergeTree {
|
|
|
1092
1057
|
return true;
|
|
1093
1058
|
}
|
|
1094
1059
|
}
|
|
1095
|
-
getSmallestSeqMoveOp() {
|
|
1096
|
-
return this.moveSeqs[0] ?? (this.localMoveSeqs.size > 0 ? -1 : undefined);
|
|
1097
|
-
}
|
|
1098
1060
|
insertingWalk(block, pos, refSeq, clientId, seq, context, isLastChildBlock = true) {
|
|
1099
|
-
let _pos;
|
|
1100
|
-
if (pos === "start") {
|
|
1101
|
-
_pos = 0;
|
|
1102
|
-
}
|
|
1103
|
-
else if (pos === "end") {
|
|
1104
|
-
_pos = this.root.mergeTree?.getLength(refSeq, clientId) ?? 0;
|
|
1105
|
-
}
|
|
1106
|
-
else {
|
|
1107
|
-
_pos = pos;
|
|
1108
|
-
}
|
|
1061
|
+
let _pos = pos;
|
|
1109
1062
|
const children = block.children;
|
|
1110
1063
|
let childIndex;
|
|
1111
1064
|
let child;
|
|
1112
1065
|
let newNode;
|
|
1113
1066
|
let fromSplit;
|
|
1114
1067
|
for (childIndex = 0; childIndex < block.childCount; childIndex++) {
|
|
1115
|
-
// TODO Non null asserting, why is this not null?
|
|
1116
1068
|
child = children[childIndex];
|
|
1117
1069
|
// ensure we walk down the far edge of the tree, even if all sub-tree is eligible for zamboni
|
|
1118
1070
|
const isLastNonLeafBlock = isLastChildBlock && !child.isLeaf() && childIndex === block.childCount - 1;
|
|
@@ -1177,9 +1129,7 @@ class MergeTree {
|
|
|
1177
1129
|
}
|
|
1178
1130
|
if (newNode) {
|
|
1179
1131
|
for (let i = block.childCount; i > childIndex; i--) {
|
|
1180
|
-
// TODO Non null asserting, why is this not null?
|
|
1181
1132
|
block.children[i] = block.children[i - 1];
|
|
1182
|
-
// TODO Non null asserting, why is this not null?
|
|
1183
1133
|
block.children[i].index = i;
|
|
1184
1134
|
}
|
|
1185
1135
|
block.assignChild(newNode, childIndex, false);
|
|
@@ -1211,7 +1161,6 @@ class MergeTree {
|
|
|
1211
1161
|
// Update ordinals to reflect lowered child count
|
|
1212
1162
|
this.nodeUpdateOrdinals(node);
|
|
1213
1163
|
for (let i = 0; i < halfCount; i++) {
|
|
1214
|
-
// TODO Non null asserting, why is this not null?
|
|
1215
1164
|
newNode.assignChild(node.children[halfCount + i], i, false);
|
|
1216
1165
|
node.children[halfCount + i] = undefined;
|
|
1217
1166
|
}
|
|
@@ -1221,7 +1170,6 @@ class MergeTree {
|
|
|
1221
1170
|
}
|
|
1222
1171
|
nodeUpdateOrdinals(block) {
|
|
1223
1172
|
for (let i = 0; i < block.childCount; i++) {
|
|
1224
|
-
// TODO Non null asserting, why is this not null?
|
|
1225
1173
|
const child = block.children[i];
|
|
1226
1174
|
block.setOrdinal(child, i);
|
|
1227
1175
|
if (!child.isLeaf()) {
|
|
@@ -1285,33 +1233,34 @@ class MergeTree {
|
|
|
1285
1233
|
}
|
|
1286
1234
|
obliterateRange(start, end, refSeq, clientId, seq, overwrite = false, opArgs) {
|
|
1287
1235
|
errorIfOptionNotTrue(this.options, "mergeTreeEnableObliterate");
|
|
1288
|
-
|
|
1289
|
-
(
|
|
1290
|
-
endPos !== undefined &&
|
|
1291
|
-
startSide !== undefined &&
|
|
1292
|
-
endSide !== undefined &&
|
|
1293
|
-
startPos !== "end" &&
|
|
1294
|
-
endPos !== "start", 0x9e2 /* start and end cannot be undefined because they were not passed in as undefined */);
|
|
1295
|
-
this.ensureIntervalBoundary(startPos, refSeq, clientId);
|
|
1296
|
-
this.ensureIntervalBoundary(endPos, refSeq, clientId);
|
|
1236
|
+
this.ensureIntervalBoundary(start, refSeq, clientId);
|
|
1237
|
+
this.ensureIntervalBoundary(end, refSeq, clientId);
|
|
1297
1238
|
let _overwrite = overwrite;
|
|
1298
1239
|
const localOverlapWithRefs = [];
|
|
1299
1240
|
const movedSegments = [];
|
|
1300
1241
|
const localSeq = seq === constants_js_1.UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
|
|
1301
|
-
if (seq !== constants_js_1.UnassignedSequenceNumber && seq !== this.moveSeqs[this.moveSeqs.length - 1]) {
|
|
1302
|
-
this.moveSeqs.push(seq);
|
|
1303
|
-
}
|
|
1304
|
-
else if (seq === constants_js_1.UnassignedSequenceNumber && localSeq !== undefined) {
|
|
1305
|
-
this.localMoveSeqs.add(localSeq);
|
|
1306
|
-
}
|
|
1307
1242
|
// eslint-disable-next-line import/no-deprecated
|
|
1308
|
-
|
|
1243
|
+
const obliterate = {
|
|
1244
|
+
clientId,
|
|
1245
|
+
end: (0, localReference_js_1.createDetachedLocalReferencePosition)(undefined),
|
|
1246
|
+
refSeq,
|
|
1247
|
+
seq,
|
|
1248
|
+
start: (0, localReference_js_1.createDetachedLocalReferencePosition)(undefined),
|
|
1249
|
+
localSeq,
|
|
1250
|
+
segmentGroup: undefined,
|
|
1251
|
+
};
|
|
1252
|
+
const { segment: startSeg } = this.getContainingSegment(start, refSeq, clientId);
|
|
1253
|
+
const { segment: endSeg } = this.getContainingSegment(end - 1, refSeq, clientId);
|
|
1254
|
+
(0, internal_1.assert)(startSeg !== undefined && endSeg !== undefined, 0xa3f /* segments cannot be undefined */);
|
|
1255
|
+
obliterate.start = this.createLocalReferencePosition(startSeg, 0, ops_js_1.ReferenceType.StayOnRemove, {
|
|
1256
|
+
obliterate,
|
|
1257
|
+
});
|
|
1258
|
+
obliterate.end = this.createLocalReferencePosition(endSeg, endSeg.cachedLength - 1, ops_js_1.ReferenceType.StayOnRemove, {
|
|
1259
|
+
obliterate,
|
|
1260
|
+
});
|
|
1309
1261
|
const markMoved = (segment, pos, _start, _end) => {
|
|
1262
|
+
var _a;
|
|
1310
1263
|
const existingMoveInfo = (0, mergeTreeNodes_js_1.toMoveInfo)(segment);
|
|
1311
|
-
if (startSide)
|
|
1312
|
-
segment.startSide = startSide;
|
|
1313
|
-
if (endSide)
|
|
1314
|
-
segment.endSide = endSide;
|
|
1315
1264
|
if (clientId !== segment.clientId &&
|
|
1316
1265
|
segment.seq !== undefined &&
|
|
1317
1266
|
seq !== constants_js_1.UnassignedSequenceNumber &&
|
|
@@ -1351,7 +1300,8 @@ class MergeTree {
|
|
|
1351
1300
|
if (this.collabWindow.collaborating) {
|
|
1352
1301
|
if (segment.movedSeq === constants_js_1.UnassignedSequenceNumber &&
|
|
1353
1302
|
clientId === this.collabWindow.clientId) {
|
|
1354
|
-
segmentGroup = this.addToPendingList(segment, segmentGroup, localSeq);
|
|
1303
|
+
obliterate.segmentGroup = this.addToPendingList(segment, obliterate.segmentGroup, localSeq);
|
|
1304
|
+
(_a = obliterate.segmentGroup).obliterateInfo ?? (_a.obliterateInfo = obliterate);
|
|
1355
1305
|
}
|
|
1356
1306
|
else {
|
|
1357
1307
|
if (MergeTree.options.zamboniSegments) {
|
|
@@ -1371,6 +1321,7 @@ class MergeTree {
|
|
|
1371
1321
|
return true;
|
|
1372
1322
|
};
|
|
1373
1323
|
this.nodeMap(refSeq, clientId, markMoved, undefined, afterMarkMoved, start, end, undefined, seq === constants_js_1.UnassignedSequenceNumber ? undefined : seq);
|
|
1324
|
+
this.obliterates.addOrUpdate(obliterate);
|
|
1374
1325
|
this.slideAckedRemovedSegmentReferences(localOverlapWithRefs);
|
|
1375
1326
|
// opArgs == undefined => test code
|
|
1376
1327
|
if (movedSegments.length > 0) {
|
|
@@ -1379,9 +1330,6 @@ class MergeTree {
|
|
|
1379
1330
|
deltaSegments: movedSegments,
|
|
1380
1331
|
});
|
|
1381
1332
|
}
|
|
1382
|
-
if (segmentGroup && localSeq !== undefined) {
|
|
1383
|
-
this.locallyMovedSegments.set(localSeq, segmentGroup);
|
|
1384
|
-
}
|
|
1385
1333
|
// these events are newly removed
|
|
1386
1334
|
// so we slide after eventing in case the consumer wants to make reference
|
|
1387
1335
|
// changes at remove time, like add a ref to track undo redo.
|
|
@@ -1528,7 +1476,6 @@ class MergeTree {
|
|
|
1528
1476
|
this.markRangeRemoved(start, start + segment.cachedLength, constants_js_1.UniversalSequenceNumber, this.collabWindow.clientId, constants_js_1.UniversalSequenceNumber, false, { op: removeOp });
|
|
1529
1477
|
} /* op.type === MergeTreeDeltaType.ANNOTATE */
|
|
1530
1478
|
else {
|
|
1531
|
-
// TODO Non null asserting, why is this not null?
|
|
1532
1479
|
const props = pendingSegmentGroup.previousProps[i];
|
|
1533
1480
|
const annotateOp = (0, opBuilder_js_1.createAnnotateRangeOp)(start, start + segment.cachedLength, props);
|
|
1534
1481
|
this.annotateRange(start, start + segment.cachedLength, props, constants_js_1.UniversalSequenceNumber, this.collabWindow.clientId, constants_js_1.UniversalSequenceNumber, { op: annotateOp },
|
|
@@ -1575,7 +1522,7 @@ class MergeTree {
|
|
|
1575
1522
|
if (_segment !== "start" &&
|
|
1576
1523
|
_segment !== "end" &&
|
|
1577
1524
|
isRemovedAndAckedOrMovedAndAcked(_segment) &&
|
|
1578
|
-
!(0, referencePositions_js_1.refTypeIncludesFlag)(refType, ops_js_1.ReferenceType.SlideOnRemove | ops_js_1.ReferenceType.Transient) &&
|
|
1525
|
+
!(0, referencePositions_js_1.refTypeIncludesFlag)(refType, ops_js_1.ReferenceType.SlideOnRemove | ops_js_1.ReferenceType.Transient | ops_js_1.ReferenceType.StayOnRemove) &&
|
|
1579
1526
|
_segment.endpointType === undefined) {
|
|
1580
1527
|
throw new internal_2.UsageError("Can only create SlideOnRemove or Transient local reference position on a removed or obliterated segment");
|
|
1581
1528
|
}
|
|
@@ -1648,7 +1595,6 @@ class MergeTree {
|
|
|
1648
1595
|
}
|
|
1649
1596
|
}
|
|
1650
1597
|
for (let i = 0; i < newOrder.length; i++) {
|
|
1651
|
-
// TODO Non null asserting, why is this not null?
|
|
1652
1598
|
const seg = newOrder[i];
|
|
1653
1599
|
const { parent, index, ordinal } = currentOrder[i];
|
|
1654
1600
|
parent?.assignChild(seg, index, false);
|
|
@@ -1735,7 +1681,6 @@ class MergeTree {
|
|
|
1735
1681
|
const rightmostTiles = (0, properties_js_1.createMap)();
|
|
1736
1682
|
const leftmostTiles = (0, properties_js_1.createMap)();
|
|
1737
1683
|
for (let i = 0; i < block.childCount; i++) {
|
|
1738
|
-
// TODO Non null asserting, why is this not null?
|
|
1739
1684
|
const node = block.children[i];
|
|
1740
1685
|
const nodeLength = nodeTotalLength(this, node);
|
|
1741
1686
|
if (nodeLength !== undefined) {
|
|
@@ -1845,18 +1790,11 @@ class MergeTree {
|
|
|
1845
1790
|
* ignored for the purposes of tracking when traversal should end.
|
|
1846
1791
|
*/
|
|
1847
1792
|
nodeMap(refSeq, clientId, leaf, accum, post, start = 0, end, localSeq, visibilitySeq = refSeq) {
|
|
1848
|
-
const
|
|
1849
|
-
if (
|
|
1793
|
+
const endPos = end ?? this.nodeLength(this.root, refSeq, clientId, localSeq) ?? 0;
|
|
1794
|
+
if (endPos === start) {
|
|
1850
1795
|
return;
|
|
1851
1796
|
}
|
|
1852
1797
|
let pos = 0;
|
|
1853
|
-
let { startPos, endPos } = (0, sequencePlace_js_1.endpointPosAndSide)(start, end);
|
|
1854
|
-
startPos = startPos === "start" || startPos === undefined ? 0 : startPos;
|
|
1855
|
-
endPos =
|
|
1856
|
-
endPos === "end" || endPos === undefined
|
|
1857
|
-
? this.root.mergeTree?.getLength(refSeq, clientId) ?? 0
|
|
1858
|
-
: endPos;
|
|
1859
|
-
(0, internal_1.assert)(startPos !== "end" && endPos !== "start", 0x9e3 /* start cannot be 'end' and end cannot be 'start' */);
|
|
1860
1798
|
(0, mergeTreeNodeWalk_js_1.depthFirstNodeWalk)(this.root, this.root.children[0], (node) => {
|
|
1861
1799
|
if (endPos <= pos) {
|
|
1862
1800
|
return mergeTreeNodeWalk_js_1.NodeAction.Exit;
|
|
@@ -1873,19 +1811,19 @@ class MergeTree {
|
|
|
1873
1811
|
}
|
|
1874
1812
|
const nextPos = pos + lenAtRefSeq;
|
|
1875
1813
|
// start is beyond the current node, so we can skip it
|
|
1876
|
-
if (
|
|
1814
|
+
if (start >= nextPos) {
|
|
1877
1815
|
pos = nextPos;
|
|
1878
1816
|
return mergeTreeNodeWalk_js_1.NodeAction.Skip;
|
|
1879
1817
|
}
|
|
1880
1818
|
if (node.isLeaf()) {
|
|
1881
|
-
if (leaf(node, pos, refSeq, clientId,
|
|
1819
|
+
if (leaf(node, pos, refSeq, clientId, start - pos, endPos - pos, accum) === false) {
|
|
1882
1820
|
return mergeTreeNodeWalk_js_1.NodeAction.Exit;
|
|
1883
1821
|
}
|
|
1884
1822
|
pos = nextPos;
|
|
1885
1823
|
}
|
|
1886
1824
|
}, undefined, post === undefined
|
|
1887
1825
|
? undefined
|
|
1888
|
-
: (block) => post(block, pos, refSeq, clientId,
|
|
1826
|
+
: (block) => post(block, pos, refSeq, clientId, start - pos, endPos - pos, accum));
|
|
1889
1827
|
}
|
|
1890
1828
|
}
|
|
1891
1829
|
exports.MergeTree = MergeTree;
|