@fluidframework/sequence 2.0.0-dev.6.4.0.192049 → 2.0.0-dev.7.2.0.204906
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 +95 -0
- package/README.md +130 -0
- package/api-extractor.json +1 -1
- package/api-report/sequence.api.md +717 -0
- package/dist/defaultMap.d.ts +1 -1
- package/dist/defaultMap.d.ts.map +1 -1
- package/dist/defaultMap.js +6 -6
- package/dist/defaultMap.js.map +1 -1
- package/dist/defaultMapInterfaces.d.ts +22 -2
- package/dist/defaultMapInterfaces.d.ts.map +1 -1
- package/dist/defaultMapInterfaces.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +164 -16
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +174 -54
- package/dist/intervalCollection.js.map +1 -1
- package/dist/intervalIndex/endpointInRangeIndex.d.ts +17 -3
- package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
- package/dist/intervalIndex/endpointInRangeIndex.js +12 -6
- package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -1
- package/dist/intervalIndex/endpointIndex.d.ts +19 -2
- package/dist/intervalIndex/endpointIndex.d.ts.map +1 -1
- package/dist/intervalIndex/endpointIndex.js +10 -5
- package/dist/intervalIndex/endpointIndex.js.map +1 -1
- package/dist/intervalIndex/idIntervalIndex.d.ts +6 -0
- package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -1
- package/dist/intervalIndex/idIntervalIndex.js +3 -0
- package/dist/intervalIndex/idIntervalIndex.js.map +1 -1
- package/dist/intervalIndex/index.d.ts +4 -4
- package/dist/intervalIndex/index.d.ts.map +1 -1
- package/dist/intervalIndex/index.js +5 -1
- package/dist/intervalIndex/index.js.map +1 -1
- package/dist/intervalIndex/intervalIndex.d.ts +1 -0
- package/dist/intervalIndex/intervalIndex.d.ts.map +1 -1
- package/dist/intervalIndex/intervalIndex.js.map +1 -1
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +17 -6
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
- package/dist/intervalIndex/overlappingIntervalsIndex.js +17 -4
- package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +5 -2
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -1
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +9 -1
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +2 -1
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
- package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
- package/dist/intervalIndex/startpointInRangeIndex.d.ts +17 -3
- package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
- package/dist/intervalIndex/startpointInRangeIndex.js +12 -8
- package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -1
- package/dist/intervalTree.d.ts +1 -1
- package/dist/intervalTree.d.ts.map +1 -1
- package/dist/intervals/interval.d.ts +4 -2
- package/dist/intervals/interval.d.ts.map +1 -1
- package/dist/intervals/interval.js +14 -5
- package/dist/intervals/interval.js.map +1 -1
- package/dist/intervals/intervalUtils.d.ts +51 -18
- package/dist/intervals/intervalUtils.d.ts.map +1 -1
- package/dist/intervals/intervalUtils.js +18 -10
- package/dist/intervals/intervalUtils.js.map +1 -1
- package/dist/intervals/sequenceInterval.d.ts +28 -13
- package/dist/intervals/sequenceInterval.d.ts.map +1 -1
- package/dist/intervals/sequenceInterval.js +124 -43
- package/dist/intervals/sequenceInterval.js.map +1 -1
- package/dist/localValues.d.ts.map +1 -1
- package/dist/localValues.js.map +1 -1
- 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 +3 -15
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +11 -19
- package/dist/revertibles.js.map +1 -1
- package/dist/sequence-alpha.d.ts +1587 -0
- package/dist/sequence-beta.d.ts +1507 -0
- package/dist/sequence-public.d.ts +1507 -0
- package/dist/sequence-untrimmed.d.ts +1759 -0
- package/dist/sequence.d.ts +8 -4
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +53 -48
- package/dist/sequence.js.map +1 -1
- package/dist/sequenceDeltaEvent.d.ts +4 -0
- package/dist/sequenceDeltaEvent.d.ts.map +1 -1
- package/dist/sequenceDeltaEvent.js +3 -0
- package/dist/sequenceDeltaEvent.js.map +1 -1
- package/dist/sequenceFactory.d.ts +3 -0
- package/dist/sequenceFactory.d.ts.map +1 -1
- package/dist/sequenceFactory.js +4 -1
- package/dist/sequenceFactory.js.map +1 -1
- package/dist/sharedIntervalCollection.d.ts +5 -0
- package/dist/sharedIntervalCollection.d.ts.map +1 -1
- package/dist/sharedIntervalCollection.js +11 -9
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/dist/sharedSequence.d.ts +6 -3
- package/dist/sharedSequence.d.ts.map +1 -1
- package/dist/sharedSequence.js +10 -8
- package/dist/sharedSequence.js.map +1 -1
- package/dist/sharedString.d.ts +17 -2
- package/dist/sharedString.d.ts.map +1 -1
- package/dist/sharedString.js +21 -7
- package/dist/sharedString.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/defaultMap.d.ts +1 -1
- package/lib/defaultMap.d.ts.map +1 -1
- package/lib/defaultMap.js +6 -6
- package/lib/defaultMap.js.map +1 -1
- package/lib/defaultMapInterfaces.d.ts +22 -2
- package/lib/defaultMapInterfaces.d.ts.map +1 -1
- package/lib/defaultMapInterfaces.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +164 -16
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +172 -55
- package/lib/intervalCollection.js.map +1 -1
- package/lib/intervalIndex/endpointInRangeIndex.d.ts +17 -3
- package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
- package/lib/intervalIndex/endpointInRangeIndex.js +12 -7
- package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -1
- package/lib/intervalIndex/endpointIndex.d.ts +19 -2
- package/lib/intervalIndex/endpointIndex.d.ts.map +1 -1
- package/lib/intervalIndex/endpointIndex.js +10 -6
- package/lib/intervalIndex/endpointIndex.js.map +1 -1
- package/lib/intervalIndex/idIntervalIndex.d.ts +6 -0
- package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -1
- package/lib/intervalIndex/idIntervalIndex.js +3 -0
- package/lib/intervalIndex/idIntervalIndex.js.map +1 -1
- package/lib/intervalIndex/index.d.ts +4 -4
- package/lib/intervalIndex/index.d.ts.map +1 -1
- package/lib/intervalIndex/index.js +4 -4
- package/lib/intervalIndex/index.js.map +1 -1
- package/lib/intervalIndex/intervalIndex.d.ts +1 -0
- package/lib/intervalIndex/intervalIndex.d.ts.map +1 -1
- package/lib/intervalIndex/intervalIndex.js.map +1 -1
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +17 -6
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
- package/lib/intervalIndex/overlappingIntervalsIndex.js +18 -5
- package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +5 -2
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -1
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +9 -1
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
- package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +2 -1
- package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
- package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
- package/lib/intervalIndex/startpointInRangeIndex.d.ts +17 -3
- package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
- package/lib/intervalIndex/startpointInRangeIndex.js +12 -9
- package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -1
- package/lib/intervalTree.d.ts +1 -1
- package/lib/intervalTree.d.ts.map +1 -1
- package/lib/intervals/interval.d.ts +4 -2
- package/lib/intervals/interval.d.ts.map +1 -1
- package/lib/intervals/interval.js +14 -5
- package/lib/intervals/interval.js.map +1 -1
- package/lib/intervals/intervalUtils.d.ts +51 -18
- package/lib/intervals/intervalUtils.d.ts.map +1 -1
- package/lib/intervals/intervalUtils.js +14 -6
- package/lib/intervals/intervalUtils.js.map +1 -1
- package/lib/intervals/sequenceInterval.d.ts +28 -13
- package/lib/intervals/sequenceInterval.d.ts.map +1 -1
- package/lib/intervals/sequenceInterval.js +125 -42
- package/lib/intervals/sequenceInterval.js.map +1 -1
- package/lib/localValues.d.ts.map +1 -1
- package/lib/localValues.js.map +1 -1
- 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 +3 -15
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +11 -19
- package/lib/revertibles.js.map +1 -1
- package/lib/sequence.d.ts +8 -4
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +56 -49
- package/lib/sequence.js.map +1 -1
- package/lib/sequenceDeltaEvent.d.ts +4 -0
- package/lib/sequenceDeltaEvent.d.ts.map +1 -1
- package/lib/sequenceDeltaEvent.js +3 -0
- package/lib/sequenceDeltaEvent.js.map +1 -1
- package/lib/sequenceFactory.d.ts +3 -0
- package/lib/sequenceFactory.d.ts.map +1 -1
- package/lib/sequenceFactory.js +4 -1
- package/lib/sequenceFactory.js.map +1 -1
- package/lib/sharedIntervalCollection.d.ts +5 -0
- package/lib/sharedIntervalCollection.d.ts.map +1 -1
- package/lib/sharedIntervalCollection.js +11 -9
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/lib/sharedSequence.d.ts +6 -3
- package/lib/sharedSequence.d.ts.map +1 -1
- package/lib/sharedSequence.js +10 -8
- package/lib/sharedSequence.js.map +1 -1
- package/lib/sharedString.d.ts +17 -2
- package/lib/sharedString.d.ts.map +1 -1
- package/lib/sharedString.js +21 -7
- package/lib/sharedString.js.map +1 -1
- package/package.json +31 -30
- package/src/defaultMapInterfaces.ts +22 -2
- package/src/index.ts +4 -1
- package/src/intervalCollection.ts +423 -82
- package/src/intervalIndex/endpointInRangeIndex.ts +23 -11
- package/src/intervalIndex/endpointIndex.ts +22 -9
- package/src/intervalIndex/idIntervalIndex.ts +7 -1
- package/src/intervalIndex/index.ts +12 -3
- package/src/intervalIndex/intervalIndex.ts +1 -0
- package/src/intervalIndex/overlappingIntervalsIndex.ts +40 -15
- package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +10 -1
- package/src/intervalIndex/sequenceIntervalIndexes.ts +2 -1
- package/src/intervalIndex/startpointInRangeIndex.ts +23 -18
- package/src/intervals/interval.ts +35 -8
- package/src/intervals/intervalUtils.ts +61 -27
- package/src/intervals/sequenceInterval.ts +197 -47
- package/src/localValues.ts +4 -1
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +14 -36
- package/src/sequence.ts +14 -5
- package/src/sequenceDeltaEvent.ts +4 -0
- package/src/sequenceFactory.ts +4 -1
- package/src/sharedIntervalCollection.ts +5 -0
- package/src/sharedSequence.ts +6 -3
- package/src/sharedString.ts +25 -2
|
@@ -3,26 +3,47 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
/* eslint-disable no-bitwise */
|
|
6
|
+
/* eslint-disable import/no-deprecated */
|
|
6
7
|
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
7
8
|
import { assert } from "@fluidframework/core-utils";
|
|
8
|
-
import { addProperties, createMap, getSlideToSegoff, MergeTreeDeltaType, ReferenceType, refTypeIncludesFlag, reservedRangeLabelsKey, UnassignedSequenceNumber, DetachedReferencePosition, } from "@fluidframework/merge-tree";
|
|
9
|
+
import { addProperties, createMap, getSlideToSegoff, MergeTreeDeltaType, ReferenceType, refTypeIncludesFlag, reservedRangeLabelsKey, UnassignedSequenceNumber, DetachedReferencePosition, UniversalSequenceNumber, SlidingPreference, } from "@fluidframework/merge-tree";
|
|
9
10
|
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
|
|
10
11
|
import { v4 as uuid } from "uuid";
|
|
11
12
|
import { IntervalOpType, IntervalStickiness, IntervalType, SequenceInterval, createPositionReferenceFromSegoff, endReferenceSlidingPreference, startReferenceSlidingPreference, sequenceIntervalHelpers, createInterval, } from "./intervals";
|
|
12
|
-
import {
|
|
13
|
+
import { EndpointIndex, OverlappingIntervalsIndex, createIdIntervalIndex, } from "./intervalIndex";
|
|
14
|
+
/**
|
|
15
|
+
* Defines a side relative to a character in a sequence.
|
|
16
|
+
*
|
|
17
|
+
* @remarks See {@link SequencePlace} for additional context on usage.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export var Side;
|
|
21
|
+
(function (Side) {
|
|
22
|
+
Side[Side["Before"] = 0] = "Before";
|
|
23
|
+
Side[Side["After"] = 1] = "After";
|
|
24
|
+
})(Side || (Side = {}));
|
|
13
25
|
const reservedIntervalIdKey = "intervalId";
|
|
26
|
+
export function sidesFromStickiness(stickiness) {
|
|
27
|
+
const startSide = (stickiness & IntervalStickiness.START) !== 0 ? Side.After : Side.Before;
|
|
28
|
+
const endSide = (stickiness & IntervalStickiness.END) !== 0 ? Side.Before : Side.After;
|
|
29
|
+
return { startSide, endSide };
|
|
30
|
+
}
|
|
14
31
|
/**
|
|
15
32
|
* Decompress an interval after loading a summary from JSON. The exact format
|
|
16
33
|
* of this compression is unspecified and subject to change
|
|
17
34
|
*/
|
|
18
35
|
function decompressInterval(interval, label) {
|
|
36
|
+
const stickiness = interval[5] ?? IntervalStickiness.END;
|
|
37
|
+
const { startSide, endSide } = sidesFromStickiness(stickiness);
|
|
19
38
|
return {
|
|
20
39
|
start: interval[0],
|
|
21
40
|
end: interval[1],
|
|
22
41
|
sequenceNumber: interval[2],
|
|
23
42
|
intervalType: interval[3],
|
|
24
43
|
properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
|
|
25
|
-
stickiness
|
|
44
|
+
stickiness,
|
|
45
|
+
startSide,
|
|
46
|
+
endSide,
|
|
26
47
|
};
|
|
27
48
|
}
|
|
28
49
|
/**
|
|
@@ -31,7 +52,7 @@ function decompressInterval(interval, label) {
|
|
|
31
52
|
*/
|
|
32
53
|
function compressInterval(interval) {
|
|
33
54
|
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
34
|
-
|
|
55
|
+
let base = [
|
|
35
56
|
start,
|
|
36
57
|
end,
|
|
37
58
|
sequenceNumber,
|
|
@@ -41,29 +62,60 @@ function compressInterval(interval) {
|
|
|
41
62
|
{ ...properties, [reservedRangeLabelsKey]: undefined },
|
|
42
63
|
];
|
|
43
64
|
if (interval.stickiness !== undefined && interval.stickiness !== IntervalStickiness.END) {
|
|
44
|
-
|
|
65
|
+
// reassignment to make it easier for typescript to reason about types
|
|
66
|
+
base = [...base, interval.stickiness];
|
|
45
67
|
}
|
|
46
68
|
return base;
|
|
47
69
|
}
|
|
70
|
+
export function endpointPosAndSide(start, end) {
|
|
71
|
+
const startIsPlainEndpoint = typeof start === "number" || start === "start" || start === "end";
|
|
72
|
+
const endIsPlainEndpoint = typeof end === "number" || end === "start" || end === "end";
|
|
73
|
+
const startSide = startIsPlainEndpoint ? Side.Before : start?.side;
|
|
74
|
+
const endSide = endIsPlainEndpoint ? Side.Before : end?.side;
|
|
75
|
+
const startPos = startIsPlainEndpoint ? start : start?.pos;
|
|
76
|
+
const endPos = endIsPlainEndpoint ? end : end?.pos;
|
|
77
|
+
return {
|
|
78
|
+
startSide,
|
|
79
|
+
endSide,
|
|
80
|
+
startPos,
|
|
81
|
+
endPos,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function toSequencePlace(pos, side) {
|
|
85
|
+
return typeof pos === "number" ? { pos, side } : pos;
|
|
86
|
+
}
|
|
87
|
+
function toOptionalSequencePlace(pos, side = Side.Before) {
|
|
88
|
+
return typeof pos === "number" ? { pos, side } : pos;
|
|
89
|
+
}
|
|
90
|
+
export function computeStickinessFromSide(startPos = -1, startSide = Side.Before, endPos = -1, endSide = Side.Before) {
|
|
91
|
+
let stickiness = IntervalStickiness.NONE;
|
|
92
|
+
if (startSide === Side.After || startPos === "start") {
|
|
93
|
+
stickiness |= IntervalStickiness.START;
|
|
94
|
+
}
|
|
95
|
+
if (endSide === Side.Before || endPos === "end") {
|
|
96
|
+
stickiness |= IntervalStickiness.END;
|
|
97
|
+
}
|
|
98
|
+
return stickiness;
|
|
99
|
+
}
|
|
48
100
|
export function createIntervalIndex() {
|
|
49
101
|
const helpers = {
|
|
50
|
-
compareEnds: (a, b) => a.end - b.end,
|
|
51
102
|
create: createInterval,
|
|
52
103
|
};
|
|
53
|
-
const lc = new LocalIntervalCollection(undefined, "", helpers);
|
|
104
|
+
const lc = new LocalIntervalCollection(undefined, "", helpers, {});
|
|
54
105
|
return lc;
|
|
55
106
|
}
|
|
56
107
|
export class LocalIntervalCollection {
|
|
57
|
-
constructor(client, label, helpers,
|
|
108
|
+
constructor(client, label, helpers, options,
|
|
58
109
|
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
59
110
|
onPositionChange) {
|
|
60
111
|
this.client = client;
|
|
61
112
|
this.label = label;
|
|
62
113
|
this.helpers = helpers;
|
|
114
|
+
this.options = options;
|
|
63
115
|
this.onPositionChange = onPositionChange;
|
|
64
|
-
this.overlappingIntervalsIndex =
|
|
116
|
+
this.overlappingIntervalsIndex = new OverlappingIntervalsIndex(client, helpers);
|
|
65
117
|
this.idIntervalIndex = createIdIntervalIndex();
|
|
66
|
-
this.endIntervalIndex =
|
|
118
|
+
this.endIntervalIndex = new EndpointIndex(client, helpers);
|
|
67
119
|
this.indexes = new Set([
|
|
68
120
|
this.overlappingIntervalsIndex,
|
|
69
121
|
this.idIntervalIndex,
|
|
@@ -117,12 +169,12 @@ export class LocalIntervalCollection {
|
|
|
117
169
|
this.removeIntervalFromIndexes(interval);
|
|
118
170
|
this.removeIntervalListeners(interval);
|
|
119
171
|
}
|
|
120
|
-
createInterval(start, end, intervalType, op
|
|
121
|
-
return this.helpers.create(this.label, start, end, this.client, intervalType, op, undefined,
|
|
172
|
+
createInterval(start, end, intervalType, op) {
|
|
173
|
+
return this.helpers.create(this.label, start, end, this.client, intervalType, op, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
122
174
|
}
|
|
123
|
-
addInterval(start, end, intervalType, props, op
|
|
175
|
+
addInterval(start, end, intervalType, props, op) {
|
|
124
176
|
var _a;
|
|
125
|
-
const interval = this.createInterval(start, end, intervalType, op
|
|
177
|
+
const interval = this.createInterval(start, end, intervalType, op);
|
|
126
178
|
if (interval) {
|
|
127
179
|
if (!interval.properties) {
|
|
128
180
|
interval.properties = createMap();
|
|
@@ -159,7 +211,7 @@ export class LocalIntervalCollection {
|
|
|
159
211
|
this.addIntervalListeners(interval);
|
|
160
212
|
}
|
|
161
213
|
changeInterval(interval, start, end, op, localSeq) {
|
|
162
|
-
const newInterval = interval.modify(this.label, start, end, op, localSeq);
|
|
214
|
+
const newInterval = interval.modify(this.label, start, end, op, localSeq, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
163
215
|
if (newInterval) {
|
|
164
216
|
this.removeExistingInterval(interval);
|
|
165
217
|
this.add(newInterval);
|
|
@@ -182,7 +234,7 @@ export class LocalIntervalCollection {
|
|
|
182
234
|
// either, so this must be special-cased.
|
|
183
235
|
return ref;
|
|
184
236
|
}
|
|
185
|
-
return this.client.createLocalReferencePosition(segment, ref.getOffset(), ReferenceType.Transient, ref.properties, ref.slidingPreference);
|
|
237
|
+
return this.client.createLocalReferencePosition(segment, ref.getOffset(), ReferenceType.Transient, ref.properties, ref.slidingPreference, ref.canSlideToEndpoint);
|
|
186
238
|
};
|
|
187
239
|
if (interval instanceof SequenceInterval) {
|
|
188
240
|
let previousInterval;
|
|
@@ -239,7 +291,6 @@ SequenceIntervalCollectionValueType._ops = makeOpsMap();
|
|
|
239
291
|
class IntervalCollectionFactory {
|
|
240
292
|
load(emitter, raw = [], options) {
|
|
241
293
|
const helpers = {
|
|
242
|
-
compareEnds: (a, b) => a.end - b.end,
|
|
243
294
|
create: createInterval,
|
|
244
295
|
};
|
|
245
296
|
const collection = new IntervalCollection(helpers, false, emitter, raw, options);
|
|
@@ -336,10 +387,18 @@ class IntervalCollectionIterator {
|
|
|
336
387
|
};
|
|
337
388
|
}
|
|
338
389
|
}
|
|
390
|
+
// solely for type checking in the implementation of add - will be removed once
|
|
391
|
+
// deprecated signatures are removed
|
|
392
|
+
const isSequencePlace = (place) => {
|
|
393
|
+
return typeof place === "number" || typeof place === "string" || place.pos !== undefined;
|
|
394
|
+
};
|
|
339
395
|
/**
|
|
340
396
|
* {@inheritdoc IIntervalCollection}
|
|
341
397
|
*/
|
|
342
398
|
export class IntervalCollection extends TypedEventEmitter {
|
|
399
|
+
get attached() {
|
|
400
|
+
return !!this.localCollection;
|
|
401
|
+
}
|
|
343
402
|
/** @internal */
|
|
344
403
|
constructor(helpers, requiresClient, emitter, serializedIntervals, options = {}) {
|
|
345
404
|
super();
|
|
@@ -355,9 +414,6 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
355
414
|
? serializedIntervals
|
|
356
415
|
: serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
|
|
357
416
|
}
|
|
358
|
-
get attached() {
|
|
359
|
-
return !!this.localCollection;
|
|
360
|
-
}
|
|
361
417
|
/**
|
|
362
418
|
* {@inheritdoc IIntervalCollection.attachIndex}
|
|
363
419
|
*/
|
|
@@ -390,6 +446,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
390
446
|
if (!this.client) {
|
|
391
447
|
throw new LoggingError("mergeTree client must exist");
|
|
392
448
|
}
|
|
449
|
+
if (pos === "start" || pos === "end") {
|
|
450
|
+
return pos;
|
|
451
|
+
}
|
|
393
452
|
const { clientId } = this.client.getCollabWindow();
|
|
394
453
|
const { segment, offset } = this.client.getContainingSegment(pos, {
|
|
395
454
|
referenceSequenceNumber: seqNumberFrom,
|
|
@@ -397,7 +456,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
397
456
|
}, localSeq);
|
|
398
457
|
// if segment is undefined, it slid off the string
|
|
399
458
|
assert(segment !== undefined, 0x54e /* No segment found */);
|
|
400
|
-
const segoff = getSlideToSegoff({ segment, offset }) ?? segment;
|
|
459
|
+
const segoff = getSlideToSegoff({ segment, offset }, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint) ?? segment;
|
|
401
460
|
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
402
461
|
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
403
462
|
return DetachedReferencePosition;
|
|
@@ -436,12 +495,18 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
436
495
|
}
|
|
437
496
|
});
|
|
438
497
|
}
|
|
439
|
-
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
|
|
498
|
+
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, this.options, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
|
|
440
499
|
if (this.savedSerializedIntervals) {
|
|
441
500
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
442
501
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
443
|
-
const { start, end, intervalType, properties,
|
|
444
|
-
const
|
|
502
|
+
const { start: startPos, end: endPos, intervalType, properties, startSide, endSide, } = serializedInterval;
|
|
503
|
+
const start = typeof startPos === "number" && startSide !== undefined
|
|
504
|
+
? { pos: startPos, side: startSide }
|
|
505
|
+
: startPos;
|
|
506
|
+
const end = typeof endPos === "number" && endSide !== undefined
|
|
507
|
+
? { pos: endPos, side: endSide }
|
|
508
|
+
: endPos;
|
|
509
|
+
const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
445
510
|
if (properties) {
|
|
446
511
|
interval.addProperties(properties);
|
|
447
512
|
}
|
|
@@ -487,28 +552,59 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
487
552
|
}
|
|
488
553
|
return this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
489
554
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
555
|
+
assertStickinessEnabled(start, end) {
|
|
556
|
+
if (!(typeof start === "number" && typeof end === "number") &&
|
|
557
|
+
!this.options.intervalStickinessEnabled) {
|
|
558
|
+
throw new UsageError("attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
add(start, end, intervalType, props) {
|
|
562
|
+
let intStart;
|
|
563
|
+
let intEnd;
|
|
564
|
+
let type;
|
|
565
|
+
let properties;
|
|
566
|
+
if (isSequencePlace(start)) {
|
|
567
|
+
intStart = start;
|
|
568
|
+
assert(end !== undefined, 0x7c0 /* end must be defined */);
|
|
569
|
+
intEnd = end;
|
|
570
|
+
assert(intervalType !== undefined, 0x7c1 /* intervalType must be defined */);
|
|
571
|
+
type = intervalType;
|
|
572
|
+
properties = props;
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
intStart = start.start;
|
|
576
|
+
intEnd = start.end;
|
|
577
|
+
type = IntervalType.SlideOnRemove;
|
|
578
|
+
properties = start.props;
|
|
579
|
+
}
|
|
494
580
|
if (!this.localCollection) {
|
|
495
581
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
496
582
|
}
|
|
497
|
-
if (
|
|
583
|
+
if (type & IntervalType.Transient) {
|
|
498
584
|
throw new LoggingError("Can not add transient intervals");
|
|
499
585
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
586
|
+
const { startSide, endSide, startPos, endPos } = endpointPosAndSide(intStart, intEnd);
|
|
587
|
+
assert(startPos !== undefined &&
|
|
588
|
+
endPos !== undefined &&
|
|
589
|
+
startSide !== undefined &&
|
|
590
|
+
endSide !== undefined, 0x793 /* start and end cannot be undefined because they were not passed in as undefined */);
|
|
591
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
592
|
+
this.assertStickinessEnabled(intStart, intEnd);
|
|
593
|
+
const interval = this.localCollection.addInterval(toSequencePlace(startPos, startSide), toSequencePlace(endPos, endSide), type, properties);
|
|
504
594
|
if (interval) {
|
|
595
|
+
if (!this.isCollaborating && interval instanceof SequenceInterval) {
|
|
596
|
+
setSlideOnRemove(interval.start);
|
|
597
|
+
setSlideOnRemove(interval.end);
|
|
598
|
+
}
|
|
505
599
|
const serializedInterval = {
|
|
506
|
-
|
|
507
|
-
|
|
600
|
+
start: startPos,
|
|
601
|
+
end: endPos,
|
|
602
|
+
intervalType: type,
|
|
508
603
|
properties: interval.properties,
|
|
509
604
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
510
|
-
start,
|
|
511
605
|
stickiness,
|
|
606
|
+
startSide,
|
|
607
|
+
endSide,
|
|
512
608
|
};
|
|
513
609
|
const localSeq = this.getNextLocalSeq();
|
|
514
610
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -572,8 +668,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
572
668
|
}
|
|
573
669
|
const interval = this.getIntervalById(id);
|
|
574
670
|
if (interval) {
|
|
575
|
-
|
|
576
|
-
const deltaProps = interval.addProperties(props, true, UnassignedSequenceNumber);
|
|
671
|
+
const deltaProps = interval.addProperties(props, true, this.isCollaborating ? UnassignedSequenceNumber : UniversalSequenceNumber);
|
|
577
672
|
const serializedInterval = interval.serialize();
|
|
578
673
|
// Emit a change op that will only change properties. Add the ID to
|
|
579
674
|
// the property bag provided by the caller.
|
|
@@ -604,9 +699,18 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
604
699
|
if (!newInterval) {
|
|
605
700
|
return undefined;
|
|
606
701
|
}
|
|
702
|
+
if (!this.isCollaborating && newInterval instanceof SequenceInterval) {
|
|
703
|
+
setSlideOnRemove(newInterval.start);
|
|
704
|
+
setSlideOnRemove(newInterval.end);
|
|
705
|
+
}
|
|
607
706
|
const serializedInterval = interval.serialize();
|
|
608
|
-
|
|
609
|
-
|
|
707
|
+
const { startPos, startSide, endPos, endSide } = endpointPosAndSide(start, end);
|
|
708
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
709
|
+
serializedInterval.start = startPos;
|
|
710
|
+
serializedInterval.end = endPos;
|
|
711
|
+
serializedInterval.startSide = startSide;
|
|
712
|
+
serializedInterval.endSide = endSide;
|
|
713
|
+
serializedInterval.stickiness = stickiness;
|
|
610
714
|
// Emit a property bag containing only the ID, as we don't intend for this op to change any properties.
|
|
611
715
|
serializedInterval.properties = {
|
|
612
716
|
[reservedIntervalIdKey]: interval.getIntervalId(),
|
|
@@ -621,7 +725,13 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
621
725
|
// No interval to change
|
|
622
726
|
return undefined;
|
|
623
727
|
}
|
|
728
|
+
get isCollaborating() {
|
|
729
|
+
return this.client?.getCollabWindow().collaborating ?? false;
|
|
730
|
+
}
|
|
624
731
|
addPendingChange(id, serializedInterval) {
|
|
732
|
+
if (!this.isCollaborating) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
625
735
|
if (serializedInterval.start !== undefined) {
|
|
626
736
|
this.addPendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
627
737
|
}
|
|
@@ -714,7 +824,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
714
824
|
// If changeInterval gives us a new interval, work with that one. Otherwise keep working with
|
|
715
825
|
// the one we originally found in the tree.
|
|
716
826
|
newInterval =
|
|
717
|
-
this.localCollection.changeInterval(interval, start, end, op) ?? interval;
|
|
827
|
+
this.localCollection.changeInterval(interval, toOptionalSequencePlace(start, serializedInterval.startSide), toOptionalSequencePlace(end, serializedInterval.endSide), op) ?? interval;
|
|
718
828
|
}
|
|
719
829
|
const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
|
|
720
830
|
if (this.onDeserialize) {
|
|
@@ -759,7 +869,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
759
869
|
if (!this.attached) {
|
|
760
870
|
throw new LoggingError("attachSequence must be called");
|
|
761
871
|
}
|
|
762
|
-
const { intervalType, properties } = serializedInterval;
|
|
872
|
+
const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
|
|
763
873
|
const { start: startRebased, end: endRebased } = this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
|
|
764
874
|
const intervalId = properties?.[reservedIntervalIdKey];
|
|
765
875
|
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
|
|
@@ -769,8 +879,12 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
769
879
|
intervalType,
|
|
770
880
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
771
881
|
properties,
|
|
882
|
+
stickiness,
|
|
883
|
+
startSide,
|
|
884
|
+
endSide,
|
|
772
885
|
};
|
|
773
886
|
if (opName === "change" &&
|
|
887
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
|
|
774
888
|
(this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
|
|
775
889
|
this.removePendingChange(serializedInterval);
|
|
776
890
|
this.addPendingChange(intervalId, rebased);
|
|
@@ -788,7 +902,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
788
902
|
assert(localInterval instanceof SequenceInterval, 0x3a0 /* localInterval must be `SequenceInterval` when used with client */);
|
|
789
903
|
// The rebased op may place this interval's endpoints on different segments. Calling `changeInterval` here
|
|
790
904
|
// updates the local client's state to be consistent with the emitted op.
|
|
791
|
-
this.localCollection?.changeInterval(localInterval, startRebased, endRebased, undefined, localSeq);
|
|
905
|
+
this.localCollection?.changeInterval(localInterval, toOptionalSequencePlace(startRebased, startSide), toOptionalSequencePlace(endRebased, endSide), undefined, localSeq);
|
|
792
906
|
}
|
|
793
907
|
return rebased;
|
|
794
908
|
}
|
|
@@ -800,18 +914,12 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
800
914
|
if (segoff.segment?.localRefs?.has(lref) !== true) {
|
|
801
915
|
return undefined;
|
|
802
916
|
}
|
|
803
|
-
const newSegoff = getSlideToSegoff(segoff);
|
|
917
|
+
const newSegoff = getSlideToSegoff(segoff, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
804
918
|
const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
|
|
805
919
|
? undefined
|
|
806
920
|
: newSegoff;
|
|
807
921
|
return value;
|
|
808
922
|
}
|
|
809
|
-
setSlideOnRemove(lref) {
|
|
810
|
-
let refType = lref.refType;
|
|
811
|
-
refType = refType & ~ReferenceType.StayOnRemove;
|
|
812
|
-
refType = refType | ReferenceType.SlideOnRemove;
|
|
813
|
-
lref.refType = refType;
|
|
814
|
-
}
|
|
815
923
|
ackInterval(interval, op) {
|
|
816
924
|
// Only SequenceIntervals need potential sliding
|
|
817
925
|
if (!(interval instanceof SequenceInterval)) {
|
|
@@ -827,10 +935,10 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
827
935
|
const hasPendingStartChange = this.hasPendingChangeStart(id);
|
|
828
936
|
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
829
937
|
if (!hasPendingStartChange) {
|
|
830
|
-
|
|
938
|
+
setSlideOnRemove(interval.start);
|
|
831
939
|
}
|
|
832
940
|
if (!hasPendingEndChange) {
|
|
833
|
-
|
|
941
|
+
setSlideOnRemove(interval.end);
|
|
834
942
|
}
|
|
835
943
|
const needsStartUpdate = newStart !== undefined && !hasPendingStartChange;
|
|
836
944
|
const needsEndUpdate = newEnd !== undefined && !hasPendingEndChange;
|
|
@@ -849,7 +957,8 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
849
957
|
}
|
|
850
958
|
if (needsStartUpdate) {
|
|
851
959
|
const props = interval.start.properties;
|
|
852
|
-
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op, startReferenceSlidingPreference(interval.stickiness))
|
|
960
|
+
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op, undefined, undefined, startReferenceSlidingPreference(interval.stickiness), startReferenceSlidingPreference(interval.stickiness) ===
|
|
961
|
+
SlidingPreference.BACKWARD);
|
|
853
962
|
if (props) {
|
|
854
963
|
interval.start.addProperties(props);
|
|
855
964
|
}
|
|
@@ -861,7 +970,8 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
861
970
|
}
|
|
862
971
|
if (needsEndUpdate) {
|
|
863
972
|
const props = interval.end.properties;
|
|
864
|
-
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op, endReferenceSlidingPreference(interval.stickiness))
|
|
973
|
+
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op, undefined, undefined, endReferenceSlidingPreference(interval.stickiness), endReferenceSlidingPreference(interval.stickiness) ===
|
|
974
|
+
SlidingPreference.FORWARD);
|
|
865
975
|
if (props) {
|
|
866
976
|
interval.end.addProperties(props);
|
|
867
977
|
}
|
|
@@ -891,7 +1001,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
891
1001
|
throw new LoggingError("attachSequence must be called");
|
|
892
1002
|
}
|
|
893
1003
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
894
|
-
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op
|
|
1004
|
+
const interval = this.localCollection.addInterval(toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before), toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before), serializedInterval.intervalType, serializedInterval.properties, op);
|
|
895
1005
|
if (interval) {
|
|
896
1006
|
if (this.onDeserialize) {
|
|
897
1007
|
this.onDeserialize(interval);
|
|
@@ -1009,11 +1119,18 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1009
1119
|
return this.localCollection.endIntervalIndex.nextInterval(pos);
|
|
1010
1120
|
}
|
|
1011
1121
|
}
|
|
1122
|
+
function setSlideOnRemove(lref) {
|
|
1123
|
+
let refType = lref.refType;
|
|
1124
|
+
refType = refType & ~ReferenceType.StayOnRemove;
|
|
1125
|
+
refType = refType | ReferenceType.SlideOnRemove;
|
|
1126
|
+
lref.refType = refType;
|
|
1127
|
+
}
|
|
1012
1128
|
/**
|
|
1013
1129
|
* Returns an object that can be used to find the interval a given LocalReferencePosition belongs to.
|
|
1014
1130
|
* @returns undefined if the reference position is not the endpoint of any interval (e.g. it was created
|
|
1015
1131
|
* on the merge tree directly by app code), otherwise an {@link IntervalLocator} for the interval this
|
|
1016
1132
|
* endpoint is a part of.
|
|
1133
|
+
* @public
|
|
1017
1134
|
*/
|
|
1018
1135
|
export function intervalLocatorFromEndpoint(potentialEndpoint) {
|
|
1019
1136
|
const { interval, [reservedRangeLabelsKey]: collectionNameArray } = potentialEndpoint.properties ?? {};
|