@fluidframework/merge-tree 2.30.0 → 2.31.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 +403 -399
- package/api-report/merge-tree.legacy.alpha.api.md +1 -0
- package/dist/MergeTreeTextHelper.d.ts +9 -3
- package/dist/MergeTreeTextHelper.d.ts.map +1 -1
- package/dist/MergeTreeTextHelper.js +5 -5
- package/dist/MergeTreeTextHelper.js.map +1 -1
- package/dist/client.d.ts +7 -13
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +136 -110
- package/dist/client.js.map +1 -1
- package/dist/endOfTreeSegment.d.ts +12 -8
- package/dist/endOfTreeSegment.d.ts.map +1 -1
- package/dist/endOfTreeSegment.js +2 -4
- package/dist/endOfTreeSegment.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/mergeTree.d.ts +37 -23
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +400 -483
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeDeltaCallback.d.ts +4 -8
- package/dist/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/dist/mergeTreeDeltaCallback.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +32 -10
- package/dist/mergeTreeNodes.d.ts.map +1 -1
- package/dist/mergeTreeNodes.js +43 -28
- package/dist/mergeTreeNodes.js.map +1 -1
- package/dist/partialLengths.d.ts +2 -2
- package/dist/partialLengths.d.ts.map +1 -1
- package/dist/partialLengths.js +181 -109
- package/dist/partialLengths.js.map +1 -1
- package/dist/perspective.d.ts +8 -27
- package/dist/perspective.d.ts.map +1 -1
- package/dist/perspective.js +7 -67
- package/dist/perspective.js.map +1 -1
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +2 -2
- package/dist/revertibles.js.map +1 -1
- package/dist/segmentInfos.d.ts +20 -106
- package/dist/segmentInfos.d.ts.map +1 -1
- package/dist/segmentInfos.js +28 -42
- package/dist/segmentInfos.js.map +1 -1
- package/dist/segmentPropertiesManager.d.ts +1 -14
- package/dist/segmentPropertiesManager.d.ts.map +1 -1
- package/dist/segmentPropertiesManager.js +3 -17
- package/dist/segmentPropertiesManager.js.map +1 -1
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js +62 -19
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/snapshotV1.d.ts.map +1 -1
- package/dist/snapshotV1.js +55 -24
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.d.ts.map +1 -1
- package/dist/snapshotlegacy.js +6 -9
- package/dist/snapshotlegacy.js.map +1 -1
- package/dist/stamps.d.ts +1 -1
- package/dist/stamps.js +1 -1
- package/dist/stamps.js.map +1 -1
- package/dist/test/Insertion.perf.spec.js +6 -51
- package/dist/test/Insertion.perf.spec.js.map +1 -1
- package/dist/test/PartialLengths.perf.spec.js +18 -25
- package/dist/test/PartialLengths.perf.spec.js.map +1 -1
- package/dist/test/Removal.perf.spec.js +13 -41
- package/dist/test/Removal.perf.spec.js.map +1 -1
- package/dist/test/beastTest.spec.d.ts.map +1 -1
- package/dist/test/beastTest.spec.js +41 -66
- package/dist/test/beastTest.spec.js.map +1 -1
- package/dist/test/client.annotateMarker.spec.js +1 -11
- package/dist/test/client.annotateMarker.spec.js.map +1 -1
- package/dist/test/client.applyMsg.spec.js +14 -14
- package/dist/test/client.applyMsg.spec.js.map +1 -1
- package/dist/test/client.getPosition.spec.js +1 -1
- package/dist/test/client.getPosition.spec.js.map +1 -1
- package/dist/test/client.localReference.spec.js +1 -1
- package/dist/test/client.localReference.spec.js.map +1 -1
- package/dist/test/client.rollback.spec.js +49 -58
- package/dist/test/client.rollback.spec.js.map +1 -1
- package/dist/test/client.rollbackFarm.spec.js +1 -1
- package/dist/test/client.rollbackFarm.spec.js.map +1 -1
- package/dist/test/client.searchForMarker.spec.js +4 -21
- package/dist/test/client.searchForMarker.spec.js.map +1 -1
- package/dist/test/index.d.ts +2 -2
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +2 -6
- package/dist/test/index.js.map +1 -1
- package/dist/test/mergeTree.annotate.deltaCallback.spec.js +14 -59
- package/dist/test/mergeTree.annotate.deltaCallback.spec.js.map +1 -1
- package/dist/test/mergeTree.annotate.spec.js +47 -63
- package/dist/test/mergeTree.annotate.spec.js.map +1 -1
- package/dist/test/mergeTree.insert.deltaCallback.spec.js +9 -62
- package/dist/test/mergeTree.insert.deltaCallback.spec.js.map +1 -1
- package/dist/test/mergeTree.insertingWalk.spec.js +59 -125
- package/dist/test/mergeTree.insertingWalk.spec.js.map +1 -1
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +12 -93
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -1
- package/dist/test/mergeTree.markRangeRemoved.spec.js +10 -7
- package/dist/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
- package/dist/test/mergeTree.walk.spec.js +2 -14
- package/dist/test/mergeTree.walk.spec.js.map +1 -1
- package/dist/test/mergeTreeOperationRunner.js +2 -2
- package/dist/test/mergeTreeOperationRunner.js.map +1 -1
- package/dist/test/obliterate.concurrent.spec.js +18 -23
- package/dist/test/obliterate.concurrent.spec.js.map +1 -1
- package/dist/test/obliterate.partialLength.spec.js +166 -136
- package/dist/test/obliterate.partialLength.spec.js.map +1 -1
- package/dist/test/obliterate.spec.js +16 -126
- package/dist/test/obliterate.spec.js.map +1 -1
- package/dist/test/partialLength.spec.js +28 -196
- package/dist/test/partialLength.spec.js.map +1 -1
- package/dist/test/perspective.spec.js +34 -0
- package/dist/test/perspective.spec.js.map +1 -1
- package/dist/test/propertyManager.spec.js +1 -1
- package/dist/test/propertyManager.spec.js.map +1 -1
- package/dist/test/resetPendingSegmentsToOp.spec.js +0 -2
- package/dist/test/resetPendingSegmentsToOp.spec.js.map +1 -1
- package/dist/test/segmentGroupCollection.spec.js +10 -4
- package/dist/test/segmentGroupCollection.spec.js.map +1 -1
- package/dist/test/testClient.d.ts +1 -0
- package/dist/test/testClient.d.ts.map +1 -1
- package/dist/test/testClient.js +16 -26
- package/dist/test/testClient.js.map +1 -1
- package/dist/test/testClientLogger.d.ts.map +1 -1
- package/dist/test/testClientLogger.js +3 -10
- package/dist/test/testClientLogger.js.map +1 -1
- package/dist/test/testServer.d.ts +2 -1
- package/dist/test/testServer.d.ts.map +1 -1
- package/dist/test/testServer.js +7 -5
- package/dist/test/testServer.js.map +1 -1
- package/dist/test/testUtils.d.ts +36 -56
- package/dist/test/testUtils.d.ts.map +1 -1
- package/dist/test/testUtils.js +68 -77
- 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 +5 -2
- package/dist/test/text.js.map +1 -1
- package/dist/textSegment.d.ts +0 -6
- package/dist/textSegment.d.ts.map +1 -1
- package/dist/textSegment.js.map +1 -1
- package/dist/zamboni.d.ts.map +1 -1
- package/dist/zamboni.js +53 -26
- package/dist/zamboni.js.map +1 -1
- package/lib/MergeTreeTextHelper.d.ts +9 -3
- package/lib/MergeTreeTextHelper.d.ts.map +1 -1
- package/lib/MergeTreeTextHelper.js +5 -5
- package/lib/MergeTreeTextHelper.js.map +1 -1
- package/lib/client.d.ts +7 -13
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +117 -116
- package/lib/client.js.map +1 -1
- package/lib/endOfTreeSegment.d.ts +12 -8
- package/lib/endOfTreeSegment.d.ts.map +1 -1
- package/lib/endOfTreeSegment.js +2 -4
- package/lib/endOfTreeSegment.js.map +1 -1
- package/lib/index.d.ts +6 -3
- 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 +37 -23
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +381 -488
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeDeltaCallback.d.ts +4 -8
- package/lib/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/lib/mergeTreeDeltaCallback.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +32 -10
- package/lib/mergeTreeNodes.d.ts.map +1 -1
- package/lib/mergeTreeNodes.js +42 -29
- package/lib/mergeTreeNodes.js.map +1 -1
- package/lib/partialLengths.d.ts +2 -2
- package/lib/partialLengths.d.ts.map +1 -1
- package/lib/partialLengths.js +160 -111
- package/lib/partialLengths.js.map +1 -1
- package/lib/perspective.d.ts +8 -27
- package/lib/perspective.d.ts.map +1 -1
- package/lib/perspective.js +8 -68
- package/lib/perspective.js.map +1 -1
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +2 -2
- package/lib/revertibles.js.map +1 -1
- package/lib/segmentInfos.d.ts +20 -106
- package/lib/segmentInfos.d.ts.map +1 -1
- package/lib/segmentInfos.js +26 -37
- package/lib/segmentInfos.js.map +1 -1
- package/lib/segmentPropertiesManager.d.ts +1 -14
- package/lib/segmentPropertiesManager.d.ts.map +1 -1
- package/lib/segmentPropertiesManager.js +2 -16
- package/lib/segmentPropertiesManager.js.map +1 -1
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js +39 -19
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/snapshotV1.d.ts.map +1 -1
- package/lib/snapshotV1.js +34 -26
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.d.ts.map +1 -1
- package/lib/snapshotlegacy.js +7 -10
- package/lib/snapshotlegacy.js.map +1 -1
- package/lib/stamps.d.ts +1 -1
- package/lib/stamps.js +1 -1
- package/lib/stamps.js.map +1 -1
- package/lib/test/Insertion.perf.spec.js +6 -51
- package/lib/test/Insertion.perf.spec.js.map +1 -1
- package/lib/test/PartialLengths.perf.spec.js +18 -25
- package/lib/test/PartialLengths.perf.spec.js.map +1 -1
- package/lib/test/Removal.perf.spec.js +13 -41
- package/lib/test/Removal.perf.spec.js.map +1 -1
- package/lib/test/beastTest.spec.d.ts.map +1 -1
- package/lib/test/beastTest.spec.js +42 -67
- package/lib/test/beastTest.spec.js.map +1 -1
- package/lib/test/client.annotateMarker.spec.js +1 -11
- package/lib/test/client.annotateMarker.spec.js.map +1 -1
- package/lib/test/client.applyMsg.spec.js +14 -14
- package/lib/test/client.applyMsg.spec.js.map +1 -1
- package/lib/test/client.getPosition.spec.js +1 -1
- 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.rollback.spec.js +50 -59
- package/lib/test/client.rollback.spec.js.map +1 -1
- package/lib/test/client.rollbackFarm.spec.js +1 -1
- package/lib/test/client.rollbackFarm.spec.js.map +1 -1
- package/lib/test/client.searchForMarker.spec.js +4 -21
- package/lib/test/client.searchForMarker.spec.js.map +1 -1
- package/lib/test/index.d.ts +2 -2
- 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.deltaCallback.spec.js +15 -60
- package/lib/test/mergeTree.annotate.deltaCallback.spec.js.map +1 -1
- package/lib/test/mergeTree.annotate.spec.js +48 -64
- package/lib/test/mergeTree.annotate.spec.js.map +1 -1
- package/lib/test/mergeTree.insert.deltaCallback.spec.js +10 -63
- package/lib/test/mergeTree.insert.deltaCallback.spec.js.map +1 -1
- package/lib/test/mergeTree.insertingWalk.spec.js +61 -127
- package/lib/test/mergeTree.insertingWalk.spec.js.map +1 -1
- package/lib/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +13 -94
- package/lib/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -1
- package/lib/test/mergeTree.markRangeRemoved.spec.js +10 -7
- package/lib/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
- package/lib/test/mergeTree.walk.spec.js +2 -14
- package/lib/test/mergeTree.walk.spec.js.map +1 -1
- package/lib/test/mergeTreeOperationRunner.js +3 -3
- package/lib/test/mergeTreeOperationRunner.js.map +1 -1
- package/lib/test/obliterate.concurrent.spec.js +18 -23
- package/lib/test/obliterate.concurrent.spec.js.map +1 -1
- package/lib/test/obliterate.partialLength.spec.js +167 -137
- package/lib/test/obliterate.partialLength.spec.js.map +1 -1
- package/lib/test/obliterate.spec.js +17 -127
- package/lib/test/obliterate.spec.js.map +1 -1
- package/lib/test/partialLength.spec.js +29 -197
- package/lib/test/partialLength.spec.js.map +1 -1
- package/lib/test/perspective.spec.js +34 -0
- package/lib/test/perspective.spec.js.map +1 -1
- package/lib/test/propertyManager.spec.js +2 -2
- package/lib/test/propertyManager.spec.js.map +1 -1
- package/lib/test/resetPendingSegmentsToOp.spec.js +0 -2
- package/lib/test/resetPendingSegmentsToOp.spec.js.map +1 -1
- package/lib/test/segmentGroupCollection.spec.js +10 -4
- package/lib/test/segmentGroupCollection.spec.js.map +1 -1
- package/lib/test/testClient.d.ts +1 -0
- package/lib/test/testClient.d.ts.map +1 -1
- package/lib/test/testClient.js +18 -28
- package/lib/test/testClient.js.map +1 -1
- package/lib/test/testClientLogger.d.ts.map +1 -1
- package/lib/test/testClientLogger.js +3 -10
- package/lib/test/testClientLogger.js.map +1 -1
- package/lib/test/testServer.d.ts +2 -1
- package/lib/test/testServer.d.ts.map +1 -1
- package/lib/test/testServer.js +7 -5
- package/lib/test/testServer.js.map +1 -1
- package/lib/test/testUtils.d.ts +36 -56
- package/lib/test/testUtils.d.ts.map +1 -1
- package/lib/test/testUtils.js +66 -48
- 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 +6 -3
- package/lib/test/text.js.map +1 -1
- package/lib/textSegment.d.ts +0 -6
- package/lib/textSegment.d.ts.map +1 -1
- package/lib/textSegment.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/lib/zamboni.d.ts.map +1 -1
- package/lib/zamboni.js +32 -28
- package/lib/zamboni.js.map +1 -1
- package/package.json +17 -20
- package/src/MergeTreeTextHelper.ts +17 -12
- package/src/client.ts +141 -197
- package/src/endOfTreeSegment.ts +11 -8
- package/src/index.ts +4 -3
- package/src/mergeTree.ts +482 -633
- package/src/mergeTreeDeltaCallback.ts +4 -8
- package/src/mergeTreeNodes.ts +66 -45
- package/src/partialLengths.ts +181 -137
- package/src/perspective.ts +17 -95
- package/src/revertibles.ts +2 -7
- package/src/segmentInfos.ts +48 -141
- package/src/segmentPropertiesManager.ts +2 -16
- package/src/snapshotLoader.ts +62 -30
- package/src/snapshotV1.ts +36 -28
- package/src/snapshotlegacy.ts +7 -16
- package/src/stamps.ts +1 -1
- package/src/textSegment.ts +0 -13
- package/src/zamboni.ts +38 -32
- package/tsconfig.json +1 -0
- package/prettier.config.cjs +0 -8
package/dist/mergeTree.js
CHANGED
|
@@ -3,8 +3,31 @@
|
|
|
3
3
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
23
|
+
if (mod && mod.__esModule) return mod;
|
|
24
|
+
var result = {};
|
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
26
|
+
__setModuleDefault(result, mod);
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
6
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.MergeTree = exports.getSlideToSegoff = exports.findRootMergeBlock = exports.errorIfOptionNotTrue = void 0;
|
|
30
|
+
exports.MergeTree = exports.getSlideToSegoff = exports.findRootMergeBlock = exports.errorIfOptionNotTrue = exports.isRemovedAndAcked = void 0;
|
|
8
31
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
9
32
|
/* eslint-disable no-bitwise */
|
|
10
33
|
const internal_1 = require("@fluidframework/core-utils/internal");
|
|
@@ -27,76 +50,71 @@ const segmentInfos_js_1 = require("./segmentInfos.js");
|
|
|
27
50
|
const segmentPropertiesManager_js_1 = require("./segmentPropertiesManager.js");
|
|
28
51
|
const sequencePlace_js_1 = require("./sequencePlace.js");
|
|
29
52
|
const sortedSegmentSet_js_1 = require("./sortedSegmentSet.js");
|
|
53
|
+
const opstampUtils = __importStar(require("./stamps.js"));
|
|
30
54
|
const zamboni_js_1 = require("./zamboni.js");
|
|
31
55
|
function isRemovedAndAcked(segment) {
|
|
32
56
|
const removalInfo = (0, segmentInfos_js_1.toRemovalInfo)(segment);
|
|
33
|
-
return removalInfo !== undefined && removalInfo.
|
|
34
|
-
}
|
|
35
|
-
function isMovedAndAcked(segment) {
|
|
36
|
-
const moveInfo = (0, segmentInfos_js_1.toMoveInfo)(segment);
|
|
37
|
-
return moveInfo !== undefined && moveInfo.movedSeq !== constants_js_1.UnassignedSequenceNumber;
|
|
38
|
-
}
|
|
39
|
-
function isRemovedAndAckedOrMovedAndAcked(segment) {
|
|
40
|
-
return isRemovedAndAcked(segment) || isMovedAndAcked(segment);
|
|
41
|
-
}
|
|
42
|
-
function isRemovedOrMoved(segment) {
|
|
43
|
-
return (0, segmentInfos_js_1.isRemoved)(segment) || (0, segmentInfos_js_1.isMoved)(segment);
|
|
57
|
+
return removalInfo !== undefined && opstampUtils.isAcked(removalInfo.removes[0]);
|
|
44
58
|
}
|
|
59
|
+
exports.isRemovedAndAcked = isRemovedAndAcked;
|
|
45
60
|
function nodeTotalLength(mergeTree, node) {
|
|
46
61
|
if (!node.isLeaf()) {
|
|
47
62
|
return node.cachedLength;
|
|
48
63
|
}
|
|
49
|
-
return mergeTree.
|
|
64
|
+
return mergeTree.leafLength(node);
|
|
50
65
|
}
|
|
51
66
|
const LRUSegmentComparer = {
|
|
52
67
|
min: { maxSeq: -2 },
|
|
53
68
|
compare: (a, b) => a.maxSeq - b.maxSeq,
|
|
54
69
|
};
|
|
55
|
-
function ackSegment(segment, segmentGroup, opArgs) {
|
|
70
|
+
function ackSegment(segment, segmentGroup, opArgs, stamp) {
|
|
56
71
|
const currentSegmentGroup = segment.segmentGroups?.dequeue();
|
|
57
72
|
(0, internal_1.assert)(currentSegmentGroup === segmentGroup, 0x043 /* "On ack, unexpected segmentGroup!" */);
|
|
58
73
|
(0, internal_1.assert)(opArgs.sequencedMessage !== undefined, 0xa6e /* must have sequencedMessage */);
|
|
59
74
|
const { op, sequencedMessage: { sequenceNumber, minimumSequenceNumber }, } = opArgs;
|
|
75
|
+
let allowIncrementalPartialLengthsUpdate = true;
|
|
60
76
|
switch (op.type) {
|
|
61
77
|
case ops_js_1.MergeTreeDeltaType.ANNOTATE: {
|
|
62
78
|
(0, internal_1.assert)(!!segment.propertyManager, 0x044 /* "On annotate ack, missing segment property manager!" */);
|
|
63
79
|
segment.propertyManager.ack(sequenceNumber, minimumSequenceNumber, op);
|
|
64
|
-
|
|
80
|
+
break;
|
|
65
81
|
}
|
|
66
82
|
case ops_js_1.MergeTreeDeltaType.INSERT: {
|
|
67
|
-
(0, internal_1.assert)(segment.
|
|
68
|
-
segment.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
(0, segmentInfos_js_1.assertRemoved)(segment);
|
|
74
|
-
segment.localRemovedSeq = undefined;
|
|
75
|
-
if (segment.removedSeq === constants_js_1.UnassignedSequenceNumber) {
|
|
76
|
-
segment.removedSeq = sequenceNumber;
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
return false;
|
|
83
|
+
(0, internal_1.assert)(opstampUtils.isLocal(segment.insert), 0x045 /* "On insert, seq number already assigned!" */);
|
|
84
|
+
segment.insert = {
|
|
85
|
+
...stamp,
|
|
86
|
+
type: "insert",
|
|
87
|
+
};
|
|
88
|
+
break;
|
|
80
89
|
}
|
|
90
|
+
case ops_js_1.MergeTreeDeltaType.REMOVE:
|
|
81
91
|
case ops_js_1.MergeTreeDeltaType.OBLITERATE:
|
|
82
92
|
case ops_js_1.MergeTreeDeltaType.OBLITERATE_SIDED: {
|
|
83
|
-
(0, segmentInfos_js_1.
|
|
84
|
-
const
|
|
85
|
-
(0, internal_1.assert)(
|
|
86
|
-
segment.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
(0, segmentInfos_js_1.assertRemoved)(segment);
|
|
94
|
+
const latestRemove = segment.removes[segment.removes.length - 1];
|
|
95
|
+
(0, internal_1.assert)(opstampUtils.isLocal(latestRemove), 0xb5d /* Expected last remove to be unacked */);
|
|
96
|
+
(0, internal_1.assert)(segment.removes.length === 1 ||
|
|
97
|
+
opstampUtils.isAcked(segment.removes[segment.removes.length - 2]), 0xb5e /* Expected prior remove to be acked */);
|
|
98
|
+
allowIncrementalPartialLengthsUpdate = segment.removes.length === 1;
|
|
99
|
+
const removeStamp = {
|
|
100
|
+
...stamp,
|
|
101
|
+
type: op.type === ops_js_1.MergeTreeDeltaType.REMOVE ? "setRemove" : "sliceRemove",
|
|
102
|
+
};
|
|
103
|
+
segment.removes[segment.removes.length - 1] = removeStamp;
|
|
104
|
+
const { obliterateInfo } = segmentGroup;
|
|
105
|
+
const hasObliterateInfo = obliterateInfo !== undefined;
|
|
106
|
+
const isObliterate = op.type !== ops_js_1.MergeTreeDeltaType.REMOVE;
|
|
107
|
+
(0, internal_1.assert)(hasObliterateInfo === isObliterate, 0xa40 /* must have obliterate info */);
|
|
108
|
+
if (hasObliterateInfo) {
|
|
109
|
+
obliterateInfo.stamp = removeStamp;
|
|
93
110
|
}
|
|
94
|
-
|
|
111
|
+
break;
|
|
95
112
|
}
|
|
96
113
|
default: {
|
|
97
114
|
throw new Error(`${op.type} is in unrecognized operation type`);
|
|
98
115
|
}
|
|
99
116
|
}
|
|
117
|
+
return allowIncrementalPartialLengthsUpdate;
|
|
100
118
|
}
|
|
101
119
|
function errorIfOptionNotTrue(options, option) {
|
|
102
120
|
if (options?.[option] !== true) {
|
|
@@ -129,9 +147,7 @@ exports.findRootMergeBlock = findRootMergeBlock;
|
|
|
129
147
|
* SlideOnRemove references is removed.
|
|
130
148
|
*/
|
|
131
149
|
function getSlideToSegment(segment, slidingPreference = localReference_js_1.SlidingPreference.FORWARD, cache, useNewSlidingBehavior = false) {
|
|
132
|
-
if (!segment ||
|
|
133
|
-
!isRemovedAndAckedOrMovedAndAcked(segment) ||
|
|
134
|
-
segment.endpointType !== undefined) {
|
|
150
|
+
if (!segment || !isRemovedAndAcked(segment) || segment.endpointType !== undefined) {
|
|
135
151
|
return [segment, undefined];
|
|
136
152
|
}
|
|
137
153
|
const cachedSegment = cache?.get(segment);
|
|
@@ -141,13 +157,12 @@ function getSlideToSegment(segment, slidingPreference = localReference_js_1.Slid
|
|
|
141
157
|
const result = {};
|
|
142
158
|
cache?.set(segment, result);
|
|
143
159
|
const goFurtherToFindSlideToSegment = (seg) => {
|
|
144
|
-
if (seg.
|
|
160
|
+
if (opstampUtils.isAcked(seg.insert) && !isRemovedAndAcked(seg)) {
|
|
145
161
|
result.seg = seg;
|
|
146
162
|
return false;
|
|
147
163
|
}
|
|
148
164
|
if (cache !== undefined &&
|
|
149
|
-
(
|
|
150
|
-
(0, segmentInfos_js_1.toMoveInfo)(seg)?.movedSeq === (0, segmentInfos_js_1.toMoveInfo)(segment)?.movedSeq)) {
|
|
165
|
+
(0, segmentInfos_js_1.toRemovalInfo)(seg)?.removes[0].seq === (0, segmentInfos_js_1.toRemovalInfo)(segment)?.removes[0].seq) {
|
|
151
166
|
cache.set(seg, result);
|
|
152
167
|
}
|
|
153
168
|
return true;
|
|
@@ -214,10 +229,10 @@ class Obliterates {
|
|
|
214
229
|
constructor(mergeTree) {
|
|
215
230
|
this.mergeTree = mergeTree;
|
|
216
231
|
/**
|
|
217
|
-
* Array containing the all
|
|
232
|
+
* Array containing the all obliterate operations within the
|
|
218
233
|
* collab window.
|
|
219
234
|
*
|
|
220
|
-
* The
|
|
235
|
+
* The obliterates are stored in sequence order which accelerates clean up in setMinSeq
|
|
221
236
|
*
|
|
222
237
|
* See https://github.com/microsoft/FluidFramework/blob/main/packages/dds/merge-tree/docs/Obliterate.md#remote-perspective
|
|
223
238
|
* for additional context
|
|
@@ -232,7 +247,7 @@ class Obliterates {
|
|
|
232
247
|
}
|
|
233
248
|
setMinSeq(minSeq) {
|
|
234
249
|
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
|
235
|
-
while (!this.seqOrdered.empty && this.seqOrdered.first?.data.seq <= minSeq) {
|
|
250
|
+
while (!this.seqOrdered.empty && this.seqOrdered.first?.data.stamp.seq <= minSeq) {
|
|
236
251
|
const ob = this.seqOrdered.shift();
|
|
237
252
|
this.startOrdered.remove(ob.data.start);
|
|
238
253
|
this.mergeTree.removeLocalReferencePosition(ob.data.start);
|
|
@@ -240,7 +255,7 @@ class Obliterates {
|
|
|
240
255
|
}
|
|
241
256
|
}
|
|
242
257
|
addOrUpdate(obliterateInfo) {
|
|
243
|
-
const { seq, start } = obliterateInfo;
|
|
258
|
+
const { stamp: { seq }, start, } = obliterateInfo;
|
|
244
259
|
if (seq !== constants_js_1.UnassignedSequenceNumber) {
|
|
245
260
|
this.seqOrdered.push(obliterateInfo);
|
|
246
261
|
}
|
|
@@ -272,12 +287,14 @@ class Obliterates {
|
|
|
272
287
|
* @internal
|
|
273
288
|
*/
|
|
274
289
|
class MergeTree {
|
|
290
|
+
get localPerspective() {
|
|
291
|
+
return this.collabWindow.localPerspective;
|
|
292
|
+
}
|
|
275
293
|
constructor(options) {
|
|
276
294
|
this.options = options;
|
|
277
295
|
this.collabWindow = new mergeTreeNodes_js_1.CollaborationWindow();
|
|
278
296
|
this.pendingSegments = new index_js_1.DoublyLinkedList();
|
|
279
297
|
this.segmentsToScour = new internal_1.Heap(LRUSegmentComparer);
|
|
280
|
-
this.localPerspective = new perspective_js_1.LocalDefaultPerspective(this.collabWindow.clientId);
|
|
281
298
|
/**
|
|
282
299
|
* Whether or not all blocks in the mergeTree currently have information about local partial lengths computed.
|
|
283
300
|
* This information is only necessary on reconnect, and otherwise costly to bookkeep.
|
|
@@ -332,61 +349,20 @@ class MergeTree {
|
|
|
332
349
|
return block;
|
|
333
350
|
}
|
|
334
351
|
/**
|
|
335
|
-
* Compute the net length of this segment from
|
|
336
|
-
* @
|
|
337
|
-
*
|
|
338
|
-
* default is to consider the local client's current perspective. Only local sequence
|
|
339
|
-
* numbers corresponding to un-acked operations give valid results.
|
|
352
|
+
* Compute the net length of this segment leaf from some perspective.
|
|
353
|
+
* @returns - Undefined if the segment has been removed and its removal is common knowledge to all collaborators (and therefore
|
|
354
|
+
* may not even be present on clients that have loaded from a summary beyond this point). Otherwise, the length of the segment.
|
|
340
355
|
*/
|
|
341
|
-
|
|
356
|
+
leafLength(segment, perspective = this.localPerspective) {
|
|
342
357
|
const removalInfo = (0, segmentInfos_js_1.toRemovalInfo)(segment);
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
// this segment removed and outside the collab window which means it is zamboni eligible
|
|
351
|
-
// this also means the segment could not exist, so we should not consider it
|
|
352
|
-
// when making decisions about conflict resolutions
|
|
353
|
-
return undefined;
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
return segment.cachedLength;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
(0, internal_1.assert)(refSeq !== undefined, 0x398 /* localSeq provided for local length without refSeq */);
|
|
360
|
-
(0, internal_1.assert)(segment.seq !== undefined, 0x399 /* segment with no seq in mergeTree */);
|
|
361
|
-
const { seq } = segment;
|
|
362
|
-
const { removedSeq, localRemovedSeq } = removalInfo ?? {};
|
|
363
|
-
const { movedSeq, localMovedSeq } = moveInfo ?? {};
|
|
364
|
-
if (seq === constants_js_1.UnassignedSequenceNumber) {
|
|
365
|
-
(0, internal_1.assert)(segment.localSeq !== undefined, 0x39a /* unacked segment with undefined localSeq */);
|
|
366
|
-
// inserted locally, still un-acked
|
|
367
|
-
if (segment.localSeq > localSeq ||
|
|
368
|
-
(localRemovedSeq !== undefined && localRemovedSeq <= localSeq) ||
|
|
369
|
-
(localMovedSeq !== undefined && localMovedSeq <= localSeq)) {
|
|
370
|
-
return 0;
|
|
371
|
-
}
|
|
372
|
-
const { cachedLength } = segment;
|
|
373
|
-
return cachedLength;
|
|
374
|
-
}
|
|
375
|
-
else {
|
|
376
|
-
// inserted remotely
|
|
377
|
-
if (seq > refSeq ||
|
|
378
|
-
(removedSeq !== undefined &&
|
|
379
|
-
removedSeq !== constants_js_1.UnassignedSequenceNumber &&
|
|
380
|
-
removedSeq <= refSeq) ||
|
|
381
|
-
(movedSeq !== undefined &&
|
|
382
|
-
movedSeq !== constants_js_1.UnassignedSequenceNumber &&
|
|
383
|
-
movedSeq <= refSeq) ||
|
|
384
|
-
(localRemovedSeq !== undefined && localRemovedSeq <= localSeq) ||
|
|
385
|
-
(localMovedSeq !== undefined && localMovedSeq <= localSeq)) {
|
|
386
|
-
return 0;
|
|
387
|
-
}
|
|
388
|
-
return segment.cachedLength;
|
|
358
|
+
if (removalInfo &&
|
|
359
|
+
(0, mergeTreeNodes_js_1.getMinSeqPerspective)(this.collabWindow).hasOccurred(removalInfo.removes[0])) {
|
|
360
|
+
// this segment's removal has already moved outside the collab window which means it is zamboni eligible
|
|
361
|
+
// this also means the segment could be completely absent from other client's in-memory merge trees,
|
|
362
|
+
// so we should not consider it when making decisions about conflict resolutions
|
|
363
|
+
return undefined;
|
|
389
364
|
}
|
|
365
|
+
return perspective.isSegmentPresent(segment) ? segment.cachedLength : 0;
|
|
390
366
|
}
|
|
391
367
|
unlinkMarker(marker) {
|
|
392
368
|
const id = marker.getId();
|
|
@@ -444,6 +420,7 @@ class MergeTree {
|
|
|
444
420
|
this.collabWindow.minSeq = minSeq;
|
|
445
421
|
this.collabWindow.collaborating = true;
|
|
446
422
|
this.collabWindow.currentSeq = currentSeq;
|
|
423
|
+
this.collabWindow.localPerspective = new perspective_js_1.LocalDefaultPerspective(localClientId);
|
|
447
424
|
this.nodeUpdateLengthNewStructure(this.root, true);
|
|
448
425
|
}
|
|
449
426
|
addToLRUSet(leaf, seq) {
|
|
@@ -456,8 +433,8 @@ class MergeTree {
|
|
|
456
433
|
this.segmentsToScour.add({ segment: leaf, maxSeq: seq });
|
|
457
434
|
}
|
|
458
435
|
}
|
|
459
|
-
getLength(
|
|
460
|
-
return this.
|
|
436
|
+
getLength(perspective) {
|
|
437
|
+
return this.nodeLength(this.root, perspective) ?? 0;
|
|
461
438
|
}
|
|
462
439
|
/**
|
|
463
440
|
* Returns the current length of the MergeTree for the local client.
|
|
@@ -465,7 +442,7 @@ class MergeTree {
|
|
|
465
442
|
get length() {
|
|
466
443
|
return this.root.cachedLength;
|
|
467
444
|
}
|
|
468
|
-
getPosition(node,
|
|
445
|
+
getPosition(node, perspective) {
|
|
469
446
|
if (node.isLeaf() && node.endpointType === "start") {
|
|
470
447
|
return 0;
|
|
471
448
|
}
|
|
@@ -479,15 +456,16 @@ class MergeTree {
|
|
|
479
456
|
if ((!!prevParent && child === prevParent) || child === node) {
|
|
480
457
|
break;
|
|
481
458
|
}
|
|
482
|
-
totalOffset += this.nodeLength(child,
|
|
459
|
+
totalOffset += this.nodeLength(child, perspective) ?? 0;
|
|
483
460
|
}
|
|
484
461
|
prevParent = parent;
|
|
485
462
|
parent = parent.parent;
|
|
486
463
|
}
|
|
487
464
|
return totalOffset;
|
|
488
465
|
}
|
|
489
|
-
getContainingSegment(pos,
|
|
490
|
-
(0, internal_1.assert)(localSeq === undefined ||
|
|
466
|
+
getContainingSegment(pos, perspective) {
|
|
467
|
+
(0, internal_1.assert)(perspective.localSeq === undefined ||
|
|
468
|
+
perspective.clientId === this.collabWindow.clientId, 0x39b /* localSeq provided for non-local client */);
|
|
491
469
|
let segment;
|
|
492
470
|
let offset;
|
|
493
471
|
const leaf = (leafSeg, _, start) => {
|
|
@@ -495,7 +473,7 @@ class MergeTree {
|
|
|
495
473
|
offset = start;
|
|
496
474
|
return false;
|
|
497
475
|
};
|
|
498
|
-
this.nodeMap(
|
|
476
|
+
this.nodeMap(perspective, leaf, undefined, pos, pos + 1);
|
|
499
477
|
return { segment, offset };
|
|
500
478
|
}
|
|
501
479
|
/**
|
|
@@ -588,7 +566,7 @@ class MergeTree {
|
|
|
588
566
|
const forwardSegmentCache = new Map();
|
|
589
567
|
const backwardSegmentCache = new Map();
|
|
590
568
|
for (const segment of segments) {
|
|
591
|
-
(0, internal_1.assert)(
|
|
569
|
+
(0, internal_1.assert)(isRemovedAndAcked(segment), 0x2f1 /* slideReferences from a segment which has not been removed and acked */);
|
|
592
570
|
if (segment.localRefs === undefined || segment.localRefs.empty) {
|
|
593
571
|
continue;
|
|
594
572
|
}
|
|
@@ -608,11 +586,6 @@ class MergeTree {
|
|
|
608
586
|
slideGroup(currentForwardSlideDestination, currentForwardSlideIsForward, currentForwardSlideGroup, forwardPred, currentForwardMaybeEndpoint);
|
|
609
587
|
slideGroup(currentBackwardSlideDestination, currentBackwardSlideIsForward, currentBackwardSlideGroup, backwardPred, currentBackwardMaybeEndpoint);
|
|
610
588
|
}
|
|
611
|
-
blockLength(node, refSeq, clientId) {
|
|
612
|
-
return this.collabWindow.collaborating && clientId !== this.collabWindow.clientId
|
|
613
|
-
? node.partialLengths.getPartialLength(refSeq, clientId)
|
|
614
|
-
: (node.cachedLength ?? 0);
|
|
615
|
-
}
|
|
616
589
|
/**
|
|
617
590
|
* Compute local partial length information
|
|
618
591
|
*
|
|
@@ -630,62 +603,27 @@ class MergeTree {
|
|
|
630
603
|
this.root.partialLengths = partialLengths_js_1.PartialSequenceLengths.combine(this.root, rebaseCollabWindow, true, true);
|
|
631
604
|
this.localPartialsComputed = true;
|
|
632
605
|
}
|
|
633
|
-
nodeLength(node,
|
|
634
|
-
if (
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
}
|
|
655
|
-
else {
|
|
656
|
-
// Sequence number within window
|
|
657
|
-
if (node.isLeaf()) {
|
|
658
|
-
const segment = node;
|
|
659
|
-
const removalInfo = (0, segmentInfos_js_1.toRemovalInfo)(segment);
|
|
660
|
-
const moveInfo = (0, segmentInfos_js_1.toMoveInfo)(segment);
|
|
661
|
-
if (removalInfo !== undefined) {
|
|
662
|
-
if ((0, mergeTreeNodes_js_1.seqLTE)(removalInfo.removedSeq, this.collabWindow.minSeq)) {
|
|
663
|
-
return undefined;
|
|
664
|
-
}
|
|
665
|
-
if ((0, mergeTreeNodes_js_1.seqLTE)(removalInfo.removedSeq, refSeq) ||
|
|
666
|
-
removalInfo.removedClientIds.includes(clientId)) {
|
|
667
|
-
return 0;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
if (moveInfo !== undefined) {
|
|
671
|
-
if ((0, mergeTreeNodes_js_1.seqLTE)(moveInfo.movedSeq, this.collabWindow.minSeq)) {
|
|
672
|
-
return undefined;
|
|
673
|
-
}
|
|
674
|
-
if ((0, mergeTreeNodes_js_1.seqLTE)(moveInfo.movedSeq, refSeq) ||
|
|
675
|
-
moveInfo.movedClientIds.includes(clientId)) {
|
|
676
|
-
return 0;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return (0, mergeTreeNodes_js_1.seqLTE)(node.seq ?? 0, refSeq) || segment.clientId === clientId
|
|
680
|
-
? segment.cachedLength
|
|
681
|
-
: 0;
|
|
682
|
-
}
|
|
683
|
-
else {
|
|
684
|
-
const partialLen = node.partialLengths.getPartialLength(refSeq, clientId);
|
|
685
|
-
partialLengths_js_1.PartialSequenceLengths.options.verifyExpected?.(this, node, refSeq, clientId);
|
|
686
|
-
return partialLen;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
606
|
+
nodeLength(node, perspective) {
|
|
607
|
+
if (node.isLeaf()) {
|
|
608
|
+
return this.leafLength(node, perspective);
|
|
609
|
+
}
|
|
610
|
+
const { refSeq, clientId, localSeq } = perspective;
|
|
611
|
+
const isLocalPerspective = !this.collabWindow.collaborating || this.collabWindow.clientId === clientId;
|
|
612
|
+
if (isLocalPerspective &&
|
|
613
|
+
(localSeq === undefined ||
|
|
614
|
+
(localSeq === this.collabWindow.localSeq && refSeq >= this.collabWindow.currentSeq))) {
|
|
615
|
+
// All changes are visible. Small note on why we allow refSeq >= this.collabWindow.currentSeq rather than just equality:
|
|
616
|
+
// merge-tree eventing occurs before the collab window is updated to account for whatever op it is processing, and we want
|
|
617
|
+
// to support resolving positions from within the event handler which account for that op. e.g. undo-redo relies on this
|
|
618
|
+
// behavior with local references.
|
|
619
|
+
return node.cachedLength;
|
|
620
|
+
}
|
|
621
|
+
if (localSeq !== undefined) {
|
|
622
|
+
this.computeLocalPartials(refSeq);
|
|
623
|
+
}
|
|
624
|
+
const length = node.partialLengths.getPartialLength(refSeq, clientId, localSeq);
|
|
625
|
+
partialLengths_js_1.PartialSequenceLengths.options.verifyExpected?.(this, node, refSeq, clientId, localSeq);
|
|
626
|
+
return length;
|
|
689
627
|
}
|
|
690
628
|
setMinSeq(minSeq) {
|
|
691
629
|
(0, internal_1.assert)(minSeq <= this.collabWindow.currentSeq, 0x04e /* "Trying to set minSeq above currentSeq of collab window!" */);
|
|
@@ -711,44 +649,61 @@ class MergeTree {
|
|
|
711
649
|
referencePositionToLocalPosition(refPos,
|
|
712
650
|
// Note: this is not `this.collabWindow.currentSeq` because we want to support resolving local reference positions to positions
|
|
713
651
|
// from within event handlers, and the collab window's sequence numbers are not updated in time in all of those cases.
|
|
714
|
-
refSeq = Number.MAX_SAFE_INTEGER, clientId = this.collabWindow.clientId, localSeq =
|
|
652
|
+
refSeq = Number.MAX_SAFE_INTEGER, clientId = this.collabWindow.clientId, localSeq = undefined) {
|
|
715
653
|
const perspective = clientId === this.collabWindow.clientId
|
|
716
654
|
? localSeq === undefined
|
|
717
655
|
? this.localPerspective
|
|
718
656
|
: new perspective_js_1.LocalReconnectingPerspective(refSeq, clientId, localSeq)
|
|
719
657
|
: new perspective_js_1.PriorPerspective(refSeq, clientId);
|
|
720
658
|
const seg = refPos.getSegment();
|
|
721
|
-
if (!(0, mergeTreeNodes_js_1.isSegmentLeaf)(seg)) {
|
|
659
|
+
if (seg === undefined || !(0, mergeTreeNodes_js_1.isSegmentLeaf)(seg)) {
|
|
722
660
|
// We have no idea where this reference is, because it refers to a segment which is not in the tree.
|
|
723
661
|
return referencePositions_js_1.DetachedReferencePosition;
|
|
724
662
|
}
|
|
725
663
|
if (refPos.isLeaf()) {
|
|
726
|
-
return this.getPosition(seg,
|
|
664
|
+
return this.getPosition(seg, perspective);
|
|
727
665
|
}
|
|
728
666
|
if ((0, referencePositions_js_1.refTypeIncludesFlag)(refPos, ops_js_1.ReferenceType.Transient) || seg.localRefs?.has(refPos)) {
|
|
729
667
|
if (seg !== this.startOfTree &&
|
|
730
668
|
seg !== this.endOfTree &&
|
|
731
669
|
!perspective.isSegmentPresent(seg)) {
|
|
732
670
|
const forward = refPos.slidingPreference === localReference_js_1.SlidingPreference.FORWARD;
|
|
733
|
-
const moveInfo = (0, segmentInfos_js_1.toMoveInfo)(seg);
|
|
734
671
|
const removeInfo = (0, segmentInfos_js_1.toRemovalInfo)(seg);
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const slideLocalSeq = moveInfo?.localMovedSeq ?? removeInfo?.localRemovedSeq;
|
|
741
|
-
const slidePerspective = slideLocalSeq === undefined
|
|
672
|
+
const firstRemove = removeInfo?.removes[0];
|
|
673
|
+
const slideSeq = firstRemove !== undefined && opstampUtils.isAcked(firstRemove)
|
|
674
|
+
? firstRemove.seq
|
|
675
|
+
: refSeq;
|
|
676
|
+
const slidePerspective = firstRemove?.localSeq === undefined
|
|
742
677
|
? new perspective_js_1.PriorPerspective(slideSeq, this.collabWindow.clientId)
|
|
743
|
-
: new perspective_js_1.LocalReconnectingPerspective(slideSeq, this.collabWindow.clientId,
|
|
744
|
-
const slidSegment =
|
|
745
|
-
return (this.getPosition(slidSegment,
|
|
678
|
+
: new perspective_js_1.LocalReconnectingPerspective(slideSeq, this.collabWindow.clientId, firstRemove.localSeq);
|
|
679
|
+
const slidSegment = this.nextSegment(slidePerspective, seg, forward);
|
|
680
|
+
return (this.getPosition(slidSegment, perspective) +
|
|
746
681
|
(forward ? 0 : slidSegment.cachedLength === 0 ? 0 : slidSegment.cachedLength - 1));
|
|
747
682
|
}
|
|
748
|
-
return this.getPosition(seg,
|
|
683
|
+
return this.getPosition(seg, perspective) + refPos.getOffset();
|
|
749
684
|
}
|
|
750
685
|
return referencePositions_js_1.DetachedReferencePosition;
|
|
751
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* Returns the immediately adjacent segment in the specified direction from this perspective.
|
|
689
|
+
* There may actually be multiple segments between the given segment and the returned segment,
|
|
690
|
+
* but they were either inserted after this perspective, or have been removed before this perspective.
|
|
691
|
+
*
|
|
692
|
+
* @param segment - The segment to start from.
|
|
693
|
+
* @param forward - The direction to search.
|
|
694
|
+
* @returns the next segment in the specified direction, or the start or end of the tree if there is no next segment.
|
|
695
|
+
*/
|
|
696
|
+
nextSegment(perspective, segment, forward = true) {
|
|
697
|
+
let next;
|
|
698
|
+
const action = (seg) => {
|
|
699
|
+
if (perspective.isSegmentPresent(seg)) {
|
|
700
|
+
next = seg;
|
|
701
|
+
return mergeTreeNodeWalk_js_1.LeafAction.Exit;
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
(forward ? mergeTreeNodeWalk_js_1.forwardExcursion : mergeTreeNodeWalk_js_1.backwardExcursion)(segment, action);
|
|
705
|
+
return next ?? (forward ? this.endOfTree : this.startOfTree);
|
|
706
|
+
}
|
|
752
707
|
/**
|
|
753
708
|
* Finds the nearest reference with ReferenceType.Tile to `startPos` in the direction dictated by `forwards`.
|
|
754
709
|
* Uses depthFirstNodeWalk in addition to block-accelerated functionality. The search position will be included in
|
|
@@ -761,9 +716,9 @@ class MergeTree {
|
|
|
761
716
|
* @param markerLabel - Label of the marker to search for
|
|
762
717
|
* @param forwards - Whether the string should be searched in the forward or backward direction
|
|
763
718
|
*/
|
|
764
|
-
searchForMarker(startPos,
|
|
719
|
+
searchForMarker(startPos, markerLabel, forwards = true) {
|
|
765
720
|
let foundMarker;
|
|
766
|
-
const { segment } = this.getContainingSegment(startPos,
|
|
721
|
+
const { segment } = this.getContainingSegment(startPos, this.localPerspective);
|
|
767
722
|
if (!(0, mergeTreeNodes_js_1.isSegmentLeaf)(segment)) {
|
|
768
723
|
return undefined;
|
|
769
724
|
}
|
|
@@ -787,14 +742,12 @@ class MergeTree {
|
|
|
787
742
|
return foundMarker;
|
|
788
743
|
}
|
|
789
744
|
updateRoot(splitNode) {
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
this.nodeUpdateLengthNewStructure(this.root);
|
|
797
|
-
}
|
|
745
|
+
const newRoot = this.makeBlock(2);
|
|
746
|
+
(0, mergeTreeNodes_js_1.assignChild)(newRoot, this.root, 0, false);
|
|
747
|
+
(0, mergeTreeNodes_js_1.assignChild)(newRoot, splitNode, 1, false);
|
|
748
|
+
this.root = newRoot;
|
|
749
|
+
this.nodeUpdateOrdinals(this.root);
|
|
750
|
+
this.nodeUpdateLengthNewStructure(this.root);
|
|
798
751
|
}
|
|
799
752
|
/**
|
|
800
753
|
* Assign sequence number to existing segment; update partial lengths to reflect the change
|
|
@@ -802,6 +755,10 @@ class MergeTree {
|
|
|
802
755
|
*/
|
|
803
756
|
ackPendingSegment(opArgs) {
|
|
804
757
|
const seq = opArgs.sequencedMessage.sequenceNumber;
|
|
758
|
+
const stamp = {
|
|
759
|
+
seq,
|
|
760
|
+
clientId: this.collabWindow.clientId,
|
|
761
|
+
};
|
|
805
762
|
const pendingSegmentGroup = this.pendingSegments.shift()?.data;
|
|
806
763
|
const nodesToUpdate = [];
|
|
807
764
|
let overwrite = false;
|
|
@@ -809,8 +766,8 @@ class MergeTree {
|
|
|
809
766
|
const deltaSegments = [];
|
|
810
767
|
const overlappingRemoves = [];
|
|
811
768
|
pendingSegmentGroup.segments.map((pendingSegment) => {
|
|
812
|
-
const overlappingRemove = !ackSegment(pendingSegment, pendingSegmentGroup, opArgs);
|
|
813
|
-
overwrite ||= overlappingRemove
|
|
769
|
+
const overlappingRemove = !ackSegment(pendingSegment, pendingSegmentGroup, opArgs, stamp);
|
|
770
|
+
overwrite ||= overlappingRemove;
|
|
814
771
|
overlappingRemoves.push(overlappingRemove);
|
|
815
772
|
if (MergeTree.options.zamboniSegments) {
|
|
816
773
|
this.addToLRUSet(pendingSegment, seq);
|
|
@@ -823,7 +780,7 @@ class MergeTree {
|
|
|
823
780
|
});
|
|
824
781
|
});
|
|
825
782
|
if (pendingSegmentGroup.obliterateInfo !== undefined) {
|
|
826
|
-
pendingSegmentGroup.obliterateInfo.
|
|
783
|
+
pendingSegmentGroup.obliterateInfo.stamp = { type: "sliceRemove", ...stamp };
|
|
827
784
|
this.obliterates.addOrUpdate(pendingSegmentGroup.obliterateInfo);
|
|
828
785
|
}
|
|
829
786
|
// Perform slides after all segments have been acked, so that
|
|
@@ -837,9 +794,8 @@ class MergeTree {
|
|
|
837
794
|
deltaSegments,
|
|
838
795
|
operation: mergeTreeDeltaCallback_js_1.MergeTreeMaintenanceType.ACKNOWLEDGED,
|
|
839
796
|
}, opArgs);
|
|
840
|
-
const clientId = this.collabWindow.clientId;
|
|
841
797
|
for (const node of nodesToUpdate) {
|
|
842
|
-
this.blockUpdatePathLengths(node,
|
|
798
|
+
this.blockUpdatePathLengths(node, stamp, overwrite);
|
|
843
799
|
}
|
|
844
800
|
}
|
|
845
801
|
if (MergeTree.options.zamboniSegments) {
|
|
@@ -873,11 +829,7 @@ class MergeTree {
|
|
|
873
829
|
// TODO: error checking
|
|
874
830
|
getMarkerFromId(id) {
|
|
875
831
|
const marker = this.idToMarker.get(id);
|
|
876
|
-
return marker === undefined ||
|
|
877
|
-
(0, segmentInfos_js_1.isRemoved)(marker) ||
|
|
878
|
-
((0, segmentInfos_js_1.isMoved)(marker) && marker.moveDst === undefined)
|
|
879
|
-
? undefined
|
|
880
|
-
: marker;
|
|
832
|
+
return marker === undefined || (0, segmentInfos_js_1.isRemoved)(marker) ? undefined : marker;
|
|
881
833
|
}
|
|
882
834
|
/**
|
|
883
835
|
* Given a position specified relative to a marker id, lookup the marker
|
|
@@ -886,14 +838,14 @@ class MergeTree {
|
|
|
886
838
|
* @param refseq - The reference sequence number at which to compute the position.
|
|
887
839
|
* @param clientId - The client id with which to compute the position.
|
|
888
840
|
*/
|
|
889
|
-
posFromRelativePos(relativePos,
|
|
841
|
+
posFromRelativePos(relativePos, perspective) {
|
|
890
842
|
let pos = -1;
|
|
891
843
|
let marker;
|
|
892
844
|
if (relativePos.id) {
|
|
893
845
|
marker = this.getMarkerFromId(relativePos.id);
|
|
894
846
|
}
|
|
895
847
|
if ((0, mergeTreeNodes_js_1.isSegmentLeaf)(marker)) {
|
|
896
|
-
pos = this.getPosition(marker,
|
|
848
|
+
pos = this.getPosition(marker, perspective);
|
|
897
849
|
if (relativePos.before) {
|
|
898
850
|
if (relativePos.offset !== undefined) {
|
|
899
851
|
pos -= relativePos.offset;
|
|
@@ -908,14 +860,14 @@ class MergeTree {
|
|
|
908
860
|
}
|
|
909
861
|
return pos;
|
|
910
862
|
}
|
|
911
|
-
insertSegments(pos, segments,
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
this.blockInsert(pos,
|
|
863
|
+
insertSegments(pos, segments, perspective, stampArg, opArgs) {
|
|
864
|
+
const stamp = { ...stampArg, type: "insert" };
|
|
865
|
+
this.ensureIntervalBoundary(pos, perspective);
|
|
866
|
+
this.blockInsert(pos, perspective, stamp, segments);
|
|
915
867
|
// opArgs == undefined => loading snapshot or test code
|
|
916
868
|
if (opArgs !== undefined) {
|
|
917
869
|
const deltaSegments = segments
|
|
918
|
-
.filter((segment) => !(0, segmentInfos_js_1.
|
|
870
|
+
.filter((segment) => !(0, segmentInfos_js_1.isRemoved)(segment))
|
|
919
871
|
.map((segment) => ({ segment }));
|
|
920
872
|
if (deltaSegments.length > 0) {
|
|
921
873
|
this.mergeTreeDeltaCallback?.(opArgs, {
|
|
@@ -926,7 +878,7 @@ class MergeTree {
|
|
|
926
878
|
}
|
|
927
879
|
if (this.collabWindow.collaborating &&
|
|
928
880
|
MergeTree.options.zamboniSegments &&
|
|
929
|
-
|
|
881
|
+
opstampUtils.isAcked(stamp)) {
|
|
930
882
|
(0, zamboni_js_1.zamboniSegments)(this);
|
|
931
883
|
}
|
|
932
884
|
}
|
|
@@ -948,19 +900,19 @@ class MergeTree {
|
|
|
948
900
|
if (remoteClientRefSeq < this.collabWindow.minSeq) {
|
|
949
901
|
return undefined;
|
|
950
902
|
}
|
|
951
|
-
const
|
|
952
|
-
const
|
|
903
|
+
const remotePerspective = new perspective_js_1.PriorPerspective(remoteClientRefSeq, remoteClientId);
|
|
904
|
+
const segmentInfo = this.getContainingSegment(remoteClientPosition, remotePerspective);
|
|
953
905
|
if ((0, mergeTreeNodes_js_1.isSegmentLeaf)(segmentInfo?.segment)) {
|
|
954
|
-
const segmentPosition = this.getPosition(segmentInfo.segment,
|
|
906
|
+
const segmentPosition = this.getPosition(segmentInfo.segment, this.localPerspective);
|
|
955
907
|
return segmentPosition + segmentInfo.offset;
|
|
956
908
|
}
|
|
957
909
|
else {
|
|
958
|
-
if (remoteClientPosition === this.getLength(
|
|
959
|
-
return this.getLength(
|
|
910
|
+
if (remoteClientPosition === this.getLength(remotePerspective)) {
|
|
911
|
+
return this.getLength(this.localPerspective);
|
|
960
912
|
}
|
|
961
913
|
}
|
|
962
914
|
}
|
|
963
|
-
blockInsert(pos,
|
|
915
|
+
blockInsert(pos, perspective, stamp, newSegments) {
|
|
964
916
|
// Keeping this function within the scope of blockInsert for readability.
|
|
965
917
|
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
966
918
|
const continueFrom = (node) => {
|
|
@@ -975,16 +927,16 @@ class MergeTree {
|
|
|
975
927
|
const saveIfLocal = (locSegment) => {
|
|
976
928
|
// Save segment so we can assign sequence number when acked by server
|
|
977
929
|
if (this.collabWindow.collaborating) {
|
|
978
|
-
if (locSegment.
|
|
979
|
-
clientId === this.collabWindow.clientId) {
|
|
980
|
-
segmentGroup = this.addToPendingList(locSegment, segmentGroup, localSeq);
|
|
930
|
+
if (opstampUtils.isLocal(locSegment.insert) &&
|
|
931
|
+
stamp.clientId === this.collabWindow.clientId) {
|
|
932
|
+
segmentGroup = this.addToPendingList(locSegment, segmentGroup, stamp.localSeq);
|
|
981
933
|
}
|
|
982
934
|
// LocSegment.seq === 0 when coming from SharedSegmentSequence.loadBody()
|
|
983
935
|
// In all other cases this has to be true (checked by addToLRUSet):
|
|
984
936
|
// locSegment.seq > this.collabWindow.currentSeq
|
|
985
|
-
else if (
|
|
986
|
-
|
|
987
|
-
this.addToLRUSet(locSegment, locSegment.seq);
|
|
937
|
+
else if (MergeTree.options.zamboniSegments &&
|
|
938
|
+
opstampUtils.greaterThan(locSegment.insert, (0, mergeTreeNodes_js_1.getMinSeqStamp)(this.collabWindow))) {
|
|
939
|
+
this.addToLRUSet(locSegment, locSegment.insert.seq);
|
|
988
940
|
}
|
|
989
941
|
}
|
|
990
942
|
};
|
|
@@ -1000,85 +952,71 @@ class MergeTree {
|
|
|
1000
952
|
}
|
|
1001
953
|
return segmentChanges;
|
|
1002
954
|
};
|
|
1003
|
-
const insertInfo = {
|
|
1004
|
-
clientId,
|
|
1005
|
-
seq,
|
|
1006
|
-
localSeq,
|
|
1007
|
-
};
|
|
1008
955
|
// TODO: build tree from segs and insert all at once
|
|
1009
956
|
let insertPos = pos;
|
|
1010
957
|
for (const newSegment of newSegments
|
|
1011
958
|
.filter((s) => s.cachedLength > 0)
|
|
1012
|
-
.map((s) => (0, segmentInfos_js_1.overwriteInfo)(s,
|
|
959
|
+
.map((s) => (0, segmentInfos_js_1.overwriteInfo)(s, { insert: stamp }))) {
|
|
1013
960
|
if (mergeTreeNodes_js_1.Marker.is(newSegment)) {
|
|
1014
961
|
const markerId = newSegment.getId();
|
|
1015
962
|
if (markerId) {
|
|
1016
963
|
this.idToMarker.set(markerId, newSegment);
|
|
1017
964
|
}
|
|
1018
965
|
}
|
|
1019
|
-
|
|
966
|
+
this.insertingWalk(insertPos, perspective, stamp, {
|
|
1020
967
|
leaf: onLeaf,
|
|
1021
968
|
candidateSegment: newSegment,
|
|
1022
969
|
continuePredicate: continueFrom,
|
|
1023
970
|
});
|
|
1024
971
|
if (!(0, mergeTreeNodes_js_1.isSegmentLeaf)(newSegment)) {
|
|
1025
972
|
// Indicates an attempt to insert past the end of the merge-tree's content.
|
|
1026
|
-
const errorConstructor = localSeq === undefined ? internal_2.DataProcessingError : internal_2.UsageError;
|
|
973
|
+
const errorConstructor = stamp.localSeq === undefined ? internal_2.DataProcessingError : internal_2.UsageError;
|
|
1027
974
|
throw new errorConstructor("MergeTree insert failed", {
|
|
1028
975
|
currentSeq: this.collabWindow.currentSeq,
|
|
1029
976
|
minSeq: this.collabWindow.minSeq,
|
|
1030
|
-
segSeq:
|
|
977
|
+
segSeq: stamp.seq,
|
|
1031
978
|
});
|
|
1032
979
|
}
|
|
1033
|
-
this.updateRoot(splitNode);
|
|
1034
980
|
insertPos += newSegment.cachedLength;
|
|
1035
981
|
if (!this.options?.mergeTreeEnableObliterate || this.obliterates.empty()) {
|
|
1036
982
|
saveIfLocal(newSegment);
|
|
1037
983
|
continue;
|
|
1038
984
|
}
|
|
985
|
+
const overlappingAckedObliterates = [];
|
|
1039
986
|
let oldest;
|
|
1040
|
-
let normalizedOldestSeq = 0;
|
|
1041
987
|
let newest;
|
|
1042
|
-
let normalizedNewestSeq = 0;
|
|
1043
|
-
const movedClientIds = [];
|
|
1044
|
-
const movedSeqs = [];
|
|
1045
988
|
let newestAcked;
|
|
1046
989
|
let oldestUnacked;
|
|
990
|
+
const refSeqStamp = {
|
|
991
|
+
seq: perspective.refSeq,
|
|
992
|
+
clientId: stamp.clientId,
|
|
993
|
+
localSeq: stamp.localSeq,
|
|
994
|
+
};
|
|
1047
995
|
for (const ob of this.obliterates.findOverlapping(newSegment)) {
|
|
1048
|
-
|
|
1049
|
-
// but is still comparable to remote seqs to keep the checks below easy
|
|
1050
|
-
// REMOTE SEQUENCE NUMBERS LOCAL SEQUENCE NUMBERS
|
|
1051
|
-
// [0, 1, 2, 3, ..., 100, ..., 1000, ..., (MAX - MaxLocalSeq), L1, L2, L3, L4, ..., L100, ..., L1000, ...(MAX)]
|
|
1052
|
-
const normalizedObSeq = ob.seq === constants_js_1.UnassignedSequenceNumber
|
|
1053
|
-
? Number.MAX_SAFE_INTEGER - this.collabWindow.localSeq + ob.localSeq
|
|
1054
|
-
: ob.seq;
|
|
1055
|
-
if (normalizedObSeq > refSeq) {
|
|
996
|
+
if (opstampUtils.greaterThan(ob.stamp, refSeqStamp)) {
|
|
1056
997
|
// Any obliterate from the same client that's inserting this segment cannot cause the segment to be marked as
|
|
1057
998
|
// obliterated (since that client must have performed the obliterate before this insertion).
|
|
1058
999
|
// We still need to consider such obliterates when determining the winning obliterate for the insertion point,
|
|
1059
1000
|
// see `obliteratePrecedingInsertion` docs.
|
|
1060
|
-
if (clientId !== ob.clientId) {
|
|
1061
|
-
if (
|
|
1062
|
-
|
|
1063
|
-
oldest = ob;
|
|
1064
|
-
movedClientIds.unshift(ob.clientId);
|
|
1065
|
-
movedSeqs.unshift(ob.seq);
|
|
1001
|
+
if (stamp.clientId !== ob.stamp.clientId) {
|
|
1002
|
+
if (opstampUtils.isAcked(ob.stamp)) {
|
|
1003
|
+
overlappingAckedObliterates.push(ob.stamp);
|
|
1066
1004
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
movedSeqs.push(ob.seq);
|
|
1005
|
+
if (oldest === undefined || opstampUtils.lessThan(ob.stamp, oldest.stamp)) {
|
|
1006
|
+
oldest = ob;
|
|
1070
1007
|
}
|
|
1071
1008
|
}
|
|
1072
|
-
if (newest === undefined ||
|
|
1073
|
-
normalizedNewestSeq = normalizedObSeq;
|
|
1009
|
+
if (newest === undefined || opstampUtils.greaterThan(ob.stamp, newest.stamp)) {
|
|
1074
1010
|
newest = ob;
|
|
1075
1011
|
}
|
|
1076
|
-
if (ob.
|
|
1077
|
-
(newestAcked === undefined ||
|
|
1012
|
+
if (opstampUtils.isAcked(ob.stamp) &&
|
|
1013
|
+
(newestAcked === undefined ||
|
|
1014
|
+
opstampUtils.greaterThan(ob.stamp, newestAcked.stamp))) {
|
|
1078
1015
|
newestAcked = ob;
|
|
1079
1016
|
}
|
|
1080
|
-
if (ob.
|
|
1081
|
-
(oldestUnacked === undefined ||
|
|
1017
|
+
if (opstampUtils.isLocal(ob.stamp) &&
|
|
1018
|
+
(oldestUnacked === undefined ||
|
|
1019
|
+
opstampUtils.greaterThan(oldestUnacked.stamp, ob.stamp))) {
|
|
1082
1020
|
// There can be one local obliterate surrounding a segment if a client repeatedly obliterates
|
|
1083
1021
|
// a region (ex: in the text ABCDEFG, obliterate D, then obliterate CE, then BF). In this case,
|
|
1084
1022
|
// the first one that's applied will be the one that actually removes the segment.
|
|
@@ -1090,32 +1028,20 @@ class MergeTree {
|
|
|
1090
1028
|
// See doc comment on obliteratePrecedingInsertion for more details: if the newest obliterate was performed
|
|
1091
1029
|
// by the same client that's inserting this segment, we let them insert into this range and therefore don't
|
|
1092
1030
|
// mark it obliterated.
|
|
1093
|
-
if (oldest && newest?.clientId !== clientId) {
|
|
1094
|
-
|
|
1095
|
-
if (newestAcked === newest || newestAcked?.clientId !== clientId) {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
localMovedSeq: oldestUnacked?.localSeq,
|
|
1101
|
-
};
|
|
1102
|
-
}
|
|
1103
|
-
else {
|
|
1104
|
-
(0, internal_1.assert)(oldestUnacked !== undefined, 0xb55 /* Expected local obliterate to be defined if newestAcked is not equal to newest */);
|
|
1105
|
-
// There's a pending local obliterate for this range, so it will be marked as obliterated by us. However,
|
|
1106
|
-
// all other clients are under the impression that the most recent acked obliterate won the right to insert
|
|
1107
|
-
// in this range.
|
|
1108
|
-
moveInfo = {
|
|
1109
|
-
movedClientIds: [oldestUnacked.clientId],
|
|
1110
|
-
movedSeq: oldestUnacked.seq,
|
|
1111
|
-
movedSeqs: [oldestUnacked.seq],
|
|
1112
|
-
localMovedSeq: oldestUnacked.localSeq,
|
|
1113
|
-
};
|
|
1031
|
+
if (oldest && newest?.stamp.clientId !== stamp.clientId) {
|
|
1032
|
+
const removeInfo = { removes: [] };
|
|
1033
|
+
if (newestAcked === newest || newestAcked?.stamp.clientId !== stamp.clientId) {
|
|
1034
|
+
removeInfo.removes = overlappingAckedObliterates;
|
|
1035
|
+
// Because we found these by looking at overlapping obliterates, they are not necessarily currently sorted by seq.
|
|
1036
|
+
// Address that now.
|
|
1037
|
+
removeInfo.removes.sort(opstampUtils.compare);
|
|
1114
1038
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1039
|
+
// Note that we don't need to worry about preserving any existing remove information since the segment is new.
|
|
1040
|
+
(0, segmentInfos_js_1.overwriteInfo)(newSegment, removeInfo);
|
|
1041
|
+
if (oldestUnacked !== undefined) {
|
|
1042
|
+
removeInfo.removes.push(oldestUnacked.stamp);
|
|
1043
|
+
(0, internal_1.assert)(oldestUnacked.segmentGroup !== undefined, 0x86c /* expected segment group to exist */);
|
|
1044
|
+
this.addToPendingList(newSegment, oldestUnacked.segmentGroup);
|
|
1119
1045
|
}
|
|
1120
1046
|
if (newSegment.parent) {
|
|
1121
1047
|
// The incremental update codepath in theory can handle most cases where segments are obliterated upon insertion,
|
|
@@ -1123,90 +1049,95 @@ class MergeTree {
|
|
|
1123
1049
|
// lengths inside the inserting walk, we'd be at risk of double-counting the insertion in any case if we allow
|
|
1124
1050
|
// incremental updates here.
|
|
1125
1051
|
const newStructure = true;
|
|
1126
|
-
this.blockUpdatePathLengths(newSegment.parent,
|
|
1052
|
+
this.blockUpdatePathLengths(newSegment.parent, removeInfo.removes[0], newStructure);
|
|
1127
1053
|
}
|
|
1128
1054
|
}
|
|
1129
1055
|
saveIfLocal(newSegment);
|
|
1130
1056
|
}
|
|
1131
1057
|
}
|
|
1132
|
-
ensureIntervalBoundary(pos,
|
|
1133
|
-
|
|
1134
|
-
|
|
1058
|
+
ensureIntervalBoundary(pos, perspective) {
|
|
1059
|
+
this.insertingWalk(pos, perspective, {
|
|
1060
|
+
seq: constants_js_1.TreeMaintenanceSequenceNumber,
|
|
1061
|
+
clientId: perspective.clientId,
|
|
1062
|
+
}, { leaf: this.splitLeafSegment });
|
|
1135
1063
|
}
|
|
1136
1064
|
// Assume called only when pos == len
|
|
1137
|
-
breakTie(pos, node,
|
|
1065
|
+
breakTie(pos, node, insertStamp) {
|
|
1138
1066
|
if (node.isLeaf()) {
|
|
1139
1067
|
if (pos !== 0) {
|
|
1140
1068
|
return false;
|
|
1141
1069
|
}
|
|
1142
|
-
|
|
1143
|
-
// if the new seg is local (UnassignedSequenceNumber) give it the highest possible
|
|
1144
|
-
// seq for comparison, as it will get a seq higher than any other seq once sequences
|
|
1145
|
-
// if the current seg is local (UnassignedSequenceNumber) give it the second highest
|
|
1146
|
-
// possible seq, as the highest is reserved for the previous.
|
|
1147
|
-
const newSeq = seq === constants_js_1.UnassignedSequenceNumber ? Number.MAX_SAFE_INTEGER : seq;
|
|
1148
|
-
const segSeq = node.seq === constants_js_1.UnassignedSequenceNumber ? Number.MAX_SAFE_INTEGER - 1 : (node.seq ?? 0);
|
|
1149
|
-
return (newSeq > segSeq ||
|
|
1150
|
-
((0, segmentInfos_js_1.isMoved)(node) && node.movedSeq !== constants_js_1.UnassignedSequenceNumber && node.movedSeq > seq) ||
|
|
1070
|
+
return (opstampUtils.greaterThan(insertStamp, node.insert) ||
|
|
1151
1071
|
((0, segmentInfos_js_1.isRemoved)(node) &&
|
|
1152
|
-
node.
|
|
1153
|
-
node.
|
|
1072
|
+
opstampUtils.isAcked(node.removes[0]) &&
|
|
1073
|
+
opstampUtils.greaterThan(node.removes[0], insertStamp)));
|
|
1154
1074
|
}
|
|
1155
1075
|
else {
|
|
1156
1076
|
return true;
|
|
1157
1077
|
}
|
|
1158
1078
|
}
|
|
1159
|
-
insertingWalk(
|
|
1079
|
+
insertingWalk(pos, perspective, stamp, context) {
|
|
1080
|
+
const { remainder } = this.insertRecursive(this.root, pos, perspective, stamp, context);
|
|
1081
|
+
if (remainder !== undefined) {
|
|
1082
|
+
this.updateRoot(remainder);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
insertRecursive(block, pos, perspective, stamp, context, isLastChildBlock = true) {
|
|
1160
1086
|
let _pos = pos;
|
|
1161
1087
|
const children = block.children;
|
|
1162
1088
|
let childIndex;
|
|
1163
1089
|
let child;
|
|
1164
1090
|
let newNode;
|
|
1165
1091
|
let fromSplit;
|
|
1092
|
+
let hadChanges = false;
|
|
1166
1093
|
for (childIndex = 0; childIndex < block.childCount; childIndex++) {
|
|
1167
1094
|
child = children[childIndex];
|
|
1168
1095
|
// ensure we walk down the far edge of the tree, even if all sub-tree is eligible for zamboni
|
|
1169
1096
|
const isLastNonLeafBlock = isLastChildBlock && !child.isLeaf() && childIndex === block.childCount - 1;
|
|
1170
|
-
const len = this.nodeLength(child,
|
|
1097
|
+
const len = this.nodeLength(child, perspective) ?? (isLastChildBlock ? 0 : undefined);
|
|
1171
1098
|
if (len === undefined) {
|
|
1172
1099
|
// if the seg len is undefined, the segment
|
|
1173
1100
|
// will be removed, so should just be skipped for now
|
|
1174
1101
|
continue;
|
|
1175
1102
|
}
|
|
1176
1103
|
(0, internal_1.assert)(len >= 0, 0x4bc /* Length should not be negative */);
|
|
1177
|
-
if (_pos < len || (_pos === len && this.breakTie(_pos, child,
|
|
1104
|
+
if (_pos < len || (_pos === len && this.breakTie(_pos, child, stamp))) {
|
|
1178
1105
|
// Found entry containing pos
|
|
1179
1106
|
if (child.isLeaf()) {
|
|
1180
1107
|
const segment = child;
|
|
1181
1108
|
const segmentChanges = context.leaf(segment, _pos, context);
|
|
1182
1109
|
if (segmentChanges.replaceCurrent) {
|
|
1110
|
+
hadChanges = true;
|
|
1183
1111
|
(0, mergeTreeNodes_js_1.assignChild)(block, segmentChanges.replaceCurrent, childIndex, false);
|
|
1184
1112
|
segmentChanges.replaceCurrent.ordinal = child.ordinal;
|
|
1185
1113
|
}
|
|
1186
1114
|
if (segmentChanges.next) {
|
|
1115
|
+
hadChanges = true;
|
|
1187
1116
|
newNode = segmentChanges.next;
|
|
1188
1117
|
childIndex++; // Insert after
|
|
1189
1118
|
}
|
|
1190
1119
|
else {
|
|
1191
|
-
|
|
1192
|
-
return undefined;
|
|
1120
|
+
return { remainder: undefined, hadChanges };
|
|
1193
1121
|
}
|
|
1194
1122
|
}
|
|
1195
1123
|
else {
|
|
1196
1124
|
const childBlock = child;
|
|
1197
1125
|
// Internal node
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1126
|
+
const insertResult = this.insertRecursive(childBlock, _pos, perspective, stamp, context, isLastNonLeafBlock);
|
|
1127
|
+
hadChanges ||= insertResult.hadChanges;
|
|
1128
|
+
if (insertResult.remainder === undefined) {
|
|
1129
|
+
if (insertResult.hadChanges) {
|
|
1130
|
+
this.blockUpdateLength(block, stamp);
|
|
1131
|
+
}
|
|
1132
|
+
return insertResult;
|
|
1202
1133
|
}
|
|
1203
|
-
else if (
|
|
1134
|
+
else if (insertResult.remainder === MergeTree.theUnfinishedNode) {
|
|
1204
1135
|
_pos -= len; // Act as if shifted segment
|
|
1205
1136
|
continue;
|
|
1206
1137
|
}
|
|
1207
1138
|
else {
|
|
1208
|
-
newNode =
|
|
1209
|
-
fromSplit =
|
|
1139
|
+
newNode = insertResult.remainder;
|
|
1140
|
+
fromSplit = insertResult.remainder;
|
|
1210
1141
|
childIndex++; // Insert after
|
|
1211
1142
|
}
|
|
1212
1143
|
}
|
|
@@ -1218,7 +1149,7 @@ class MergeTree {
|
|
|
1218
1149
|
}
|
|
1219
1150
|
if (!newNode && _pos === 0) {
|
|
1220
1151
|
if (context.continuePredicate?.(block)) {
|
|
1221
|
-
return MergeTree.theUnfinishedNode;
|
|
1152
|
+
return { remainder: MergeTree.theUnfinishedNode, hadChanges };
|
|
1222
1153
|
}
|
|
1223
1154
|
else {
|
|
1224
1155
|
const segmentChanges = context.leaf(undefined, _pos, context);
|
|
@@ -1227,6 +1158,7 @@ class MergeTree {
|
|
|
1227
1158
|
}
|
|
1228
1159
|
}
|
|
1229
1160
|
if (newNode) {
|
|
1161
|
+
hadChanges = true;
|
|
1230
1162
|
for (let i = block.childCount; i > childIndex; i--) {
|
|
1231
1163
|
block.children[i] = block.children[i - 1];
|
|
1232
1164
|
block.children[i].index = i;
|
|
@@ -1238,19 +1170,19 @@ class MergeTree {
|
|
|
1238
1170
|
if (fromSplit) {
|
|
1239
1171
|
this.nodeUpdateOrdinals(fromSplit);
|
|
1240
1172
|
}
|
|
1241
|
-
this.blockUpdateLength(block,
|
|
1242
|
-
return undefined;
|
|
1173
|
+
this.blockUpdateLength(block, stamp);
|
|
1174
|
+
return { remainder: undefined, hadChanges };
|
|
1243
1175
|
}
|
|
1244
1176
|
else {
|
|
1245
1177
|
// Don't update ordinals because higher block will do it
|
|
1246
1178
|
const newNodeFromSplit = this.split(block);
|
|
1247
|
-
partialLengths_js_1.PartialSequenceLengths.options.verifyExpected?.(this, block, refSeq, clientId);
|
|
1248
|
-
partialLengths_js_1.PartialSequenceLengths.options.verifyExpected?.(this, newNodeFromSplit, refSeq, clientId);
|
|
1249
|
-
return newNodeFromSplit;
|
|
1179
|
+
partialLengths_js_1.PartialSequenceLengths.options.verifyExpected?.(this, block, perspective.refSeq, stamp.clientId);
|
|
1180
|
+
partialLengths_js_1.PartialSequenceLengths.options.verifyExpected?.(this, newNodeFromSplit, perspective.refSeq, stamp.clientId);
|
|
1181
|
+
return { remainder: newNodeFromSplit, hadChanges };
|
|
1250
1182
|
}
|
|
1251
1183
|
}
|
|
1252
1184
|
else {
|
|
1253
|
-
return undefined;
|
|
1185
|
+
return { remainder: undefined, hadChanges };
|
|
1254
1186
|
}
|
|
1255
1187
|
}
|
|
1256
1188
|
split(node) {
|
|
@@ -1285,16 +1217,14 @@ class MergeTree {
|
|
|
1285
1217
|
* @param clientId - The id of the client making the annotate
|
|
1286
1218
|
* @param seq - The sequence number of the annotate operation
|
|
1287
1219
|
* @param opArgs - The op args for the annotate op. this is passed to the merge tree callback if there is one
|
|
1288
|
-
* @param rollback - Whether this is for a local rollback and what kind
|
|
1289
1220
|
*/
|
|
1290
|
-
annotateRange(start, end, propsOrAdjust,
|
|
1221
|
+
annotateRange(start, end, propsOrAdjust, perspective, stamp, opArgs) {
|
|
1291
1222
|
if (propsOrAdjust.adjust !== undefined) {
|
|
1292
1223
|
errorIfOptionNotTrue(this.options, "mergeTreeEnableAnnotateAdjust");
|
|
1293
1224
|
}
|
|
1294
|
-
this.ensureIntervalBoundary(start,
|
|
1295
|
-
this.ensureIntervalBoundary(end,
|
|
1225
|
+
this.ensureIntervalBoundary(start, perspective);
|
|
1226
|
+
this.ensureIntervalBoundary(end, perspective);
|
|
1296
1227
|
const deltaSegments = [];
|
|
1297
|
-
const localSeq = seq === constants_js_1.UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
|
|
1298
1228
|
let segmentGroup;
|
|
1299
1229
|
const opObj = propsOrAdjust.props ?? propsOrAdjust.adjust;
|
|
1300
1230
|
const annotateSegment = (segment) => {
|
|
@@ -1302,23 +1232,23 @@ class MergeTree {
|
|
|
1302
1232
|
!(mergeTreeNodes_js_1.reservedMarkerIdKey in opObj) ||
|
|
1303
1233
|
opObj.markerId === segment.properties?.markerId, 0x5ad /* Cannot change the markerId of an existing marker */);
|
|
1304
1234
|
const propertyManager = (segment.propertyManager ??= new segmentPropertiesManager_js_1.PropertiesManager());
|
|
1305
|
-
const propertyDeltas = propertyManager.handleProperties(propsOrAdjust, segment, seq, this.collabWindow.minSeq, this.collabWindow.collaborating, rollback);
|
|
1306
|
-
if (!
|
|
1235
|
+
const propertyDeltas = propertyManager.handleProperties(propsOrAdjust, segment, stamp.seq, this.collabWindow.minSeq, this.collabWindow.collaborating, opArgs?.rollback === true);
|
|
1236
|
+
if (!(0, segmentInfos_js_1.isRemoved)(segment)) {
|
|
1307
1237
|
deltaSegments.push({ segment, propertyDeltas });
|
|
1308
1238
|
}
|
|
1309
1239
|
if (this.collabWindow.collaborating) {
|
|
1310
|
-
if (
|
|
1311
|
-
segmentGroup = this.addToPendingList(segment, segmentGroup, localSeq, propertyDeltas);
|
|
1240
|
+
if (opstampUtils.isLocal(stamp)) {
|
|
1241
|
+
segmentGroup = this.addToPendingList(segment, segmentGroup, stamp.localSeq, propertyDeltas);
|
|
1312
1242
|
}
|
|
1313
1243
|
else {
|
|
1314
1244
|
if (MergeTree.options.zamboniSegments) {
|
|
1315
|
-
this.addToLRUSet(segment, seq);
|
|
1245
|
+
this.addToLRUSet(segment, stamp.seq);
|
|
1316
1246
|
}
|
|
1317
1247
|
}
|
|
1318
1248
|
}
|
|
1319
1249
|
return true;
|
|
1320
1250
|
};
|
|
1321
|
-
this.nodeMap(
|
|
1251
|
+
this.nodeMap(perspective, annotateSegment, undefined, start, end);
|
|
1322
1252
|
// OpArgs == undefined => test code
|
|
1323
1253
|
if (deltaSegments.length > 0) {
|
|
1324
1254
|
this.mergeTreeDeltaCallback?.(opArgs, {
|
|
@@ -1327,34 +1257,28 @@ class MergeTree {
|
|
|
1327
1257
|
});
|
|
1328
1258
|
}
|
|
1329
1259
|
if (this.collabWindow.collaborating &&
|
|
1330
|
-
|
|
1260
|
+
opstampUtils.isAcked(stamp) &&
|
|
1331
1261
|
MergeTree.options.zamboniSegments) {
|
|
1332
1262
|
(0, zamboni_js_1.zamboniSegments)(this);
|
|
1333
1263
|
}
|
|
1334
1264
|
}
|
|
1335
|
-
obliterateRangeSided(start, end,
|
|
1265
|
+
obliterateRangeSided(start, end, perspective, stamp, opArgs) {
|
|
1336
1266
|
const startPos = start.side === sequencePlace_js_1.Side.Before ? start.pos : start.pos + 1;
|
|
1337
1267
|
const endPos = end.side === sequencePlace_js_1.Side.Before ? end.pos : end.pos + 1;
|
|
1338
|
-
this.ensureIntervalBoundary(startPos,
|
|
1339
|
-
this.ensureIntervalBoundary(endPos,
|
|
1268
|
+
this.ensureIntervalBoundary(startPos, perspective);
|
|
1269
|
+
this.ensureIntervalBoundary(endPos, perspective);
|
|
1340
1270
|
let _overwrite = false;
|
|
1341
1271
|
const localOverlapWithRefs = [];
|
|
1342
|
-
const
|
|
1343
|
-
const localSeq = seq === constants_js_1.UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
|
|
1344
|
-
const perspective = seq === constants_js_1.UnassignedSequenceNumber
|
|
1345
|
-
? this.localPerspective
|
|
1346
|
-
: new perspective_js_1.PriorPerspective(refSeq, clientId);
|
|
1272
|
+
const removedSegments = [];
|
|
1347
1273
|
const obliterate = {
|
|
1348
|
-
clientId,
|
|
1349
|
-
end: (0, localReference_js_1.createDetachedLocalReferencePosition)(undefined),
|
|
1350
|
-
refSeq,
|
|
1351
|
-
seq,
|
|
1352
1274
|
start: (0, localReference_js_1.createDetachedLocalReferencePosition)(undefined),
|
|
1353
|
-
|
|
1275
|
+
end: (0, localReference_js_1.createDetachedLocalReferencePosition)(undefined),
|
|
1276
|
+
refSeq: perspective.refSeq,
|
|
1277
|
+
stamp,
|
|
1354
1278
|
segmentGroup: undefined,
|
|
1355
1279
|
};
|
|
1356
|
-
const { segment: startSeg } = this.getContainingSegment(start.pos,
|
|
1357
|
-
const { segment: endSeg } = this.getContainingSegment(end.pos,
|
|
1280
|
+
const { segment: startSeg } = this.getContainingSegment(start.pos, perspective);
|
|
1281
|
+
const { segment: endSeg } = this.getContainingSegment(end.pos, perspective);
|
|
1358
1282
|
(0, internal_1.assert)((0, mergeTreeNodes_js_1.isSegmentLeaf)(startSeg) && (0, mergeTreeNodes_js_1.isSegmentLeaf)(endSeg), 0xa3f /* segments cannot be undefined */);
|
|
1359
1283
|
obliterate.start = this.createLocalReferencePosition(startSeg, start.side === sequencePlace_js_1.Side.Before ? 0 : Math.max(startSeg.cachedLength - 1, 0), ops_js_1.ReferenceType.StayOnRemove, {
|
|
1360
1284
|
obliterate,
|
|
@@ -1368,15 +1292,15 @@ class MergeTree {
|
|
|
1368
1292
|
// at which point they are added to the segment group.
|
|
1369
1293
|
obliterate.segmentGroup = {
|
|
1370
1294
|
segments: [],
|
|
1371
|
-
localSeq,
|
|
1295
|
+
localSeq: stamp.localSeq,
|
|
1372
1296
|
refSeq: this.collabWindow.currentSeq,
|
|
1373
1297
|
obliterateInfo: obliterate,
|
|
1374
1298
|
};
|
|
1375
|
-
if (this.collabWindow.collaborating && clientId === this.collabWindow.clientId) {
|
|
1299
|
+
if (this.collabWindow.collaborating && stamp.clientId === this.collabWindow.clientId) {
|
|
1376
1300
|
this.pendingSegments.push(obliterate.segmentGroup);
|
|
1377
1301
|
}
|
|
1378
1302
|
this.obliterates.addOrUpdate(obliterate);
|
|
1379
|
-
const
|
|
1303
|
+
const markRemoved = (segment, pos) => {
|
|
1380
1304
|
if ((start.side === sequencePlace_js_1.Side.After && startPos === pos + segment.cachedLength) || // exclusive start segment
|
|
1381
1305
|
(end.side === sequencePlace_js_1.Side.Before && endPos === pos && perspective.isSegmentPresent(segment)) // exclusive end segment
|
|
1382
1306
|
) {
|
|
@@ -1384,173 +1308,136 @@ class MergeTree {
|
|
|
1384
1308
|
// These segments are outside of the obliteration range though, so return true to keep walking.
|
|
1385
1309
|
return true;
|
|
1386
1310
|
}
|
|
1387
|
-
const
|
|
1311
|
+
const existingRemoveInfo = (0, segmentInfos_js_1.toRemovalInfo)(segment);
|
|
1388
1312
|
// The "last-to-obliterate-gets-to-insert" policy described by the doc comment on `obliteratePrecedingInsertion`
|
|
1389
1313
|
// is mostly handled by logic at insertion time, but we need a small bit of handling here.
|
|
1390
1314
|
// Specifically, we want to avoid marking a local-only segment as obliterated when we know one of our own local obliterates
|
|
1391
1315
|
// will win against the obliterate we're processing, hence the early exit.
|
|
1392
|
-
if (segment.
|
|
1393
|
-
segment.obliteratePrecedingInsertion?.seq === constants_js_1.UnassignedSequenceNumber &&
|
|
1394
|
-
|
|
1316
|
+
if (opstampUtils.isLocal(segment.insert) &&
|
|
1317
|
+
segment.obliteratePrecedingInsertion?.stamp.seq === constants_js_1.UnassignedSequenceNumber &&
|
|
1318
|
+
opstampUtils.isAcked(stamp)) {
|
|
1395
1319
|
// We chose to not obliterate this segment because we are aware of an unacked local obliteration.
|
|
1396
1320
|
// The local obliterate has not been sequenced yet, so it is still the newest obliterate we are aware of.
|
|
1397
1321
|
// Other clients will also choose not to obliterate this segment because the most recent obliteration has the same clientId
|
|
1398
1322
|
return true;
|
|
1399
1323
|
}
|
|
1400
1324
|
// Partial lengths incrementality is not supported for overlapping obliterate/removes.
|
|
1401
|
-
_overwrite ||=
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1325
|
+
_overwrite ||= existingRemoveInfo !== undefined;
|
|
1326
|
+
// - Record the segment as removed
|
|
1327
|
+
// - If this was the first thing to remove the segment from the local view, add it to removedSegments
|
|
1328
|
+
// - Otherwise, if it was the first thing to remove the segment from the acked view, add it to localOverlapWithRefs (so we can slide them)
|
|
1329
|
+
if (existingRemoveInfo === undefined) {
|
|
1330
|
+
const removed = (0, segmentInfos_js_1.overwriteInfo)(segment, {
|
|
1331
|
+
removes: [stamp],
|
|
1408
1332
|
});
|
|
1409
|
-
|
|
1410
|
-
if (existingRemoval === undefined) {
|
|
1411
|
-
movedSegments.push(movedSeg);
|
|
1412
|
-
}
|
|
1413
|
-
else if (existingRemoval.removedSeq === constants_js_1.UnassignedSequenceNumber &&
|
|
1414
|
-
segment.localRefs?.empty === false) {
|
|
1415
|
-
// We removed this locally already so we don't need to event it again, but it might have references
|
|
1416
|
-
// that need sliding now that a move may have been acked.
|
|
1417
|
-
localOverlapWithRefs.push(segment);
|
|
1418
|
-
}
|
|
1333
|
+
removedSegments.push(removed);
|
|
1419
1334
|
}
|
|
1420
1335
|
else {
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
// The list isn't ordered, but we keep the first move at the head
|
|
1427
|
-
// for partialLengths bookkeeping purposes
|
|
1428
|
-
existingMoveInfo.movedClientIds.unshift(clientId);
|
|
1429
|
-
existingMoveInfo.movedSeq = seq;
|
|
1430
|
-
existingMoveInfo.movedSeqs.unshift(seq);
|
|
1431
|
-
if (segment.localRefs?.empty === false) {
|
|
1432
|
-
localOverlapWithRefs.push(segment);
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
else {
|
|
1436
|
-
// Do not replace earlier sequence number for move
|
|
1437
|
-
existingMoveInfo.movedClientIds.push(clientId);
|
|
1438
|
-
existingMoveInfo.movedSeqs.push(seq);
|
|
1336
|
+
// The segment has already been removed, so we don't need to add it to removedSegments. However,
|
|
1337
|
+
// if it's only been removed locally, we still need to slide any references that may exist on it.
|
|
1338
|
+
if (!opstampUtils.hasAnyAckedOperation(existingRemoveInfo.removes) &&
|
|
1339
|
+
segment.localRefs?.empty === false) {
|
|
1340
|
+
localOverlapWithRefs.push(segment);
|
|
1439
1341
|
}
|
|
1342
|
+
opstampUtils.spliceIntoList(existingRemoveInfo.removes, stamp);
|
|
1440
1343
|
}
|
|
1441
|
-
(0, segmentInfos_js_1.
|
|
1442
|
-
// Save segment so can assign
|
|
1344
|
+
(0, segmentInfos_js_1.assertRemoved)(segment);
|
|
1345
|
+
// Save segment so can assign sequence number when acked by server
|
|
1443
1346
|
if (this.collabWindow.collaborating) {
|
|
1444
|
-
if (segment.
|
|
1445
|
-
clientId === this.collabWindow.clientId) {
|
|
1446
|
-
obliterate.segmentGroup = this.addToPendingList(segment, obliterate.segmentGroup, localSeq);
|
|
1347
|
+
if (opstampUtils.isLocal(segment.removes[0]) &&
|
|
1348
|
+
stamp.clientId === this.collabWindow.clientId) {
|
|
1349
|
+
obliterate.segmentGroup = this.addToPendingList(segment, obliterate.segmentGroup, stamp.localSeq);
|
|
1447
1350
|
}
|
|
1448
1351
|
else {
|
|
1449
1352
|
if (MergeTree.options.zamboniSegments) {
|
|
1450
|
-
this.addToLRUSet(segment, seq);
|
|
1353
|
+
this.addToLRUSet(segment, stamp.seq);
|
|
1451
1354
|
}
|
|
1452
1355
|
}
|
|
1453
1356
|
}
|
|
1454
1357
|
return true;
|
|
1455
1358
|
};
|
|
1456
|
-
const
|
|
1359
|
+
const afterMarkRemoved = (node) => {
|
|
1457
1360
|
if (_overwrite) {
|
|
1458
1361
|
this.nodeUpdateLengthNewStructure(node);
|
|
1459
1362
|
}
|
|
1460
1363
|
else {
|
|
1461
|
-
this.blockUpdateLength(node,
|
|
1364
|
+
this.blockUpdateLength(node, stamp);
|
|
1462
1365
|
}
|
|
1463
1366
|
return true;
|
|
1464
1367
|
};
|
|
1465
|
-
this.nodeMap(
|
|
1466
|
-
|
|
1368
|
+
this.nodeMap(perspective, markRemoved, afterMarkRemoved, start.pos, end.pos + 1, // include the segment containing the end reference
|
|
1369
|
+
// Use a visibilityPerspective which includes all segments (including local ones) which are in the obliteration range.
|
|
1370
|
+
// This ensures that concurrently inserted segments will also be marked obliterated.
|
|
1371
|
+
opstampUtils.isLocal(stamp)
|
|
1372
|
+
? perspective
|
|
1373
|
+
: new perspective_js_1.RemoteObliteratePerspective(stamp.clientId));
|
|
1467
1374
|
this.slideAckedRemovedSegmentReferences(localOverlapWithRefs);
|
|
1468
1375
|
// opArgs == undefined => test code
|
|
1469
1376
|
if (start.pos !== end.pos || start.side !== end.side) {
|
|
1470
1377
|
this.mergeTreeDeltaCallback?.(opArgs, {
|
|
1471
1378
|
operation: ops_js_1.MergeTreeDeltaType.OBLITERATE,
|
|
1472
|
-
deltaSegments:
|
|
1379
|
+
deltaSegments: removedSegments.map((segment) => ({ segment })),
|
|
1473
1380
|
});
|
|
1474
1381
|
}
|
|
1475
1382
|
// these events are newly removed
|
|
1476
1383
|
// so we slide after eventing in case the consumer wants to make reference
|
|
1477
1384
|
// changes at remove time, like add a ref to track undo redo.
|
|
1478
|
-
if (!this.collabWindow.collaborating || clientId !== this.collabWindow.clientId) {
|
|
1479
|
-
this.slideAckedRemovedSegmentReferences(
|
|
1385
|
+
if (!this.collabWindow.collaborating || stamp.clientId !== this.collabWindow.clientId) {
|
|
1386
|
+
this.slideAckedRemovedSegmentReferences(removedSegments);
|
|
1480
1387
|
}
|
|
1481
1388
|
if (this.collabWindow.collaborating &&
|
|
1482
|
-
|
|
1389
|
+
opstampUtils.isAcked(stamp) &&
|
|
1483
1390
|
MergeTree.options.zamboniSegments) {
|
|
1484
1391
|
(0, zamboni_js_1.zamboniSegments)(this);
|
|
1485
1392
|
}
|
|
1486
1393
|
}
|
|
1487
|
-
obliterateRange(start, end,
|
|
1394
|
+
obliterateRange(start, end, perspective, stampArg, opArgs) {
|
|
1488
1395
|
errorIfOptionNotTrue(this.options, "mergeTreeEnableObliterate");
|
|
1396
|
+
const stamp = { ...stampArg, type: "sliceRemove" };
|
|
1489
1397
|
if (this.options?.mergeTreeEnableSidedObliterate) {
|
|
1490
1398
|
(0, internal_1.assert)(typeof start === "object" && typeof end === "object", 0xa45 /* Start and end must be of type InteriorSequencePlace if mergeTreeEnableSidedObliterate is enabled. */);
|
|
1491
|
-
this.obliterateRangeSided(start, end,
|
|
1399
|
+
this.obliterateRangeSided(start, end, perspective, stamp, opArgs);
|
|
1492
1400
|
}
|
|
1493
1401
|
else {
|
|
1494
1402
|
(0, internal_1.assert)(typeof start === "number" && typeof end === "number", 0xa46 /* Start and end must be numbers if mergeTreeEnableSidedObliterate is not enabled. */);
|
|
1495
|
-
this.obliterateRangeSided({ pos: start, side: sequencePlace_js_1.Side.Before }, { pos: end - 1, side: sequencePlace_js_1.Side.After },
|
|
1403
|
+
this.obliterateRangeSided({ pos: start, side: sequencePlace_js_1.Side.Before }, { pos: end - 1, side: sequencePlace_js_1.Side.After }, perspective, stamp, opArgs);
|
|
1496
1404
|
}
|
|
1497
1405
|
}
|
|
1498
|
-
markRangeRemoved(start, end,
|
|
1406
|
+
markRangeRemoved(start, end, perspective, stampArg, opArgs) {
|
|
1499
1407
|
let _overwrite = false;
|
|
1500
|
-
|
|
1501
|
-
this.ensureIntervalBoundary(
|
|
1408
|
+
const stamp = { ...stampArg, type: "setRemove" };
|
|
1409
|
+
this.ensureIntervalBoundary(start, perspective);
|
|
1410
|
+
this.ensureIntervalBoundary(end, perspective);
|
|
1502
1411
|
let segmentGroup;
|
|
1503
1412
|
const removedSegments = [];
|
|
1504
1413
|
const localOverlapWithRefs = [];
|
|
1505
|
-
const localSeq = seq === constants_js_1.UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
|
|
1506
1414
|
const markRemoved = (segment, pos, _start, _end) => {
|
|
1507
1415
|
const existingRemovalInfo = (0, segmentInfos_js_1.toRemovalInfo)(segment);
|
|
1508
1416
|
// Partial lengths incrementality is not supported for overlapping obliterate/removes.
|
|
1509
|
-
_overwrite ||= existingRemovalInfo !== undefined
|
|
1417
|
+
_overwrite ||= existingRemovalInfo !== undefined;
|
|
1510
1418
|
if (existingRemovalInfo === undefined) {
|
|
1511
1419
|
const removed = (0, segmentInfos_js_1.overwriteInfo)(segment, {
|
|
1512
|
-
|
|
1513
|
-
removedSeq: seq,
|
|
1514
|
-
localRemovedSeq: localSeq,
|
|
1420
|
+
removes: [stamp],
|
|
1515
1421
|
});
|
|
1516
|
-
|
|
1517
|
-
if (existingMoveInfo === undefined) {
|
|
1518
|
-
removedSegments.push(removed);
|
|
1519
|
-
}
|
|
1520
|
-
else if (existingMoveInfo.movedSeq === constants_js_1.UnassignedSequenceNumber &&
|
|
1521
|
-
segment.localRefs?.empty === false) {
|
|
1522
|
-
// We moved this locally already so we don't need to event it again, but it might have references
|
|
1523
|
-
// that need sliding now that a remove may have been acked.
|
|
1524
|
-
localOverlapWithRefs.push(segment);
|
|
1525
|
-
}
|
|
1422
|
+
removedSegments.push(removed);
|
|
1526
1423
|
}
|
|
1527
1424
|
else {
|
|
1528
|
-
if (existingRemovalInfo.
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
// The list isn't ordered, but we keep the first removal at the head
|
|
1532
|
-
// for partialLengths bookkeeping purposes
|
|
1533
|
-
existingRemovalInfo.removedClientIds.unshift(clientId);
|
|
1534
|
-
existingRemovalInfo.removedSeq = seq;
|
|
1535
|
-
if (segment.localRefs?.empty === false) {
|
|
1536
|
-
localOverlapWithRefs.push(segment);
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
else {
|
|
1540
|
-
// Do not replace earlier sequence number for remove
|
|
1541
|
-
existingRemovalInfo.removedClientIds.push(clientId);
|
|
1425
|
+
if (!opstampUtils.hasAnyAckedOperation(existingRemovalInfo.removes) &&
|
|
1426
|
+
segment.localRefs?.empty === false) {
|
|
1427
|
+
localOverlapWithRefs.push(segment);
|
|
1542
1428
|
}
|
|
1429
|
+
opstampUtils.spliceIntoList(existingRemovalInfo.removes, stamp);
|
|
1543
1430
|
}
|
|
1544
1431
|
(0, segmentInfos_js_1.assertRemoved)(segment);
|
|
1545
1432
|
// Save segment so we can assign removed sequence number when acked by server
|
|
1546
1433
|
if (this.collabWindow.collaborating) {
|
|
1547
|
-
if (segment.
|
|
1548
|
-
clientId === this.collabWindow.clientId) {
|
|
1549
|
-
segmentGroup = this.addToPendingList(segment, segmentGroup, localSeq);
|
|
1434
|
+
if (opstampUtils.isLocal(segment.removes[0]) &&
|
|
1435
|
+
stamp.clientId === this.collabWindow.clientId) {
|
|
1436
|
+
segmentGroup = this.addToPendingList(segment, segmentGroup, stamp.localSeq);
|
|
1550
1437
|
}
|
|
1551
1438
|
else {
|
|
1552
1439
|
if (MergeTree.options.zamboniSegments) {
|
|
1553
|
-
this.addToLRUSet(segment, seq);
|
|
1440
|
+
this.addToLRUSet(segment, stamp.seq);
|
|
1554
1441
|
}
|
|
1555
1442
|
}
|
|
1556
1443
|
}
|
|
@@ -1561,11 +1448,11 @@ class MergeTree {
|
|
|
1561
1448
|
this.nodeUpdateLengthNewStructure(node);
|
|
1562
1449
|
}
|
|
1563
1450
|
else {
|
|
1564
|
-
this.blockUpdateLength(node,
|
|
1451
|
+
this.blockUpdateLength(node, stamp);
|
|
1565
1452
|
}
|
|
1566
1453
|
return true;
|
|
1567
1454
|
};
|
|
1568
|
-
this.nodeMap(
|
|
1455
|
+
this.nodeMap(perspective, markRemoved, afterMarkRemoved, start, end);
|
|
1569
1456
|
// these segments are already viewed as being removed locally and are not event-ed
|
|
1570
1457
|
// so can slide non-StayOnRemove refs immediately
|
|
1571
1458
|
this.slideAckedRemovedSegmentReferences(localOverlapWithRefs);
|
|
@@ -1579,11 +1466,11 @@ class MergeTree {
|
|
|
1579
1466
|
// these events are newly removed
|
|
1580
1467
|
// so we slide after eventing in case the consumer wants to make reference
|
|
1581
1468
|
// changes at remove time, like add a ref to track undo redo.
|
|
1582
|
-
if (!this.collabWindow.collaborating || clientId !== this.collabWindow.clientId) {
|
|
1469
|
+
if (!this.collabWindow.collaborating || stamp.clientId !== this.collabWindow.clientId) {
|
|
1583
1470
|
this.slideAckedRemovedSegmentReferences(removedSegments);
|
|
1584
1471
|
}
|
|
1585
1472
|
if (this.collabWindow.collaborating &&
|
|
1586
|
-
|
|
1473
|
+
opstampUtils.isAcked(stamp) &&
|
|
1587
1474
|
MergeTree.options.zamboniSegments) {
|
|
1588
1475
|
(0, zamboni_js_1.zamboniSegments)(this);
|
|
1589
1476
|
}
|
|
@@ -1602,15 +1489,25 @@ class MergeTree {
|
|
|
1602
1489
|
pendingSegmentGroup.segments.forEach((segment) => {
|
|
1603
1490
|
const segmentSegmentGroup = segment?.segmentGroups?.pop();
|
|
1604
1491
|
(0, internal_1.assert)(segmentSegmentGroup === pendingSegmentGroup, 0x3ee /* Unexpected segmentGroup in segment */);
|
|
1605
|
-
(0, internal_1.assert)((0, segmentInfos_js_1.isRemoved)(segment) &&
|
|
1492
|
+
(0, internal_1.assert)((0, segmentInfos_js_1.isRemoved)(segment) &&
|
|
1493
|
+
segment.removes[0].clientId === this.collabWindow.clientId &&
|
|
1494
|
+
segment.removes[0].type === "setRemove", 0x39d /* Rollback segment removedClientId does not match local client */);
|
|
1606
1495
|
let updateNode = segment.parent;
|
|
1496
|
+
// This also removes obliterates, but that should be ok as we can only remove a segment once.
|
|
1497
|
+
// If we were able to remove it locally, that also means there are no remote removals (since rollback is synchronous).
|
|
1607
1498
|
(0, segmentInfos_js_1.removeRemovalInfo)(segment);
|
|
1608
1499
|
for (updateNode; updateNode !== undefined; updateNode = updateNode.parent) {
|
|
1609
|
-
this.blockUpdateLength(updateNode,
|
|
1500
|
+
this.blockUpdateLength(updateNode, {
|
|
1501
|
+
seq: constants_js_1.UnassignedSequenceNumber,
|
|
1502
|
+
clientId: this.collabWindow.clientId,
|
|
1503
|
+
});
|
|
1610
1504
|
}
|
|
1611
1505
|
// Note: optional chaining short-circuits:
|
|
1612
1506
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining#short-circuiting
|
|
1613
|
-
this.mergeTreeDeltaCallback?.({
|
|
1507
|
+
this.mergeTreeDeltaCallback?.({
|
|
1508
|
+
op: (0, opBuilder_js_1.createInsertSegmentOp)(this.findRollbackPosition(segment), segment),
|
|
1509
|
+
rollback: true,
|
|
1510
|
+
}, {
|
|
1614
1511
|
operation: ops_js_1.MergeTreeDeltaType.INSERT,
|
|
1615
1512
|
deltaSegments: [{ segment }],
|
|
1616
1513
|
});
|
|
@@ -1630,15 +1527,27 @@ class MergeTree {
|
|
|
1630
1527
|
(0, internal_1.assert)(segmentSegmentGroup === pendingSegmentGroup, 0x3ef /* Unexpected segmentGroup in segment */);
|
|
1631
1528
|
const start = this.findRollbackPosition(segment);
|
|
1632
1529
|
if (op.type === ops_js_1.MergeTreeDeltaType.INSERT) {
|
|
1633
|
-
segment.
|
|
1634
|
-
|
|
1530
|
+
segment.insert = {
|
|
1531
|
+
type: "insert",
|
|
1532
|
+
seq: constants_js_1.UniversalSequenceNumber,
|
|
1533
|
+
clientId: this.collabWindow.clientId,
|
|
1534
|
+
};
|
|
1635
1535
|
const removeOp = (0, opBuilder_js_1.createRemoveRangeOp)(start, start + segment.cachedLength);
|
|
1636
|
-
|
|
1536
|
+
const removeStamp = {
|
|
1537
|
+
type: "setRemove",
|
|
1538
|
+
seq: constants_js_1.UniversalSequenceNumber,
|
|
1539
|
+
clientId: this.collabWindow.clientId,
|
|
1540
|
+
};
|
|
1541
|
+
this.markRangeRemoved(start, start + segment.cachedLength, this.localPerspective, removeStamp, { op: removeOp, rollback: true });
|
|
1637
1542
|
} /* op.type === MergeTreeDeltaType.ANNOTATE */
|
|
1638
1543
|
else {
|
|
1639
1544
|
const props = pendingSegmentGroup.previousProps[i];
|
|
1640
1545
|
const annotateOp = (0, opBuilder_js_1.createAnnotateRangeOp)(start, start + segment.cachedLength, props);
|
|
1641
|
-
|
|
1546
|
+
const annotateStamp = {
|
|
1547
|
+
seq: constants_js_1.UniversalSequenceNumber,
|
|
1548
|
+
clientId: this.collabWindow.clientId,
|
|
1549
|
+
};
|
|
1550
|
+
this.annotateRange(start, start + segment.cachedLength, { props }, this.localPerspective, annotateStamp, { op: annotateOp, rollback: true });
|
|
1642
1551
|
i++;
|
|
1643
1552
|
}
|
|
1644
1553
|
}
|
|
@@ -1679,7 +1588,7 @@ class MergeTree {
|
|
|
1679
1588
|
createLocalReferencePosition(_segment, offset, refType, properties, slidingPreference, canSlideToEndpoint) {
|
|
1680
1589
|
if (_segment !== "start" &&
|
|
1681
1590
|
_segment !== "end" &&
|
|
1682
|
-
|
|
1591
|
+
isRemovedAndAcked(_segment) &&
|
|
1683
1592
|
!(0, referencePositions_js_1.refTypeIncludesFlag)(refType, ops_js_1.ReferenceType.SlideOnRemove | ops_js_1.ReferenceType.Transient | ops_js_1.ReferenceType.StayOnRemove) &&
|
|
1684
1593
|
_segment.endpointType === undefined) {
|
|
1685
1594
|
throw new internal_2.UsageError("Can only create SlideOnRemove or Transient local reference position on a removed or obliterated segment");
|
|
@@ -1723,15 +1632,15 @@ class MergeTree {
|
|
|
1723
1632
|
affectedSegments.insertAfter(lastLocalSegment, segmentToSlide.data);
|
|
1724
1633
|
}
|
|
1725
1634
|
else if ((0, segmentInfos_js_1.isRemoved)(segmentToSlide.data)) {
|
|
1726
|
-
(0, internal_1.assert)(segmentToSlide.data.
|
|
1635
|
+
(0, internal_1.assert)(segmentToSlide.data.removes[0].seq !== undefined, 0x54d /* Removed segment that hasnt had its removal acked should be locally removed */);
|
|
1727
1636
|
// Slide each locally removed item past all segments that have localSeq > lremoveItem.localSeq
|
|
1728
1637
|
// but not past remotely removed segments;
|
|
1729
1638
|
let cur = segmentToSlide;
|
|
1730
1639
|
let scan = cur.next;
|
|
1731
1640
|
while (scan !== undefined &&
|
|
1732
1641
|
!isRemovedAndAcked(scan.data) &&
|
|
1733
|
-
scan.data.localSeq !== undefined &&
|
|
1734
|
-
scan.data.
|
|
1642
|
+
scan.data.insert.localSeq !== undefined &&
|
|
1643
|
+
opstampUtils.greaterThan(scan.data.insert, segmentToSlide.data.removes[0])) {
|
|
1735
1644
|
cur = scan;
|
|
1736
1645
|
scan = scan.next;
|
|
1737
1646
|
}
|
|
@@ -1816,11 +1725,11 @@ class MergeTree {
|
|
|
1816
1725
|
}
|
|
1817
1726
|
};
|
|
1818
1727
|
(0, mergeTreeNodeWalk_js_1.walkAllChildSegments)(this.root, (seg) => {
|
|
1819
|
-
if ((0, segmentInfos_js_1.isRemoved)(seg) || seg.
|
|
1728
|
+
if ((0, segmentInfos_js_1.isRemoved)(seg) || opstampUtils.isLocal(seg.insert)) {
|
|
1820
1729
|
if (isRemovedAndAcked(seg)) {
|
|
1821
1730
|
rangeContainsRemoteRemovedSegs = true;
|
|
1822
1731
|
}
|
|
1823
|
-
if (seg.
|
|
1732
|
+
if (opstampUtils.isLocal(seg.insert)) {
|
|
1824
1733
|
rangeContainsLocalSegs = true;
|
|
1825
1734
|
}
|
|
1826
1735
|
currentRangeToNormalize.push(seg);
|
|
@@ -1848,7 +1757,7 @@ class MergeTree {
|
|
|
1848
1757
|
}
|
|
1849
1758
|
if (node.isLeaf()) {
|
|
1850
1759
|
const segment = node;
|
|
1851
|
-
if ((this.
|
|
1760
|
+
if ((this.leafLength(segment) ?? 0) > 0 && mergeTreeNodes_js_1.Marker.is(segment)) {
|
|
1852
1761
|
const markerId = segment.getId();
|
|
1853
1762
|
// Also in insertMarker but need for reload segs case
|
|
1854
1763
|
// can add option for this only from reload segs
|
|
@@ -1878,33 +1787,33 @@ class MergeTree {
|
|
|
1878
1787
|
block.rightmostTiles = rightmostTiles;
|
|
1879
1788
|
block.cachedLength = len;
|
|
1880
1789
|
}
|
|
1881
|
-
blockUpdatePathLengths(startBlock,
|
|
1790
|
+
blockUpdatePathLengths(startBlock, stamp, newStructure = false) {
|
|
1882
1791
|
let block = startBlock;
|
|
1883
1792
|
while (block !== undefined) {
|
|
1884
1793
|
if (newStructure) {
|
|
1885
1794
|
this.nodeUpdateLengthNewStructure(block);
|
|
1886
1795
|
}
|
|
1887
1796
|
else {
|
|
1888
|
-
this.blockUpdateLength(block,
|
|
1797
|
+
this.blockUpdateLength(block, stamp);
|
|
1889
1798
|
}
|
|
1890
1799
|
block = block.parent;
|
|
1891
1800
|
}
|
|
1892
1801
|
}
|
|
1893
|
-
blockUpdateLength(node,
|
|
1802
|
+
blockUpdateLength(node, stamp) {
|
|
1894
1803
|
this.blockUpdate(node);
|
|
1895
1804
|
this.localPartialsComputed = false;
|
|
1896
1805
|
if (this.collabWindow.collaborating &&
|
|
1897
|
-
|
|
1898
|
-
seq !== constants_js_1.TreeMaintenanceSequenceNumber) {
|
|
1806
|
+
opstampUtils.isAcked(stamp) &&
|
|
1807
|
+
stamp.seq !== constants_js_1.TreeMaintenanceSequenceNumber) {
|
|
1899
1808
|
if (node.partialLengths !== undefined &&
|
|
1900
1809
|
MergeTree.options.incrementalUpdate &&
|
|
1901
|
-
clientId !== constants_js_1.NonCollabClient) {
|
|
1902
|
-
node.partialLengths.update(node, seq, clientId, this.collabWindow);
|
|
1810
|
+
stamp.clientId !== constants_js_1.NonCollabClient) {
|
|
1811
|
+
node.partialLengths.update(node, stamp.seq, stamp.clientId, this.collabWindow);
|
|
1903
1812
|
}
|
|
1904
1813
|
else {
|
|
1905
1814
|
node.partialLengths = partialLengths_js_1.PartialSequenceLengths.combine(node, this.collabWindow);
|
|
1906
1815
|
}
|
|
1907
|
-
partialLengths_js_1.PartialSequenceLengths.options.verifyExpected?.(this, node, seq, clientId);
|
|
1816
|
+
partialLengths_js_1.PartialSequenceLengths.options.verifyExpected?.(this, node, stamp.seq, stamp.clientId);
|
|
1908
1817
|
}
|
|
1909
1818
|
}
|
|
1910
1819
|
/**
|
|
@@ -1914,16 +1823,16 @@ class MergeTree {
|
|
|
1914
1823
|
*
|
|
1915
1824
|
* See `this.nodeMap` for additional documentation
|
|
1916
1825
|
*/
|
|
1917
|
-
mapRange(handler,
|
|
1826
|
+
mapRange(handler, perspective, accum, start, end, splitRange = false, visibilityPerspective = perspective) {
|
|
1918
1827
|
if (splitRange) {
|
|
1919
1828
|
if (start) {
|
|
1920
|
-
this.ensureIntervalBoundary(start,
|
|
1829
|
+
this.ensureIntervalBoundary(start, perspective);
|
|
1921
1830
|
}
|
|
1922
1831
|
if (end) {
|
|
1923
|
-
this.ensureIntervalBoundary(end,
|
|
1832
|
+
this.ensureIntervalBoundary(end, perspective);
|
|
1924
1833
|
}
|
|
1925
1834
|
}
|
|
1926
|
-
this.nodeMap(
|
|
1835
|
+
this.nodeMap(perspective, (seg, pos, _start, _end) => handler(seg, pos, perspective.refSeq, perspective.clientId, _start, _end, accum), undefined, start, end, visibilityPerspective);
|
|
1927
1836
|
}
|
|
1928
1837
|
/**
|
|
1929
1838
|
* Map over all visible segments in a given range
|
|
@@ -1948,8 +1857,8 @@ class MergeTree {
|
|
|
1948
1857
|
* but it will not count as a segment within the range. That is, it will be
|
|
1949
1858
|
* ignored for the purposes of tracking when traversal should end.
|
|
1950
1859
|
*/
|
|
1951
|
-
nodeMap(
|
|
1952
|
-
const endPos = end ?? this.nodeLength(this.root,
|
|
1860
|
+
nodeMap(perspective, leaf, post, start = 0, end, visibilityPerspective = perspective) {
|
|
1861
|
+
const endPos = end ?? this.nodeLength(this.root, perspective) ?? 0;
|
|
1953
1862
|
if (endPos === start) {
|
|
1954
1863
|
return;
|
|
1955
1864
|
}
|
|
@@ -1958,12 +1867,15 @@ class MergeTree {
|
|
|
1958
1867
|
if (endPos <= pos) {
|
|
1959
1868
|
return mergeTreeNodeWalk_js_1.NodeAction.Exit;
|
|
1960
1869
|
}
|
|
1961
|
-
const len = this.nodeLength(node,
|
|
1962
|
-
const lenAtRefSeq = (
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1870
|
+
const len = this.nodeLength(node, visibilityPerspective);
|
|
1871
|
+
const lenAtRefSeq = (visibilityPerspective === perspective ? len : this.nodeLength(node, perspective)) ??
|
|
1872
|
+
0;
|
|
1873
|
+
// NOTE: This code ensures that obliterates have a chance to mark segments which have been inserted locally
|
|
1874
|
+
// as also having been obliterated on the local client. With the introduction of RemoteObliteratePerspective,
|
|
1875
|
+
// it's feasible we could remove it if the `nodeLength` calculation also respects that perspective for blocks
|
|
1876
|
+
// and not just leaves.
|
|
1877
|
+
const isUnackedAndInObliterate = visibilityPerspective !== perspective &&
|
|
1878
|
+
(!node.isLeaf() || opstampUtils.isLocal(node.insert));
|
|
1967
1879
|
if ((len === undefined && lenAtRefSeq === 0) ||
|
|
1968
1880
|
(len === 0 && !isUnackedAndInObliterate && lenAtRefSeq === 0)) {
|
|
1969
1881
|
return mergeTreeNodeWalk_js_1.NodeAction.Skip;
|
|
@@ -1989,5 +1901,10 @@ MergeTree.options = {
|
|
|
1989
1901
|
insertAfterRemovedSegs: true,
|
|
1990
1902
|
zamboniSegments: true,
|
|
1991
1903
|
};
|
|
1904
|
+
/**
|
|
1905
|
+
* A sentinel value that indicates an inserting walk should continue to the next block sibling.
|
|
1906
|
+
* This can occur for example when tie-break forces insertion of a segment past an entire block (and
|
|
1907
|
+
* the inserting walk first recurses into the block before realizing that).
|
|
1908
|
+
*/
|
|
1992
1909
|
MergeTree.theUnfinishedNode = { childCount: -1 };
|
|
1993
1910
|
//# sourceMappingURL=mergeTree.js.map
|