@fluidframework/merge-tree 2.2.0 → 2.3.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/CHANGELOG.md +4 -0
- package/api-report/merge-tree.legacy.alpha.api.md +20 -0
- 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 -4
- 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 +1 -30
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +131 -167
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +16 -1
- 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.spec.js +55 -0
- 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/testClientLogger.d.ts.map +1 -1
- package/dist/test/testClientLogger.js +17 -7
- package/dist/test/testClientLogger.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 -4
- 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 +1 -30
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +132 -168
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +16 -1
- 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.spec.js +55 -0
- 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/testClientLogger.d.ts.map +1 -1
- package/lib/test/testClientLogger.js +18 -8
- package/lib/test/testClientLogger.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 +8 -9
- package/src/index.ts +1 -0
- package/src/localReference.ts +1 -3
- package/src/mergeTree.ts +185 -208
- package/src/mergeTreeNodes.ts +22 -3
- 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
|
@@ -25,11 +25,8 @@ 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
27
|
const sequencePlace_js_1 = require("./sequencePlace.js");
|
|
28
|
+
const sortedSegmentSet_js_1 = require("./sortedSegmentSet.js");
|
|
28
29
|
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
30
|
function markSegmentMoved(seg, moveInfo) {
|
|
34
31
|
seg.moveDst = moveInfo.moveDst;
|
|
35
32
|
seg.movedClientIds = [...moveInfo.movedClientIds];
|
|
@@ -180,6 +177,67 @@ function getSlideToSegoff(segoff, slidingPreference = localReference_js_1.Slidin
|
|
|
180
177
|
exports.getSlideToSegoff = getSlideToSegoff;
|
|
181
178
|
const forwardPred = (ref) => ref.slidingPreference !== localReference_js_1.SlidingPreference.BACKWARD;
|
|
182
179
|
const backwardPred = (ref) => ref.slidingPreference === localReference_js_1.SlidingPreference.BACKWARD;
|
|
180
|
+
class Obliterates {
|
|
181
|
+
constructor(mergeTree) {
|
|
182
|
+
this.mergeTree = mergeTree;
|
|
183
|
+
/**
|
|
184
|
+
* Array containing the all move operations within the
|
|
185
|
+
* collab window.
|
|
186
|
+
*
|
|
187
|
+
* The moves are stored in sequence order which accelerates clean up in setMinSeq
|
|
188
|
+
*
|
|
189
|
+
* See https://github.com/microsoft/FluidFramework/blob/main/packages/dds/merge-tree/docs/Obliterate.md#remote-perspective
|
|
190
|
+
* for additional context
|
|
191
|
+
*/
|
|
192
|
+
// eslint-disable-next-line import/no-deprecated
|
|
193
|
+
this.seqOrdered = new index_js_1.DoublyLinkedList();
|
|
194
|
+
/**
|
|
195
|
+
* This contains a sorted lists of all obliterate starts
|
|
196
|
+
* and is used to accelerate finding overlapping obliterates
|
|
197
|
+
* as well as determining if there are any obliterates at all.
|
|
198
|
+
*/
|
|
199
|
+
this.startOrdered = new sortedSegmentSet_js_1.SortedSegmentSet();
|
|
200
|
+
}
|
|
201
|
+
setMinSeq(minSeq) {
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
|
203
|
+
while (!this.seqOrdered.empty && this.seqOrdered.first?.data.seq <= minSeq) {
|
|
204
|
+
const ob = this.seqOrdered.shift();
|
|
205
|
+
this.startOrdered.remove(ob.data.start);
|
|
206
|
+
this.mergeTree.removeLocalReferencePosition(ob.data.start);
|
|
207
|
+
this.mergeTree.removeLocalReferencePosition(ob.data.end);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// eslint-disable-next-line import/no-deprecated
|
|
211
|
+
addOrUpdate(obliterateInfo) {
|
|
212
|
+
const { seq, start } = obliterateInfo;
|
|
213
|
+
if (seq !== constants_js_1.UnassignedSequenceNumber) {
|
|
214
|
+
this.seqOrdered.push(obliterateInfo);
|
|
215
|
+
}
|
|
216
|
+
this.startOrdered.addOrUpdate(start);
|
|
217
|
+
}
|
|
218
|
+
empty() {
|
|
219
|
+
return this.startOrdered.size === 0;
|
|
220
|
+
}
|
|
221
|
+
// eslint-disable-next-line import/no-deprecated
|
|
222
|
+
findOverlapping(seg) {
|
|
223
|
+
// eslint-disable-next-line import/no-deprecated
|
|
224
|
+
const overlapping = [];
|
|
225
|
+
for (const start of this.startOrdered.items) {
|
|
226
|
+
if (start.getSegment().ordinal <= seg.ordinal) {
|
|
227
|
+
// eslint-disable-next-line import/no-deprecated
|
|
228
|
+
const ob = start.properties?.obliterate;
|
|
229
|
+
if (ob.end.getSegment().ordinal >= seg.ordinal) {
|
|
230
|
+
overlapping.push(ob);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
// the start is past the seg, so exit
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return overlapping;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
183
241
|
/**
|
|
184
242
|
* @internal
|
|
185
243
|
*/
|
|
@@ -200,36 +258,7 @@ class MergeTree {
|
|
|
200
258
|
// for now assume only markers have ids and so point directly at the Segment
|
|
201
259
|
// if we need to have pointers to non-markers, we can change to point at local refs
|
|
202
260
|
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();
|
|
261
|
+
this.obliterates = new Obliterates(this);
|
|
233
262
|
this.splitLeafSegment = (segment, pos) => {
|
|
234
263
|
if (!(pos > 0 && segment)) {
|
|
235
264
|
return {};
|
|
@@ -345,7 +374,6 @@ class MergeTree {
|
|
|
345
374
|
childIndex++, nodeIndex++ // Advance to next child & node
|
|
346
375
|
) {
|
|
347
376
|
// Insert the next node into the current block
|
|
348
|
-
// TODO Non null asserting, why is this not null?
|
|
349
377
|
this.addNode(block, nodes[nodeIndex]);
|
|
350
378
|
}
|
|
351
379
|
// Calculate this block's info. Previously this was inlined into the above loop as a micro-optimization,
|
|
@@ -354,8 +382,7 @@ class MergeTree {
|
|
|
354
382
|
this.blockUpdate(block);
|
|
355
383
|
}
|
|
356
384
|
return blocks.length === 1 // If there is only one block at this layer...
|
|
357
|
-
? //
|
|
358
|
-
blocks[0] // ...then we're done. Return the root.
|
|
385
|
+
? blocks[0] // ...then we're done. Return the root.
|
|
359
386
|
: buildMergeBlock(blocks); // ...otherwise recursively build the next layer above blocks.
|
|
360
387
|
};
|
|
361
388
|
if (segments.length > 0) {
|
|
@@ -403,7 +430,6 @@ class MergeTree {
|
|
|
403
430
|
while (parent) {
|
|
404
431
|
const children = parent.children;
|
|
405
432
|
for (let childIndex = 0; childIndex < parent.childCount; childIndex++) {
|
|
406
|
-
// TODO Non null asserting, why is this not null?
|
|
407
433
|
const child = children[childIndex];
|
|
408
434
|
if ((!!prevParent && child === prevParent) || child === node) {
|
|
409
435
|
break;
|
|
@@ -618,8 +644,7 @@ class MergeTree {
|
|
|
618
644
|
(0, internal_1.assert)(this.collabWindow.minSeq <= minSeq, 0x04f /* "minSeq of collab window > target minSeq!" */);
|
|
619
645
|
if (minSeq > this.collabWindow.minSeq) {
|
|
620
646
|
this.collabWindow.minSeq = minSeq;
|
|
621
|
-
|
|
622
|
-
this.moveSeqs = firstMoveSeqIdx === -1 ? [] : this.moveSeqs.slice(firstMoveSeqIdx);
|
|
647
|
+
this.obliterates.setMinSeq(minSeq);
|
|
623
648
|
if (MergeTree.options.zamboniSegments) {
|
|
624
649
|
(0, zamboni_js_1.zamboniSegments)(this);
|
|
625
650
|
}
|
|
@@ -727,34 +752,8 @@ class MergeTree {
|
|
|
727
752
|
const deltaSegments = [];
|
|
728
753
|
const overlappingRemoves = [];
|
|
729
754
|
pendingSegmentGroup.segments.map((pendingSegment) => {
|
|
730
|
-
const localMovedSeq = pendingSegment.localMovedSeq;
|
|
731
755
|
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
756
|
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
757
|
overlappingRemoves.push(overlappingRemove);
|
|
759
758
|
if (MergeTree.options.zamboniSegments) {
|
|
760
759
|
this.addToLRUSet(pendingSegment, seq);
|
|
@@ -766,6 +765,9 @@ class MergeTree {
|
|
|
766
765
|
segment: pendingSegment,
|
|
767
766
|
});
|
|
768
767
|
});
|
|
768
|
+
if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE) {
|
|
769
|
+
this.obliterates.addOrUpdate(pendingSegmentGroup.obliterateInfo);
|
|
770
|
+
}
|
|
769
771
|
// Perform slides after all segments have been acked, so that
|
|
770
772
|
// positions after slide are final
|
|
771
773
|
if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.REMOVE ||
|
|
@@ -971,90 +973,54 @@ class MergeTree {
|
|
|
971
973
|
this.updateRoot(splitNode);
|
|
972
974
|
saveIfLocal(newSegment);
|
|
973
975
|
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) {
|
|
976
|
+
if (!this.options?.mergeTreeEnableObliterate || this.obliterates.empty()) {
|
|
980
977
|
continue;
|
|
981
978
|
}
|
|
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;
|
|
979
|
+
// eslint-disable-next-line import/no-deprecated
|
|
980
|
+
let oldest;
|
|
981
|
+
let normalizedOldestSeq = 0;
|
|
982
|
+
// eslint-disable-next-line import/no-deprecated
|
|
983
|
+
let newest;
|
|
984
|
+
let normalizedNewestSeq = 0;
|
|
985
|
+
const movedClientIds = [];
|
|
986
|
+
const movedSeqs = [];
|
|
987
|
+
for (const ob of this.obliterates.findOverlapping(newSegment)) {
|
|
988
|
+
// compute a normalized seq that takes into account local seqs
|
|
989
|
+
// but is still comparable to remote seqs to keep the checks below easy
|
|
990
|
+
// REMOTE SEQUENCE NUMBERS LOCAL SEQUENCE NUMBERS
|
|
991
|
+
// [0, 1, 2, 3, ..., 100, ..., 1000, ..., (MAX - MaxLocalSeq), L1, L2, L3, L4, ..., L100, ..., L1000, ...(MAX)]
|
|
992
|
+
const normalizedObSeq = ob.seq === constants_js_1.UnassignedSequenceNumber
|
|
993
|
+
? Number.MAX_SAFE_INTEGER - this.collabWindow.localSeq + ob.localSeq
|
|
994
|
+
: ob.seq;
|
|
995
|
+
if (normalizedObSeq > refSeq) {
|
|
996
|
+
if (oldest === undefined || normalizedOldestSeq > normalizedObSeq) {
|
|
997
|
+
normalizedOldestSeq = normalizedObSeq;
|
|
998
|
+
oldest = ob;
|
|
999
|
+
movedClientIds.unshift(ob.clientId);
|
|
1000
|
+
movedSeqs.unshift(ob.seq);
|
|
1018
1001
|
}
|
|
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;
|
|
1002
|
+
else {
|
|
1003
|
+
if (newest === undefined || normalizedNewestSeq < normalizedObSeq) {
|
|
1004
|
+
normalizedNewestSeq = normalizedObSeq;
|
|
1005
|
+
newest = ob;
|
|
1006
|
+
}
|
|
1007
|
+
movedClientIds.push(ob.clientId);
|
|
1008
|
+
movedSeqs.push(ob.seq);
|
|
1029
1009
|
}
|
|
1030
1010
|
}
|
|
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 */);
|
|
1011
|
+
}
|
|
1012
|
+
if (oldest && newest?.clientId !== clientId) {
|
|
1046
1013
|
const moveInfo = {
|
|
1047
1014
|
movedClientIds,
|
|
1048
|
-
movedSeq:
|
|
1049
|
-
movedSeqs
|
|
1050
|
-
localMovedSeq:
|
|
1051
|
-
wasMovedOnInsert:
|
|
1015
|
+
movedSeq: oldest.seq,
|
|
1016
|
+
movedSeqs,
|
|
1017
|
+
localMovedSeq: oldest.localSeq,
|
|
1018
|
+
wasMovedOnInsert: oldest.seq !== constants_js_1.UnassignedSequenceNumber,
|
|
1052
1019
|
};
|
|
1053
1020
|
markSegmentMoved(newSegment, moveInfo);
|
|
1054
1021
|
if (moveInfo.localMovedSeq !== undefined) {
|
|
1055
|
-
|
|
1056
|
-
(
|
|
1057
|
-
this.addToPendingList(newSegment, movedSegmentGroup, localSeq);
|
|
1022
|
+
(0, internal_1.assert)(oldest.segmentGroup !== undefined, 0x86c /* expected segment group to exist */);
|
|
1023
|
+
this.addToPendingList(newSegment, oldest.segmentGroup);
|
|
1058
1024
|
}
|
|
1059
1025
|
if (newSegment.parent) {
|
|
1060
1026
|
this.blockUpdatePathLengths(newSegment.parent, seq, clientId);
|
|
@@ -1092,9 +1058,6 @@ class MergeTree {
|
|
|
1092
1058
|
return true;
|
|
1093
1059
|
}
|
|
1094
1060
|
}
|
|
1095
|
-
getSmallestSeqMoveOp() {
|
|
1096
|
-
return this.moveSeqs[0] ?? (this.localMoveSeqs.size > 0 ? -1 : undefined);
|
|
1097
|
-
}
|
|
1098
1061
|
insertingWalk(block, pos, refSeq, clientId, seq, context, isLastChildBlock = true) {
|
|
1099
1062
|
let _pos;
|
|
1100
1063
|
if (pos === "start") {
|
|
@@ -1112,7 +1075,6 @@ class MergeTree {
|
|
|
1112
1075
|
let newNode;
|
|
1113
1076
|
let fromSplit;
|
|
1114
1077
|
for (childIndex = 0; childIndex < block.childCount; childIndex++) {
|
|
1115
|
-
// TODO Non null asserting, why is this not null?
|
|
1116
1078
|
child = children[childIndex];
|
|
1117
1079
|
// ensure we walk down the far edge of the tree, even if all sub-tree is eligible for zamboni
|
|
1118
1080
|
const isLastNonLeafBlock = isLastChildBlock && !child.isLeaf() && childIndex === block.childCount - 1;
|
|
@@ -1177,9 +1139,7 @@ class MergeTree {
|
|
|
1177
1139
|
}
|
|
1178
1140
|
if (newNode) {
|
|
1179
1141
|
for (let i = block.childCount; i > childIndex; i--) {
|
|
1180
|
-
// TODO Non null asserting, why is this not null?
|
|
1181
1142
|
block.children[i] = block.children[i - 1];
|
|
1182
|
-
// TODO Non null asserting, why is this not null?
|
|
1183
1143
|
block.children[i].index = i;
|
|
1184
1144
|
}
|
|
1185
1145
|
block.assignChild(newNode, childIndex, false);
|
|
@@ -1211,7 +1171,6 @@ class MergeTree {
|
|
|
1211
1171
|
// Update ordinals to reflect lowered child count
|
|
1212
1172
|
this.nodeUpdateOrdinals(node);
|
|
1213
1173
|
for (let i = 0; i < halfCount; i++) {
|
|
1214
|
-
// TODO Non null asserting, why is this not null?
|
|
1215
1174
|
newNode.assignChild(node.children[halfCount + i], i, false);
|
|
1216
1175
|
node.children[halfCount + i] = undefined;
|
|
1217
1176
|
}
|
|
@@ -1221,7 +1180,6 @@ class MergeTree {
|
|
|
1221
1180
|
}
|
|
1222
1181
|
nodeUpdateOrdinals(block) {
|
|
1223
1182
|
for (let i = 0; i < block.childCount; i++) {
|
|
1224
|
-
// TODO Non null asserting, why is this not null?
|
|
1225
1183
|
const child = block.children[i];
|
|
1226
1184
|
block.setOrdinal(child, i);
|
|
1227
1185
|
if (!child.isLeaf()) {
|
|
@@ -1298,20 +1256,30 @@ class MergeTree {
|
|
|
1298
1256
|
const localOverlapWithRefs = [];
|
|
1299
1257
|
const movedSegments = [];
|
|
1300
1258
|
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
1259
|
// eslint-disable-next-line import/no-deprecated
|
|
1308
|
-
|
|
1260
|
+
const obliterate = {
|
|
1261
|
+
clientId,
|
|
1262
|
+
end: (0, localReference_js_1.createDetachedLocalReferencePosition)(undefined),
|
|
1263
|
+
refSeq,
|
|
1264
|
+
seq,
|
|
1265
|
+
start: (0, localReference_js_1.createDetachedLocalReferencePosition)(undefined),
|
|
1266
|
+
localSeq,
|
|
1267
|
+
segmentGroup: undefined,
|
|
1268
|
+
};
|
|
1269
|
+
const normalizedStartPos = startPos === "start" || startPos === undefined ? 0 : startPos;
|
|
1270
|
+
const normalizedEndPos = endPos === "end" || endPos === undefined ? this.getLength(refSeq, clientId) : endPos;
|
|
1271
|
+
const { segment: startSeg } = this.getContainingSegment(normalizedStartPos, refSeq, clientId);
|
|
1272
|
+
const { segment: endSeg } = this.getContainingSegment(normalizedEndPos - 1, refSeq, clientId);
|
|
1273
|
+
(0, internal_1.assert)(startSeg !== undefined && endSeg !== undefined, 0xa3f /* segments cannot be undefined */);
|
|
1274
|
+
obliterate.start = this.createLocalReferencePosition(startSeg, 0, ops_js_1.ReferenceType.StayOnRemove, {
|
|
1275
|
+
obliterate,
|
|
1276
|
+
});
|
|
1277
|
+
obliterate.end = this.createLocalReferencePosition(endSeg, endSeg.cachedLength - 1, ops_js_1.ReferenceType.StayOnRemove, {
|
|
1278
|
+
obliterate,
|
|
1279
|
+
});
|
|
1309
1280
|
const markMoved = (segment, pos, _start, _end) => {
|
|
1281
|
+
var _a;
|
|
1310
1282
|
const existingMoveInfo = (0, mergeTreeNodes_js_1.toMoveInfo)(segment);
|
|
1311
|
-
if (startSide)
|
|
1312
|
-
segment.startSide = startSide;
|
|
1313
|
-
if (endSide)
|
|
1314
|
-
segment.endSide = endSide;
|
|
1315
1283
|
if (clientId !== segment.clientId &&
|
|
1316
1284
|
segment.seq !== undefined &&
|
|
1317
1285
|
seq !== constants_js_1.UnassignedSequenceNumber &&
|
|
@@ -1351,7 +1319,8 @@ class MergeTree {
|
|
|
1351
1319
|
if (this.collabWindow.collaborating) {
|
|
1352
1320
|
if (segment.movedSeq === constants_js_1.UnassignedSequenceNumber &&
|
|
1353
1321
|
clientId === this.collabWindow.clientId) {
|
|
1354
|
-
segmentGroup = this.addToPendingList(segment, segmentGroup, localSeq);
|
|
1322
|
+
obliterate.segmentGroup = this.addToPendingList(segment, obliterate.segmentGroup, localSeq);
|
|
1323
|
+
(_a = obliterate.segmentGroup).obliterateInfo ?? (_a.obliterateInfo = obliterate);
|
|
1355
1324
|
}
|
|
1356
1325
|
else {
|
|
1357
1326
|
if (MergeTree.options.zamboniSegments) {
|
|
@@ -1371,6 +1340,7 @@ class MergeTree {
|
|
|
1371
1340
|
return true;
|
|
1372
1341
|
};
|
|
1373
1342
|
this.nodeMap(refSeq, clientId, markMoved, undefined, afterMarkMoved, start, end, undefined, seq === constants_js_1.UnassignedSequenceNumber ? undefined : seq);
|
|
1343
|
+
this.obliterates.addOrUpdate(obliterate);
|
|
1374
1344
|
this.slideAckedRemovedSegmentReferences(localOverlapWithRefs);
|
|
1375
1345
|
// opArgs == undefined => test code
|
|
1376
1346
|
if (movedSegments.length > 0) {
|
|
@@ -1379,9 +1349,6 @@ class MergeTree {
|
|
|
1379
1349
|
deltaSegments: movedSegments,
|
|
1380
1350
|
});
|
|
1381
1351
|
}
|
|
1382
|
-
if (segmentGroup && localSeq !== undefined) {
|
|
1383
|
-
this.locallyMovedSegments.set(localSeq, segmentGroup);
|
|
1384
|
-
}
|
|
1385
1352
|
// these events are newly removed
|
|
1386
1353
|
// so we slide after eventing in case the consumer wants to make reference
|
|
1387
1354
|
// changes at remove time, like add a ref to track undo redo.
|
|
@@ -1528,7 +1495,6 @@ class MergeTree {
|
|
|
1528
1495
|
this.markRangeRemoved(start, start + segment.cachedLength, constants_js_1.UniversalSequenceNumber, this.collabWindow.clientId, constants_js_1.UniversalSequenceNumber, false, { op: removeOp });
|
|
1529
1496
|
} /* op.type === MergeTreeDeltaType.ANNOTATE */
|
|
1530
1497
|
else {
|
|
1531
|
-
// TODO Non null asserting, why is this not null?
|
|
1532
1498
|
const props = pendingSegmentGroup.previousProps[i];
|
|
1533
1499
|
const annotateOp = (0, opBuilder_js_1.createAnnotateRangeOp)(start, start + segment.cachedLength, props);
|
|
1534
1500
|
this.annotateRange(start, start + segment.cachedLength, props, constants_js_1.UniversalSequenceNumber, this.collabWindow.clientId, constants_js_1.UniversalSequenceNumber, { op: annotateOp },
|
|
@@ -1575,7 +1541,7 @@ class MergeTree {
|
|
|
1575
1541
|
if (_segment !== "start" &&
|
|
1576
1542
|
_segment !== "end" &&
|
|
1577
1543
|
isRemovedAndAckedOrMovedAndAcked(_segment) &&
|
|
1578
|
-
!(0, referencePositions_js_1.refTypeIncludesFlag)(refType, ops_js_1.ReferenceType.SlideOnRemove | ops_js_1.ReferenceType.Transient) &&
|
|
1544
|
+
!(0, referencePositions_js_1.refTypeIncludesFlag)(refType, ops_js_1.ReferenceType.SlideOnRemove | ops_js_1.ReferenceType.Transient | ops_js_1.ReferenceType.StayOnRemove) &&
|
|
1579
1545
|
_segment.endpointType === undefined) {
|
|
1580
1546
|
throw new internal_2.UsageError("Can only create SlideOnRemove or Transient local reference position on a removed or obliterated segment");
|
|
1581
1547
|
}
|
|
@@ -1648,7 +1614,6 @@ class MergeTree {
|
|
|
1648
1614
|
}
|
|
1649
1615
|
}
|
|
1650
1616
|
for (let i = 0; i < newOrder.length; i++) {
|
|
1651
|
-
// TODO Non null asserting, why is this not null?
|
|
1652
1617
|
const seg = newOrder[i];
|
|
1653
1618
|
const { parent, index, ordinal } = currentOrder[i];
|
|
1654
1619
|
parent?.assignChild(seg, index, false);
|
|
@@ -1735,7 +1700,6 @@ class MergeTree {
|
|
|
1735
1700
|
const rightmostTiles = (0, properties_js_1.createMap)();
|
|
1736
1701
|
const leftmostTiles = (0, properties_js_1.createMap)();
|
|
1737
1702
|
for (let i = 0; i < block.childCount; i++) {
|
|
1738
|
-
// TODO Non null asserting, why is this not null?
|
|
1739
1703
|
const node = block.children[i];
|
|
1740
1704
|
const nodeLength = nodeTotalLength(this, node);
|
|
1741
1705
|
if (nodeLength !== undefined) {
|