@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/src/mergeTree.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
LocalReferencePosition,
|
|
26
26
|
SlidingPreference,
|
|
27
27
|
anyLocalReferencePosition,
|
|
28
|
+
createDetachedLocalReferencePosition,
|
|
28
29
|
filterLocalReferencePositions,
|
|
29
30
|
} from "./localReference.js";
|
|
30
31
|
import {
|
|
@@ -62,6 +63,8 @@ import {
|
|
|
62
63
|
seqLTE,
|
|
63
64
|
toMoveInfo,
|
|
64
65
|
toRemovalInfo,
|
|
66
|
+
// eslint-disable-next-line import/no-deprecated
|
|
67
|
+
type ObliterateInfo,
|
|
65
68
|
} from "./mergeTreeNodes.js";
|
|
66
69
|
import type { TrackingGroup } from "./mergeTreeTracking.js";
|
|
67
70
|
import {
|
|
@@ -87,16 +90,9 @@ import {
|
|
|
87
90
|
} from "./referencePositions.js";
|
|
88
91
|
// eslint-disable-next-line import/no-deprecated
|
|
89
92
|
import { PropertiesRollback } from "./segmentPropertiesManager.js";
|
|
90
|
-
import {
|
|
93
|
+
import { SortedSegmentSet } from "./sortedSegmentSet.js";
|
|
91
94
|
import { zamboniSegments } from "./zamboni.js";
|
|
92
95
|
|
|
93
|
-
function wasRemovedAfter(seg: ISegment, seq: number): boolean {
|
|
94
|
-
return (
|
|
95
|
-
seg.removedSeq !== UnassignedSequenceNumber &&
|
|
96
|
-
(seg.removedSeq === undefined || seg.removedSeq > seq)
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
96
|
function markSegmentMoved(seg: ISegment, moveInfo: IMoveInfo): void {
|
|
101
97
|
seg.moveDst = moveInfo.moveDst;
|
|
102
98
|
seg.movedClientIds = [...moveInfo.movedClientIds];
|
|
@@ -414,6 +410,71 @@ const forwardPred = (ref: LocalReferencePosition): boolean =>
|
|
|
414
410
|
const backwardPred = (ref: LocalReferencePosition): boolean =>
|
|
415
411
|
ref.slidingPreference === SlidingPreference.BACKWARD;
|
|
416
412
|
|
|
413
|
+
class Obliterates {
|
|
414
|
+
/**
|
|
415
|
+
* Array containing the all move operations within the
|
|
416
|
+
* collab window.
|
|
417
|
+
*
|
|
418
|
+
* The moves are stored in sequence order which accelerates clean up in setMinSeq
|
|
419
|
+
*
|
|
420
|
+
* See https://github.com/microsoft/FluidFramework/blob/main/packages/dds/merge-tree/docs/Obliterate.md#remote-perspective
|
|
421
|
+
* for additional context
|
|
422
|
+
*/
|
|
423
|
+
// eslint-disable-next-line import/no-deprecated
|
|
424
|
+
private readonly seqOrdered = new DoublyLinkedList<ObliterateInfo>();
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* This contains a sorted lists of all obliterate starts
|
|
428
|
+
* and is used to accelerate finding overlapping obliterates
|
|
429
|
+
* as well as determining if there are any obliterates at all.
|
|
430
|
+
*/
|
|
431
|
+
private readonly startOrdered = new SortedSegmentSet<LocalReferencePosition>();
|
|
432
|
+
|
|
433
|
+
constructor(private readonly mergeTree: MergeTree) {}
|
|
434
|
+
|
|
435
|
+
public setMinSeq(minSeq: number): void {
|
|
436
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
|
437
|
+
while (!this.seqOrdered.empty && this.seqOrdered.first?.data.seq! <= minSeq) {
|
|
438
|
+
const ob = this.seqOrdered.shift()!;
|
|
439
|
+
this.startOrdered.remove(ob.data.start);
|
|
440
|
+
this.mergeTree.removeLocalReferencePosition(ob.data.start);
|
|
441
|
+
this.mergeTree.removeLocalReferencePosition(ob.data.end);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// eslint-disable-next-line import/no-deprecated
|
|
446
|
+
public addOrUpdate(obliterateInfo: ObliterateInfo): void {
|
|
447
|
+
const { seq, start } = obliterateInfo;
|
|
448
|
+
if (seq !== UnassignedSequenceNumber) {
|
|
449
|
+
this.seqOrdered.push(obliterateInfo);
|
|
450
|
+
}
|
|
451
|
+
this.startOrdered.addOrUpdate(start);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
public empty(): boolean {
|
|
455
|
+
return this.startOrdered.size === 0;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// eslint-disable-next-line import/no-deprecated
|
|
459
|
+
public findOverlapping(seg: ISegment): Iterable<ObliterateInfo> {
|
|
460
|
+
// eslint-disable-next-line import/no-deprecated
|
|
461
|
+
const overlapping: ObliterateInfo[] = [];
|
|
462
|
+
for (const start of this.startOrdered.items) {
|
|
463
|
+
if (start.getSegment()!.ordinal <= seg.ordinal) {
|
|
464
|
+
// eslint-disable-next-line import/no-deprecated
|
|
465
|
+
const ob = start.properties?.obliterate as ObliterateInfo;
|
|
466
|
+
if (ob.end.getSegment()!.ordinal >= seg.ordinal) {
|
|
467
|
+
overlapping.push(ob);
|
|
468
|
+
}
|
|
469
|
+
} else {
|
|
470
|
+
// the start is past the seg, so exit
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return overlapping;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
417
478
|
/**
|
|
418
479
|
* @internal
|
|
419
480
|
*/
|
|
@@ -448,38 +509,7 @@ export class MergeTree {
|
|
|
448
509
|
public mergeTreeDeltaCallback?: MergeTreeDeltaCallback;
|
|
449
510
|
public mergeTreeMaintenanceCallback?: MergeTreeMaintenanceCallback;
|
|
450
511
|
|
|
451
|
-
|
|
452
|
-
* Array containing the sequence number of all move operations within the
|
|
453
|
-
* collab window
|
|
454
|
-
*
|
|
455
|
-
* When a segment is inserted, we must traverse to the left and right of it
|
|
456
|
-
* to determine whether the segment was inserted into an obliterated range.
|
|
457
|
-
* By keeping track of all move seqs, we can significantly reduce the search
|
|
458
|
-
* space we must traverse.
|
|
459
|
-
*
|
|
460
|
-
* Sequence numbers in `moveSeqs` are sorted to accelerate bookkeeping.
|
|
461
|
-
*
|
|
462
|
-
* See https://github.com/microsoft/FluidFramework/blob/main/packages/dds/merge-tree/docs/Obliterate.md#remote-perspective
|
|
463
|
-
* for additional context
|
|
464
|
-
*/
|
|
465
|
-
private moveSeqs: number[] = [];
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Similar to moveSeqs, but tracks local moves. These are not the move
|
|
469
|
-
* operations within the collab window, but rather local moves that have
|
|
470
|
-
* not been acked.
|
|
471
|
-
*/
|
|
472
|
-
private readonly localMoveSeqs: Set<number> = new Set();
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Groups of segments moved by local moves/obliterates
|
|
476
|
-
*
|
|
477
|
-
* When a local obliterate is acked, we must also ack segments that were
|
|
478
|
-
* concurrently obliterated on insert. We check this segment group to find
|
|
479
|
-
* such segments
|
|
480
|
-
*/
|
|
481
|
-
// eslint-disable-next-line import/no-deprecated
|
|
482
|
-
private readonly locallyMovedSegments: Map<number, SegmentGroup> = new Map();
|
|
512
|
+
private readonly obliterates = new Obliterates(this);
|
|
483
513
|
|
|
484
514
|
public constructor(public options?: IMergeTreeOptions) {
|
|
485
515
|
this._root = this.makeBlock(0);
|
|
@@ -617,8 +647,7 @@ export class MergeTree {
|
|
|
617
647
|
childIndex++, nodeIndex++ // Advance to next child & node
|
|
618
648
|
) {
|
|
619
649
|
// Insert the next node into the current block
|
|
620
|
-
|
|
621
|
-
this.addNode(block, nodes[nodeIndex]!);
|
|
650
|
+
this.addNode(block, nodes[nodeIndex]);
|
|
622
651
|
}
|
|
623
652
|
|
|
624
653
|
// Calculate this block's info. Previously this was inlined into the above loop as a micro-optimization,
|
|
@@ -628,8 +657,7 @@ export class MergeTree {
|
|
|
628
657
|
}
|
|
629
658
|
|
|
630
659
|
return blocks.length === 1 // If there is only one block at this layer...
|
|
631
|
-
? //
|
|
632
|
-
blocks[0]! // ...then we're done. Return the root.
|
|
660
|
+
? blocks[0] // ...then we're done. Return the root.
|
|
633
661
|
: buildMergeBlock(blocks); // ...otherwise recursively build the next layer above blocks.
|
|
634
662
|
};
|
|
635
663
|
if (segments.length > 0) {
|
|
@@ -688,8 +716,7 @@ export class MergeTree {
|
|
|
688
716
|
while (parent) {
|
|
689
717
|
const children = parent.children;
|
|
690
718
|
for (let childIndex = 0; childIndex < parent.childCount; childIndex++) {
|
|
691
|
-
|
|
692
|
-
const child = children[childIndex]!;
|
|
719
|
+
const child = children[childIndex];
|
|
693
720
|
if ((!!prevParent && child === prevParent) || child === node) {
|
|
694
721
|
break;
|
|
695
722
|
}
|
|
@@ -1052,8 +1079,7 @@ export class MergeTree {
|
|
|
1052
1079
|
|
|
1053
1080
|
if (minSeq > this.collabWindow.minSeq) {
|
|
1054
1081
|
this.collabWindow.minSeq = minSeq;
|
|
1055
|
-
|
|
1056
|
-
this.moveSeqs = firstMoveSeqIdx === -1 ? [] : this.moveSeqs.slice(firstMoveSeqIdx);
|
|
1082
|
+
this.obliterates.setMinSeq(minSeq);
|
|
1057
1083
|
if (MergeTree.options.zamboniSegments) {
|
|
1058
1084
|
zamboniSegments(this);
|
|
1059
1085
|
}
|
|
@@ -1192,42 +1218,10 @@ export class MergeTree {
|
|
|
1192
1218
|
const deltaSegments: IMergeTreeSegmentDelta[] = [];
|
|
1193
1219
|
const overlappingRemoves: boolean[] = [];
|
|
1194
1220
|
pendingSegmentGroup.segments.map((pendingSegment: ISegmentLeaf) => {
|
|
1195
|
-
const localMovedSeq = pendingSegment.localMovedSeq;
|
|
1196
1221
|
const overlappingRemove = !pendingSegment.ack(pendingSegmentGroup, opArgs);
|
|
1197
1222
|
|
|
1198
|
-
if (opArgs.op.type === MergeTreeDeltaType.OBLITERATE && localMovedSeq !== undefined) {
|
|
1199
|
-
const locallyMovedSegments = this.locallyMovedSegments.get(localMovedSeq);
|
|
1200
|
-
|
|
1201
|
-
if (locallyMovedSegments) {
|
|
1202
|
-
// Disabling because a for of loop causes the type of segment to be ISegment, which does not have parent information stored
|
|
1203
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
1204
|
-
locallyMovedSegments.segments.forEach((segment: ISegmentLeaf) => {
|
|
1205
|
-
segment.localMovedSeq = undefined;
|
|
1206
|
-
|
|
1207
|
-
if (!nodesToUpdate.includes(segment.parent!)) {
|
|
1208
|
-
nodesToUpdate.push(segment.parent!);
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
if (segment.movedSeq === UnassignedSequenceNumber) {
|
|
1212
|
-
segment.movedSeq = seq;
|
|
1213
|
-
}
|
|
1214
|
-
});
|
|
1215
|
-
|
|
1216
|
-
this.locallyMovedSegments.delete(localMovedSeq);
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
1223
|
overwrite = overlappingRemove || overwrite;
|
|
1221
1224
|
|
|
1222
|
-
if (opArgs.op.type === MergeTreeDeltaType.OBLITERATE) {
|
|
1223
|
-
if (seq !== this.moveSeqs[this.moveSeqs.length - 1]) {
|
|
1224
|
-
this.moveSeqs.push(seq);
|
|
1225
|
-
}
|
|
1226
|
-
if (localMovedSeq !== undefined) {
|
|
1227
|
-
this.localMoveSeqs.delete(localMovedSeq);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
1225
|
overlappingRemoves.push(overlappingRemove);
|
|
1232
1226
|
if (MergeTree.options.zamboniSegments) {
|
|
1233
1227
|
this.addToLRUSet(pendingSegment, seq);
|
|
@@ -1240,6 +1234,10 @@ export class MergeTree {
|
|
|
1240
1234
|
});
|
|
1241
1235
|
});
|
|
1242
1236
|
|
|
1237
|
+
if (opArgs.op.type === MergeTreeDeltaType.OBLITERATE) {
|
|
1238
|
+
this.obliterates.addOrUpdate(pendingSegmentGroup.obliterateInfo!);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1243
1241
|
// Perform slides after all segments have been acked, so that
|
|
1244
1242
|
// positions after slide are final
|
|
1245
1243
|
if (
|
|
@@ -1516,115 +1514,62 @@ export class MergeTree {
|
|
|
1516
1514
|
|
|
1517
1515
|
insertPos += newSegment.cachedLength;
|
|
1518
1516
|
|
|
1519
|
-
if (!this.options?.mergeTreeEnableObliterate) {
|
|
1517
|
+
if (!this.options?.mergeTreeEnableObliterate || this.obliterates.empty()) {
|
|
1520
1518
|
continue;
|
|
1521
1519
|
}
|
|
1522
1520
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
const
|
|
1531
|
-
const
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
moveUpperBound = Math.min(moveUpperBound, seg.seq ?? Number.POSITIVE_INFINITY);
|
|
1554
|
-
}
|
|
1555
|
-
// If we've reached a segment that existed before any of our in-collab-window move ops
|
|
1556
|
-
// happened, no need to continue.
|
|
1557
|
-
return moveUpperBound >= smallestSeqMoveOp;
|
|
1558
|
-
};
|
|
1559
|
-
|
|
1560
|
-
const findRightMovedSegment = (seg: ISegment): boolean => {
|
|
1561
|
-
const movedSeqs = seg.movedSeqs?.filter((movedSeq) => movedSeq >= refSeq) ?? [];
|
|
1562
|
-
const localMovedSeqs = seg.localMovedSeq ? [seg.localMovedSeq] : [];
|
|
1563
|
-
|
|
1564
|
-
for (const movedSeq of movedSeqs) {
|
|
1565
|
-
const left = leftAckedSegments[movedSeq];
|
|
1566
|
-
if (left) {
|
|
1567
|
-
_movedSeq = movedSeq;
|
|
1568
|
-
const clientIdIdx = left.movedSeqs?.indexOf(movedSeq) ?? -1;
|
|
1569
|
-
const movedClientId = left.movedClientIds?.[clientIdIdx];
|
|
1570
|
-
assert(movedClientId !== undefined, 0x869 /* expected client id to exist */);
|
|
1571
|
-
movedClientIds = [movedClientId];
|
|
1572
|
-
return false;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
for (const localMovedSeq of localMovedSeqs) {
|
|
1577
|
-
const left = leftLocalSegments[localMovedSeq];
|
|
1578
|
-
if (left) {
|
|
1579
|
-
_localMovedSeq = localMovedSeq;
|
|
1580
|
-
const clientIdIdx = left.movedSeqs?.indexOf(UnassignedSequenceNumber) ?? -1;
|
|
1581
|
-
const movedClientId = left.movedClientIds?.[clientIdIdx];
|
|
1582
|
-
assert(movedClientId !== undefined, 0x86a /* expected client id to exist */);
|
|
1583
|
-
movedClientIds = [movedClientId];
|
|
1584
|
-
return false;
|
|
1521
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1522
|
+
let oldest: ObliterateInfo | undefined;
|
|
1523
|
+
let normalizedOldestSeq: number = 0;
|
|
1524
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1525
|
+
let newest: ObliterateInfo | undefined;
|
|
1526
|
+
let normalizedNewestSeq: number = 0;
|
|
1527
|
+
const movedClientIds: number[] = [];
|
|
1528
|
+
const movedSeqs: number[] = [];
|
|
1529
|
+
for (const ob of this.obliterates.findOverlapping(newSegment)) {
|
|
1530
|
+
// compute a normalized seq that takes into account local seqs
|
|
1531
|
+
// but is still comparable to remote seqs to keep the checks below easy
|
|
1532
|
+
// REMOTE SEQUENCE NUMBERS LOCAL SEQUENCE NUMBERS
|
|
1533
|
+
// [0, 1, 2, 3, ..., 100, ..., 1000, ..., (MAX - MaxLocalSeq), L1, L2, L3, L4, ..., L100, ..., L1000, ...(MAX)]
|
|
1534
|
+
const normalizedObSeq =
|
|
1535
|
+
ob.seq === UnassignedSequenceNumber
|
|
1536
|
+
? Number.MAX_SAFE_INTEGER - this.collabWindow.localSeq + ob.localSeq!
|
|
1537
|
+
: ob.seq;
|
|
1538
|
+
if (normalizedObSeq > refSeq) {
|
|
1539
|
+
if (oldest === undefined || normalizedOldestSeq > normalizedObSeq) {
|
|
1540
|
+
normalizedOldestSeq = normalizedObSeq;
|
|
1541
|
+
oldest = ob;
|
|
1542
|
+
movedClientIds.unshift(ob.clientId);
|
|
1543
|
+
movedSeqs.unshift(ob.seq);
|
|
1544
|
+
} else {
|
|
1545
|
+
if (newest === undefined || normalizedNewestSeq < normalizedObSeq) {
|
|
1546
|
+
normalizedNewestSeq = normalizedObSeq;
|
|
1547
|
+
newest = ob;
|
|
1548
|
+
}
|
|
1549
|
+
movedClientIds.push(ob.clientId);
|
|
1550
|
+
movedSeqs.push(ob.seq);
|
|
1585
1551
|
}
|
|
1586
1552
|
}
|
|
1553
|
+
}
|
|
1587
1554
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
if (!isRemoved(seg) || wasRemovedAfter(seg, moveUpperBound)) {
|
|
1593
|
-
moveUpperBound = Math.min(moveUpperBound, seg.seq ?? Number.POSITIVE_INFINITY);
|
|
1594
|
-
}
|
|
1595
|
-
// If we've reached a segment that existed before any of our in-collab-window move ops
|
|
1596
|
-
// happened, no need to continue.
|
|
1597
|
-
return moveUpperBound >= smallestSeqMoveOp;
|
|
1598
|
-
};
|
|
1599
|
-
|
|
1600
|
-
backwardExcursion(newSegment, findLeftMovedSegment);
|
|
1601
|
-
moveUpperBound = Number.POSITIVE_INFINITY;
|
|
1602
|
-
forwardExcursion(newSegment, findRightMovedSegment);
|
|
1603
|
-
|
|
1604
|
-
if (_localMovedSeq !== undefined || _movedSeq !== undefined) {
|
|
1605
|
-
assert(
|
|
1606
|
-
movedClientIds !== undefined,
|
|
1607
|
-
0x86b /* movedClientIds should be set if local/moved seq is set */,
|
|
1608
|
-
);
|
|
1609
|
-
const moveInfo = {
|
|
1555
|
+
if (oldest && newest?.clientId !== clientId) {
|
|
1556
|
+
const moveInfo: IMoveInfo = {
|
|
1610
1557
|
movedClientIds,
|
|
1611
|
-
movedSeq:
|
|
1612
|
-
movedSeqs
|
|
1613
|
-
localMovedSeq:
|
|
1614
|
-
wasMovedOnInsert:
|
|
1558
|
+
movedSeq: oldest.seq,
|
|
1559
|
+
movedSeqs,
|
|
1560
|
+
localMovedSeq: oldest.localSeq,
|
|
1561
|
+
wasMovedOnInsert: oldest.seq !== UnassignedSequenceNumber,
|
|
1615
1562
|
};
|
|
1616
1563
|
|
|
1617
1564
|
markSegmentMoved(newSegment, moveInfo);
|
|
1618
1565
|
|
|
1619
1566
|
if (moveInfo.localMovedSeq !== undefined) {
|
|
1620
|
-
const movedSegmentGroup = this.locallyMovedSegments.get(moveInfo.localMovedSeq);
|
|
1621
|
-
|
|
1622
1567
|
assert(
|
|
1623
|
-
|
|
1568
|
+
oldest.segmentGroup !== undefined,
|
|
1624
1569
|
0x86c /* expected segment group to exist */,
|
|
1625
1570
|
);
|
|
1626
1571
|
|
|
1627
|
-
this.addToPendingList(newSegment,
|
|
1572
|
+
this.addToPendingList(newSegment, oldest.segmentGroup);
|
|
1628
1573
|
}
|
|
1629
1574
|
|
|
1630
1575
|
if (newSegment.parent) {
|
|
@@ -1655,11 +1600,7 @@ export class MergeTree {
|
|
|
1655
1600
|
return { next };
|
|
1656
1601
|
};
|
|
1657
1602
|
|
|
1658
|
-
private ensureIntervalBoundary(
|
|
1659
|
-
pos: number | "start" | "end",
|
|
1660
|
-
refSeq: number,
|
|
1661
|
-
clientId: number,
|
|
1662
|
-
): void {
|
|
1603
|
+
private ensureIntervalBoundary(pos: number, refSeq: number, clientId: number): void {
|
|
1663
1604
|
const splitNode = this.insertingWalk(
|
|
1664
1605
|
this.root,
|
|
1665
1606
|
pos,
|
|
@@ -1701,27 +1642,16 @@ export class MergeTree {
|
|
|
1701
1642
|
}
|
|
1702
1643
|
}
|
|
1703
1644
|
|
|
1704
|
-
private getSmallestSeqMoveOp(): number | undefined {
|
|
1705
|
-
return this.moveSeqs[0] ?? (this.localMoveSeqs.size > 0 ? -1 : undefined);
|
|
1706
|
-
}
|
|
1707
|
-
|
|
1708
1645
|
private insertingWalk(
|
|
1709
1646
|
block: MergeBlock,
|
|
1710
|
-
pos: number
|
|
1647
|
+
pos: number,
|
|
1711
1648
|
refSeq: number,
|
|
1712
1649
|
clientId: number,
|
|
1713
1650
|
seq: number,
|
|
1714
1651
|
context: InsertContext,
|
|
1715
1652
|
isLastChildBlock: boolean = true,
|
|
1716
1653
|
): MergeBlock | undefined {
|
|
1717
|
-
let _pos: number;
|
|
1718
|
-
if (pos === "start") {
|
|
1719
|
-
_pos = 0;
|
|
1720
|
-
} else if (pos === "end") {
|
|
1721
|
-
_pos = this.root.mergeTree?.getLength(refSeq, clientId) ?? 0;
|
|
1722
|
-
} else {
|
|
1723
|
-
_pos = pos;
|
|
1724
|
-
}
|
|
1654
|
+
let _pos: number = pos;
|
|
1725
1655
|
|
|
1726
1656
|
const children = block.children;
|
|
1727
1657
|
let childIndex: number;
|
|
@@ -1729,8 +1659,7 @@ export class MergeTree {
|
|
|
1729
1659
|
let newNode: IMergeNode | undefined;
|
|
1730
1660
|
let fromSplit: MergeBlock | undefined;
|
|
1731
1661
|
for (childIndex = 0; childIndex < block.childCount; childIndex++) {
|
|
1732
|
-
|
|
1733
|
-
child = children[childIndex]!;
|
|
1662
|
+
child = children[childIndex];
|
|
1734
1663
|
// ensure we walk down the far edge of the tree, even if all sub-tree is eligible for zamboni
|
|
1735
1664
|
const isLastNonLeafBlock =
|
|
1736
1665
|
isLastChildBlock && !child.isLeaf() && childIndex === block.childCount - 1;
|
|
@@ -1801,10 +1730,8 @@ export class MergeTree {
|
|
|
1801
1730
|
}
|
|
1802
1731
|
if (newNode) {
|
|
1803
1732
|
for (let i = block.childCount; i > childIndex; i--) {
|
|
1804
|
-
|
|
1805
|
-
block.children[i] =
|
|
1806
|
-
// TODO Non null asserting, why is this not null?
|
|
1807
|
-
block.children[i]!.index = i;
|
|
1733
|
+
block.children[i] = block.children[i - 1];
|
|
1734
|
+
block.children[i].index = i;
|
|
1808
1735
|
}
|
|
1809
1736
|
block.assignChild(newNode, childIndex, false);
|
|
1810
1737
|
block.childCount++;
|
|
@@ -1841,8 +1768,7 @@ export class MergeTree {
|
|
|
1841
1768
|
// Update ordinals to reflect lowered child count
|
|
1842
1769
|
this.nodeUpdateOrdinals(node);
|
|
1843
1770
|
for (let i = 0; i < halfCount; i++) {
|
|
1844
|
-
|
|
1845
|
-
newNode.assignChild(node.children[halfCount + i]!, i, false);
|
|
1771
|
+
newNode.assignChild(node.children[halfCount + i], i, false);
|
|
1846
1772
|
node.children[halfCount + i] = undefined!;
|
|
1847
1773
|
}
|
|
1848
1774
|
this.nodeUpdateLengthNewStructure(node);
|
|
@@ -1852,8 +1778,7 @@ export class MergeTree {
|
|
|
1852
1778
|
|
|
1853
1779
|
public nodeUpdateOrdinals(block: MergeBlock): void {
|
|
1854
1780
|
for (let i = 0; i < block.childCount; i++) {
|
|
1855
|
-
|
|
1856
|
-
const child = block.children[i]!;
|
|
1781
|
+
const child = block.children[i];
|
|
1857
1782
|
block.setOrdinal(child, i);
|
|
1858
1783
|
if (!child.isLeaf()) {
|
|
1859
1784
|
this.nodeUpdateOrdinals(child);
|
|
@@ -1942,8 +1867,8 @@ export class MergeTree {
|
|
|
1942
1867
|
}
|
|
1943
1868
|
|
|
1944
1869
|
public obliterateRange(
|
|
1945
|
-
start:
|
|
1946
|
-
end:
|
|
1870
|
+
start: number,
|
|
1871
|
+
end: number,
|
|
1947
1872
|
refSeq: number,
|
|
1948
1873
|
clientId: number,
|
|
1949
1874
|
seq: number,
|
|
@@ -1952,33 +1877,50 @@ export class MergeTree {
|
|
|
1952
1877
|
): void {
|
|
1953
1878
|
errorIfOptionNotTrue(this.options, "mergeTreeEnableObliterate");
|
|
1954
1879
|
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
assert(
|
|
1958
|
-
startPos !== undefined &&
|
|
1959
|
-
endPos !== undefined &&
|
|
1960
|
-
startSide !== undefined &&
|
|
1961
|
-
endSide !== undefined &&
|
|
1962
|
-
startPos !== "end" &&
|
|
1963
|
-
endPos !== "start",
|
|
1964
|
-
0x9e2 /* start and end cannot be undefined because they were not passed in as undefined */,
|
|
1965
|
-
);
|
|
1966
|
-
|
|
1967
|
-
this.ensureIntervalBoundary(startPos, refSeq, clientId);
|
|
1968
|
-
this.ensureIntervalBoundary(endPos, refSeq, clientId);
|
|
1880
|
+
this.ensureIntervalBoundary(start, refSeq, clientId);
|
|
1881
|
+
this.ensureIntervalBoundary(end, refSeq, clientId);
|
|
1969
1882
|
|
|
1970
1883
|
let _overwrite = overwrite;
|
|
1971
1884
|
const localOverlapWithRefs: ISegment[] = [];
|
|
1972
1885
|
const movedSegments: IMergeTreeSegmentDelta[] = [];
|
|
1973
1886
|
const localSeq =
|
|
1974
1887
|
seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
|
|
1975
|
-
if (seq !== UnassignedSequenceNumber && seq !== this.moveSeqs[this.moveSeqs.length - 1]) {
|
|
1976
|
-
this.moveSeqs.push(seq);
|
|
1977
|
-
} else if (seq === UnassignedSequenceNumber && localSeq !== undefined) {
|
|
1978
|
-
this.localMoveSeqs.add(localSeq);
|
|
1979
|
-
}
|
|
1980
1888
|
// eslint-disable-next-line import/no-deprecated
|
|
1981
|
-
|
|
1889
|
+
const obliterate: ObliterateInfo = {
|
|
1890
|
+
clientId,
|
|
1891
|
+
end: createDetachedLocalReferencePosition(undefined),
|
|
1892
|
+
refSeq,
|
|
1893
|
+
seq,
|
|
1894
|
+
start: createDetachedLocalReferencePosition(undefined),
|
|
1895
|
+
localSeq,
|
|
1896
|
+
segmentGroup: undefined,
|
|
1897
|
+
};
|
|
1898
|
+
|
|
1899
|
+
const { segment: startSeg } = this.getContainingSegment(start, refSeq, clientId);
|
|
1900
|
+
const { segment: endSeg } = this.getContainingSegment(end - 1, refSeq, clientId);
|
|
1901
|
+
assert(
|
|
1902
|
+
startSeg !== undefined && endSeg !== undefined,
|
|
1903
|
+
0xa3f /* segments cannot be undefined */,
|
|
1904
|
+
);
|
|
1905
|
+
|
|
1906
|
+
obliterate.start = this.createLocalReferencePosition(
|
|
1907
|
+
startSeg,
|
|
1908
|
+
0,
|
|
1909
|
+
ReferenceType.StayOnRemove,
|
|
1910
|
+
{
|
|
1911
|
+
obliterate,
|
|
1912
|
+
},
|
|
1913
|
+
);
|
|
1914
|
+
|
|
1915
|
+
obliterate.end = this.createLocalReferencePosition(
|
|
1916
|
+
endSeg,
|
|
1917
|
+
endSeg.cachedLength - 1,
|
|
1918
|
+
ReferenceType.StayOnRemove,
|
|
1919
|
+
{
|
|
1920
|
+
obliterate,
|
|
1921
|
+
},
|
|
1922
|
+
);
|
|
1923
|
+
|
|
1982
1924
|
const markMoved = (
|
|
1983
1925
|
segment: ISegment,
|
|
1984
1926
|
pos: number,
|
|
@@ -1986,9 +1928,6 @@ export class MergeTree {
|
|
|
1986
1928
|
_end: number,
|
|
1987
1929
|
): boolean => {
|
|
1988
1930
|
const existingMoveInfo = toMoveInfo(segment);
|
|
1989
|
-
if (startSide) segment.startSide = startSide;
|
|
1990
|
-
if (endSide) segment.endSide = endSide;
|
|
1991
|
-
|
|
1992
1931
|
if (
|
|
1993
1932
|
clientId !== segment.clientId &&
|
|
1994
1933
|
segment.seq !== undefined &&
|
|
@@ -2034,7 +1973,12 @@ export class MergeTree {
|
|
|
2034
1973
|
segment.movedSeq === UnassignedSequenceNumber &&
|
|
2035
1974
|
clientId === this.collabWindow.clientId
|
|
2036
1975
|
) {
|
|
2037
|
-
segmentGroup = this.addToPendingList(
|
|
1976
|
+
obliterate.segmentGroup = this.addToPendingList(
|
|
1977
|
+
segment,
|
|
1978
|
+
obliterate.segmentGroup,
|
|
1979
|
+
localSeq,
|
|
1980
|
+
);
|
|
1981
|
+
obliterate.segmentGroup.obliterateInfo ??= obliterate;
|
|
2038
1982
|
} else {
|
|
2039
1983
|
if (MergeTree.options.zamboniSegments) {
|
|
2040
1984
|
this.addToLRUSet(segment, seq);
|
|
@@ -2070,6 +2014,8 @@ export class MergeTree {
|
|
|
2070
2014
|
seq === UnassignedSequenceNumber ? undefined : seq,
|
|
2071
2015
|
);
|
|
2072
2016
|
|
|
2017
|
+
this.obliterates.addOrUpdate(obliterate);
|
|
2018
|
+
|
|
2073
2019
|
this.slideAckedRemovedSegmentReferences(localOverlapWithRefs);
|
|
2074
2020
|
// opArgs == undefined => test code
|
|
2075
2021
|
if (movedSegments.length > 0) {
|
|
@@ -2079,10 +2025,6 @@ export class MergeTree {
|
|
|
2079
2025
|
});
|
|
2080
2026
|
}
|
|
2081
2027
|
|
|
2082
|
-
if (segmentGroup! && localSeq !== undefined) {
|
|
2083
|
-
this.locallyMovedSegments.set(localSeq, segmentGroup);
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
2028
|
// these events are newly removed
|
|
2087
2029
|
// so we slide after eventing in case the consumer wants to make reference
|
|
2088
2030
|
// changes at remove time, like add a ref to track undo redo.
|
|
@@ -2292,8 +2234,7 @@ export class MergeTree {
|
|
|
2292
2234
|
{ op: removeOp },
|
|
2293
2235
|
);
|
|
2294
2236
|
} /* op.type === MergeTreeDeltaType.ANNOTATE */ else {
|
|
2295
|
-
|
|
2296
|
-
const props = pendingSegmentGroup.previousProps![i]!;
|
|
2237
|
+
const props = pendingSegmentGroup.previousProps![i];
|
|
2297
2238
|
const annotateOp = createAnnotateRangeOp(start, start + segment.cachedLength, props);
|
|
2298
2239
|
this.annotateRange(
|
|
2299
2240
|
start,
|
|
@@ -2366,7 +2307,10 @@ export class MergeTree {
|
|
|
2366
2307
|
_segment !== "start" &&
|
|
2367
2308
|
_segment !== "end" &&
|
|
2368
2309
|
isRemovedAndAckedOrMovedAndAcked(_segment) &&
|
|
2369
|
-
!refTypeIncludesFlag(
|
|
2310
|
+
!refTypeIncludesFlag(
|
|
2311
|
+
refType,
|
|
2312
|
+
ReferenceType.SlideOnRemove | ReferenceType.Transient | ReferenceType.StayOnRemove,
|
|
2313
|
+
) &&
|
|
2370
2314
|
_segment.endpointType === undefined
|
|
2371
2315
|
) {
|
|
2372
2316
|
throw new UsageError(
|
|
@@ -2466,9 +2410,8 @@ export class MergeTree {
|
|
|
2466
2410
|
}
|
|
2467
2411
|
|
|
2468
2412
|
for (let i = 0; i < newOrder.length; i++) {
|
|
2469
|
-
|
|
2470
|
-
const
|
|
2471
|
-
const { parent, index, ordinal } = currentOrder[i]!;
|
|
2413
|
+
const seg = newOrder[i];
|
|
2414
|
+
const { parent, index, ordinal } = currentOrder[i];
|
|
2472
2415
|
parent?.assignChild(seg, index, false);
|
|
2473
2416
|
seg.ordinal = ordinal;
|
|
2474
2417
|
}
|
|
@@ -2561,8 +2504,7 @@ export class MergeTree {
|
|
|
2561
2504
|
const leftmostTiles = createMap<Marker>();
|
|
2562
2505
|
|
|
2563
2506
|
for (let i = 0; i < block.childCount; i++) {
|
|
2564
|
-
|
|
2565
|
-
const node = block.children[i]!;
|
|
2507
|
+
const node = block.children[i];
|
|
2566
2508
|
const nodeLength = nodeTotalLength(this, node);
|
|
2567
2509
|
if (nodeLength !== undefined) {
|
|
2568
2510
|
len ??= 0;
|
|
@@ -2707,28 +2649,18 @@ export class MergeTree {
|
|
|
2707
2649
|
leaf: ISegmentAction<TClientData>,
|
|
2708
2650
|
accum: TClientData,
|
|
2709
2651
|
post?: BlockAction<TClientData>,
|
|
2710
|
-
start:
|
|
2711
|
-
end?:
|
|
2652
|
+
start: number = 0,
|
|
2653
|
+
end?: number,
|
|
2712
2654
|
localSeq?: number,
|
|
2713
2655
|
visibilitySeq: number = refSeq,
|
|
2714
2656
|
): void {
|
|
2715
|
-
const
|
|
2716
|
-
if (
|
|
2657
|
+
const endPos = end ?? this.nodeLength(this.root, refSeq, clientId, localSeq) ?? 0;
|
|
2658
|
+
if (endPos === start) {
|
|
2717
2659
|
return;
|
|
2718
2660
|
}
|
|
2719
2661
|
|
|
2720
2662
|
let pos = 0;
|
|
2721
|
-
let { startPos, endPos } = endpointPosAndSide(start, end);
|
|
2722
2663
|
|
|
2723
|
-
startPos = startPos === "start" || startPos === undefined ? 0 : startPos;
|
|
2724
|
-
endPos =
|
|
2725
|
-
endPos === "end" || endPos === undefined
|
|
2726
|
-
? this.root.mergeTree?.getLength(refSeq, clientId) ?? 0
|
|
2727
|
-
: endPos;
|
|
2728
|
-
assert(
|
|
2729
|
-
startPos !== "end" && endPos !== "start",
|
|
2730
|
-
0x9e3 /* start cannot be 'end' and end cannot be 'start' */,
|
|
2731
|
-
);
|
|
2732
2664
|
depthFirstNodeWalk(
|
|
2733
2665
|
this.root,
|
|
2734
2666
|
this.root.children[0],
|
|
@@ -2756,15 +2688,13 @@ export class MergeTree {
|
|
|
2756
2688
|
|
|
2757
2689
|
const nextPos = pos + lenAtRefSeq;
|
|
2758
2690
|
// start is beyond the current node, so we can skip it
|
|
2759
|
-
if (
|
|
2691
|
+
if (start >= nextPos) {
|
|
2760
2692
|
pos = nextPos;
|
|
2761
2693
|
return NodeAction.Skip;
|
|
2762
2694
|
}
|
|
2763
2695
|
|
|
2764
2696
|
if (node.isLeaf()) {
|
|
2765
|
-
if (
|
|
2766
|
-
leaf(node, pos, refSeq, clientId, startPos - pos, endPos - pos, accum) === false
|
|
2767
|
-
) {
|
|
2697
|
+
if (leaf(node, pos, refSeq, clientId, start - pos, endPos - pos, accum) === false) {
|
|
2768
2698
|
return NodeAction.Exit;
|
|
2769
2699
|
}
|
|
2770
2700
|
pos = nextPos;
|
|
@@ -2774,7 +2704,7 @@ export class MergeTree {
|
|
|
2774
2704
|
post === undefined
|
|
2775
2705
|
? undefined
|
|
2776
2706
|
: (block): boolean =>
|
|
2777
|
-
post(block, pos, refSeq, clientId,
|
|
2707
|
+
post(block, pos, refSeq, clientId, start - pos, endPos - pos, accum),
|
|
2778
2708
|
);
|
|
2779
2709
|
}
|
|
2780
2710
|
}
|