@fluidframework/sequence 2.0.0-dev.5.2.0.169897 → 2.0.0-dev.6.4.0.191258
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 +142 -0
- package/README.md +4 -3
- package/dist/defaultMap.d.ts +1 -1
- package/dist/defaultMap.d.ts.map +1 -1
- package/dist/defaultMap.js +9 -10
- package/dist/defaultMap.js.map +1 -1
- package/dist/defaultMapInterfaces.d.ts +1 -1
- package/dist/defaultMapInterfaces.d.ts.map +1 -1
- package/dist/defaultMapInterfaces.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -9
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +14 -437
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +96 -916
- package/dist/intervalCollection.js.map +1 -1
- package/dist/intervalIndex/endpointInRangeIndex.d.ts +20 -0
- package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
- package/dist/intervalIndex/endpointInRangeIndex.js +60 -0
- package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -0
- package/dist/intervalIndex/endpointIndex.d.ts +21 -0
- package/dist/intervalIndex/endpointIndex.d.ts.map +1 -0
- package/dist/intervalIndex/endpointIndex.js +42 -0
- package/dist/intervalIndex/endpointIndex.js.map +1 -0
- package/dist/intervalIndex/idIntervalIndex.d.ts +12 -0
- package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -0
- package/dist/intervalIndex/idIntervalIndex.js +41 -0
- package/dist/intervalIndex/idIntervalIndex.js.map +1 -0
- package/dist/intervalIndex/index.d.ts +13 -0
- package/dist/intervalIndex/index.d.ts.map +1 -0
- package/dist/intervalIndex/index.js +20 -0
- package/dist/intervalIndex/index.js.map +1 -0
- package/dist/intervalIndex/intervalIndex.d.ts +29 -0
- package/dist/intervalIndex/intervalIndex.d.ts.map +1 -0
- package/dist/intervalIndex/intervalIndex.js +7 -0
- package/dist/intervalIndex/intervalIndex.js.map +1 -0
- package/dist/intervalIndex/intervalIndexUtils.d.ts +17 -0
- package/dist/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
- package/dist/intervalIndex/intervalIndexUtils.js +22 -0
- package/dist/intervalIndex/intervalIndexUtils.js.map +1 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +33 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.js +103 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +8 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +33 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +33 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.js +7 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -0
- package/dist/intervalIndex/startpointInRangeIndex.d.ts +20 -0
- package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
- package/dist/intervalIndex/startpointInRangeIndex.js +62 -0
- package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -0
- package/dist/intervalTree.d.ts +2 -56
- package/dist/intervalTree.d.ts.map +1 -1
- package/dist/intervalTree.js +2 -11
- package/dist/intervalTree.js.map +1 -1
- package/dist/intervals/index.d.ts +8 -0
- package/dist/intervals/index.d.ts.map +1 -0
- package/dist/intervals/index.js +23 -0
- package/dist/intervals/index.js.map +1 -0
- package/dist/intervals/interval.d.ts +88 -0
- package/dist/intervals/interval.d.ts.map +1 -0
- package/dist/intervals/interval.js +180 -0
- package/dist/intervals/interval.js.map +1 -0
- package/dist/intervals/intervalUtils.d.ts +200 -0
- package/dist/intervals/intervalUtils.d.ts.map +1 -0
- package/dist/intervals/intervalUtils.js +79 -0
- package/dist/intervals/intervalUtils.js.map +1 -0
- package/dist/intervals/sequenceInterval.d.ts +132 -0
- package/dist/intervals/sequenceInterval.d.ts.map +1 -0
- package/dist/intervals/sequenceInterval.js +313 -0
- package/dist/intervals/sequenceInterval.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/revertibles.d.ts +1 -1
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +85 -52
- package/dist/revertibles.js.map +1 -1
- package/dist/sequence.d.ts +33 -4
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +91 -47
- package/dist/sequence.js.map +1 -1
- package/dist/sequenceDeltaEvent.d.ts +8 -3
- package/dist/sequenceDeltaEvent.d.ts.map +1 -1
- package/dist/sequenceDeltaEvent.js +3 -4
- package/dist/sequenceDeltaEvent.js.map +1 -1
- package/dist/sharedIntervalCollection.d.ts +2 -1
- package/dist/sharedIntervalCollection.d.ts.map +1 -1
- package/dist/sharedIntervalCollection.js +2 -2
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/dist/sharedSequence.d.ts +9 -0
- package/dist/sharedSequence.d.ts.map +1 -1
- package/dist/sharedSequence.js +9 -6
- package/dist/sharedSequence.js.map +1 -1
- package/dist/sharedString.d.ts.map +1 -1
- package/dist/sharedString.js +9 -29
- package/dist/sharedString.js.map +1 -1
- package/lib/defaultMap.d.ts +1 -1
- package/lib/defaultMap.d.ts.map +1 -1
- package/lib/defaultMap.js +5 -6
- package/lib/defaultMap.js.map +1 -1
- package/lib/defaultMapInterfaces.d.ts +1 -1
- package/lib/defaultMapInterfaces.d.ts.map +1 -1
- package/lib/defaultMapInterfaces.js.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +14 -437
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +64 -877
- package/lib/intervalCollection.js.map +1 -1
- package/lib/intervalIndex/endpointInRangeIndex.d.ts +20 -0
- package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
- package/lib/intervalIndex/endpointInRangeIndex.js +56 -0
- package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -0
- package/lib/intervalIndex/endpointIndex.d.ts +21 -0
- package/lib/intervalIndex/endpointIndex.d.ts.map +1 -0
- package/lib/intervalIndex/endpointIndex.js +38 -0
- package/lib/intervalIndex/endpointIndex.js.map +1 -0
- package/lib/intervalIndex/idIntervalIndex.d.ts +12 -0
- package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -0
- package/lib/intervalIndex/idIntervalIndex.js +37 -0
- package/lib/intervalIndex/idIntervalIndex.js.map +1 -0
- package/lib/intervalIndex/index.d.ts +13 -0
- package/lib/intervalIndex/index.d.ts.map +1 -0
- package/lib/intervalIndex/index.js +11 -0
- package/lib/intervalIndex/index.js.map +1 -0
- package/lib/intervalIndex/intervalIndex.d.ts +29 -0
- package/lib/intervalIndex/intervalIndex.d.ts.map +1 -0
- package/lib/intervalIndex/intervalIndex.js +6 -0
- package/lib/intervalIndex/intervalIndex.js.map +1 -0
- package/lib/intervalIndex/intervalIndexUtils.d.ts +17 -0
- package/lib/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
- package/lib/intervalIndex/intervalIndexUtils.js +18 -0
- package/lib/intervalIndex/intervalIndexUtils.js.map +1 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +33 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.js +98 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +8 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +29 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +33 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.js +6 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -0
- package/lib/intervalIndex/startpointInRangeIndex.d.ts +20 -0
- package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
- package/lib/intervalIndex/startpointInRangeIndex.js +58 -0
- package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -0
- package/lib/intervalTree.d.ts +2 -56
- package/lib/intervalTree.d.ts.map +1 -1
- package/lib/intervalTree.js +2 -11
- package/lib/intervalTree.js.map +1 -1
- package/lib/intervals/index.d.ts +8 -0
- package/lib/intervals/index.d.ts.map +1 -0
- package/lib/intervals/index.js +8 -0
- package/lib/intervals/index.js.map +1 -0
- package/lib/intervals/interval.d.ts +88 -0
- package/lib/intervals/interval.d.ts.map +1 -0
- package/lib/intervals/interval.js +175 -0
- package/lib/intervals/interval.js.map +1 -0
- package/lib/intervals/intervalUtils.d.ts +200 -0
- package/lib/intervals/intervalUtils.d.ts.map +1 -0
- package/lib/intervals/intervalUtils.js +74 -0
- package/lib/intervals/intervalUtils.js.map +1 -0
- package/lib/intervals/sequenceInterval.d.ts +132 -0
- package/lib/intervals/sequenceInterval.d.ts.map +1 -0
- package/lib/intervals/sequenceInterval.js +305 -0
- package/lib/intervals/sequenceInterval.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/revertibles.d.ts +1 -1
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +69 -36
- package/lib/revertibles.js.map +1 -1
- package/lib/sequence.d.ts +33 -4
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +86 -41
- package/lib/sequence.js.map +1 -1
- package/lib/sequenceDeltaEvent.d.ts +8 -3
- package/lib/sequenceDeltaEvent.d.ts.map +1 -1
- package/lib/sequenceDeltaEvent.js +2 -3
- package/lib/sequenceDeltaEvent.js.map +1 -1
- package/lib/sharedIntervalCollection.d.ts +2 -1
- package/lib/sharedIntervalCollection.d.ts.map +1 -1
- package/lib/sharedIntervalCollection.js +1 -1
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/lib/sharedSequence.d.ts +9 -0
- package/lib/sharedSequence.d.ts.map +1 -1
- package/lib/sharedSequence.js +8 -5
- package/lib/sharedSequence.js.map +1 -1
- package/lib/sharedString.d.ts.map +1 -1
- package/lib/sharedString.js +9 -29
- package/lib/sharedString.js.map +1 -1
- package/package.json +31 -34
- package/src/defaultMap.ts +2 -1
- package/src/defaultMapInterfaces.ts +1 -1
- package/src/index.ts +21 -9
- package/src/intervalCollection.ts +118 -1403
- package/src/intervalIndex/endpointInRangeIndex.ts +104 -0
- package/src/intervalIndex/endpointIndex.ts +78 -0
- package/src/intervalIndex/idIntervalIndex.ts +58 -0
- package/src/intervalIndex/index.ts +16 -0
- package/src/intervalIndex/intervalIndex.ts +31 -0
- package/src/intervalIndex/intervalIndexUtils.ts +27 -0
- package/src/intervalIndex/overlappingIntervalsIndex.ts +162 -0
- package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +71 -0
- package/src/intervalIndex/sequenceIntervalIndexes.ts +32 -0
- package/src/intervalIndex/startpointInRangeIndex.ts +109 -0
- package/src/intervalTree.ts +3 -75
- package/src/intervals/index.ts +25 -0
- package/src/intervals/interval.ts +230 -0
- package/src/intervals/intervalUtils.ts +256 -0
- package/src/intervals/sequenceInterval.ts +494 -0
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +81 -16
- package/src/sequence.ts +100 -35
- package/src/sequenceDeltaEvent.ts +12 -4
- package/src/sharedIntervalCollection.ts +2 -3
- package/src/sharedSequence.ts +11 -5
- package/src/sharedString.ts +8 -25
|
@@ -2,53 +2,15 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
6
|
-
var t = {};
|
|
7
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
8
|
-
t[p] = s[p];
|
|
9
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
10
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
11
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
12
|
-
t[p[i]] = s[p[i]];
|
|
13
|
-
}
|
|
14
|
-
return t;
|
|
15
|
-
};
|
|
16
5
|
/* eslint-disable no-bitwise */
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { addProperties,
|
|
20
|
-
import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
6
|
+
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
7
|
+
import { assert } from "@fluidframework/core-utils";
|
|
8
|
+
import { addProperties, createMap, getSlideToSegoff, MergeTreeDeltaType, ReferenceType, refTypeIncludesFlag, reservedRangeLabelsKey, UnassignedSequenceNumber, DetachedReferencePosition, } from "@fluidframework/merge-tree";
|
|
9
|
+
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
|
|
21
10
|
import { v4 as uuid } from "uuid";
|
|
22
|
-
import {
|
|
11
|
+
import { IntervalOpType, IntervalStickiness, IntervalType, SequenceInterval, createPositionReferenceFromSegoff, endReferenceSlidingPreference, startReferenceSlidingPreference, sequenceIntervalHelpers, createInterval, } from "./intervals";
|
|
12
|
+
import { createEndpointIndex, createIdIntervalIndex, createOverlappingIntervalsIndex, } from "./intervalIndex";
|
|
23
13
|
const reservedIntervalIdKey = "intervalId";
|
|
24
|
-
/**
|
|
25
|
-
* Values are used in persisted formats (ops) and revertibles.
|
|
26
|
-
* @alpha
|
|
27
|
-
*/
|
|
28
|
-
export const IntervalOpType = {
|
|
29
|
-
ADD: "add",
|
|
30
|
-
DELETE: "delete",
|
|
31
|
-
CHANGE: "change",
|
|
32
|
-
PROPERTY_CHANGED: "propertyChanged",
|
|
33
|
-
POSITION_REMOVE: "positionRemove",
|
|
34
|
-
};
|
|
35
|
-
export var IntervalType;
|
|
36
|
-
(function (IntervalType) {
|
|
37
|
-
IntervalType[IntervalType["Simple"] = 0] = "Simple";
|
|
38
|
-
IntervalType[IntervalType["Nest"] = 1] = "Nest";
|
|
39
|
-
/**
|
|
40
|
-
* SlideOnRemove indicates that the ends of the interval will slide if the segment
|
|
41
|
-
* they reference is removed and acked.
|
|
42
|
-
* See `packages\dds\merge-tree\docs\REFERENCEPOSITIONS.md` for details
|
|
43
|
-
* SlideOnRemove is the default interval behavior and does not need to be specified.
|
|
44
|
-
*/
|
|
45
|
-
IntervalType[IntervalType["SlideOnRemove"] = 2] = "SlideOnRemove";
|
|
46
|
-
/**
|
|
47
|
-
* A temporary interval, used internally
|
|
48
|
-
* @internal
|
|
49
|
-
*/
|
|
50
|
-
IntervalType[IntervalType["Transient"] = 4] = "Transient";
|
|
51
|
-
})(IntervalType || (IntervalType = {}));
|
|
52
14
|
/**
|
|
53
15
|
* Decompress an interval after loading a summary from JSON. The exact format
|
|
54
16
|
* of this compression is unspecified and subject to change
|
|
@@ -59,7 +21,7 @@ function decompressInterval(interval, label) {
|
|
|
59
21
|
end: interval[1],
|
|
60
22
|
sequenceNumber: interval[2],
|
|
61
23
|
intervalType: interval[3],
|
|
62
|
-
properties:
|
|
24
|
+
properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
|
|
63
25
|
stickiness: interval[5],
|
|
64
26
|
};
|
|
65
27
|
}
|
|
@@ -74,764 +36,23 @@ function compressInterval(interval) {
|
|
|
74
36
|
end,
|
|
75
37
|
sequenceNumber,
|
|
76
38
|
intervalType,
|
|
77
|
-
|
|
39
|
+
// remove the `referenceRangeLabels` property as it is already stored
|
|
40
|
+
// in the `label` field of the summary
|
|
41
|
+
{ ...properties, [reservedRangeLabelsKey]: undefined },
|
|
78
42
|
];
|
|
79
43
|
if (interval.stickiness !== undefined && interval.stickiness !== IntervalStickiness.END) {
|
|
80
44
|
base.push(interval.stickiness);
|
|
81
45
|
}
|
|
82
46
|
return base;
|
|
83
47
|
}
|
|
84
|
-
function startReferenceSlidingPreference(stickiness) {
|
|
85
|
-
// if any start stickiness, prefer sliding backwards
|
|
86
|
-
return (stickiness & IntervalStickiness.START) !== 0
|
|
87
|
-
? SlidingPreference.BACKWARD
|
|
88
|
-
: SlidingPreference.FORWARD;
|
|
89
|
-
}
|
|
90
|
-
function endReferenceSlidingPreference(stickiness) {
|
|
91
|
-
// if any end stickiness, prefer sliding forwards
|
|
92
|
-
return (stickiness & IntervalStickiness.END) !== 0
|
|
93
|
-
? SlidingPreference.FORWARD
|
|
94
|
-
: SlidingPreference.BACKWARD;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Determines how an interval should expand when segments are inserted adjacent
|
|
98
|
-
* to the range it spans
|
|
99
|
-
*
|
|
100
|
-
* Note that interval stickiness is currently an experimental feature and must
|
|
101
|
-
* be explicitly enabled with the `intervalStickinessEnabled` flag
|
|
102
|
-
*/
|
|
103
|
-
export const IntervalStickiness = {
|
|
104
|
-
/**
|
|
105
|
-
* Interval does not expand to include adjacent segments
|
|
106
|
-
*/
|
|
107
|
-
NONE: 0b00,
|
|
108
|
-
/**
|
|
109
|
-
* Interval expands to include segments inserted adjacent to the start
|
|
110
|
-
*/
|
|
111
|
-
START: 0b01,
|
|
112
|
-
/**
|
|
113
|
-
* Interval expands to include segments inserted adjacent to the end
|
|
114
|
-
*
|
|
115
|
-
* This is the default stickiness
|
|
116
|
-
*/
|
|
117
|
-
END: 0b10,
|
|
118
|
-
/**
|
|
119
|
-
* Interval expands to include all segments inserted adjacent to it
|
|
120
|
-
*/
|
|
121
|
-
FULL: 0b11,
|
|
122
|
-
};
|
|
123
|
-
/**
|
|
124
|
-
* Serializable interval whose endpoints are plain-old numbers.
|
|
125
|
-
*/
|
|
126
|
-
export class Interval {
|
|
127
|
-
constructor(start, end, props) {
|
|
128
|
-
this.start = start;
|
|
129
|
-
this.end = end;
|
|
130
|
-
this.propertyManager = new PropertiesManager();
|
|
131
|
-
this.properties = {};
|
|
132
|
-
if (props) {
|
|
133
|
-
this.addProperties(props);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* {@inheritDoc ISerializableInterval.getIntervalId}
|
|
138
|
-
*/
|
|
139
|
-
getIntervalId() {
|
|
140
|
-
var _a;
|
|
141
|
-
const id = (_a = this.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
142
|
-
assert(id !== undefined, 0x5e1 /* interval ID should not be undefined */);
|
|
143
|
-
return `${id}`;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* @returns an array containing any auxiliary property sets added with `addPropertySet`.
|
|
147
|
-
*/
|
|
148
|
-
getAdditionalPropertySets() {
|
|
149
|
-
var _a;
|
|
150
|
-
return (_a = this.auxProps) !== null && _a !== void 0 ? _a : [];
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Adds an auxiliary set of properties to this interval.
|
|
154
|
-
* These properties can be recovered using `getAdditionalPropertySets`
|
|
155
|
-
* @param props - set of properties to add
|
|
156
|
-
* @remarks - This gets called as part of the default conflict resolver for `IIntervalCollection<Interval>`
|
|
157
|
-
* (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
|
|
158
|
-
* This functionality seems half-baked.
|
|
159
|
-
*/
|
|
160
|
-
addPropertySet(props) {
|
|
161
|
-
if (this.auxProps === undefined) {
|
|
162
|
-
this.auxProps = [];
|
|
163
|
-
}
|
|
164
|
-
this.auxProps.push(props);
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* {@inheritDoc ISerializableInterval.serialize}
|
|
168
|
-
* @internal
|
|
169
|
-
*/
|
|
170
|
-
serialize() {
|
|
171
|
-
const serializedInterval = {
|
|
172
|
-
end: this.end,
|
|
173
|
-
intervalType: 0,
|
|
174
|
-
sequenceNumber: 0,
|
|
175
|
-
start: this.start,
|
|
176
|
-
};
|
|
177
|
-
if (this.properties) {
|
|
178
|
-
serializedInterval.properties = this.properties;
|
|
179
|
-
}
|
|
180
|
-
return serializedInterval;
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* {@inheritDoc IInterval.clone}
|
|
184
|
-
*/
|
|
185
|
-
clone() {
|
|
186
|
-
return new Interval(this.start, this.end, this.properties);
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* {@inheritDoc IInterval.compare}
|
|
190
|
-
*/
|
|
191
|
-
compare(b) {
|
|
192
|
-
const startResult = this.compareStart(b);
|
|
193
|
-
if (startResult === 0) {
|
|
194
|
-
const endResult = this.compareEnd(b);
|
|
195
|
-
if (endResult === 0) {
|
|
196
|
-
const thisId = this.getIntervalId();
|
|
197
|
-
if (thisId) {
|
|
198
|
-
const bId = b.getIntervalId();
|
|
199
|
-
if (bId) {
|
|
200
|
-
return thisId > bId ? 1 : thisId < bId ? -1 : 0;
|
|
201
|
-
}
|
|
202
|
-
return 0;
|
|
203
|
-
}
|
|
204
|
-
return 0;
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
return endResult;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
return startResult;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* {@inheritDoc IInterval.compareStart}
|
|
216
|
-
*/
|
|
217
|
-
compareStart(b) {
|
|
218
|
-
return this.start - b.start;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* {@inheritDoc IInterval.compareEnd}
|
|
222
|
-
*/
|
|
223
|
-
compareEnd(b) {
|
|
224
|
-
return this.end - b.end;
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* {@inheritDoc IInterval.overlaps}
|
|
228
|
-
*/
|
|
229
|
-
overlaps(b) {
|
|
230
|
-
const result = this.start <= b.end && this.end >= b.start;
|
|
231
|
-
return result;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* {@inheritDoc IInterval.union}
|
|
235
|
-
* @deprecated - This API was never intended to be public and will be marked internal in a future release.
|
|
236
|
-
*/
|
|
237
|
-
union(b) {
|
|
238
|
-
return new Interval(Math.min(this.start, b.start), Math.max(this.end, b.end), this.properties);
|
|
239
|
-
}
|
|
240
|
-
getProperties() {
|
|
241
|
-
return this.properties;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* {@inheritDoc ISerializableInterval.addProperties}
|
|
245
|
-
* @deprecated - This API was never intended to be public and will be marked internal in a future release.
|
|
246
|
-
*/
|
|
247
|
-
addProperties(newProps, collaborating = false, seq, op) {
|
|
248
|
-
if (newProps) {
|
|
249
|
-
this.initializeProperties();
|
|
250
|
-
return this.propertyManager.addProperties(this.properties, newProps, op, seq, collaborating);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* {@inheritDoc IInterval.modify}
|
|
255
|
-
* @deprecated - This API was never intended to be public and will be marked internal in a future release.
|
|
256
|
-
*/
|
|
257
|
-
modify(label, start, end, op) {
|
|
258
|
-
const startPos = start !== null && start !== void 0 ? start : this.start;
|
|
259
|
-
const endPos = end !== null && end !== void 0 ? end : this.end;
|
|
260
|
-
if (this.start === startPos && this.end === endPos) {
|
|
261
|
-
// Return undefined to indicate that no change is necessary.
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
const newInterval = new Interval(startPos, endPos);
|
|
265
|
-
if (this.properties) {
|
|
266
|
-
newInterval.initializeProperties();
|
|
267
|
-
this.propertyManager.copyTo(this.properties, newInterval.properties, newInterval.propertyManager);
|
|
268
|
-
}
|
|
269
|
-
return newInterval;
|
|
270
|
-
}
|
|
271
|
-
initializeProperties() {
|
|
272
|
-
if (!this.propertyManager) {
|
|
273
|
-
this.propertyManager = new PropertiesManager();
|
|
274
|
-
}
|
|
275
|
-
if (!this.properties) {
|
|
276
|
-
this.properties = createMap();
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Interval implementation whose ends are associated with positions in a mutatable sequence.
|
|
282
|
-
* As such, when content is inserted into the middle of the interval, the interval expands to
|
|
283
|
-
* include that content.
|
|
284
|
-
*
|
|
285
|
-
* @remarks - The endpoint's position should be treated exclusively to get reasonable behavior--i.e.
|
|
286
|
-
* an interval referring to "hello" in "hello world" should have a start position of 0 and an end
|
|
287
|
-
* position of 5.
|
|
288
|
-
*
|
|
289
|
-
* To see why, consider what happens if "llo wor" is removed from the string to make "held".
|
|
290
|
-
* The interval's startpoint remains on the "h" (it isn't altered), but the interval's endpoint
|
|
291
|
-
* slides forward to the next unremoved position, which is the "l" in "held".
|
|
292
|
-
* Users would generally expect the interval to now refer to "he" (as it is the subset of content
|
|
293
|
-
* remaining after the removal), hence the "l" should be excluded.
|
|
294
|
-
* If the interval endpoint was treated inclusively, the interval would now refer to "hel", which
|
|
295
|
-
* is undesirable.
|
|
296
|
-
*
|
|
297
|
-
* Since the end of an interval is treated exclusively but cannot be greater than or equal to the
|
|
298
|
-
* length of the associated sequence, application models which leverage interval collections should
|
|
299
|
-
* consider inserting a marker at the end of the sequence to represent the end of the content.
|
|
300
|
-
*/
|
|
301
|
-
export class SequenceInterval {
|
|
302
|
-
constructor(client,
|
|
303
|
-
/**
|
|
304
|
-
* Start endpoint of this interval.
|
|
305
|
-
* @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
|
|
306
|
-
*/
|
|
307
|
-
start,
|
|
308
|
-
/**
|
|
309
|
-
* End endpoint of this interval.
|
|
310
|
-
* @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
|
|
311
|
-
*/
|
|
312
|
-
end, intervalType, props, stickiness = IntervalStickiness.END) {
|
|
313
|
-
this.client = client;
|
|
314
|
-
this.start = start;
|
|
315
|
-
this.end = end;
|
|
316
|
-
this.intervalType = intervalType;
|
|
317
|
-
this.stickiness = stickiness;
|
|
318
|
-
this.propertyManager = new PropertiesManager();
|
|
319
|
-
this.properties = {};
|
|
320
|
-
if (props) {
|
|
321
|
-
this.addProperties(props);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Subscribes to position change events on this interval if there are no current listeners.
|
|
326
|
-
* @internal
|
|
327
|
-
*/
|
|
328
|
-
addPositionChangeListeners(beforePositionChange, afterPositionChange) {
|
|
329
|
-
var _a, _b;
|
|
330
|
-
var _c, _d;
|
|
331
|
-
if (this.callbacks === undefined) {
|
|
332
|
-
this.callbacks = {
|
|
333
|
-
beforePositionChange,
|
|
334
|
-
afterPositionChange,
|
|
335
|
-
};
|
|
336
|
-
const startCbs = ((_a = (_c = this.start).callbacks) !== null && _a !== void 0 ? _a : (_c.callbacks = {}));
|
|
337
|
-
const endCbs = ((_b = (_d = this.end).callbacks) !== null && _b !== void 0 ? _b : (_d.callbacks = {}));
|
|
338
|
-
startCbs.beforeSlide = endCbs.beforeSlide = beforePositionChange;
|
|
339
|
-
startCbs.afterSlide = endCbs.afterSlide = afterPositionChange;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Removes the currently subscribed position change listeners.
|
|
344
|
-
* @internal
|
|
345
|
-
*/
|
|
346
|
-
removePositionChangeListeners() {
|
|
347
|
-
if (this.callbacks) {
|
|
348
|
-
this.callbacks = undefined;
|
|
349
|
-
this.start.callbacks = undefined;
|
|
350
|
-
this.end.callbacks = undefined;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* {@inheritDoc ISerializableInterval.serialize}
|
|
355
|
-
* @internal
|
|
356
|
-
*/
|
|
357
|
-
serialize() {
|
|
358
|
-
const startPosition = this.client.localReferencePositionToPosition(this.start);
|
|
359
|
-
const endPosition = this.client.localReferencePositionToPosition(this.end);
|
|
360
|
-
const serializedInterval = {
|
|
361
|
-
end: endPosition,
|
|
362
|
-
intervalType: this.intervalType,
|
|
363
|
-
sequenceNumber: this.client.getCurrentSeq(),
|
|
364
|
-
start: startPosition,
|
|
365
|
-
};
|
|
366
|
-
if (this.properties) {
|
|
367
|
-
serializedInterval.properties = this.properties;
|
|
368
|
-
}
|
|
369
|
-
if (this.stickiness !== IntervalStickiness.END) {
|
|
370
|
-
serializedInterval.stickiness = this.stickiness;
|
|
371
|
-
}
|
|
372
|
-
return serializedInterval;
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* {@inheritDoc IInterval.clone}
|
|
376
|
-
*/
|
|
377
|
-
clone() {
|
|
378
|
-
return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties, this.stickiness);
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* {@inheritDoc IInterval.compare}
|
|
382
|
-
*/
|
|
383
|
-
compare(b) {
|
|
384
|
-
const startResult = this.compareStart(b);
|
|
385
|
-
if (startResult === 0) {
|
|
386
|
-
const endResult = this.compareEnd(b);
|
|
387
|
-
if (endResult === 0) {
|
|
388
|
-
const thisId = this.getIntervalId();
|
|
389
|
-
if (thisId) {
|
|
390
|
-
const bId = b.getIntervalId();
|
|
391
|
-
if (bId) {
|
|
392
|
-
return thisId > bId ? 1 : thisId < bId ? -1 : 0;
|
|
393
|
-
}
|
|
394
|
-
return 0;
|
|
395
|
-
}
|
|
396
|
-
return 0;
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
return endResult;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
return startResult;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* {@inheritDoc IInterval.compareStart}
|
|
408
|
-
*/
|
|
409
|
-
compareStart(b) {
|
|
410
|
-
return compareReferencePositions(this.start, b.start);
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* {@inheritDoc IInterval.compareEnd}
|
|
414
|
-
*/
|
|
415
|
-
compareEnd(b) {
|
|
416
|
-
return compareReferencePositions(this.end, b.end);
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* {@inheritDoc IInterval.overlaps}
|
|
420
|
-
*/
|
|
421
|
-
overlaps(b) {
|
|
422
|
-
const result = compareReferencePositions(this.start, b.end) <= 0 &&
|
|
423
|
-
compareReferencePositions(this.end, b.start) >= 0;
|
|
424
|
-
return result;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* {@inheritDoc ISerializableInterval.getIntervalId}
|
|
428
|
-
*/
|
|
429
|
-
getIntervalId() {
|
|
430
|
-
var _a;
|
|
431
|
-
const id = (_a = this.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
432
|
-
assert(id !== undefined, 0x5e2 /* interval ID should not be undefined */);
|
|
433
|
-
return `${id}`;
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* {@inheritDoc IInterval.union}
|
|
437
|
-
* @deprecated - This API was never intended to be public and will be marked internal in a future release.
|
|
438
|
-
*/
|
|
439
|
-
union(b) {
|
|
440
|
-
return new SequenceInterval(this.client, minReferencePosition(this.start, b.start), maxReferencePosition(this.end, b.end), this.intervalType);
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* {@inheritDoc ISerializableInterval.addProperties}
|
|
444
|
-
* @deprecated - This API was never intended to be public and will be marked internal in a future release.
|
|
445
|
-
*/
|
|
446
|
-
addProperties(newProps, collab = false, seq, op) {
|
|
447
|
-
this.initializeProperties();
|
|
448
|
-
return this.propertyManager.addProperties(this.properties, newProps, op, seq, collab);
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* @returns whether this interval overlaps two numerical positions.
|
|
452
|
-
*/
|
|
453
|
-
overlapsPos(bstart, bend) {
|
|
454
|
-
const startPos = this.client.localReferencePositionToPosition(this.start);
|
|
455
|
-
const endPos = this.client.localReferencePositionToPosition(this.end);
|
|
456
|
-
return endPos > bstart && startPos < bend;
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* {@inheritDoc IInterval.modify}
|
|
460
|
-
* @deprecated - This API was never intended to be public and will be marked internal in a future release.
|
|
461
|
-
*/
|
|
462
|
-
modify(label, start, end, op, localSeq, stickiness = IntervalStickiness.END) {
|
|
463
|
-
const getRefType = (baseType) => {
|
|
464
|
-
let refType = baseType;
|
|
465
|
-
if (op === undefined) {
|
|
466
|
-
refType &= ~ReferenceType.SlideOnRemove;
|
|
467
|
-
refType |= ReferenceType.StayOnRemove;
|
|
468
|
-
}
|
|
469
|
-
return refType;
|
|
470
|
-
};
|
|
471
|
-
let startRef = this.start;
|
|
472
|
-
if (start !== undefined) {
|
|
473
|
-
startRef = createPositionReference(this.client, start, getRefType(this.start.refType), op, undefined, localSeq, startReferenceSlidingPreference(stickiness));
|
|
474
|
-
if (this.start.properties) {
|
|
475
|
-
startRef.addProperties(this.start.properties);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
let endRef = this.end;
|
|
479
|
-
if (end !== undefined) {
|
|
480
|
-
endRef = createPositionReference(this.client, end, getRefType(this.end.refType), op, undefined, localSeq, endReferenceSlidingPreference(stickiness));
|
|
481
|
-
if (this.end.properties) {
|
|
482
|
-
endRef.addProperties(this.end.properties);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
const newInterval = new SequenceInterval(this.client, startRef, endRef, this.intervalType);
|
|
486
|
-
if (this.properties) {
|
|
487
|
-
newInterval.initializeProperties();
|
|
488
|
-
this.propertyManager.copyTo(this.properties, newInterval.properties, newInterval.propertyManager);
|
|
489
|
-
}
|
|
490
|
-
return newInterval;
|
|
491
|
-
}
|
|
492
|
-
initializeProperties() {
|
|
493
|
-
if (!this.propertyManager) {
|
|
494
|
-
this.propertyManager = new PropertiesManager();
|
|
495
|
-
}
|
|
496
|
-
if (!this.properties) {
|
|
497
|
-
this.properties = createMap();
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot, slidingPreference) {
|
|
502
|
-
if (segoff.segment) {
|
|
503
|
-
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined, slidingPreference);
|
|
504
|
-
return ref;
|
|
505
|
-
}
|
|
506
|
-
// Creating references on detached segments is allowed for:
|
|
507
|
-
// - Transient segments
|
|
508
|
-
// - References coming from a remote client (location may have been concurrently removed)
|
|
509
|
-
// - References being rebased to a new sequence number
|
|
510
|
-
// (segment they originally referred to may have been removed with no suitable replacement)
|
|
511
|
-
if (!op &&
|
|
512
|
-
!localSeq &&
|
|
513
|
-
!fromSnapshot &&
|
|
514
|
-
!refTypeIncludesFlag(refType, ReferenceType.Transient)) {
|
|
515
|
-
throw new UsageError("Non-transient references need segment");
|
|
516
|
-
}
|
|
517
|
-
return createDetachedLocalReferencePosition(refType);
|
|
518
|
-
}
|
|
519
|
-
function createPositionReference(client, pos, refType, op, fromSnapshot, localSeq, slidingPreference) {
|
|
520
|
-
let segoff;
|
|
521
|
-
if (op) {
|
|
522
|
-
assert((refType & ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
|
|
523
|
-
segoff = client.getContainingSegment(pos, {
|
|
524
|
-
referenceSequenceNumber: op.referenceSequenceNumber,
|
|
525
|
-
clientId: op.clientId,
|
|
526
|
-
});
|
|
527
|
-
segoff = client.getSlideToSegment(segoff);
|
|
528
|
-
}
|
|
529
|
-
else {
|
|
530
|
-
assert((refType & ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
|
|
531
|
-
segoff = client.getContainingSegment(pos, undefined, localSeq);
|
|
532
|
-
}
|
|
533
|
-
return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot, slidingPreference);
|
|
534
|
-
}
|
|
535
|
-
export function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot, stickiness = IntervalStickiness.END) {
|
|
536
|
-
let beginRefType = ReferenceType.RangeBegin;
|
|
537
|
-
let endRefType = ReferenceType.RangeEnd;
|
|
538
|
-
if (intervalType === IntervalType.Transient) {
|
|
539
|
-
beginRefType = ReferenceType.Transient;
|
|
540
|
-
endRefType = ReferenceType.Transient;
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
if (intervalType === IntervalType.Nest) {
|
|
544
|
-
beginRefType = ReferenceType.NestBegin;
|
|
545
|
-
endRefType = ReferenceType.NestEnd;
|
|
546
|
-
}
|
|
547
|
-
// All non-transient interval references must eventually be SlideOnRemove
|
|
548
|
-
// To ensure eventual consistency, they must start as StayOnRemove when
|
|
549
|
-
// pending (created locally and creation op is not acked)
|
|
550
|
-
if (op || fromSnapshot) {
|
|
551
|
-
beginRefType |= ReferenceType.SlideOnRemove;
|
|
552
|
-
endRefType |= ReferenceType.SlideOnRemove;
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
555
|
-
beginRefType |= ReferenceType.StayOnRemove;
|
|
556
|
-
endRefType |= ReferenceType.StayOnRemove;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot, undefined, startReferenceSlidingPreference(stickiness));
|
|
560
|
-
const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot, undefined, endReferenceSlidingPreference(stickiness));
|
|
561
|
-
const rangeProp = {
|
|
562
|
-
[reservedRangeLabelsKey]: [label],
|
|
563
|
-
};
|
|
564
|
-
startLref.addProperties(rangeProp);
|
|
565
|
-
endLref.addProperties(rangeProp);
|
|
566
|
-
const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp, stickiness);
|
|
567
|
-
return ival;
|
|
568
|
-
}
|
|
569
48
|
export function createIntervalIndex() {
|
|
570
49
|
const helpers = {
|
|
571
|
-
compareEnds:
|
|
50
|
+
compareEnds: (a, b) => a.end - b.end,
|
|
572
51
|
create: createInterval,
|
|
573
52
|
};
|
|
574
53
|
const lc = new LocalIntervalCollection(undefined, "", helpers);
|
|
575
54
|
return lc;
|
|
576
55
|
}
|
|
577
|
-
class OverlappingIntervalsIndex {
|
|
578
|
-
constructor(client, helpers) {
|
|
579
|
-
this.client = client;
|
|
580
|
-
this.helpers = helpers;
|
|
581
|
-
this.intervalTree = new IntervalTree();
|
|
582
|
-
}
|
|
583
|
-
map(fn) {
|
|
584
|
-
this.intervalTree.map(fn);
|
|
585
|
-
}
|
|
586
|
-
mapUntil(fn) {
|
|
587
|
-
this.intervalTree.mapUntil(fn);
|
|
588
|
-
}
|
|
589
|
-
gatherIterationResults(results, iteratesForward, start, end) {
|
|
590
|
-
if (this.intervalTree.intervals.isEmpty()) {
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
if (start === undefined && end === undefined) {
|
|
594
|
-
// No start/end provided. Gather the whole tree in the specified order.
|
|
595
|
-
if (iteratesForward) {
|
|
596
|
-
this.intervalTree.map((interval) => {
|
|
597
|
-
results.push(interval);
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
this.intervalTree.mapBackward((interval) => {
|
|
602
|
-
results.push(interval);
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
else {
|
|
607
|
-
const transientInterval = this.helpers.create("transient", start, end, this.client, IntervalType.Transient);
|
|
608
|
-
if (start === undefined) {
|
|
609
|
-
// Only end position provided. Since the tree is not sorted by end position,
|
|
610
|
-
// walk the whole tree in the specified order, gathering intervals that match the end.
|
|
611
|
-
if (iteratesForward) {
|
|
612
|
-
this.intervalTree.map((interval) => {
|
|
613
|
-
if (transientInterval.compareEnd(interval) === 0) {
|
|
614
|
-
results.push(interval);
|
|
615
|
-
}
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
this.intervalTree.mapBackward((interval) => {
|
|
620
|
-
if (transientInterval.compareEnd(interval) === 0) {
|
|
621
|
-
results.push(interval);
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
else {
|
|
627
|
-
// Start and (possibly) end provided. Walk the subtrees that may contain
|
|
628
|
-
// this start position.
|
|
629
|
-
const compareFn = end === undefined
|
|
630
|
-
? (node) => {
|
|
631
|
-
return transientInterval.compareStart(node.key);
|
|
632
|
-
}
|
|
633
|
-
: (node) => {
|
|
634
|
-
return transientInterval.compare(node.key);
|
|
635
|
-
};
|
|
636
|
-
const continueLeftFn = (cmpResult) => cmpResult <= 0;
|
|
637
|
-
const continueRightFn = (cmpResult) => cmpResult >= 0;
|
|
638
|
-
const actionFn = (node) => {
|
|
639
|
-
results.push(node.key);
|
|
640
|
-
};
|
|
641
|
-
if (iteratesForward) {
|
|
642
|
-
this.intervalTree.intervals.walkExactMatchesForward(compareFn, actionFn, continueLeftFn, continueRightFn);
|
|
643
|
-
}
|
|
644
|
-
else {
|
|
645
|
-
this.intervalTree.intervals.walkExactMatchesBackward(compareFn, actionFn, continueLeftFn, continueRightFn);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* @returns an array of all intervals contained in this collection that overlap the range
|
|
652
|
-
* `[startPosition, endPosition)`.
|
|
653
|
-
*/
|
|
654
|
-
findOverlappingIntervals(startPosition, endPosition) {
|
|
655
|
-
if (endPosition < startPosition || this.intervalTree.intervals.isEmpty()) {
|
|
656
|
-
return [];
|
|
657
|
-
}
|
|
658
|
-
const transientInterval = this.helpers.create("transient", startPosition, endPosition, this.client, IntervalType.Transient);
|
|
659
|
-
const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
|
|
660
|
-
return overlappingIntervalNodes.map((node) => node.key);
|
|
661
|
-
}
|
|
662
|
-
remove(interval) {
|
|
663
|
-
this.intervalTree.removeExisting(interval);
|
|
664
|
-
}
|
|
665
|
-
add(interval) {
|
|
666
|
-
this.intervalTree.put(interval);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
class IdIntervalIndex {
|
|
670
|
-
constructor() {
|
|
671
|
-
this.intervalIdMap = new Map();
|
|
672
|
-
}
|
|
673
|
-
add(interval) {
|
|
674
|
-
const id = interval.getIntervalId();
|
|
675
|
-
assert(id !== undefined, 0x2c0 /* "ID must be created before adding interval to collection" */);
|
|
676
|
-
// Make the ID immutable.
|
|
677
|
-
Object.defineProperty(interval.properties, reservedIntervalIdKey, {
|
|
678
|
-
configurable: false,
|
|
679
|
-
enumerable: true,
|
|
680
|
-
writable: false,
|
|
681
|
-
});
|
|
682
|
-
this.intervalIdMap.set(id, interval);
|
|
683
|
-
}
|
|
684
|
-
remove(interval) {
|
|
685
|
-
const id = interval.getIntervalId();
|
|
686
|
-
assert(id !== undefined, 0x311 /* expected id to exist on interval */);
|
|
687
|
-
this.intervalIdMap.delete(id);
|
|
688
|
-
}
|
|
689
|
-
getIntervalById(id) {
|
|
690
|
-
return this.intervalIdMap.get(id);
|
|
691
|
-
}
|
|
692
|
-
[Symbol.iterator]() {
|
|
693
|
-
return this.intervalIdMap.values();
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
class EndpointIndex {
|
|
697
|
-
constructor(client, helpers) {
|
|
698
|
-
this.client = client;
|
|
699
|
-
this.helpers = helpers;
|
|
700
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
701
|
-
this.endIntervalTree = new RedBlackTree(helpers.compareEnds);
|
|
702
|
-
}
|
|
703
|
-
previousInterval(pos) {
|
|
704
|
-
const transientInterval = this.helpers.create("transient", pos, pos, this.client, IntervalType.Transient);
|
|
705
|
-
const rbNode = this.endIntervalTree.floor(transientInterval);
|
|
706
|
-
if (rbNode) {
|
|
707
|
-
return rbNode.data;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
nextInterval(pos) {
|
|
711
|
-
const transientInterval = this.helpers.create("transient", pos, pos, this.client, IntervalType.Transient);
|
|
712
|
-
const rbNode = this.endIntervalTree.ceil(transientInterval);
|
|
713
|
-
if (rbNode) {
|
|
714
|
-
return rbNode.data;
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
add(interval) {
|
|
718
|
-
this.endIntervalTree.put(interval, interval);
|
|
719
|
-
}
|
|
720
|
-
remove(interval) {
|
|
721
|
-
this.endIntervalTree.remove(interval);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
/**
|
|
725
|
-
* Interface for intervals that have comparison override properties.
|
|
726
|
-
*/
|
|
727
|
-
const forceCompare = Symbol();
|
|
728
|
-
/**
|
|
729
|
-
* Compares two objects based on their comparison override properties.
|
|
730
|
-
* @returns A number indicating the order of the intervals (negative for a is lower than b, 0 for tie, positive for a is greater than b).
|
|
731
|
-
*/
|
|
732
|
-
function compareOverrideables(a, b) {
|
|
733
|
-
var _a, _b;
|
|
734
|
-
const forceCompareA = (_a = a[forceCompare]) !== null && _a !== void 0 ? _a : 0;
|
|
735
|
-
const forceCompareB = (_b = b[forceCompare]) !== null && _b !== void 0 ? _b : 0;
|
|
736
|
-
return forceCompareA - forceCompareB;
|
|
737
|
-
}
|
|
738
|
-
class EndpointInRangeIndex {
|
|
739
|
-
constructor(helpers, client) {
|
|
740
|
-
this.helpers = helpers;
|
|
741
|
-
this.client = client;
|
|
742
|
-
this.intervalTree = new RedBlackTree((a, b) => {
|
|
743
|
-
const compareEndsResult = helpers.compareEnds(a, b);
|
|
744
|
-
if (compareEndsResult !== 0) {
|
|
745
|
-
return compareEndsResult;
|
|
746
|
-
}
|
|
747
|
-
const overrideablesComparison = compareOverrideables(a, b);
|
|
748
|
-
if (overrideablesComparison !== 0) {
|
|
749
|
-
return overrideablesComparison;
|
|
750
|
-
}
|
|
751
|
-
const aId = a.getIntervalId();
|
|
752
|
-
const bId = b.getIntervalId();
|
|
753
|
-
if (aId !== undefined && bId !== undefined) {
|
|
754
|
-
return aId.localeCompare(bId);
|
|
755
|
-
}
|
|
756
|
-
return 0;
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
add(interval) {
|
|
760
|
-
this.intervalTree.put(interval, interval);
|
|
761
|
-
}
|
|
762
|
-
remove(interval) {
|
|
763
|
-
this.intervalTree.remove(interval);
|
|
764
|
-
}
|
|
765
|
-
findIntervalsWithEndpointInRange(start, end) {
|
|
766
|
-
if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
|
|
767
|
-
return [];
|
|
768
|
-
}
|
|
769
|
-
const results = [];
|
|
770
|
-
const action = (node) => {
|
|
771
|
-
results.push(node.data);
|
|
772
|
-
return true;
|
|
773
|
-
};
|
|
774
|
-
const transientStartInterval = this.helpers.create("transient", start, start, this.client, IntervalType.Transient);
|
|
775
|
-
const transientEndInterval = this.helpers.create("transient", end, end, this.client, IntervalType.Transient);
|
|
776
|
-
// Add comparison overrides to the transient intervals
|
|
777
|
-
transientStartInterval[forceCompare] = -1;
|
|
778
|
-
transientEndInterval[forceCompare] = 1;
|
|
779
|
-
this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
|
|
780
|
-
return results;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
class StartpointInRangeIndex {
|
|
784
|
-
constructor(helpers, client) {
|
|
785
|
-
this.helpers = helpers;
|
|
786
|
-
this.client = client;
|
|
787
|
-
this.intervalTree = new RedBlackTree((a, b) => {
|
|
788
|
-
assert(typeof helpers.compareStarts === "function", 0x6d1 /* compareStarts does not exist in the helpers */);
|
|
789
|
-
const compareStartsResult = helpers.compareStarts(a, b);
|
|
790
|
-
if (compareStartsResult !== 0) {
|
|
791
|
-
return compareStartsResult;
|
|
792
|
-
}
|
|
793
|
-
const overrideablesComparison = compareOverrideables(a, b);
|
|
794
|
-
if (overrideablesComparison !== 0) {
|
|
795
|
-
return overrideablesComparison;
|
|
796
|
-
}
|
|
797
|
-
const aId = a.getIntervalId();
|
|
798
|
-
const bId = b.getIntervalId();
|
|
799
|
-
if (aId !== undefined && bId !== undefined) {
|
|
800
|
-
return aId.localeCompare(bId);
|
|
801
|
-
}
|
|
802
|
-
return 0;
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
add(interval) {
|
|
806
|
-
this.intervalTree.put(interval, interval);
|
|
807
|
-
}
|
|
808
|
-
remove(interval) {
|
|
809
|
-
this.intervalTree.remove(interval);
|
|
810
|
-
}
|
|
811
|
-
findIntervalsWithStartpointInRange(start, end) {
|
|
812
|
-
if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
|
|
813
|
-
return [];
|
|
814
|
-
}
|
|
815
|
-
const results = [];
|
|
816
|
-
const action = (node) => {
|
|
817
|
-
results.push(node.data);
|
|
818
|
-
return true;
|
|
819
|
-
};
|
|
820
|
-
const transientStartInterval = this.helpers.create("transient", start, start, this.client, IntervalType.Transient);
|
|
821
|
-
const transientEndInterval = this.helpers.create("transient", end, end, this.client, IntervalType.Transient);
|
|
822
|
-
// Add comparison overrides to the transient intervals
|
|
823
|
-
transientStartInterval[forceCompare] = -1;
|
|
824
|
-
transientEndInterval[forceCompare] = 1;
|
|
825
|
-
this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
|
|
826
|
-
return results;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
export function createEndpointInRangeIndex(helpers, client) {
|
|
830
|
-
return new EndpointInRangeIndex(helpers, client);
|
|
831
|
-
}
|
|
832
|
-
export function createStartpointInRangeIndex(helpers, client) {
|
|
833
|
-
return new StartpointInRangeIndex(helpers, client);
|
|
834
|
-
}
|
|
835
56
|
export class LocalIntervalCollection {
|
|
836
57
|
constructor(client, label, helpers,
|
|
837
58
|
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
@@ -840,9 +61,9 @@ export class LocalIntervalCollection {
|
|
|
840
61
|
this.label = label;
|
|
841
62
|
this.helpers = helpers;
|
|
842
63
|
this.onPositionChange = onPositionChange;
|
|
843
|
-
this.overlappingIntervalsIndex =
|
|
844
|
-
this.idIntervalIndex =
|
|
845
|
-
this.endIntervalIndex =
|
|
64
|
+
this.overlappingIntervalsIndex = createOverlappingIntervalsIndex(client, helpers);
|
|
65
|
+
this.idIntervalIndex = createIdIntervalIndex();
|
|
66
|
+
this.endIntervalIndex = createEndpointIndex(client, helpers);
|
|
846
67
|
this.indexes = new Set([
|
|
847
68
|
this.overlappingIntervalsIndex,
|
|
848
69
|
this.idIntervalIndex,
|
|
@@ -862,8 +83,7 @@ export class LocalIntervalCollection {
|
|
|
862
83
|
* @returns The interval's existing or newly created id
|
|
863
84
|
*/
|
|
864
85
|
ensureSerializedId(serializedInterval) {
|
|
865
|
-
|
|
866
|
-
let id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
86
|
+
let id = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
867
87
|
if (id === undefined) {
|
|
868
88
|
// Back-compat: 0.39 and earlier did not have IDs on intervals. If an interval from such a client
|
|
869
89
|
// comes over the wire, create a non-unique one based on start/end.
|
|
@@ -902,16 +122,22 @@ export class LocalIntervalCollection {
|
|
|
902
122
|
}
|
|
903
123
|
addInterval(start, end, intervalType, props, op, stickiness = IntervalStickiness.END) {
|
|
904
124
|
var _a;
|
|
905
|
-
var _b;
|
|
906
125
|
const interval = this.createInterval(start, end, intervalType, op, stickiness);
|
|
907
126
|
if (interval) {
|
|
908
127
|
if (!interval.properties) {
|
|
909
128
|
interval.properties = createMap();
|
|
910
129
|
}
|
|
911
130
|
if (props) {
|
|
131
|
+
// This check is intended to prevent scenarios where a random interval is created and then
|
|
132
|
+
// inserted into a collection. The aim is to ensure that the collection is created first
|
|
133
|
+
// then the user can create/add intervals based on the collection
|
|
134
|
+
if (props[reservedRangeLabelsKey] !== undefined &&
|
|
135
|
+
props[reservedRangeLabelsKey][0] !== this.label) {
|
|
136
|
+
throw new LoggingError("Adding an interval that belongs to another interval collection is not permitted");
|
|
137
|
+
}
|
|
912
138
|
interval.addProperties(props);
|
|
913
139
|
}
|
|
914
|
-
(_a =
|
|
140
|
+
(_a = interval.properties)[reservedIntervalIdKey] ?? (_a[reservedIntervalIdKey] = uuid());
|
|
915
141
|
this.add(interval);
|
|
916
142
|
}
|
|
917
143
|
return interval;
|
|
@@ -971,12 +197,11 @@ export class LocalIntervalCollection {
|
|
|
971
197
|
this.removeIntervalFromIndexes(interval);
|
|
972
198
|
}
|
|
973
199
|
}, () => {
|
|
974
|
-
var _a;
|
|
975
200
|
assert(previousInterval !== undefined, 0x3fa /* Invalid interleaving of before/after slide */);
|
|
976
201
|
pendingChanges--;
|
|
977
202
|
if (pendingChanges === 0) {
|
|
978
203
|
this.addIntervalToIndexes(interval);
|
|
979
|
-
|
|
204
|
+
this.onPositionChange?.(interval, previousInterval);
|
|
980
205
|
previousInterval = undefined;
|
|
981
206
|
}
|
|
982
207
|
});
|
|
@@ -989,18 +214,6 @@ export class LocalIntervalCollection {
|
|
|
989
214
|
}
|
|
990
215
|
}
|
|
991
216
|
LocalIntervalCollection.legacyIdPrefix = "legacy";
|
|
992
|
-
export const compareSequenceIntervalEnds = (a, b) => compareReferencePositions(a.end, b.end);
|
|
993
|
-
export const compareSequenceIntervalStarts = (a, b) => compareReferencePositions(a.start, b.start);
|
|
994
|
-
export const sequenceIntervalHelpers = {
|
|
995
|
-
compareEnds: compareSequenceIntervalEnds,
|
|
996
|
-
compareStarts: compareSequenceIntervalStarts,
|
|
997
|
-
create: createSequenceInterval,
|
|
998
|
-
};
|
|
999
|
-
export const intervalHelpers = {
|
|
1000
|
-
compareEnds: (a, b) => a.end - b.end,
|
|
1001
|
-
compareStarts: (a, b) => a.start - b.start,
|
|
1002
|
-
create: createInterval,
|
|
1003
|
-
};
|
|
1004
217
|
class SequenceIntervalCollectionFactory {
|
|
1005
218
|
load(emitter, raw = [], options) {
|
|
1006
219
|
return new IntervalCollection(sequenceIntervalHelpers, true, emitter, raw, options);
|
|
@@ -1023,18 +236,10 @@ export class SequenceIntervalCollectionValueType {
|
|
|
1023
236
|
SequenceIntervalCollectionValueType.Name = "sharedStringIntervalCollection";
|
|
1024
237
|
SequenceIntervalCollectionValueType._factory = new SequenceIntervalCollectionFactory();
|
|
1025
238
|
SequenceIntervalCollectionValueType._ops = makeOpsMap();
|
|
1026
|
-
const compareIntervalEnds = (a, b) => a.end - b.end;
|
|
1027
|
-
function createInterval(label, start, end, client, intervalType, op, fromSnapshot) {
|
|
1028
|
-
const rangeProp = {};
|
|
1029
|
-
if (label && label.length > 0) {
|
|
1030
|
-
rangeProp[reservedRangeLabelsKey] = [label];
|
|
1031
|
-
}
|
|
1032
|
-
return new Interval(start, end, rangeProp);
|
|
1033
|
-
}
|
|
1034
239
|
class IntervalCollectionFactory {
|
|
1035
240
|
load(emitter, raw = [], options) {
|
|
1036
241
|
const helpers = {
|
|
1037
|
-
compareEnds:
|
|
242
|
+
compareEnds: (a, b) => a.end - b.end,
|
|
1038
243
|
create: createInterval,
|
|
1039
244
|
};
|
|
1040
245
|
const collection = new IntervalCollection(helpers, false, emitter, raw, options);
|
|
@@ -1063,7 +268,7 @@ export function makeOpsMap() {
|
|
|
1063
268
|
const rebase = (collection, op, localOpMetadata) => {
|
|
1064
269
|
const { localSeq } = localOpMetadata;
|
|
1065
270
|
const rebasedValue = collection.rebaseLocalInterval(op.opName, op.value, localSeq);
|
|
1066
|
-
const rebasedOp =
|
|
271
|
+
const rebasedOp = { ...op, value: rebasedValue };
|
|
1067
272
|
return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
|
|
1068
273
|
};
|
|
1069
274
|
return new Map([
|
|
@@ -1157,25 +362,23 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1157
362
|
* {@inheritdoc IIntervalCollection.attachIndex}
|
|
1158
363
|
*/
|
|
1159
364
|
attachIndex(index) {
|
|
1160
|
-
var _a;
|
|
1161
365
|
if (!this.attached) {
|
|
1162
366
|
throw new LoggingError("The local interval collection must exist");
|
|
1163
367
|
}
|
|
1164
368
|
for (const interval of this) {
|
|
1165
369
|
index.add(interval);
|
|
1166
370
|
}
|
|
1167
|
-
|
|
371
|
+
this.localCollection?.appendIndex(index);
|
|
1168
372
|
}
|
|
1169
373
|
/**
|
|
1170
374
|
* {@inheritdoc IIntervalCollection.detachIndex}
|
|
1171
375
|
*/
|
|
1172
376
|
detachIndex(index) {
|
|
1173
|
-
var _a;
|
|
1174
377
|
if (!this.attached) {
|
|
1175
378
|
throw new LoggingError("The local interval collection must exist");
|
|
1176
379
|
}
|
|
1177
380
|
// Avoid removing intervals if the index does not exist
|
|
1178
|
-
if (!
|
|
381
|
+
if (!this.localCollection?.removeIndex(index)) {
|
|
1179
382
|
return false;
|
|
1180
383
|
}
|
|
1181
384
|
for (const interval of this) {
|
|
@@ -1184,7 +387,6 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1184
387
|
return true;
|
|
1185
388
|
}
|
|
1186
389
|
rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
|
|
1187
|
-
var _a;
|
|
1188
390
|
if (!this.client) {
|
|
1189
391
|
throw new LoggingError("mergeTree client must exist");
|
|
1190
392
|
}
|
|
@@ -1195,7 +397,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1195
397
|
}, localSeq);
|
|
1196
398
|
// if segment is undefined, it slid off the string
|
|
1197
399
|
assert(segment !== undefined, 0x54e /* No segment found */);
|
|
1198
|
-
const segoff = (
|
|
400
|
+
const segoff = getSlideToSegoff({ segment, offset }) ?? segment;
|
|
1199
401
|
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
1200
402
|
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
1201
403
|
return DetachedReferencePosition;
|
|
@@ -1207,7 +409,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1207
409
|
assert(this.client !== undefined, 0x550 /* Client should be defined when computing rebased position */);
|
|
1208
410
|
const original = this.localSeqToSerializedInterval.get(localSeq);
|
|
1209
411
|
assert(original !== undefined, 0x551 /* Failed to store pending serialized interval info for this localSeq. */);
|
|
1210
|
-
const rebased =
|
|
412
|
+
const rebased = { ...original };
|
|
1211
413
|
const { start, end, sequenceNumber } = original;
|
|
1212
414
|
if (start !== undefined) {
|
|
1213
415
|
rebased.start = this.rebasePositionWithSegmentSlide(start, sequenceNumber, localSeq);
|
|
@@ -1234,7 +436,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1234
436
|
}
|
|
1235
437
|
});
|
|
1236
438
|
}
|
|
1237
|
-
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true));
|
|
439
|
+
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
|
|
1238
440
|
if (this.savedSerializedIntervals) {
|
|
1239
441
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
1240
442
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
@@ -1257,7 +459,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1257
459
|
}
|
|
1258
460
|
return 0;
|
|
1259
461
|
}
|
|
1260
|
-
emitChange(interval, previousInterval, local, op) {
|
|
462
|
+
emitChange(interval, previousInterval, local, slide, op) {
|
|
1261
463
|
// Temporarily make references transient so that positional queries work (non-transient refs
|
|
1262
464
|
// on resolve to DetachedPosition on any segments that don't contain them). The original refType
|
|
1263
465
|
// is restored as single-endpoint changes re-use previous references.
|
|
@@ -1268,12 +470,12 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1268
470
|
endRefType = previousInterval.end.refType;
|
|
1269
471
|
previousInterval.start.refType = ReferenceType.Transient;
|
|
1270
472
|
previousInterval.end.refType = ReferenceType.Transient;
|
|
1271
|
-
this.emit("changeInterval", interval, previousInterval, local, op);
|
|
473
|
+
this.emit("changeInterval", interval, previousInterval, local, op, slide);
|
|
1272
474
|
previousInterval.start.refType = startRefType;
|
|
1273
475
|
previousInterval.end.refType = endRefType;
|
|
1274
476
|
}
|
|
1275
477
|
else {
|
|
1276
|
-
this.emit("changeInterval", interval, previousInterval, local, op);
|
|
478
|
+
this.emit("changeInterval", interval, previousInterval, local, op, slide);
|
|
1277
479
|
}
|
|
1278
480
|
}
|
|
1279
481
|
/**
|
|
@@ -1289,7 +491,6 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1289
491
|
* {@inheritdoc IIntervalCollection.add}
|
|
1290
492
|
*/
|
|
1291
493
|
add(start, end, intervalType, props, stickiness = IntervalStickiness.END) {
|
|
1292
|
-
var _a, _b;
|
|
1293
494
|
if (!this.localCollection) {
|
|
1294
495
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
1295
496
|
}
|
|
@@ -1305,7 +506,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1305
506
|
end,
|
|
1306
507
|
intervalType,
|
|
1307
508
|
properties: interval.properties,
|
|
1308
|
-
sequenceNumber:
|
|
509
|
+
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1309
510
|
start,
|
|
1310
511
|
stickiness,
|
|
1311
512
|
};
|
|
@@ -1364,6 +565,11 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1364
565
|
if (!props) {
|
|
1365
566
|
throw new LoggingError("changeProperties should be called with a property set");
|
|
1366
567
|
}
|
|
568
|
+
// prevent the overwriting of an interval label, it should remain unchanged
|
|
569
|
+
// once it has been inserted into the collection.
|
|
570
|
+
if (props[reservedRangeLabelsKey] !== undefined) {
|
|
571
|
+
throw new LoggingError("The label property should not be modified once inserted to the collection");
|
|
572
|
+
}
|
|
1367
573
|
const interval = this.getIntervalById(id);
|
|
1368
574
|
if (interval) {
|
|
1369
575
|
// Pass Unassigned as the sequence number to indicate that this is a local op that is waiting for an ack.
|
|
@@ -1409,7 +615,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1409
615
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
1410
616
|
this.emitter.emit("change", undefined, serializedInterval, { localSeq });
|
|
1411
617
|
this.addPendingChange(id, serializedInterval);
|
|
1412
|
-
this.emitChange(newInterval, interval, true);
|
|
618
|
+
this.emitChange(newInterval, interval, true, false);
|
|
1413
619
|
return newInterval;
|
|
1414
620
|
}
|
|
1415
621
|
// No interval to change
|
|
@@ -1432,9 +638,8 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1432
638
|
entries.push(serializedInterval);
|
|
1433
639
|
}
|
|
1434
640
|
removePendingChange(serializedInterval) {
|
|
1435
|
-
var _a;
|
|
1436
641
|
// Change ops always have an ID.
|
|
1437
|
-
const id =
|
|
642
|
+
const id = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
1438
643
|
if (serializedInterval.start !== undefined) {
|
|
1439
644
|
this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
1440
645
|
}
|
|
@@ -1449,8 +654,8 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1449
654
|
if (entries.length === 0) {
|
|
1450
655
|
pendingChanges.delete(id);
|
|
1451
656
|
}
|
|
1452
|
-
if (
|
|
1453
|
-
|
|
657
|
+
if (pendingChange?.start !== serializedInterval.start ||
|
|
658
|
+
pendingChange?.end !== serializedInterval.end) {
|
|
1454
659
|
throw new LoggingError("Mismatch in pending changes");
|
|
1455
660
|
}
|
|
1456
661
|
}
|
|
@@ -1465,20 +670,19 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1465
670
|
}
|
|
1466
671
|
/** @internal */
|
|
1467
672
|
ackChange(serializedInterval, local, op, localOpMetadata) {
|
|
1468
|
-
var _a, _b, _c, _d;
|
|
1469
673
|
if (!this.localCollection) {
|
|
1470
674
|
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1471
675
|
}
|
|
1472
676
|
if (local) {
|
|
1473
677
|
assert(localOpMetadata !== undefined, 0x552 /* op metadata should be defined for local op */);
|
|
1474
|
-
this.localSeqToSerializedInterval.delete(localOpMetadata
|
|
678
|
+
this.localSeqToSerializedInterval.delete(localOpMetadata?.localSeq);
|
|
1475
679
|
// This is an ack from the server. Remove the pending change.
|
|
1476
680
|
this.removePendingChange(serializedInterval);
|
|
1477
681
|
}
|
|
1478
682
|
// Note that the ID is in the property bag only to allow us to find the interval.
|
|
1479
683
|
// This API cannot change the ID, and writing to the ID property will result in an exception. So we
|
|
1480
684
|
// strip it out of the properties here.
|
|
1481
|
-
const
|
|
685
|
+
const { [reservedIntervalIdKey]: id, ...newProps } = serializedInterval.properties ?? {};
|
|
1482
686
|
assert(id !== undefined, 0x3fe /* id must exist on the interval */);
|
|
1483
687
|
const interval = this.getIntervalById(id);
|
|
1484
688
|
if (!interval) {
|
|
@@ -1487,9 +691,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1487
691
|
}
|
|
1488
692
|
if (local) {
|
|
1489
693
|
// Let the propertyManager prune its pending change-properties set.
|
|
1490
|
-
|
|
694
|
+
interval.propertyManager?.ackPendingProperties({
|
|
1491
695
|
type: MergeTreeDeltaType.ANNOTATE,
|
|
1492
|
-
props:
|
|
696
|
+
props: serializedInterval.properties ?? {},
|
|
1493
697
|
});
|
|
1494
698
|
this.ackInterval(interval, op);
|
|
1495
699
|
}
|
|
@@ -1510,14 +714,14 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1510
714
|
// If changeInterval gives us a new interval, work with that one. Otherwise keep working with
|
|
1511
715
|
// the one we originally found in the tree.
|
|
1512
716
|
newInterval =
|
|
1513
|
-
|
|
717
|
+
this.localCollection.changeInterval(interval, start, end, op) ?? interval;
|
|
1514
718
|
}
|
|
1515
719
|
const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
|
|
1516
720
|
if (this.onDeserialize) {
|
|
1517
721
|
this.onDeserialize(newInterval);
|
|
1518
722
|
}
|
|
1519
723
|
if (newInterval !== interval) {
|
|
1520
|
-
this.emitChange(newInterval, interval, local, op);
|
|
724
|
+
this.emitChange(newInterval, interval, local, false, op);
|
|
1521
725
|
}
|
|
1522
726
|
const changedProperties = Object.keys(newProps).length > 0;
|
|
1523
727
|
if (changedProperties) {
|
|
@@ -1525,18 +729,6 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1525
729
|
}
|
|
1526
730
|
}
|
|
1527
731
|
}
|
|
1528
|
-
/**
|
|
1529
|
-
* @deprecated - This functionality was useful when adding two intervals at the same start/end positions resulted
|
|
1530
|
-
* in a conflict. This is no longer the case (as of PR#6407), as interval collections support multiple intervals
|
|
1531
|
-
* at the same location and gives each interval a unique id.
|
|
1532
|
-
*
|
|
1533
|
-
* As such, the conflict resolver is never invoked and unnecessary. This API will be removed in an upcoming release.
|
|
1534
|
-
*/
|
|
1535
|
-
addConflictResolver(_) {
|
|
1536
|
-
if (!this.localCollection) {
|
|
1537
|
-
throw new LoggingError("attachSequence must be called");
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
732
|
/**
|
|
1541
733
|
* {@inheritdoc IIntervalCollection.attachDeserializer}
|
|
1542
734
|
*/
|
|
@@ -1560,7 +752,6 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1560
752
|
* @internal
|
|
1561
753
|
*/
|
|
1562
754
|
rebaseLocalInterval(opName, serializedInterval, localSeq) {
|
|
1563
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1564
755
|
if (!this.client) {
|
|
1565
756
|
// If there's no associated mergeTree client, the originally submitted op is still correct.
|
|
1566
757
|
return serializedInterval;
|
|
@@ -1569,14 +760,14 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1569
760
|
throw new LoggingError("attachSequence must be called");
|
|
1570
761
|
}
|
|
1571
762
|
const { intervalType, properties } = serializedInterval;
|
|
1572
|
-
const { start: startRebased, end: endRebased } =
|
|
1573
|
-
const intervalId = properties
|
|
1574
|
-
const localInterval =
|
|
763
|
+
const { start: startRebased, end: endRebased } = this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
|
|
764
|
+
const intervalId = properties?.[reservedIntervalIdKey];
|
|
765
|
+
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
|
|
1575
766
|
const rebased = {
|
|
1576
767
|
start: startRebased,
|
|
1577
768
|
end: endRebased,
|
|
1578
769
|
intervalType,
|
|
1579
|
-
sequenceNumber:
|
|
770
|
+
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1580
771
|
properties,
|
|
1581
772
|
};
|
|
1582
773
|
if (opName === "change" &&
|
|
@@ -1588,7 +779,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1588
779
|
if (startRebased === DetachedReferencePosition ||
|
|
1589
780
|
endRebased === DetachedReferencePosition) {
|
|
1590
781
|
if (localInterval) {
|
|
1591
|
-
|
|
782
|
+
this.localCollection?.removeExistingInterval(localInterval);
|
|
1592
783
|
}
|
|
1593
784
|
return undefined;
|
|
1594
785
|
}
|
|
@@ -1597,20 +788,19 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1597
788
|
assert(localInterval instanceof SequenceInterval, 0x3a0 /* localInterval must be `SequenceInterval` when used with client */);
|
|
1598
789
|
// The rebased op may place this interval's endpoints on different segments. Calling `changeInterval` here
|
|
1599
790
|
// updates the local client's state to be consistent with the emitted op.
|
|
1600
|
-
|
|
791
|
+
this.localCollection?.changeInterval(localInterval, startRebased, endRebased, undefined, localSeq);
|
|
1601
792
|
}
|
|
1602
793
|
return rebased;
|
|
1603
794
|
}
|
|
1604
795
|
getSlideToSegment(lref) {
|
|
1605
|
-
var _a, _b;
|
|
1606
796
|
if (!this.client) {
|
|
1607
797
|
throw new LoggingError("client does not exist");
|
|
1608
798
|
}
|
|
1609
799
|
const segoff = { segment: lref.getSegment(), offset: lref.getOffset() };
|
|
1610
|
-
if (
|
|
800
|
+
if (segoff.segment?.localRefs?.has(lref) !== true) {
|
|
1611
801
|
return undefined;
|
|
1612
802
|
}
|
|
1613
|
-
const newSegoff =
|
|
803
|
+
const newSegoff = getSlideToSegoff(segoff);
|
|
1614
804
|
const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
|
|
1615
805
|
? undefined
|
|
1616
806
|
: newSegoff;
|
|
@@ -1623,7 +813,6 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1623
813
|
lref.refType = refType;
|
|
1624
814
|
}
|
|
1625
815
|
ackInterval(interval, op) {
|
|
1626
|
-
var _a, _b;
|
|
1627
816
|
// Only SequenceIntervals need potential sliding
|
|
1628
817
|
if (!(interval instanceof SequenceInterval)) {
|
|
1629
818
|
return;
|
|
@@ -1668,7 +857,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1668
857
|
// remove and rebuild start interval as transient for event
|
|
1669
858
|
this.client.removeLocalReferencePosition(oldInterval.start);
|
|
1670
859
|
oldInterval.start.refType = ReferenceType.Transient;
|
|
1671
|
-
|
|
860
|
+
oldSeg?.localRefs?.addLocalRef(oldInterval.start, oldInterval.start.getOffset());
|
|
1672
861
|
}
|
|
1673
862
|
if (needsEndUpdate) {
|
|
1674
863
|
const props = interval.end.properties;
|
|
@@ -1680,19 +869,18 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1680
869
|
const oldSeg = oldInterval.end.getSegment();
|
|
1681
870
|
this.client.removeLocalReferencePosition(oldInterval.end);
|
|
1682
871
|
oldInterval.end.refType = ReferenceType.Transient;
|
|
1683
|
-
|
|
872
|
+
oldSeg?.localRefs?.addLocalRef(oldInterval.end, oldInterval.end.getOffset());
|
|
1684
873
|
}
|
|
1685
874
|
this.localCollection.add(interval);
|
|
1686
|
-
this.emitChange(interval, oldInterval, true, op);
|
|
875
|
+
this.emitChange(interval, oldInterval, true, true, op);
|
|
1687
876
|
}
|
|
1688
877
|
}
|
|
1689
878
|
/** @internal */
|
|
1690
879
|
ackAdd(serializedInterval, local, op, localOpMetadata) {
|
|
1691
|
-
var _a;
|
|
1692
880
|
if (local) {
|
|
1693
881
|
assert(localOpMetadata !== undefined, 0x553 /* op metadata should be defined for local op */);
|
|
1694
882
|
this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
|
|
1695
|
-
const id =
|
|
883
|
+
const id = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
1696
884
|
const localInterval = this.getIntervalById(id);
|
|
1697
885
|
if (localInterval) {
|
|
1698
886
|
this.ackInterval(localInterval, op);
|
|
@@ -1828,9 +1016,8 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1828
1016
|
* endpoint is a part of.
|
|
1829
1017
|
*/
|
|
1830
1018
|
export function intervalLocatorFromEndpoint(potentialEndpoint) {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
return interval && (collectionNameArray === null || collectionNameArray === void 0 ? void 0 : collectionNameArray.length) === 1
|
|
1019
|
+
const { interval, [reservedRangeLabelsKey]: collectionNameArray } = potentialEndpoint.properties ?? {};
|
|
1020
|
+
return interval && collectionNameArray?.length === 1
|
|
1834
1021
|
? { label: collectionNameArray[0], interval }
|
|
1835
1022
|
: undefined;
|
|
1836
1023
|
}
|