@fluidframework/merge-tree 2.12.0 → 2.13.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/dist/MergeTreeTextHelper.d.ts.map +1 -1
- package/dist/MergeTreeTextHelper.js +0 -2
- package/dist/MergeTreeTextHelper.js.map +1 -1
- package/dist/attributionPolicy.d.ts.map +1 -1
- package/dist/attributionPolicy.js +6 -16
- package/dist/attributionPolicy.js.map +1 -1
- package/dist/client.d.ts +3 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +39 -28
- package/dist/client.js.map +1 -1
- package/dist/endOfTreeSegment.d.ts +2 -1
- package/dist/endOfTreeSegment.d.ts.map +1 -1
- package/dist/endOfTreeSegment.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/mergeTree.d.ts +8 -7
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +190 -216
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeNodeWalk.d.ts.map +1 -1
- package/dist/mergeTreeNodeWalk.js +3 -2
- package/dist/mergeTreeNodeWalk.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +76 -162
- package/dist/mergeTreeNodes.d.ts.map +1 -1
- package/dist/mergeTreeNodes.js +100 -112
- package/dist/mergeTreeNodes.js.map +1 -1
- package/dist/mergeTreeTracking.d.ts.map +1 -1
- package/dist/mergeTreeTracking.js +0 -2
- package/dist/mergeTreeTracking.js.map +1 -1
- package/dist/partialLengths.d.ts +2 -2
- package/dist/partialLengths.d.ts.map +1 -1
- package/dist/partialLengths.js +28 -26
- package/dist/partialLengths.js.map +1 -1
- package/dist/perspective.d.ts +3 -2
- package/dist/perspective.d.ts.map +1 -1
- package/dist/perspective.js +11 -4
- package/dist/perspective.js.map +1 -1
- package/dist/referencePositions.d.ts.map +1 -1
- package/dist/referencePositions.js +4 -1
- package/dist/referencePositions.js.map +1 -1
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +10 -11
- package/dist/revertibles.js.map +1 -1
- package/dist/segmentGroupCollection.d.ts +4 -4
- package/dist/segmentGroupCollection.d.ts.map +1 -1
- package/dist/segmentGroupCollection.js +0 -6
- package/dist/segmentGroupCollection.js.map +1 -1
- package/dist/segmentInfos.d.ts +257 -0
- package/dist/segmentInfos.d.ts.map +1 -0
- package/dist/segmentInfos.js +166 -0
- package/dist/segmentInfos.js.map +1 -0
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js +38 -44
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/snapshotV1.d.ts.map +1 -1
- package/dist/snapshotV1.js +9 -12
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.d.ts +2 -2
- package/dist/snapshotlegacy.d.ts.map +1 -1
- package/dist/snapshotlegacy.js +5 -3
- package/dist/snapshotlegacy.js.map +1 -1
- package/dist/sortedSegmentSet.d.ts.map +1 -1
- package/dist/sortedSegmentSet.js +5 -8
- package/dist/sortedSegmentSet.js.map +1 -1
- package/dist/test/beastTest.spec.d.ts +0 -2
- package/dist/test/beastTest.spec.d.ts.map +1 -1
- package/dist/test/beastTest.spec.js +1 -5
- package/dist/test/beastTest.spec.js.map +1 -1
- package/dist/test/client.annotateMarker.spec.js.map +1 -1
- package/dist/test/client.applyMsg.spec.js +15 -12
- package/dist/test/client.applyMsg.spec.js.map +1 -1
- package/dist/test/client.attributionFarm.spec.js.map +1 -1
- package/dist/test/client.getPosition.spec.js +3 -2
- package/dist/test/client.getPosition.spec.js.map +1 -1
- package/dist/test/client.localReference.spec.js +6 -6
- package/dist/test/client.localReference.spec.js.map +1 -1
- package/dist/test/client.localReferenceFarm.spec.js.map +1 -1
- package/dist/test/client.rollback.spec.js.map +1 -1
- package/dist/test/dirname.cjs +0 -1
- package/dist/test/dirname.cjs.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +2 -4
- package/dist/test/index.js.map +1 -1
- package/dist/test/mergeTree.annotate.spec.js +3 -0
- package/dist/test/mergeTree.annotate.spec.js.map +1 -1
- package/dist/test/mergeTree.insertingWalk.spec.js +1 -1
- package/dist/test/mergeTree.insertingWalk.spec.js.map +1 -1
- package/dist/test/mergeTree.markRangeRemoved.spec.js +2 -0
- package/dist/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
- package/dist/test/mergeTree.walk.spec.js.map +1 -1
- package/dist/test/mergeTreeOperationRunner.d.ts.map +1 -1
- package/dist/test/mergeTreeOperationRunner.js +2 -3
- package/dist/test/mergeTreeOperationRunner.js.map +1 -1
- package/dist/test/obliterate.spec.js.map +1 -1
- package/dist/test/propertyManager.spec.js.map +1 -1
- package/dist/test/reconnectHelper.d.ts +2 -1
- package/dist/test/reconnectHelper.d.ts.map +1 -1
- package/dist/test/reconnectHelper.js.map +1 -1
- package/dist/test/resetPendingSegmentsToOp.spec.js.map +1 -1
- package/dist/test/revertibleFarm.spec.js.map +1 -1
- package/dist/test/segmentGroupCollection.spec.js +15 -3
- package/dist/test/segmentGroupCollection.spec.js.map +1 -1
- package/dist/test/snapshot.utils.d.ts +2 -2
- package/dist/test/snapshot.utils.d.ts.map +1 -1
- package/dist/test/snapshot.utils.js.map +1 -1
- package/dist/test/sortedSegmentSet.spec.js +4 -3
- package/dist/test/sortedSegmentSet.spec.js.map +1 -1
- package/dist/test/testClient.d.ts +8 -6
- package/dist/test/testClient.d.ts.map +1 -1
- package/dist/test/testClient.js +29 -27
- package/dist/test/testClient.js.map +1 -1
- package/dist/test/testClientLogger.d.ts.map +1 -1
- package/dist/test/testClientLogger.js +6 -4
- package/dist/test/testClientLogger.js.map +1 -1
- package/dist/test/testUtils.d.ts +2 -2
- package/dist/test/testUtils.d.ts.map +1 -1
- package/dist/test/testUtils.js +32 -8
- package/dist/test/testUtils.js.map +1 -1
- package/dist/test/text.d.ts +2 -2
- package/dist/test/text.d.ts.map +1 -1
- package/dist/test/text.js +12 -6
- package/dist/test/text.js.map +1 -1
- package/dist/test/tracking.spec.js.map +1 -1
- package/dist/test/wordUnitTests.spec.js +1 -1
- package/dist/test/wordUnitTests.spec.js.map +1 -1
- package/dist/zamboni.d.ts.map +1 -1
- package/dist/zamboni.js +8 -7
- package/dist/zamboni.js.map +1 -1
- package/lib/MergeTreeTextHelper.d.ts.map +1 -1
- package/lib/MergeTreeTextHelper.js +0 -2
- package/lib/MergeTreeTextHelper.js.map +1 -1
- package/lib/attributionPolicy.d.ts.map +1 -1
- package/lib/attributionPolicy.js +6 -16
- package/lib/attributionPolicy.js.map +1 -1
- package/lib/client.d.ts +3 -4
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +40 -29
- package/lib/client.js.map +1 -1
- package/lib/endOfTreeSegment.d.ts +2 -1
- package/lib/endOfTreeSegment.d.ts.map +1 -1
- package/lib/endOfTreeSegment.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/mergeTree.d.ts +8 -7
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +177 -205
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeNodeWalk.d.ts.map +1 -1
- package/lib/mergeTreeNodeWalk.js +3 -2
- package/lib/mergeTreeNodeWalk.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +76 -162
- package/lib/mergeTreeNodes.d.ts.map +1 -1
- package/lib/mergeTreeNodes.js +95 -108
- package/lib/mergeTreeNodes.js.map +1 -1
- package/lib/mergeTreeTracking.d.ts.map +1 -1
- package/lib/mergeTreeTracking.js +0 -2
- package/lib/mergeTreeTracking.js.map +1 -1
- package/lib/partialLengths.d.ts +2 -2
- package/lib/partialLengths.d.ts.map +1 -1
- package/lib/partialLengths.js +25 -23
- package/lib/partialLengths.js.map +1 -1
- package/lib/perspective.d.ts +3 -2
- package/lib/perspective.d.ts.map +1 -1
- package/lib/perspective.js +11 -4
- package/lib/perspective.js.map +1 -1
- package/lib/referencePositions.d.ts.map +1 -1
- package/lib/referencePositions.js +4 -1
- package/lib/referencePositions.js.map +1 -1
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +8 -9
- package/lib/revertibles.js.map +1 -1
- package/lib/segmentGroupCollection.d.ts +4 -4
- package/lib/segmentGroupCollection.d.ts.map +1 -1
- package/lib/segmentGroupCollection.js +0 -6
- package/lib/segmentGroupCollection.js.map +1 -1
- package/lib/segmentInfos.d.ts +257 -0
- package/lib/segmentInfos.d.ts.map +1 -0
- package/lib/segmentInfos.js +145 -0
- package/lib/segmentInfos.js.map +1 -0
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js +38 -44
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/snapshotV1.d.ts.map +1 -1
- package/lib/snapshotV1.js +9 -12
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.d.ts +2 -2
- package/lib/snapshotlegacy.d.ts.map +1 -1
- package/lib/snapshotlegacy.js +5 -3
- package/lib/snapshotlegacy.js.map +1 -1
- package/lib/sortedSegmentSet.d.ts.map +1 -1
- package/lib/sortedSegmentSet.js +5 -8
- package/lib/sortedSegmentSet.js.map +1 -1
- package/lib/test/beastTest.spec.d.ts +0 -2
- package/lib/test/beastTest.spec.d.ts.map +1 -1
- package/lib/test/beastTest.spec.js +0 -3
- package/lib/test/beastTest.spec.js.map +1 -1
- package/lib/test/client.annotateMarker.spec.js.map +1 -1
- package/lib/test/client.applyMsg.spec.js +15 -12
- package/lib/test/client.applyMsg.spec.js.map +1 -1
- package/lib/test/client.attributionFarm.spec.js.map +1 -1
- package/lib/test/client.getPosition.spec.js +3 -2
- package/lib/test/client.getPosition.spec.js.map +1 -1
- package/lib/test/client.localReference.spec.js +1 -1
- package/lib/test/client.localReference.spec.js.map +1 -1
- package/lib/test/client.localReferenceFarm.spec.js.map +1 -1
- package/lib/test/client.rollback.spec.js +1 -1
- package/lib/test/client.rollback.spec.js.map +1 -1
- package/lib/test/dirname.cjs +0 -1
- package/lib/test/dirname.cjs.map +1 -1
- package/lib/test/index.d.ts +1 -1
- package/lib/test/index.d.ts.map +1 -1
- package/lib/test/index.js +1 -1
- package/lib/test/index.js.map +1 -1
- package/lib/test/mergeTree.annotate.spec.js +3 -0
- package/lib/test/mergeTree.annotate.spec.js.map +1 -1
- package/lib/test/mergeTree.insertingWalk.spec.js +2 -2
- package/lib/test/mergeTree.insertingWalk.spec.js.map +1 -1
- package/lib/test/mergeTree.markRangeRemoved.spec.js +2 -0
- package/lib/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
- package/lib/test/mergeTree.walk.spec.js.map +1 -1
- package/lib/test/mergeTreeOperationRunner.d.ts.map +1 -1
- package/lib/test/mergeTreeOperationRunner.js +1 -2
- package/lib/test/mergeTreeOperationRunner.js.map +1 -1
- package/lib/test/obliterate.spec.js.map +1 -1
- package/lib/test/propertyManager.spec.js.map +1 -1
- package/lib/test/reconnectHelper.d.ts +2 -1
- package/lib/test/reconnectHelper.d.ts.map +1 -1
- package/lib/test/reconnectHelper.js.map +1 -1
- package/lib/test/resetPendingSegmentsToOp.spec.js.map +1 -1
- package/lib/test/revertibleFarm.spec.js.map +1 -1
- package/lib/test/segmentGroupCollection.spec.js +15 -3
- package/lib/test/segmentGroupCollection.spec.js.map +1 -1
- package/lib/test/snapshot.utils.d.ts +2 -2
- package/lib/test/snapshot.utils.d.ts.map +1 -1
- package/lib/test/snapshot.utils.js.map +1 -1
- package/lib/test/sortedSegmentSet.spec.js +4 -3
- package/lib/test/sortedSegmentSet.spec.js.map +1 -1
- package/lib/test/testClient.d.ts +8 -6
- package/lib/test/testClient.d.ts.map +1 -1
- package/lib/test/testClient.js +30 -28
- package/lib/test/testClient.js.map +1 -1
- package/lib/test/testClientLogger.d.ts.map +1 -1
- package/lib/test/testClientLogger.js +5 -3
- package/lib/test/testClientLogger.js.map +1 -1
- package/lib/test/testUtils.d.ts +2 -2
- package/lib/test/testUtils.d.ts.map +1 -1
- package/lib/test/testUtils.js +9 -8
- package/lib/test/testUtils.js.map +1 -1
- package/lib/test/text.d.ts +2 -2
- package/lib/test/text.d.ts.map +1 -1
- package/lib/test/text.js +12 -6
- package/lib/test/text.js.map +1 -1
- package/lib/test/tracking.spec.js.map +1 -1
- package/lib/test/wordUnitTests.spec.js +1 -1
- package/lib/test/wordUnitTests.spec.js.map +1 -1
- package/lib/zamboni.d.ts.map +1 -1
- package/lib/zamboni.js +7 -6
- package/lib/zamboni.js.map +1 -1
- package/package.json +17 -17
- package/src/MergeTreeTextHelper.ts +2 -4
- package/src/attributionPolicy.ts +5 -13
- package/src/client.ts +55 -44
- package/src/endOfTreeSegment.ts +3 -3
- package/src/index.ts +4 -6
- package/src/mergeTree.ts +245 -282
- package/src/mergeTreeNodeWalk.ts +3 -2
- package/src/mergeTreeNodes.ts +190 -322
- package/src/mergeTreeTracking.ts +0 -3
- package/src/partialLengths.ts +42 -27
- package/src/perspective.ts +27 -4
- package/src/referencePositions.ts +4 -1
- package/src/revertibles.ts +19 -13
- package/src/segmentGroupCollection.ts +7 -18
- package/src/segmentInfos.ts +377 -0
- package/src/snapshotLoader.ts +60 -57
- package/src/snapshotV1.ts +14 -16
- package/src/snapshotlegacy.ts +12 -17
- package/src/sortedSegmentSet.ts +6 -8
- package/src/zamboni.ts +10 -12
package/lib/mergeTree.js
CHANGED
|
@@ -14,40 +14,19 @@ import {
|
|
|
14
14
|
LocalReferenceCollection, SlidingPreference, anyLocalReferencePosition, createDetachedLocalReferencePosition, filterLocalReferencePositions, } from "./localReference.js";
|
|
15
15
|
import { MergeTreeMaintenanceType, } from "./mergeTreeDeltaCallback.js";
|
|
16
16
|
import { NodeAction, backwardExcursion, depthFirstNodeWalk, forwardExcursion, walkAllChildSegments, } from "./mergeTreeNodeWalk.js";
|
|
17
|
-
import {
|
|
18
|
-
// eslint-disable-next-line import/no-deprecated
|
|
19
|
-
CollaborationWindow, Marker, MaxNodesInBlock, MergeBlock, reservedMarkerIdKey, seqLTE, toMoveInfo, toRemovalInfo, } from "./mergeTreeNodes.js";
|
|
17
|
+
import { CollaborationWindow, Marker, MaxNodesInBlock, MergeBlock, assertSegmentLeaf, assignChild, isSegmentLeaf, reservedMarkerIdKey, seqLTE, } from "./mergeTreeNodes.js";
|
|
20
18
|
import { createAnnotateRangeOp, createInsertSegmentOp, createRemoveRangeOp, } from "./opBuilder.js";
|
|
21
19
|
import { MergeTreeDeltaType, ReferenceType, } from "./ops.js";
|
|
22
20
|
import { PartialSequenceLengths } from "./partialLengths.js";
|
|
23
21
|
import { PerspectiveImpl, isSegmentPresent } from "./perspective.js";
|
|
24
22
|
import { createMap, extend, extendIfUndefined } from "./properties.js";
|
|
25
23
|
import { DetachedReferencePosition, refGetTileLabels, refHasTileLabel, refTypeIncludesFlag, } from "./referencePositions.js";
|
|
26
|
-
// eslint-disable-next-line import/no-deprecated
|
|
27
24
|
import { SegmentGroupCollection } from "./segmentGroupCollection.js";
|
|
28
|
-
|
|
25
|
+
import { assertMoved, assertRemoved, isMergeNodeInfo, isMoved, isRemoved, overwriteInfo, removeRemovalInfo, toMoveInfo, toRemovalInfo, } from "./segmentInfos.js";
|
|
29
26
|
import { copyPropertiesAndManager, PropertiesManager, PropertiesRollback, } from "./segmentPropertiesManager.js";
|
|
30
27
|
import { Side } from "./sequencePlace.js";
|
|
31
28
|
import { SortedSegmentSet } from "./sortedSegmentSet.js";
|
|
32
29
|
import { zamboniSegments } from "./zamboni.js";
|
|
33
|
-
// eslint-disable-next-line import/no-deprecated
|
|
34
|
-
function markSegmentMoved(seg, moveInfo) {
|
|
35
|
-
seg.moveDst = moveInfo.moveDst;
|
|
36
|
-
seg.movedClientIds = [...moveInfo.movedClientIds];
|
|
37
|
-
seg.movedSeqs = [moveInfo.movedSeq];
|
|
38
|
-
seg.movedSeq = moveInfo.movedSeq;
|
|
39
|
-
seg.localMovedSeq = moveInfo.localMovedSeq;
|
|
40
|
-
seg.wasMovedOnInsert = moveInfo.wasMovedOnInsert;
|
|
41
|
-
}
|
|
42
|
-
// eslint-disable-next-line import/no-deprecated
|
|
43
|
-
function isMoved(segment) {
|
|
44
|
-
return toMoveInfo(segment) !== undefined;
|
|
45
|
-
}
|
|
46
|
-
// eslint-disable-next-line import/no-deprecated
|
|
47
|
-
function isRemoved(segment) {
|
|
48
|
-
return toRemovalInfo(segment) !== undefined;
|
|
49
|
-
}
|
|
50
|
-
// eslint-disable-next-line import/no-deprecated
|
|
51
30
|
function isRemovedAndAcked(segment) {
|
|
52
31
|
const removalInfo = toRemovalInfo(segment);
|
|
53
32
|
return removalInfo !== undefined && removalInfo.removedSeq !== UnassignedSequenceNumber;
|
|
@@ -91,29 +70,25 @@ function ackSegment(segment, segmentGroup, opArgs) {
|
|
|
91
70
|
return true;
|
|
92
71
|
}
|
|
93
72
|
case MergeTreeDeltaType.REMOVE: {
|
|
94
|
-
|
|
95
|
-
const removalInfo = toRemovalInfo(segment);
|
|
96
|
-
assert(removalInfo !== undefined, 0x046 /* "On remove ack, missing removal info!" */);
|
|
73
|
+
assertRemoved(segment);
|
|
97
74
|
segment.localRemovedSeq = undefined;
|
|
98
|
-
if (
|
|
99
|
-
|
|
75
|
+
if (segment.removedSeq === UnassignedSequenceNumber) {
|
|
76
|
+
segment.removedSeq = sequenceNumber;
|
|
100
77
|
return true;
|
|
101
78
|
}
|
|
102
79
|
return false;
|
|
103
80
|
}
|
|
104
81
|
case MergeTreeDeltaType.OBLITERATE:
|
|
105
82
|
case MergeTreeDeltaType.OBLITERATE_SIDED: {
|
|
106
|
-
|
|
107
|
-
const moveInfo = toMoveInfo(segment);
|
|
108
|
-
assert(moveInfo !== undefined, 0x86e /* On obliterate ack, missing move info! */);
|
|
83
|
+
assertMoved(segment);
|
|
109
84
|
const obliterateInfo = segmentGroup.obliterateInfo;
|
|
110
85
|
assert(obliterateInfo !== undefined, 0xa40 /* must have obliterate info */);
|
|
111
86
|
segment.localMovedSeq = obliterateInfo.localSeq = undefined;
|
|
112
|
-
const seqIdx =
|
|
87
|
+
const seqIdx = segment.movedSeqs.indexOf(UnassignedSequenceNumber);
|
|
113
88
|
assert(seqIdx !== -1, 0x86f /* expected movedSeqs to contain unacked seq */);
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
|
|
89
|
+
segment.movedSeqs[seqIdx] = sequenceNumber;
|
|
90
|
+
if (segment.movedSeq === UnassignedSequenceNumber) {
|
|
91
|
+
segment.movedSeq = sequenceNumber;
|
|
117
92
|
return true;
|
|
118
93
|
}
|
|
119
94
|
return false;
|
|
@@ -150,7 +125,6 @@ export function findRootMergeBlock(segmentOrNode) {
|
|
|
150
125
|
* entries for all segments visited during excursion.
|
|
151
126
|
* This can reduce the number of times the tree needs to be scanned if a range containing many
|
|
152
127
|
* SlideOnRemove references is removed.
|
|
153
|
-
* @internal
|
|
154
128
|
*/
|
|
155
129
|
function getSlideToSegment(segment, slidingPreference = SlidingPreference.FORWARD, cache, useNewSlidingBehavior = false) {
|
|
156
130
|
if (!segment ||
|
|
@@ -170,7 +144,8 @@ function getSlideToSegment(segment, slidingPreference = SlidingPreference.FORWAR
|
|
|
170
144
|
return false;
|
|
171
145
|
}
|
|
172
146
|
if (cache !== undefined &&
|
|
173
|
-
(seg
|
|
147
|
+
(toRemovalInfo(seg)?.removedSeq === toRemovalInfo(segment)?.removedSeq ||
|
|
148
|
+
toMoveInfo(seg)?.movedSeq === toMoveInfo(segment)?.movedSeq)) {
|
|
174
149
|
cache.set(seg, result);
|
|
175
150
|
}
|
|
176
151
|
return true;
|
|
@@ -217,7 +192,7 @@ function getSlideToSegment(segment, slidingPreference = SlidingPreference.FORWAR
|
|
|
217
192
|
* @internal
|
|
218
193
|
*/
|
|
219
194
|
export function getSlideToSegoff(segoff, slidingPreference = SlidingPreference.FORWARD, useNewSlidingBehavior = false) {
|
|
220
|
-
if (segoff.segment
|
|
195
|
+
if (!isSegmentLeaf(segoff.segment)) {
|
|
221
196
|
return segoff;
|
|
222
197
|
}
|
|
223
198
|
const [segment, _] = getSlideToSegment(segoff.segment, slidingPreference, undefined, useNewSlidingBehavior);
|
|
@@ -244,7 +219,6 @@ class Obliterates {
|
|
|
244
219
|
* See https://github.com/microsoft/FluidFramework/blob/main/packages/dds/merge-tree/docs/Obliterate.md#remote-perspective
|
|
245
220
|
* for additional context
|
|
246
221
|
*/
|
|
247
|
-
// eslint-disable-next-line import/no-deprecated
|
|
248
222
|
this.seqOrdered = new DoublyLinkedList();
|
|
249
223
|
/**
|
|
250
224
|
* This contains a sorted lists of all obliterate starts
|
|
@@ -262,7 +236,6 @@ class Obliterates {
|
|
|
262
236
|
this.mergeTree.removeLocalReferencePosition(ob.data.end);
|
|
263
237
|
}
|
|
264
238
|
}
|
|
265
|
-
// eslint-disable-next-line import/no-deprecated
|
|
266
239
|
addOrUpdate(obliterateInfo) {
|
|
267
240
|
const { seq, start } = obliterateInfo;
|
|
268
241
|
if (seq !== UnassignedSequenceNumber) {
|
|
@@ -273,14 +246,14 @@ class Obliterates {
|
|
|
273
246
|
empty() {
|
|
274
247
|
return this.startOrdered.size === 0;
|
|
275
248
|
}
|
|
276
|
-
// eslint-disable-next-line import/no-deprecated
|
|
277
249
|
findOverlapping(seg) {
|
|
278
|
-
// eslint-disable-next-line import/no-deprecated
|
|
279
250
|
const overlapping = [];
|
|
280
251
|
for (const start of this.startOrdered.items) {
|
|
281
|
-
|
|
252
|
+
const startSeg = start.getSegment();
|
|
253
|
+
if (isMergeNodeInfo(startSeg) && startSeg.ordinal <= seg.ordinal) {
|
|
282
254
|
const ob = start.properties?.obliterate;
|
|
283
|
-
|
|
255
|
+
const endSeg = ob.end.getSegment();
|
|
256
|
+
if (isMergeNodeInfo(endSeg) && endSeg.ordinal >= seg.ordinal) {
|
|
284
257
|
overlapping.push(ob);
|
|
285
258
|
}
|
|
286
259
|
}
|
|
@@ -298,9 +271,7 @@ class Obliterates {
|
|
|
298
271
|
export class MergeTree {
|
|
299
272
|
constructor(options) {
|
|
300
273
|
this.options = options;
|
|
301
|
-
// eslint-disable-next-line import/no-deprecated
|
|
302
274
|
this.collabWindow = new CollaborationWindow();
|
|
303
|
-
// eslint-disable-next-line import/no-deprecated
|
|
304
275
|
this.pendingSegments = new DoublyLinkedList();
|
|
305
276
|
this.segmentsToScour = new Heap(LRUSegmentComparer);
|
|
306
277
|
/**
|
|
@@ -318,6 +289,7 @@ export class MergeTree {
|
|
|
318
289
|
return {};
|
|
319
290
|
}
|
|
320
291
|
const next = segment.splitAt(pos);
|
|
292
|
+
assertSegmentLeaf(next);
|
|
321
293
|
if (segment?.segmentGroups) {
|
|
322
294
|
next.segmentGroups ?? (next.segmentGroups = new SegmentGroupCollection(next));
|
|
323
295
|
segment.segmentGroups.copyTo(next.segmentGroups);
|
|
@@ -380,7 +352,9 @@ export class MergeTree {
|
|
|
380
352
|
}
|
|
381
353
|
assert(refSeq !== undefined, 0x398 /* localSeq provided for local length without refSeq */);
|
|
382
354
|
assert(segment.seq !== undefined, 0x399 /* segment with no seq in mergeTree */);
|
|
383
|
-
const { seq
|
|
355
|
+
const { seq } = segment;
|
|
356
|
+
const { removedSeq, localRemovedSeq } = removalInfo ?? {};
|
|
357
|
+
const { movedSeq, localMovedSeq } = moveInfo ?? {};
|
|
384
358
|
if (seq === UnassignedSequenceNumber) {
|
|
385
359
|
assert(segment.localSeq !== undefined, 0x39a /* unacked segment with undefined localSeq */);
|
|
386
360
|
// inserted locally, still un-acked
|
|
@@ -416,7 +390,7 @@ export class MergeTree {
|
|
|
416
390
|
}
|
|
417
391
|
addNode(block, node) {
|
|
418
392
|
const index = block.childCount++;
|
|
419
|
-
|
|
393
|
+
assignChild(block, node, index, false);
|
|
420
394
|
return index;
|
|
421
395
|
}
|
|
422
396
|
reloadFromSegments(segments) {
|
|
@@ -510,12 +484,12 @@ export class MergeTree {
|
|
|
510
484
|
assert(localSeq === undefined || clientId === this.collabWindow.clientId, 0x39b /* localSeq provided for non-local client */);
|
|
511
485
|
let segment;
|
|
512
486
|
let offset;
|
|
513
|
-
const leaf = (leafSeg,
|
|
487
|
+
const leaf = (leafSeg, _, start) => {
|
|
514
488
|
segment = leafSeg;
|
|
515
489
|
offset = start;
|
|
516
490
|
return false;
|
|
517
491
|
};
|
|
518
|
-
this.nodeMap(refSeq, clientId, leaf, undefined,
|
|
492
|
+
this.nodeMap(refSeq, clientId, leaf, undefined, pos, pos + 1, localSeq);
|
|
519
493
|
return { segment, offset };
|
|
520
494
|
}
|
|
521
495
|
/**
|
|
@@ -650,7 +624,6 @@ export class MergeTree {
|
|
|
650
624
|
if (this.localPartialsComputed) {
|
|
651
625
|
return;
|
|
652
626
|
}
|
|
653
|
-
// eslint-disable-next-line import/no-deprecated
|
|
654
627
|
const rebaseCollabWindow = new CollaborationWindow();
|
|
655
628
|
rebaseCollabWindow.loadFrom(this.collabWindow);
|
|
656
629
|
if (refSeq < this.collabWindow.minSeq) {
|
|
@@ -734,24 +707,26 @@ export class MergeTree {
|
|
|
734
707
|
*/
|
|
735
708
|
referencePositionToLocalPosition(refPos, refSeq = Number.MAX_SAFE_INTEGER, clientId = this.collabWindow.clientId, localSeq = this.collabWindow.localSeq) {
|
|
736
709
|
const seg = refPos.getSegment();
|
|
737
|
-
if (seg
|
|
710
|
+
if (!isSegmentLeaf(seg)) {
|
|
738
711
|
// We have no idea where this reference is, because it refers to a segment which is not in the tree.
|
|
739
712
|
return DetachedReferencePosition;
|
|
740
713
|
}
|
|
741
714
|
if (refPos.isLeaf()) {
|
|
742
|
-
return this.getPosition(
|
|
715
|
+
return this.getPosition(seg, refSeq, clientId, localSeq);
|
|
743
716
|
}
|
|
744
717
|
if (refTypeIncludesFlag(refPos, ReferenceType.Transient) || seg.localRefs?.has(refPos)) {
|
|
745
718
|
if (seg !== this.startOfTree &&
|
|
746
719
|
seg !== this.endOfTree &&
|
|
747
720
|
!isSegmentPresent(seg, { refSeq, localSeq })) {
|
|
748
721
|
const forward = refPos.slidingPreference === SlidingPreference.FORWARD;
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
722
|
+
const moveInfo = toMoveInfo(seg);
|
|
723
|
+
const removeInfo = toRemovalInfo(seg);
|
|
724
|
+
const slideSeq = moveInfo !== undefined && moveInfo.movedSeq !== UnassignedSequenceNumber
|
|
725
|
+
? moveInfo.movedSeq
|
|
726
|
+
: removeInfo !== undefined && removeInfo.removedSeq !== UnassignedSequenceNumber
|
|
727
|
+
? removeInfo.removedSeq
|
|
753
728
|
: refSeq;
|
|
754
|
-
const slideLocalSeq =
|
|
729
|
+
const slideLocalSeq = moveInfo?.localMovedSeq ?? removeInfo?.localRemovedSeq;
|
|
755
730
|
const perspective = new PerspectiveImpl(this, {
|
|
756
731
|
refSeq: slideSeq,
|
|
757
732
|
localSeq: slideLocalSeq,
|
|
@@ -779,11 +754,10 @@ export class MergeTree {
|
|
|
779
754
|
searchForMarker(startPos, clientId, markerLabel, forwards = true) {
|
|
780
755
|
let foundMarker;
|
|
781
756
|
const { segment } = this.getContainingSegment(startPos, UniversalSequenceNumber, clientId);
|
|
782
|
-
|
|
783
|
-
if (segWithParent?.parent === undefined) {
|
|
757
|
+
if (!isSegmentLeaf(segment)) {
|
|
784
758
|
return undefined;
|
|
785
759
|
}
|
|
786
|
-
depthFirstNodeWalk(
|
|
760
|
+
depthFirstNodeWalk(segment.parent, segment, (node) => {
|
|
787
761
|
if (node.isLeaf()) {
|
|
788
762
|
if (Marker.is(node) && refHasTileLabel(node, markerLabel)) {
|
|
789
763
|
foundMarker = node;
|
|
@@ -805,8 +779,8 @@ export class MergeTree {
|
|
|
805
779
|
updateRoot(splitNode) {
|
|
806
780
|
if (splitNode !== undefined) {
|
|
807
781
|
const newRoot = this.makeBlock(2);
|
|
808
|
-
|
|
809
|
-
|
|
782
|
+
assignChild(newRoot, this.root, 0, false);
|
|
783
|
+
assignChild(newRoot, splitNode, 1, false);
|
|
810
784
|
this.root = newRoot;
|
|
811
785
|
this.nodeUpdateOrdinals(this.root);
|
|
812
786
|
this.nodeUpdateLengthNewStructure(this.root);
|
|
@@ -862,9 +836,7 @@ export class MergeTree {
|
|
|
862
836
|
zamboniSegments(this);
|
|
863
837
|
}
|
|
864
838
|
}
|
|
865
|
-
addToPendingList(segment,
|
|
866
|
-
// eslint-disable-next-line import/no-deprecated
|
|
867
|
-
segmentGroup, localSeq, previousProps) {
|
|
839
|
+
addToPendingList(segment, segmentGroup, localSeq, previousProps) {
|
|
868
840
|
let _segmentGroup = segmentGroup;
|
|
869
841
|
if (_segmentGroup === undefined) {
|
|
870
842
|
_segmentGroup = {
|
|
@@ -884,7 +856,6 @@ export class MergeTree {
|
|
|
884
856
|
if (previousProps) {
|
|
885
857
|
_segmentGroup.previousProps.push(previousProps);
|
|
886
858
|
}
|
|
887
|
-
// eslint-disable-next-line import/no-deprecated
|
|
888
859
|
const segmentGroups = (segment.segmentGroups ?? (segment.segmentGroups = new SegmentGroupCollection(segment)));
|
|
889
860
|
segmentGroups.enqueue(_segmentGroup);
|
|
890
861
|
return _segmentGroup;
|
|
@@ -911,7 +882,7 @@ export class MergeTree {
|
|
|
911
882
|
if (relativePos.id) {
|
|
912
883
|
marker = this.getMarkerFromId(relativePos.id);
|
|
913
884
|
}
|
|
914
|
-
if (marker) {
|
|
885
|
+
if (isSegmentLeaf(marker)) {
|
|
915
886
|
pos = this.getPosition(marker, refseq, clientId);
|
|
916
887
|
if (relativePos.before) {
|
|
917
888
|
if (relativePos.offset !== undefined) {
|
|
@@ -969,7 +940,7 @@ export class MergeTree {
|
|
|
969
940
|
}
|
|
970
941
|
const segmentInfo = this.getContainingSegment(remoteClientPosition, remoteClientRefSeq, remoteClientId);
|
|
971
942
|
const { currentSeq, clientId } = this.collabWindow;
|
|
972
|
-
if (segmentInfo?.segment) {
|
|
943
|
+
if (isSegmentLeaf(segmentInfo?.segment)) {
|
|
973
944
|
const segmentPosition = this.getPosition(segmentInfo.segment, currentSeq, clientId);
|
|
974
945
|
return segmentPosition + segmentInfo.offset;
|
|
975
946
|
}
|
|
@@ -990,7 +961,6 @@ export class MergeTree {
|
|
|
990
961
|
});
|
|
991
962
|
return siblingExists;
|
|
992
963
|
};
|
|
993
|
-
// eslint-disable-next-line import/no-deprecated
|
|
994
964
|
let segmentGroup;
|
|
995
965
|
const saveIfLocal = (locSegment) => {
|
|
996
966
|
// Save segment so we can assign sequence number when acked by server
|
|
@@ -1020,93 +990,95 @@ export class MergeTree {
|
|
|
1020
990
|
}
|
|
1021
991
|
return segmentChanges;
|
|
1022
992
|
};
|
|
993
|
+
const insertInfo = {
|
|
994
|
+
clientId,
|
|
995
|
+
seq,
|
|
996
|
+
localSeq,
|
|
997
|
+
};
|
|
1023
998
|
// TODO: build tree from segs and insert all at once
|
|
1024
999
|
let insertPos = pos;
|
|
1025
|
-
for (const newSegment of newSegments
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
if (
|
|
1031
|
-
|
|
1032
|
-
if (markerId) {
|
|
1033
|
-
this.idToMarker.set(markerId, newSegment);
|
|
1034
|
-
}
|
|
1000
|
+
for (const newSegment of newSegments
|
|
1001
|
+
.filter((s) => s.cachedLength > 0)
|
|
1002
|
+
.map((s) => overwriteInfo(s, insertInfo))) {
|
|
1003
|
+
if (Marker.is(newSegment)) {
|
|
1004
|
+
const markerId = newSegment.getId();
|
|
1005
|
+
if (markerId) {
|
|
1006
|
+
this.idToMarker.set(markerId, newSegment);
|
|
1035
1007
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1008
|
+
}
|
|
1009
|
+
const splitNode = this.insertingWalk(this.root, insertPos, refSeq, clientId, seq, {
|
|
1010
|
+
leaf: onLeaf,
|
|
1011
|
+
candidateSegment: newSegment,
|
|
1012
|
+
continuePredicate: continueFrom,
|
|
1013
|
+
});
|
|
1014
|
+
if (!isSegmentLeaf(newSegment)) {
|
|
1015
|
+
// Indicates an attempt to insert past the end of the merge-tree's content.
|
|
1016
|
+
const errorConstructor = localSeq === undefined ? DataProcessingError : UsageError;
|
|
1017
|
+
throw new errorConstructor("MergeTree insert failed", {
|
|
1018
|
+
currentSeq: this.collabWindow.currentSeq,
|
|
1019
|
+
minSeq: this.collabWindow.minSeq,
|
|
1020
|
+
segSeq: insertInfo.seq,
|
|
1040
1021
|
});
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
? Number.MAX_SAFE_INTEGER - this.collabWindow.localSeq + ob.localSeq
|
|
1069
|
-
: ob.seq;
|
|
1070
|
-
if (normalizedObSeq > refSeq) {
|
|
1071
|
-
if (oldest === undefined || normalizedOldestSeq > normalizedObSeq) {
|
|
1072
|
-
normalizedOldestSeq = normalizedObSeq;
|
|
1073
|
-
oldest = ob;
|
|
1074
|
-
movedClientIds.unshift(ob.clientId);
|
|
1075
|
-
movedSeqs.unshift(ob.seq);
|
|
1076
|
-
}
|
|
1077
|
-
else {
|
|
1078
|
-
movedClientIds.push(ob.clientId);
|
|
1079
|
-
movedSeqs.push(ob.seq);
|
|
1080
|
-
}
|
|
1081
|
-
if (newest === undefined || normalizedNewestSeq < normalizedObSeq) {
|
|
1082
|
-
normalizedNewestSeq = normalizedObSeq;
|
|
1083
|
-
newest = ob;
|
|
1084
|
-
}
|
|
1022
|
+
}
|
|
1023
|
+
this.updateRoot(splitNode);
|
|
1024
|
+
insertPos += newSegment.cachedLength;
|
|
1025
|
+
if (!this.options?.mergeTreeEnableObliterate || this.obliterates.empty()) {
|
|
1026
|
+
saveIfLocal(newSegment);
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
let oldest;
|
|
1030
|
+
let normalizedOldestSeq = 0;
|
|
1031
|
+
let newest;
|
|
1032
|
+
let normalizedNewestSeq = 0;
|
|
1033
|
+
const movedClientIds = [];
|
|
1034
|
+
const movedSeqs = [];
|
|
1035
|
+
for (const ob of this.obliterates.findOverlapping(newSegment)) {
|
|
1036
|
+
// compute a normalized seq that takes into account local seqs
|
|
1037
|
+
// but is still comparable to remote seqs to keep the checks below easy
|
|
1038
|
+
// REMOTE SEQUENCE NUMBERS LOCAL SEQUENCE NUMBERS
|
|
1039
|
+
// [0, 1, 2, 3, ..., 100, ..., 1000, ..., (MAX - MaxLocalSeq), L1, L2, L3, L4, ..., L100, ..., L1000, ...(MAX)]
|
|
1040
|
+
const normalizedObSeq = ob.seq === UnassignedSequenceNumber
|
|
1041
|
+
? Number.MAX_SAFE_INTEGER - this.collabWindow.localSeq + ob.localSeq
|
|
1042
|
+
: ob.seq;
|
|
1043
|
+
if (normalizedObSeq > refSeq) {
|
|
1044
|
+
if (oldest === undefined || normalizedOldestSeq > normalizedObSeq) {
|
|
1045
|
+
normalizedOldestSeq = normalizedObSeq;
|
|
1046
|
+
oldest = ob;
|
|
1047
|
+
movedClientIds.unshift(ob.clientId);
|
|
1048
|
+
movedSeqs.unshift(ob.seq);
|
|
1085
1049
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
const moveInfo = {
|
|
1090
|
-
movedClientIds,
|
|
1091
|
-
movedSeq: oldest.seq,
|
|
1092
|
-
movedSeqs,
|
|
1093
|
-
localMovedSeq: oldest.localSeq,
|
|
1094
|
-
wasMovedOnInsert: oldest.seq !== UnassignedSequenceNumber,
|
|
1095
|
-
};
|
|
1096
|
-
markSegmentMoved(newSegment, moveInfo);
|
|
1097
|
-
if (moveInfo.localMovedSeq !== undefined) {
|
|
1098
|
-
assert(oldest.segmentGroup !== undefined, 0x86c /* expected segment group to exist */);
|
|
1099
|
-
this.addToPendingList(newSegment, oldest.segmentGroup);
|
|
1050
|
+
else {
|
|
1051
|
+
movedClientIds.push(ob.clientId);
|
|
1052
|
+
movedSeqs.push(ob.seq);
|
|
1100
1053
|
}
|
|
1101
|
-
if (
|
|
1102
|
-
|
|
1054
|
+
if (newest === undefined || normalizedNewestSeq < normalizedObSeq) {
|
|
1055
|
+
normalizedNewestSeq = normalizedObSeq;
|
|
1056
|
+
newest = ob;
|
|
1103
1057
|
}
|
|
1104
1058
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1059
|
+
}
|
|
1060
|
+
if (oldest && newest?.clientId !== clientId) {
|
|
1061
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1062
|
+
const moveInfo = {
|
|
1063
|
+
movedClientIds,
|
|
1064
|
+
movedSeq: oldest.seq,
|
|
1065
|
+
movedSeqs,
|
|
1066
|
+
localMovedSeq: oldest.localSeq,
|
|
1067
|
+
wasMovedOnInsert: oldest.seq !== UnassignedSequenceNumber,
|
|
1068
|
+
};
|
|
1069
|
+
overwriteInfo(newSegment, moveInfo);
|
|
1070
|
+
if (moveInfo.localMovedSeq !== undefined) {
|
|
1071
|
+
assert(oldest.segmentGroup !== undefined, 0x86c /* expected segment group to exist */);
|
|
1072
|
+
this.addToPendingList(newSegment, oldest.segmentGroup);
|
|
1107
1073
|
}
|
|
1108
|
-
|
|
1074
|
+
if (newSegment.parent) {
|
|
1075
|
+
this.blockUpdatePathLengths(newSegment.parent, seq, clientId);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
else if (oldest && newest?.clientId === clientId) {
|
|
1079
|
+
newSegment.prevObliterateByInserter = newest;
|
|
1109
1080
|
}
|
|
1081
|
+
saveIfLocal(newSegment);
|
|
1110
1082
|
}
|
|
1111
1083
|
}
|
|
1112
1084
|
ensureIntervalBoundary(pos, refSeq, clientId) {
|
|
@@ -1127,10 +1099,8 @@ export class MergeTree {
|
|
|
1127
1099
|
const newSeq = seq === UnassignedSequenceNumber ? Number.MAX_SAFE_INTEGER : seq;
|
|
1128
1100
|
const segSeq = node.seq === UnassignedSequenceNumber ? Number.MAX_SAFE_INTEGER - 1 : (node.seq ?? 0);
|
|
1129
1101
|
return (newSeq > segSeq ||
|
|
1130
|
-
(node.movedSeq !==
|
|
1131
|
-
|
|
1132
|
-
node.movedSeq > seq) ||
|
|
1133
|
-
(node.removedSeq !== undefined &&
|
|
1102
|
+
(isMoved(node) && node.movedSeq !== UnassignedSequenceNumber && node.movedSeq > seq) ||
|
|
1103
|
+
(isRemoved(node) &&
|
|
1134
1104
|
node.removedSeq !== UnassignedSequenceNumber &&
|
|
1135
1105
|
node.removedSeq > seq));
|
|
1136
1106
|
}
|
|
@@ -1162,7 +1132,7 @@ export class MergeTree {
|
|
|
1162
1132
|
const segment = child;
|
|
1163
1133
|
const segmentChanges = context.leaf(segment, _pos, context);
|
|
1164
1134
|
if (segmentChanges.replaceCurrent) {
|
|
1165
|
-
|
|
1135
|
+
assignChild(block, segmentChanges.replaceCurrent, childIndex, false);
|
|
1166
1136
|
segmentChanges.replaceCurrent.ordinal = child.ordinal;
|
|
1167
1137
|
}
|
|
1168
1138
|
if (segmentChanges.next) {
|
|
@@ -1213,7 +1183,7 @@ export class MergeTree {
|
|
|
1213
1183
|
block.children[i] = block.children[i - 1];
|
|
1214
1184
|
block.children[i].index = i;
|
|
1215
1185
|
}
|
|
1216
|
-
|
|
1186
|
+
assignChild(block, newNode, childIndex, false);
|
|
1217
1187
|
block.childCount++;
|
|
1218
1188
|
block.setOrdinal(newNode, childIndex);
|
|
1219
1189
|
if (block.childCount < MaxNodesInBlock) {
|
|
@@ -1242,7 +1212,7 @@ export class MergeTree {
|
|
|
1242
1212
|
// Update ordinals to reflect lowered child count
|
|
1243
1213
|
this.nodeUpdateOrdinals(node);
|
|
1244
1214
|
for (let i = 0; i < halfCount; i++) {
|
|
1245
|
-
|
|
1215
|
+
assignChild(newNode, node.children[halfCount + i], i, false);
|
|
1246
1216
|
node.children[halfCount + i] = undefined;
|
|
1247
1217
|
}
|
|
1248
1218
|
this.nodeUpdateLengthNewStructure(node);
|
|
@@ -1269,9 +1239,7 @@ export class MergeTree {
|
|
|
1269
1239
|
* @param opArgs - The op args for the annotate op. this is passed to the merge tree callback if there is one
|
|
1270
1240
|
* @param rollback - Whether this is for a local rollback and what kind
|
|
1271
1241
|
*/
|
|
1272
|
-
annotateRange(start, end, propsOrAdjust, refSeq, clientId, seq, opArgs,
|
|
1273
|
-
// eslint-disable-next-line import/no-deprecated
|
|
1274
|
-
rollback = PropertiesRollback.None) {
|
|
1242
|
+
annotateRange(start, end, propsOrAdjust, refSeq, clientId, seq, opArgs, rollback = PropertiesRollback.None) {
|
|
1275
1243
|
if (propsOrAdjust.adjust !== undefined) {
|
|
1276
1244
|
errorIfOptionNotTrue(this.options, "mergeTreeEnableAnnotateAdjust");
|
|
1277
1245
|
}
|
|
@@ -1279,7 +1247,6 @@ export class MergeTree {
|
|
|
1279
1247
|
this.ensureIntervalBoundary(end, refSeq, clientId);
|
|
1280
1248
|
const deltaSegments = [];
|
|
1281
1249
|
const localSeq = seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
|
|
1282
|
-
// eslint-disable-next-line import/no-deprecated
|
|
1283
1250
|
let segmentGroup;
|
|
1284
1251
|
const opObj = propsOrAdjust.props ?? propsOrAdjust.adjust;
|
|
1285
1252
|
const annotateSegment = (segment) => {
|
|
@@ -1303,7 +1270,7 @@ export class MergeTree {
|
|
|
1303
1270
|
}
|
|
1304
1271
|
return true;
|
|
1305
1272
|
};
|
|
1306
|
-
this.nodeMap(refSeq, clientId, annotateSegment, undefined,
|
|
1273
|
+
this.nodeMap(refSeq, clientId, annotateSegment, undefined, start, end);
|
|
1307
1274
|
// OpArgs == undefined => test code
|
|
1308
1275
|
if (deltaSegments.length > 0) {
|
|
1309
1276
|
this.mergeTreeDeltaCallback?.(opArgs, {
|
|
@@ -1324,9 +1291,9 @@ export class MergeTree {
|
|
|
1324
1291
|
this.ensureIntervalBoundary(endPos, refSeq, clientId);
|
|
1325
1292
|
let _overwrite = false;
|
|
1326
1293
|
const localOverlapWithRefs = [];
|
|
1294
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1327
1295
|
const movedSegments = [];
|
|
1328
1296
|
const localSeq = seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
|
|
1329
|
-
// eslint-disable-next-line import/no-deprecated
|
|
1330
1297
|
const obliterate = {
|
|
1331
1298
|
clientId,
|
|
1332
1299
|
end: createDetachedLocalReferencePosition(undefined),
|
|
@@ -1338,7 +1305,7 @@ export class MergeTree {
|
|
|
1338
1305
|
};
|
|
1339
1306
|
const { segment: startSeg } = this.getContainingSegment(start.pos, refSeq, clientId);
|
|
1340
1307
|
const { segment: endSeg } = this.getContainingSegment(end.pos, refSeq, clientId);
|
|
1341
|
-
assert(startSeg
|
|
1308
|
+
assert(isSegmentLeaf(startSeg) && isSegmentLeaf(endSeg), 0xa3f /* segments cannot be undefined */);
|
|
1342
1309
|
obliterate.start = this.createLocalReferencePosition(startSeg, start.side === Side.Before ? 0 : Math.max(startSeg.cachedLength - 1, 0), ReferenceType.StayOnRemove, {
|
|
1343
1310
|
obliterate,
|
|
1344
1311
|
});
|
|
@@ -1359,7 +1326,7 @@ export class MergeTree {
|
|
|
1359
1326
|
this.pendingSegments.push(obliterate.segmentGroup);
|
|
1360
1327
|
}
|
|
1361
1328
|
this.obliterates.addOrUpdate(obliterate);
|
|
1362
|
-
const markMoved = (segment, pos
|
|
1329
|
+
const markMoved = (segment, pos) => {
|
|
1363
1330
|
if ((start.side === Side.After && startPos === pos + segment.cachedLength) || // exclusive start segment
|
|
1364
1331
|
(end.side === Side.Before &&
|
|
1365
1332
|
endPos === pos &&
|
|
@@ -1376,23 +1343,27 @@ export class MergeTree {
|
|
|
1376
1343
|
// Other clients will also choose not to obliterate this segment because the most recent obliteration has the same clientId
|
|
1377
1344
|
return true;
|
|
1378
1345
|
}
|
|
1379
|
-
|
|
1346
|
+
const wasMovedOnInsert = clientId !== segment.clientId &&
|
|
1380
1347
|
segment.seq !== undefined &&
|
|
1381
1348
|
seq !== UnassignedSequenceNumber &&
|
|
1382
|
-
(refSeq < segment.seq || segment.seq === UnassignedSequenceNumber)
|
|
1383
|
-
segment.wasMovedOnInsert = true;
|
|
1384
|
-
}
|
|
1349
|
+
(refSeq < segment.seq || segment.seq === UnassignedSequenceNumber);
|
|
1385
1350
|
if (existingMoveInfo === undefined) {
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1351
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1352
|
+
const movedSeg = overwriteInfo(segment, {
|
|
1353
|
+
movedClientIds: [clientId],
|
|
1354
|
+
movedSeq: seq,
|
|
1355
|
+
localMovedSeq: localSeq,
|
|
1356
|
+
movedSeqs: [seq],
|
|
1357
|
+
wasMovedOnInsert,
|
|
1358
|
+
});
|
|
1359
|
+
if (!toRemovalInfo(movedSeg)) {
|
|
1360
|
+
movedSegments.push(movedSeg);
|
|
1392
1361
|
}
|
|
1393
1362
|
}
|
|
1394
1363
|
else {
|
|
1395
1364
|
_overwrite = true;
|
|
1365
|
+
// never move wasMovedOnInsert from true to false
|
|
1366
|
+
existingMoveInfo.wasMovedOnInsert || (existingMoveInfo.wasMovedOnInsert = wasMovedOnInsert);
|
|
1396
1367
|
if (existingMoveInfo.movedSeq === UnassignedSequenceNumber) {
|
|
1397
1368
|
// we moved this locally, but someone else moved it first
|
|
1398
1369
|
// so put them at the head of the list
|
|
@@ -1411,6 +1382,7 @@ export class MergeTree {
|
|
|
1411
1382
|
existingMoveInfo.movedSeqs.push(seq);
|
|
1412
1383
|
}
|
|
1413
1384
|
}
|
|
1385
|
+
assertMoved(segment);
|
|
1414
1386
|
// Save segment so can assign moved sequence number when acked by server
|
|
1415
1387
|
if (this.collabWindow.collaborating) {
|
|
1416
1388
|
if (segment.movedSeq === UnassignedSequenceNumber &&
|
|
@@ -1425,7 +1397,7 @@ export class MergeTree {
|
|
|
1425
1397
|
}
|
|
1426
1398
|
return true;
|
|
1427
1399
|
};
|
|
1428
|
-
const afterMarkMoved = (node
|
|
1400
|
+
const afterMarkMoved = (node) => {
|
|
1429
1401
|
if (_overwrite) {
|
|
1430
1402
|
this.nodeUpdateLengthNewStructure(node);
|
|
1431
1403
|
}
|
|
@@ -1434,21 +1406,21 @@ export class MergeTree {
|
|
|
1434
1406
|
}
|
|
1435
1407
|
return true;
|
|
1436
1408
|
};
|
|
1437
|
-
this.nodeMap(refSeq, clientId, markMoved,
|
|
1409
|
+
this.nodeMap(refSeq, clientId, markMoved, afterMarkMoved, start.pos, end.pos + 1, // include the segment containing the end reference
|
|
1438
1410
|
undefined, seq === UnassignedSequenceNumber ? undefined : seq);
|
|
1439
1411
|
this.slideAckedRemovedSegmentReferences(localOverlapWithRefs);
|
|
1440
1412
|
// opArgs == undefined => test code
|
|
1441
1413
|
if (start.pos !== end.pos || start.side !== end.side) {
|
|
1442
1414
|
this.mergeTreeDeltaCallback?.(opArgs, {
|
|
1443
1415
|
operation: MergeTreeDeltaType.OBLITERATE,
|
|
1444
|
-
deltaSegments: movedSegments,
|
|
1416
|
+
deltaSegments: movedSegments.map((segment) => ({ segment })),
|
|
1445
1417
|
});
|
|
1446
1418
|
}
|
|
1447
1419
|
// these events are newly removed
|
|
1448
1420
|
// so we slide after eventing in case the consumer wants to make reference
|
|
1449
1421
|
// changes at remove time, like add a ref to track undo redo.
|
|
1450
1422
|
if (!this.collabWindow.collaborating || clientId !== this.collabWindow.clientId) {
|
|
1451
|
-
this.slideAckedRemovedSegmentReferences(movedSegments
|
|
1423
|
+
this.slideAckedRemovedSegmentReferences(movedSegments);
|
|
1452
1424
|
}
|
|
1453
1425
|
if (this.collabWindow.collaborating &&
|
|
1454
1426
|
seq !== UnassignedSequenceNumber &&
|
|
@@ -1471,19 +1443,22 @@ export class MergeTree {
|
|
|
1471
1443
|
let _overwrite = false;
|
|
1472
1444
|
this.ensureIntervalBoundary(start, refSeq, clientId);
|
|
1473
1445
|
this.ensureIntervalBoundary(end, refSeq, clientId);
|
|
1474
|
-
// eslint-disable-next-line import/no-deprecated
|
|
1475
1446
|
let segmentGroup;
|
|
1447
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1476
1448
|
const removedSegments = [];
|
|
1477
1449
|
const localOverlapWithRefs = [];
|
|
1478
1450
|
const localSeq = seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
|
|
1479
1451
|
const markRemoved = (segment, pos, _start, _end) => {
|
|
1480
1452
|
const existingRemovalInfo = toRemovalInfo(segment);
|
|
1481
1453
|
if (existingRemovalInfo === undefined) {
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1454
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1455
|
+
const removed = overwriteInfo(segment, {
|
|
1456
|
+
removedClientIds: [clientId],
|
|
1457
|
+
removedSeq: seq,
|
|
1458
|
+
localRemovedSeq: localSeq,
|
|
1459
|
+
});
|
|
1460
|
+
if (!toMoveInfo(removed)) {
|
|
1461
|
+
removedSegments.push(removed);
|
|
1487
1462
|
}
|
|
1488
1463
|
}
|
|
1489
1464
|
else {
|
|
@@ -1504,6 +1479,7 @@ export class MergeTree {
|
|
|
1504
1479
|
existingRemovalInfo.removedClientIds.push(clientId);
|
|
1505
1480
|
}
|
|
1506
1481
|
}
|
|
1482
|
+
assertRemoved(segment);
|
|
1507
1483
|
// Save segment so we can assign removed sequence number when acked by server
|
|
1508
1484
|
if (this.collabWindow.collaborating) {
|
|
1509
1485
|
if (segment.removedSeq === UnassignedSequenceNumber &&
|
|
@@ -1518,7 +1494,7 @@ export class MergeTree {
|
|
|
1518
1494
|
}
|
|
1519
1495
|
return true;
|
|
1520
1496
|
};
|
|
1521
|
-
const afterMarkRemoved = (node
|
|
1497
|
+
const afterMarkRemoved = (node) => {
|
|
1522
1498
|
if (_overwrite) {
|
|
1523
1499
|
this.nodeUpdateLengthNewStructure(node);
|
|
1524
1500
|
}
|
|
@@ -1527,7 +1503,7 @@ export class MergeTree {
|
|
|
1527
1503
|
}
|
|
1528
1504
|
return true;
|
|
1529
1505
|
};
|
|
1530
|
-
this.nodeMap(refSeq, clientId, markRemoved,
|
|
1506
|
+
this.nodeMap(refSeq, clientId, markRemoved, afterMarkRemoved, start, end);
|
|
1531
1507
|
// these segments are already viewed as being removed locally and are not event-ed
|
|
1532
1508
|
// so can slide non-StayOnRemove refs immediately
|
|
1533
1509
|
this.slideAckedRemovedSegmentReferences(localOverlapWithRefs);
|
|
@@ -1535,14 +1511,14 @@ export class MergeTree {
|
|
|
1535
1511
|
if (removedSegments.length > 0) {
|
|
1536
1512
|
this.mergeTreeDeltaCallback?.(opArgs, {
|
|
1537
1513
|
operation: MergeTreeDeltaType.REMOVE,
|
|
1538
|
-
deltaSegments: removedSegments,
|
|
1514
|
+
deltaSegments: removedSegments.map((segment) => ({ segment })),
|
|
1539
1515
|
});
|
|
1540
1516
|
}
|
|
1541
1517
|
// these events are newly removed
|
|
1542
1518
|
// so we slide after eventing in case the consumer wants to make reference
|
|
1543
1519
|
// changes at remove time, like add a ref to track undo redo.
|
|
1544
1520
|
if (!this.collabWindow.collaborating || clientId !== this.collabWindow.clientId) {
|
|
1545
|
-
this.slideAckedRemovedSegmentReferences(removedSegments
|
|
1521
|
+
this.slideAckedRemovedSegmentReferences(removedSegments);
|
|
1546
1522
|
}
|
|
1547
1523
|
if (this.collabWindow.collaborating &&
|
|
1548
1524
|
seq !== UnassignedSequenceNumber &&
|
|
@@ -1553,24 +1529,21 @@ export class MergeTree {
|
|
|
1553
1529
|
/**
|
|
1554
1530
|
* Revert an unacked local op
|
|
1555
1531
|
*/
|
|
1556
|
-
// eslint-disable-next-line import/no-deprecated
|
|
1557
1532
|
rollback(op, localOpMetadata) {
|
|
1558
1533
|
if (op.type === MergeTreeDeltaType.REMOVE) {
|
|
1559
|
-
const pendingSegmentGroup = this.pendingSegments.pop
|
|
1534
|
+
const pendingSegmentGroup = this.pendingSegments.pop()?.data;
|
|
1560
1535
|
if (pendingSegmentGroup === undefined || pendingSegmentGroup !== localOpMetadata) {
|
|
1561
1536
|
throw new Error("Rollback op doesn't match last edit");
|
|
1562
1537
|
}
|
|
1563
1538
|
// Disabling because a for of loop causes the type of segment to be ISegmentLeaf, which does not have parent information stored
|
|
1564
1539
|
// eslint-disable-next-line unicorn/no-array-for-each
|
|
1565
1540
|
pendingSegmentGroup.segments.forEach((segment) => {
|
|
1566
|
-
const segmentSegmentGroup = segment?.segmentGroups?.pop
|
|
1541
|
+
const segmentSegmentGroup = segment?.segmentGroups?.pop();
|
|
1567
1542
|
assert(segmentSegmentGroup === pendingSegmentGroup, 0x3ee /* Unexpected segmentGroup in segment */);
|
|
1568
|
-
assert(segment.removedClientIds
|
|
1569
|
-
|
|
1570
|
-
segment
|
|
1571
|
-
|
|
1572
|
-
segment.localRemovedSeq = undefined;
|
|
1573
|
-
for (let updateNode = segment.parent; updateNode !== undefined; updateNode = updateNode.parent) {
|
|
1543
|
+
assert(isRemoved(segment) && segment.removedClientIds[0] === this.collabWindow.clientId, 0x39d /* Rollback segment removedClientId does not match local client */);
|
|
1544
|
+
let updateNode = segment.parent;
|
|
1545
|
+
removeRemovalInfo(segment);
|
|
1546
|
+
for (updateNode; updateNode !== undefined; updateNode = updateNode.parent) {
|
|
1574
1547
|
this.blockUpdateLength(updateNode, UnassignedSequenceNumber, this.collabWindow.clientId);
|
|
1575
1548
|
}
|
|
1576
1549
|
// Note: optional chaining short-circuits:
|
|
@@ -1583,7 +1556,7 @@ export class MergeTree {
|
|
|
1583
1556
|
}
|
|
1584
1557
|
else if (op.type === MergeTreeDeltaType.INSERT ||
|
|
1585
1558
|
op.type === MergeTreeDeltaType.ANNOTATE) {
|
|
1586
|
-
const pendingSegmentGroup = this.pendingSegments.pop
|
|
1559
|
+
const pendingSegmentGroup = this.pendingSegments.pop()?.data;
|
|
1587
1560
|
if (pendingSegmentGroup === undefined ||
|
|
1588
1561
|
pendingSegmentGroup !== localOpMetadata ||
|
|
1589
1562
|
(op.type === MergeTreeDeltaType.ANNOTATE && !pendingSegmentGroup.previousProps)) {
|
|
@@ -1591,7 +1564,7 @@ export class MergeTree {
|
|
|
1591
1564
|
}
|
|
1592
1565
|
let i = 0;
|
|
1593
1566
|
for (const segment of pendingSegmentGroup.segments) {
|
|
1594
|
-
const segmentSegmentGroup = segment?.segmentGroups?.pop
|
|
1567
|
+
const segmentSegmentGroup = segment?.segmentGroups?.pop();
|
|
1595
1568
|
assert(segmentSegmentGroup === pendingSegmentGroup, 0x3ef /* Unexpected segmentGroup in segment */);
|
|
1596
1569
|
const start = this.findRollbackPosition(segment);
|
|
1597
1570
|
if (op.type === MergeTreeDeltaType.INSERT) {
|
|
@@ -1623,7 +1596,7 @@ export class MergeTree {
|
|
|
1623
1596
|
return false;
|
|
1624
1597
|
}
|
|
1625
1598
|
// If not removed, increase position
|
|
1626
|
-
if (seg
|
|
1599
|
+
if (!isRemoved(seg)) {
|
|
1627
1600
|
segmentPosition += seg.cachedLength;
|
|
1628
1601
|
}
|
|
1629
1602
|
return true;
|
|
@@ -1657,6 +1630,7 @@ export class MergeTree {
|
|
|
1657
1630
|
segment = this.endOfTree;
|
|
1658
1631
|
}
|
|
1659
1632
|
else {
|
|
1633
|
+
assertSegmentLeaf(_segment);
|
|
1660
1634
|
segment = _segment;
|
|
1661
1635
|
}
|
|
1662
1636
|
// eslint-disable-next-line import/no-deprecated
|
|
@@ -1721,7 +1695,7 @@ export class MergeTree {
|
|
|
1721
1695
|
for (let i = 0; i < newOrder.length; i++) {
|
|
1722
1696
|
const seg = newOrder[i];
|
|
1723
1697
|
const { parent, index, ordinal } = currentOrder[i];
|
|
1724
|
-
|
|
1698
|
+
assignChild(parent, seg, index, false);
|
|
1725
1699
|
seg.ordinal = ordinal;
|
|
1726
1700
|
}
|
|
1727
1701
|
for (const [segment, groups] of perSegmentTrackingGroups.entries()) {
|
|
@@ -1888,7 +1862,7 @@ export class MergeTree {
|
|
|
1888
1862
|
this.ensureIntervalBoundary(end, refSeq, clientId);
|
|
1889
1863
|
}
|
|
1890
1864
|
}
|
|
1891
|
-
this.nodeMap(refSeq, clientId, handler, accum, undefined, start, end, undefined, visibilitySeq);
|
|
1865
|
+
this.nodeMap(refSeq, clientId, (seg, pos, _start, _end) => handler(seg, pos, refSeq, clientId, _start, _end, accum), undefined, start, end, undefined, visibilitySeq);
|
|
1892
1866
|
}
|
|
1893
1867
|
/**
|
|
1894
1868
|
* Map over all visible segments in a given range
|
|
@@ -1913,7 +1887,7 @@ export class MergeTree {
|
|
|
1913
1887
|
* but it will not count as a segment within the range. That is, it will be
|
|
1914
1888
|
* ignored for the purposes of tracking when traversal should end.
|
|
1915
1889
|
*/
|
|
1916
|
-
nodeMap(refSeq, clientId, leaf,
|
|
1890
|
+
nodeMap(refSeq, clientId, leaf, post, start = 0, end, localSeq, visibilitySeq = refSeq) {
|
|
1917
1891
|
const endPos = end ?? this.nodeLength(this.root, refSeq, clientId, localSeq) ?? 0;
|
|
1918
1892
|
if (endPos === start) {
|
|
1919
1893
|
return;
|
|
@@ -1940,14 +1914,12 @@ export class MergeTree {
|
|
|
1940
1914
|
return NodeAction.Skip;
|
|
1941
1915
|
}
|
|
1942
1916
|
if (node.isLeaf()) {
|
|
1943
|
-
if (leaf(node, pos,
|
|
1917
|
+
if (leaf(node, pos, start - pos, endPos - pos) === false) {
|
|
1944
1918
|
return NodeAction.Exit;
|
|
1945
1919
|
}
|
|
1946
1920
|
pos = nextPos;
|
|
1947
1921
|
}
|
|
1948
|
-
}, undefined, post
|
|
1949
|
-
? undefined
|
|
1950
|
-
: (block) => post(block, pos, refSeq, clientId, start - pos, endPos - pos, accum));
|
|
1922
|
+
}, undefined, post);
|
|
1951
1923
|
}
|
|
1952
1924
|
}
|
|
1953
1925
|
MergeTree.options = {
|