@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
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/* eslint-disable no-bitwise */
|
|
7
|
+
/* eslint-disable import/no-deprecated */
|
|
7
8
|
|
|
8
9
|
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
9
10
|
import { assert } from "@fluidframework/core-utils";
|
|
@@ -22,6 +23,8 @@ import {
|
|
|
22
23
|
reservedRangeLabelsKey,
|
|
23
24
|
UnassignedSequenceNumber,
|
|
24
25
|
DetachedReferencePosition,
|
|
26
|
+
UniversalSequenceNumber,
|
|
27
|
+
SlidingPreference,
|
|
25
28
|
} from "@fluidframework/merge-tree";
|
|
26
29
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
27
30
|
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
|
|
@@ -53,15 +56,64 @@ import {
|
|
|
53
56
|
createInterval,
|
|
54
57
|
} from "./intervals";
|
|
55
58
|
import {
|
|
59
|
+
EndpointIndex,
|
|
56
60
|
IEndpointIndex,
|
|
57
61
|
IIdIntervalIndex,
|
|
58
62
|
IOverlappingIntervalsIndex,
|
|
59
63
|
IntervalIndex,
|
|
60
|
-
|
|
64
|
+
OverlappingIntervalsIndex,
|
|
61
65
|
createIdIntervalIndex,
|
|
62
|
-
createOverlappingIntervalsIndex,
|
|
63
66
|
} from "./intervalIndex";
|
|
64
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Defines a position and side relative to a character in a sequence.
|
|
70
|
+
*
|
|
71
|
+
* For this purpose, sequences look like:
|
|
72
|
+
*
|
|
73
|
+
* `{start} - {character 0} - {character 1} - ... - {character N} - {end}`
|
|
74
|
+
*
|
|
75
|
+
* Each `{value}` in the diagram is a character within a sequence.
|
|
76
|
+
* Each `-` in the above diagram is a position where text could be inserted.
|
|
77
|
+
* Each position between a `{value}` and a `-` is a `SequencePlace`.
|
|
78
|
+
*
|
|
79
|
+
* The special endpoints `{start}` and `{end}` refer to positions outside the
|
|
80
|
+
* contents of the string.
|
|
81
|
+
*
|
|
82
|
+
* This gives us 2N + 2 possible positions to refer to within a string, where N
|
|
83
|
+
* is the number of characters.
|
|
84
|
+
*
|
|
85
|
+
* If the position is specified with a bare number, the side defaults to
|
|
86
|
+
* `Side.Before`.
|
|
87
|
+
*
|
|
88
|
+
* If a SequencePlace is the endpoint of a range (e.g. start/end of an interval or search range),
|
|
89
|
+
* the Side value means it is exclusive if it is nearer to the other position and inclusive if it is farther.
|
|
90
|
+
* E.g. the start of a range with Side.After is exclusive of the character at the position.
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
export type SequencePlace = number | "start" | "end" | InteriorSequencePlace;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A sequence place that does not refer to the special endpoint segments.
|
|
97
|
+
*
|
|
98
|
+
* See {@link SequencePlace} for additional context.
|
|
99
|
+
* @public
|
|
100
|
+
*/
|
|
101
|
+
export interface InteriorSequencePlace {
|
|
102
|
+
pos: number;
|
|
103
|
+
side: Side;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Defines a side relative to a character in a sequence.
|
|
108
|
+
*
|
|
109
|
+
* @remarks See {@link SequencePlace} for additional context on usage.
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
112
|
+
export enum Side {
|
|
113
|
+
Before = 0,
|
|
114
|
+
After = 1,
|
|
115
|
+
}
|
|
116
|
+
|
|
65
117
|
const reservedIntervalIdKey = "intervalId";
|
|
66
118
|
|
|
67
119
|
export interface ISerializedIntervalCollectionV2 {
|
|
@@ -70,6 +122,13 @@ export interface ISerializedIntervalCollectionV2 {
|
|
|
70
122
|
intervals: CompressedSerializedInterval[];
|
|
71
123
|
}
|
|
72
124
|
|
|
125
|
+
export function sidesFromStickiness(stickiness: IntervalStickiness) {
|
|
126
|
+
const startSide = (stickiness & IntervalStickiness.START) !== 0 ? Side.After : Side.Before;
|
|
127
|
+
const endSide = (stickiness & IntervalStickiness.END) !== 0 ? Side.Before : Side.After;
|
|
128
|
+
|
|
129
|
+
return { startSide, endSide };
|
|
130
|
+
}
|
|
131
|
+
|
|
73
132
|
/**
|
|
74
133
|
* Decompress an interval after loading a summary from JSON. The exact format
|
|
75
134
|
* of this compression is unspecified and subject to change
|
|
@@ -78,13 +137,17 @@ function decompressInterval(
|
|
|
78
137
|
interval: CompressedSerializedInterval,
|
|
79
138
|
label?: string,
|
|
80
139
|
): ISerializedInterval {
|
|
140
|
+
const stickiness = interval[5] ?? IntervalStickiness.END;
|
|
141
|
+
const { startSide, endSide } = sidesFromStickiness(stickiness);
|
|
81
142
|
return {
|
|
82
143
|
start: interval[0],
|
|
83
144
|
end: interval[1],
|
|
84
145
|
sequenceNumber: interval[2],
|
|
85
146
|
intervalType: interval[3],
|
|
86
147
|
properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
|
|
87
|
-
stickiness
|
|
148
|
+
stickiness,
|
|
149
|
+
startSide,
|
|
150
|
+
endSide,
|
|
88
151
|
};
|
|
89
152
|
}
|
|
90
153
|
|
|
@@ -95,7 +158,7 @@ function decompressInterval(
|
|
|
95
158
|
function compressInterval(interval: ISerializedInterval): CompressedSerializedInterval {
|
|
96
159
|
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
97
160
|
|
|
98
|
-
|
|
161
|
+
let base: CompressedSerializedInterval = [
|
|
99
162
|
start,
|
|
100
163
|
end,
|
|
101
164
|
sequenceNumber,
|
|
@@ -106,18 +169,69 @@ function compressInterval(interval: ISerializedInterval): CompressedSerializedIn
|
|
|
106
169
|
];
|
|
107
170
|
|
|
108
171
|
if (interval.stickiness !== undefined && interval.stickiness !== IntervalStickiness.END) {
|
|
109
|
-
|
|
172
|
+
// reassignment to make it easier for typescript to reason about types
|
|
173
|
+
base = [...base, interval.stickiness];
|
|
110
174
|
}
|
|
111
175
|
|
|
112
176
|
return base;
|
|
113
177
|
}
|
|
114
178
|
|
|
179
|
+
export function endpointPosAndSide(
|
|
180
|
+
start: SequencePlace | undefined,
|
|
181
|
+
end: SequencePlace | undefined,
|
|
182
|
+
) {
|
|
183
|
+
const startIsPlainEndpoint = typeof start === "number" || start === "start" || start === "end";
|
|
184
|
+
const endIsPlainEndpoint = typeof end === "number" || end === "start" || end === "end";
|
|
185
|
+
|
|
186
|
+
const startSide = startIsPlainEndpoint ? Side.Before : start?.side;
|
|
187
|
+
const endSide = endIsPlainEndpoint ? Side.Before : end?.side;
|
|
188
|
+
|
|
189
|
+
const startPos = startIsPlainEndpoint ? start : start?.pos;
|
|
190
|
+
const endPos = endIsPlainEndpoint ? end : end?.pos;
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
startSide,
|
|
194
|
+
endSide,
|
|
195
|
+
startPos,
|
|
196
|
+
endPos,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function toSequencePlace(pos: number | "start" | "end", side: Side): SequencePlace {
|
|
201
|
+
return typeof pos === "number" ? { pos, side } : pos;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function toOptionalSequencePlace(
|
|
205
|
+
pos: number | "start" | "end" | undefined,
|
|
206
|
+
side: Side = Side.Before,
|
|
207
|
+
): SequencePlace | undefined {
|
|
208
|
+
return typeof pos === "number" ? { pos, side } : pos;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function computeStickinessFromSide(
|
|
212
|
+
startPos: number | "start" | "end" | undefined = -1,
|
|
213
|
+
startSide: Side = Side.Before,
|
|
214
|
+
endPos: number | "start" | "end" | undefined = -1,
|
|
215
|
+
endSide: Side = Side.Before,
|
|
216
|
+
): IntervalStickiness {
|
|
217
|
+
let stickiness: IntervalStickiness = IntervalStickiness.NONE;
|
|
218
|
+
|
|
219
|
+
if (startSide === Side.After || startPos === "start") {
|
|
220
|
+
stickiness |= IntervalStickiness.START;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (endSide === Side.Before || endPos === "end") {
|
|
224
|
+
stickiness |= IntervalStickiness.END;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return stickiness as IntervalStickiness;
|
|
228
|
+
}
|
|
229
|
+
|
|
115
230
|
export function createIntervalIndex() {
|
|
116
231
|
const helpers: IIntervalHelpers<Interval> = {
|
|
117
|
-
compareEnds: (a: Interval, b: Interval) => a.end - b.end,
|
|
118
232
|
create: createInterval,
|
|
119
233
|
};
|
|
120
|
-
const lc = new LocalIntervalCollection<Interval>(undefined as any as Client, "", helpers);
|
|
234
|
+
const lc = new LocalIntervalCollection<Interval>(undefined as any as Client, "", helpers, {});
|
|
121
235
|
return lc;
|
|
122
236
|
}
|
|
123
237
|
|
|
@@ -132,15 +246,16 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
132
246
|
private readonly client: Client,
|
|
133
247
|
private readonly label: string,
|
|
134
248
|
private readonly helpers: IIntervalHelpers<TInterval>,
|
|
249
|
+
private readonly options: Partial<SequenceOptions>,
|
|
135
250
|
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
136
251
|
private readonly onPositionChange?: (
|
|
137
252
|
interval: TInterval,
|
|
138
253
|
previousInterval: TInterval,
|
|
139
254
|
) => void,
|
|
140
255
|
) {
|
|
141
|
-
this.overlappingIntervalsIndex =
|
|
256
|
+
this.overlappingIntervalsIndex = new OverlappingIntervalsIndex(client, helpers);
|
|
142
257
|
this.idIntervalIndex = createIdIntervalIndex<TInterval>();
|
|
143
|
-
this.endIntervalIndex =
|
|
258
|
+
this.endIntervalIndex = new EndpointIndex(client, helpers);
|
|
144
259
|
this.indexes = new Set([
|
|
145
260
|
this.overlappingIntervalsIndex,
|
|
146
261
|
this.idIntervalIndex,
|
|
@@ -148,7 +263,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
148
263
|
]);
|
|
149
264
|
}
|
|
150
265
|
|
|
151
|
-
public createLegacyId(start: number, end: number): string {
|
|
266
|
+
public createLegacyId(start: number | "start" | "end", end: number | "start" | "end"): string {
|
|
152
267
|
// Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
|
|
153
268
|
// without ID's.
|
|
154
269
|
return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
|
|
@@ -203,11 +318,10 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
203
318
|
}
|
|
204
319
|
|
|
205
320
|
public createInterval(
|
|
206
|
-
start:
|
|
207
|
-
end:
|
|
321
|
+
start: SequencePlace,
|
|
322
|
+
end: SequencePlace,
|
|
208
323
|
intervalType: IntervalType,
|
|
209
324
|
op?: ISequencedDocumentMessage,
|
|
210
|
-
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
211
325
|
): TInterval {
|
|
212
326
|
return this.helpers.create(
|
|
213
327
|
this.label,
|
|
@@ -217,19 +331,18 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
217
331
|
intervalType,
|
|
218
332
|
op,
|
|
219
333
|
undefined,
|
|
220
|
-
|
|
334
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
221
335
|
);
|
|
222
336
|
}
|
|
223
337
|
|
|
224
338
|
public addInterval(
|
|
225
|
-
start:
|
|
226
|
-
end:
|
|
339
|
+
start: SequencePlace,
|
|
340
|
+
end: SequencePlace,
|
|
227
341
|
intervalType: IntervalType,
|
|
228
342
|
props?: PropertySet,
|
|
229
343
|
op?: ISequencedDocumentMessage,
|
|
230
|
-
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
231
344
|
) {
|
|
232
|
-
const interval: TInterval = this.createInterval(start, end, intervalType, op
|
|
345
|
+
const interval: TInterval = this.createInterval(start, end, intervalType, op);
|
|
233
346
|
if (interval) {
|
|
234
347
|
if (!interval.properties) {
|
|
235
348
|
interval.properties = createMap<any>();
|
|
@@ -276,14 +389,19 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
276
389
|
|
|
277
390
|
public changeInterval(
|
|
278
391
|
interval: TInterval,
|
|
279
|
-
start:
|
|
280
|
-
end:
|
|
392
|
+
start: SequencePlace | undefined,
|
|
393
|
+
end: SequencePlace | undefined,
|
|
281
394
|
op?: ISequencedDocumentMessage,
|
|
282
395
|
localSeq?: number,
|
|
283
396
|
) {
|
|
284
|
-
const newInterval = interval.modify(
|
|
285
|
-
|
|
286
|
-
|
|
397
|
+
const newInterval = interval.modify(
|
|
398
|
+
this.label,
|
|
399
|
+
start,
|
|
400
|
+
end,
|
|
401
|
+
op,
|
|
402
|
+
localSeq,
|
|
403
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
404
|
+
) as TInterval | undefined;
|
|
287
405
|
if (newInterval) {
|
|
288
406
|
this.removeExistingInterval(interval);
|
|
289
407
|
this.add(newInterval);
|
|
@@ -317,6 +435,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
317
435
|
ReferenceType.Transient,
|
|
318
436
|
ref.properties,
|
|
319
437
|
ref.slidingPreference,
|
|
438
|
+
ref.canSlideToEndpoint,
|
|
320
439
|
);
|
|
321
440
|
};
|
|
322
441
|
if (interval instanceof SequenceInterval) {
|
|
@@ -410,7 +529,6 @@ class IntervalCollectionFactory implements IValueFactory<IntervalCollection<Inte
|
|
|
410
529
|
options?: Partial<SequenceOptions>,
|
|
411
530
|
): IntervalCollection<Interval> {
|
|
412
531
|
const helpers: IIntervalHelpers<Interval> = {
|
|
413
|
-
compareEnds: (a: Interval, b: Interval) => a.end - b.end,
|
|
414
532
|
create: createInterval,
|
|
415
533
|
};
|
|
416
534
|
const collection = new IntervalCollection<Interval>(helpers, false, emitter, raw, options);
|
|
@@ -505,6 +623,9 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
|
|
|
505
623
|
]);
|
|
506
624
|
}
|
|
507
625
|
|
|
626
|
+
/**
|
|
627
|
+
* @public
|
|
628
|
+
*/
|
|
508
629
|
export type DeserializeCallback = (properties: PropertySet) => void;
|
|
509
630
|
|
|
510
631
|
class IntervalCollectionIterator<TInterval extends ISerializableInterval>
|
|
@@ -542,6 +663,7 @@ class IntervalCollectionIterator<TInterval extends ISerializableInterval>
|
|
|
542
663
|
|
|
543
664
|
/**
|
|
544
665
|
* Change events emitted by `IntervalCollection`s
|
|
666
|
+
* @public
|
|
545
667
|
*/
|
|
546
668
|
export interface IIntervalCollectionEvent<TInterval extends ISerializableInterval> extends IEvent {
|
|
547
669
|
/**
|
|
@@ -600,9 +722,16 @@ export interface IIntervalCollectionEvent<TInterval extends ISerializableInterva
|
|
|
600
722
|
);
|
|
601
723
|
}
|
|
602
724
|
|
|
725
|
+
// solely for type checking in the implementation of add - will be removed once
|
|
726
|
+
// deprecated signatures are removed
|
|
727
|
+
const isSequencePlace = (place: any): place is SequencePlace => {
|
|
728
|
+
return typeof place === "number" || typeof place === "string" || place.pos !== undefined;
|
|
729
|
+
};
|
|
730
|
+
|
|
603
731
|
/**
|
|
604
732
|
* Collection of intervals that supports addition, modification, removal, and efficient spatial querying.
|
|
605
733
|
* Changes to this collection will be incur updates on collaborating clients (i.e. they are not local-only).
|
|
734
|
+
* @public
|
|
606
735
|
*/
|
|
607
736
|
export interface IIntervalCollection<TInterval extends ISerializableInterval>
|
|
608
737
|
extends TypedEventEmitter<IIntervalCollectionEvent<TInterval>> {
|
|
@@ -631,22 +760,101 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
|
|
|
631
760
|
getIntervalById(id: string): TInterval | undefined;
|
|
632
761
|
/**
|
|
633
762
|
* Creates a new interval and add it to the collection.
|
|
763
|
+
* @deprecated call IntervalCollection.add without specifying an intervalType
|
|
634
764
|
* @param start - interval start position (inclusive)
|
|
635
765
|
* @param end - interval end position (exclusive)
|
|
636
766
|
* @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
|
|
637
767
|
* @param props - properties of the interval
|
|
638
|
-
* @param stickiness - {@link (IntervalStickiness:type)} to apply to the added interval.
|
|
639
768
|
* @returns The created interval
|
|
640
|
-
* @remarks See documentation on {@link SequenceInterval} for comments on
|
|
641
|
-
* with how the current
|
|
769
|
+
* @remarks See documentation on {@link SequenceInterval} for comments on
|
|
770
|
+
* interval endpoint semantics: there are subtleties with how the current
|
|
771
|
+
* half-open behavior is represented.
|
|
772
|
+
*
|
|
773
|
+
* Note that intervals may behave unexpectedly if the entire contents
|
|
774
|
+
* of the string are deleted. In this case, it is possible for one endpoint
|
|
775
|
+
* of the interval to become detached, while the other remains on the string.
|
|
776
|
+
*
|
|
777
|
+
* By adjusting the `side` and `pos` values of the `start` and `end` parameters,
|
|
778
|
+
* it is possible to control whether the interval expands to include content
|
|
779
|
+
* inserted at its start or end.
|
|
780
|
+
*
|
|
781
|
+
* See {@link SequencePlace} for more details on the model.
|
|
782
|
+
*
|
|
783
|
+
* @example
|
|
784
|
+
*
|
|
785
|
+
* Given the string "ABCD":
|
|
786
|
+
*
|
|
787
|
+
*```typescript
|
|
788
|
+
* // Refers to "BC". If any content is inserted before B or after C, this
|
|
789
|
+
* // interval will include that content
|
|
790
|
+
* //
|
|
791
|
+
* // Picture:
|
|
792
|
+
* // \{start\} - A[- B - C -]D - \{end\}
|
|
793
|
+
* // \{start\} - A - B - C - D - \{end\}
|
|
794
|
+
* collection.add(\{ pos: 0, side: Side.After \}, \{ pos: 3, side: Side.Before \}, IntervalType.SlideOnRemove);
|
|
795
|
+
* // Equivalent to specifying the same positions and Side.Before.
|
|
796
|
+
* // Refers to "ABC". Content inserted after C will be included in the
|
|
797
|
+
* // interval, but content inserted before A will not.
|
|
798
|
+
* // \{start\} -[A - B - C -]D - \{end\}
|
|
799
|
+
* // \{start\} - A - B - C - D - \{end\}
|
|
800
|
+
* collection.add(0, 3, IntervalType.SlideOnRemove);
|
|
801
|
+
*```
|
|
802
|
+
*
|
|
803
|
+
* In the case of the first example, if text is deleted,
|
|
804
|
+
*
|
|
805
|
+
* ```typescript
|
|
806
|
+
* // Delete the character "B"
|
|
807
|
+
* string.removeRange(1, 2);
|
|
808
|
+
* ```
|
|
809
|
+
*
|
|
810
|
+
* The start point of the interval will slide to the position immediately
|
|
811
|
+
* before "C", and the same will be true.
|
|
812
|
+
*
|
|
813
|
+
* ```
|
|
814
|
+
* \{start\} - A[- C -]D - \{end\}
|
|
815
|
+
* ```
|
|
816
|
+
*
|
|
817
|
+
* In this case, text inserted immediately before "C" would be included in
|
|
818
|
+
* the interval.
|
|
819
|
+
*
|
|
820
|
+
* ```typescript
|
|
821
|
+
* string.insertText(1, "EFG");
|
|
822
|
+
* ```
|
|
823
|
+
*
|
|
824
|
+
* With the string now being,
|
|
825
|
+
*
|
|
826
|
+
* ```
|
|
827
|
+
* \{start\} - A[- E - F - G - C -]D - \{end\}
|
|
828
|
+
* ```
|
|
829
|
+
*
|
|
830
|
+
* @privateRemarks TODO: ADO:5205 the above comment regarding behavior in
|
|
831
|
+
* the case that the entire interval has been deleted should be resolved at
|
|
832
|
+
* the same time as this ticket
|
|
642
833
|
*/
|
|
643
834
|
add(
|
|
644
|
-
start:
|
|
645
|
-
end:
|
|
835
|
+
start: SequencePlace,
|
|
836
|
+
end: SequencePlace,
|
|
646
837
|
intervalType: IntervalType,
|
|
647
838
|
props?: PropertySet,
|
|
648
|
-
stickiness?: IntervalStickiness,
|
|
649
839
|
): TInterval;
|
|
840
|
+
/**
|
|
841
|
+
* Creates a new interval and add it to the collection.
|
|
842
|
+
* @param start - interval start position (inclusive)
|
|
843
|
+
* @param end - interval end position (exclusive)
|
|
844
|
+
* @param props - properties of the interval
|
|
845
|
+
* @returns - the created interval
|
|
846
|
+
* @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
|
|
847
|
+
* with how the current half-open behavior is represented.
|
|
848
|
+
*/
|
|
849
|
+
add({
|
|
850
|
+
start,
|
|
851
|
+
end,
|
|
852
|
+
props,
|
|
853
|
+
}: {
|
|
854
|
+
start: SequencePlace;
|
|
855
|
+
end: SequencePlace;
|
|
856
|
+
props?: PropertySet;
|
|
857
|
+
}): TInterval;
|
|
650
858
|
/**
|
|
651
859
|
* Removes an interval from the collection.
|
|
652
860
|
* @param id - Id of the interval to remove
|
|
@@ -663,11 +871,11 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
|
|
|
663
871
|
/**
|
|
664
872
|
* Changes the endpoints of an existing interval.
|
|
665
873
|
* @param id - Id of the interval to change
|
|
666
|
-
* @param start - New start value
|
|
667
|
-
* @param end - New end value
|
|
874
|
+
* @param start - New start value. To leave the endpoint unchanged, pass the current value.
|
|
875
|
+
* @param end - New end value. To leave the endpoint unchanged, pass the current value.
|
|
668
876
|
* @returns the interval that was changed, if it existed in the collection.
|
|
669
877
|
*/
|
|
670
|
-
change(id: string, start
|
|
878
|
+
change(id: string, start: SequencePlace, end: SequencePlace): TInterval | undefined;
|
|
671
879
|
|
|
672
880
|
attachDeserializer(onDeserialize: DeserializeCallback): void;
|
|
673
881
|
/**
|
|
@@ -810,13 +1018,18 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
810
1018
|
}
|
|
811
1019
|
|
|
812
1020
|
private rebasePositionWithSegmentSlide(
|
|
813
|
-
pos: number,
|
|
1021
|
+
pos: number | "start" | "end",
|
|
814
1022
|
seqNumberFrom: number,
|
|
815
1023
|
localSeq: number,
|
|
816
|
-
): number | undefined {
|
|
1024
|
+
): number | "start" | "end" | undefined {
|
|
817
1025
|
if (!this.client) {
|
|
818
1026
|
throw new LoggingError("mergeTree client must exist");
|
|
819
1027
|
}
|
|
1028
|
+
|
|
1029
|
+
if (pos === "start" || pos === "end") {
|
|
1030
|
+
return pos;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
820
1033
|
const { clientId } = this.client.getCollabWindow();
|
|
821
1034
|
const { segment, offset } = this.client.getContainingSegment(
|
|
822
1035
|
pos,
|
|
@@ -830,7 +1043,12 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
830
1043
|
// if segment is undefined, it slid off the string
|
|
831
1044
|
assert(segment !== undefined, 0x54e /* No segment found */);
|
|
832
1045
|
|
|
833
|
-
const segoff =
|
|
1046
|
+
const segoff =
|
|
1047
|
+
getSlideToSegoff(
|
|
1048
|
+
{ segment, offset },
|
|
1049
|
+
undefined,
|
|
1050
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1051
|
+
) ?? segment;
|
|
834
1052
|
|
|
835
1053
|
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
836
1054
|
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
@@ -894,12 +1112,28 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
894
1112
|
client,
|
|
895
1113
|
label,
|
|
896
1114
|
this.helpers,
|
|
1115
|
+
this.options,
|
|
897
1116
|
(interval, previousInterval) => this.emitChange(interval, previousInterval, true, true),
|
|
898
1117
|
);
|
|
899
1118
|
if (this.savedSerializedIntervals) {
|
|
900
1119
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
901
1120
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
902
|
-
const {
|
|
1121
|
+
const {
|
|
1122
|
+
start: startPos,
|
|
1123
|
+
end: endPos,
|
|
1124
|
+
intervalType,
|
|
1125
|
+
properties,
|
|
1126
|
+
startSide,
|
|
1127
|
+
endSide,
|
|
1128
|
+
} = serializedInterval;
|
|
1129
|
+
const start =
|
|
1130
|
+
typeof startPos === "number" && startSide !== undefined
|
|
1131
|
+
? { pos: startPos, side: startSide }
|
|
1132
|
+
: startPos;
|
|
1133
|
+
const end =
|
|
1134
|
+
typeof endPos === "number" && endSide !== undefined
|
|
1135
|
+
? { pos: endPos, side: endSide }
|
|
1136
|
+
: endPos;
|
|
903
1137
|
const interval = this.helpers.create(
|
|
904
1138
|
label,
|
|
905
1139
|
start,
|
|
@@ -908,7 +1142,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
908
1142
|
intervalType,
|
|
909
1143
|
undefined,
|
|
910
1144
|
true,
|
|
911
|
-
|
|
1145
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
912
1146
|
);
|
|
913
1147
|
if (properties) {
|
|
914
1148
|
interval.addProperties(properties);
|
|
@@ -965,45 +1199,111 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
965
1199
|
return this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
966
1200
|
}
|
|
967
1201
|
|
|
1202
|
+
private assertStickinessEnabled(start: SequencePlace, end: SequencePlace) {
|
|
1203
|
+
if (
|
|
1204
|
+
!(typeof start === "number" && typeof end === "number") &&
|
|
1205
|
+
!this.options.intervalStickinessEnabled
|
|
1206
|
+
) {
|
|
1207
|
+
throw new UsageError(
|
|
1208
|
+
"attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag",
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
968
1213
|
/**
|
|
969
1214
|
* {@inheritdoc IIntervalCollection.add}
|
|
1215
|
+
* @deprecated call IntervalCollection.add without specifying an intervalType
|
|
970
1216
|
*/
|
|
971
1217
|
public add(
|
|
972
|
-
start:
|
|
973
|
-
end:
|
|
1218
|
+
start: SequencePlace,
|
|
1219
|
+
end: SequencePlace,
|
|
974
1220
|
intervalType: IntervalType,
|
|
975
1221
|
props?: PropertySet,
|
|
976
|
-
|
|
1222
|
+
): TInterval;
|
|
1223
|
+
|
|
1224
|
+
public add({
|
|
1225
|
+
start,
|
|
1226
|
+
end,
|
|
1227
|
+
props,
|
|
1228
|
+
}: {
|
|
1229
|
+
start: SequencePlace;
|
|
1230
|
+
end: SequencePlace;
|
|
1231
|
+
props?: PropertySet;
|
|
1232
|
+
}): TInterval;
|
|
1233
|
+
|
|
1234
|
+
public add(
|
|
1235
|
+
start:
|
|
1236
|
+
| SequencePlace
|
|
1237
|
+
| {
|
|
1238
|
+
start: SequencePlace;
|
|
1239
|
+
end: SequencePlace;
|
|
1240
|
+
props?: PropertySet;
|
|
1241
|
+
},
|
|
1242
|
+
end?: SequencePlace,
|
|
1243
|
+
intervalType?: IntervalType,
|
|
1244
|
+
props?: PropertySet,
|
|
977
1245
|
): TInterval {
|
|
1246
|
+
let intStart: SequencePlace;
|
|
1247
|
+
let intEnd: SequencePlace;
|
|
1248
|
+
let type: IntervalType;
|
|
1249
|
+
let properties: PropertySet | undefined;
|
|
1250
|
+
|
|
1251
|
+
if (isSequencePlace(start)) {
|
|
1252
|
+
intStart = start;
|
|
1253
|
+
assert(end !== undefined, 0x7c0 /* end must be defined */);
|
|
1254
|
+
intEnd = end;
|
|
1255
|
+
assert(intervalType !== undefined, 0x7c1 /* intervalType must be defined */);
|
|
1256
|
+
type = intervalType;
|
|
1257
|
+
properties = props;
|
|
1258
|
+
} else {
|
|
1259
|
+
intStart = start.start;
|
|
1260
|
+
intEnd = start.end;
|
|
1261
|
+
type = IntervalType.SlideOnRemove;
|
|
1262
|
+
properties = start.props;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
978
1265
|
if (!this.localCollection) {
|
|
979
1266
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
980
1267
|
}
|
|
981
|
-
if (
|
|
1268
|
+
if (type & IntervalType.Transient) {
|
|
982
1269
|
throw new LoggingError("Can not add transient intervals");
|
|
983
1270
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1271
|
+
|
|
1272
|
+
const { startSide, endSide, startPos, endPos } = endpointPosAndSide(intStart, intEnd);
|
|
1273
|
+
|
|
1274
|
+
assert(
|
|
1275
|
+
startPos !== undefined &&
|
|
1276
|
+
endPos !== undefined &&
|
|
1277
|
+
startSide !== undefined &&
|
|
1278
|
+
endSide !== undefined,
|
|
1279
|
+
0x793 /* start and end cannot be undefined because they were not passed in as undefined */,
|
|
1280
|
+
);
|
|
1281
|
+
|
|
1282
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
1283
|
+
|
|
1284
|
+
this.assertStickinessEnabled(intStart, intEnd);
|
|
989
1285
|
|
|
990
1286
|
const interval: TInterval = this.localCollection.addInterval(
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
undefined,
|
|
996
|
-
stickiness,
|
|
1287
|
+
toSequencePlace(startPos, startSide),
|
|
1288
|
+
toSequencePlace(endPos, endSide),
|
|
1289
|
+
type,
|
|
1290
|
+
properties,
|
|
997
1291
|
);
|
|
998
1292
|
|
|
999
1293
|
if (interval) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1294
|
+
if (!this.isCollaborating && interval instanceof SequenceInterval) {
|
|
1295
|
+
setSlideOnRemove(interval.start);
|
|
1296
|
+
setSlideOnRemove(interval.end);
|
|
1297
|
+
}
|
|
1298
|
+
const serializedInterval: ISerializedInterval = {
|
|
1299
|
+
start: startPos,
|
|
1300
|
+
end: endPos,
|
|
1301
|
+
intervalType: type,
|
|
1003
1302
|
properties: interval.properties,
|
|
1004
1303
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1005
|
-
start,
|
|
1006
1304
|
stickiness,
|
|
1305
|
+
startSide,
|
|
1306
|
+
endSide,
|
|
1007
1307
|
};
|
|
1008
1308
|
const localSeq = this.getNextLocalSeq();
|
|
1009
1309
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1080,8 +1380,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1080
1380
|
|
|
1081
1381
|
const interval = this.getIntervalById(id);
|
|
1082
1382
|
if (interval) {
|
|
1083
|
-
|
|
1084
|
-
|
|
1383
|
+
const deltaProps = interval.addProperties(
|
|
1384
|
+
props,
|
|
1385
|
+
true,
|
|
1386
|
+
this.isCollaborating ? UnassignedSequenceNumber : UniversalSequenceNumber,
|
|
1387
|
+
);
|
|
1085
1388
|
const serializedInterval: ISerializedInterval = interval.serialize();
|
|
1086
1389
|
|
|
1087
1390
|
// Emit a change op that will only change properties. Add the ID to
|
|
@@ -1101,7 +1404,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1101
1404
|
/**
|
|
1102
1405
|
* {@inheritdoc IIntervalCollection.change}
|
|
1103
1406
|
*/
|
|
1104
|
-
public change(id: string, start
|
|
1407
|
+
public change(id: string, start: SequencePlace, end: SequencePlace) {
|
|
1105
1408
|
if (!this.localCollection) {
|
|
1106
1409
|
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1107
1410
|
}
|
|
@@ -1117,9 +1420,18 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1117
1420
|
if (!newInterval) {
|
|
1118
1421
|
return undefined;
|
|
1119
1422
|
}
|
|
1423
|
+
if (!this.isCollaborating && newInterval instanceof SequenceInterval) {
|
|
1424
|
+
setSlideOnRemove(newInterval.start);
|
|
1425
|
+
setSlideOnRemove(newInterval.end);
|
|
1426
|
+
}
|
|
1120
1427
|
const serializedInterval: SerializedIntervalDelta = interval.serialize();
|
|
1121
|
-
|
|
1122
|
-
|
|
1428
|
+
const { startPos, startSide, endPos, endSide } = endpointPosAndSide(start, end);
|
|
1429
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
1430
|
+
serializedInterval.start = startPos;
|
|
1431
|
+
serializedInterval.end = endPos;
|
|
1432
|
+
serializedInterval.startSide = startSide;
|
|
1433
|
+
serializedInterval.endSide = endSide;
|
|
1434
|
+
serializedInterval.stickiness = stickiness;
|
|
1123
1435
|
// Emit a property bag containing only the ID, as we don't intend for this op to change any properties.
|
|
1124
1436
|
serializedInterval.properties = {
|
|
1125
1437
|
[reservedIntervalIdKey]: interval.getIntervalId(),
|
|
@@ -1135,7 +1447,14 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1135
1447
|
return undefined;
|
|
1136
1448
|
}
|
|
1137
1449
|
|
|
1450
|
+
private get isCollaborating(): boolean {
|
|
1451
|
+
return this.client?.getCollabWindow().collaborating ?? false;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1138
1454
|
private addPendingChange(id: string, serializedInterval: SerializedIntervalDelta) {
|
|
1455
|
+
if (!this.isCollaborating) {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1139
1458
|
if (serializedInterval.start !== undefined) {
|
|
1140
1459
|
this.addPendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
1141
1460
|
}
|
|
@@ -1241,8 +1560,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1241
1560
|
} else {
|
|
1242
1561
|
// If there are pending changes with this ID, don't apply the remote start/end change, as the local ack
|
|
1243
1562
|
// should be the winning change.
|
|
1244
|
-
let start: number | undefined;
|
|
1245
|
-
let end: number | undefined;
|
|
1563
|
+
let start: number | "start" | "end" | undefined;
|
|
1564
|
+
let end: number | "start" | "end" | undefined;
|
|
1246
1565
|
// Track pending start/end independently of one another.
|
|
1247
1566
|
if (!this.hasPendingChangeStart(id)) {
|
|
1248
1567
|
start = serializedInterval.start;
|
|
@@ -1256,7 +1575,12 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1256
1575
|
// If changeInterval gives us a new interval, work with that one. Otherwise keep working with
|
|
1257
1576
|
// the one we originally found in the tree.
|
|
1258
1577
|
newInterval =
|
|
1259
|
-
this.localCollection.changeInterval(
|
|
1578
|
+
this.localCollection.changeInterval(
|
|
1579
|
+
interval,
|
|
1580
|
+
toOptionalSequencePlace(start, serializedInterval.startSide),
|
|
1581
|
+
toOptionalSequencePlace(end, serializedInterval.endSide),
|
|
1582
|
+
op,
|
|
1583
|
+
) ?? interval;
|
|
1260
1584
|
}
|
|
1261
1585
|
const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
|
|
1262
1586
|
if (this.onDeserialize) {
|
|
@@ -1312,7 +1636,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1312
1636
|
throw new LoggingError("attachSequence must be called");
|
|
1313
1637
|
}
|
|
1314
1638
|
|
|
1315
|
-
const { intervalType, properties } = serializedInterval;
|
|
1639
|
+
const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
|
|
1316
1640
|
|
|
1317
1641
|
const { start: startRebased, end: endRebased } =
|
|
1318
1642
|
this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
|
|
@@ -1326,10 +1650,14 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1326
1650
|
intervalType,
|
|
1327
1651
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1328
1652
|
properties,
|
|
1653
|
+
stickiness,
|
|
1654
|
+
startSide,
|
|
1655
|
+
endSide,
|
|
1329
1656
|
};
|
|
1330
1657
|
|
|
1331
1658
|
if (
|
|
1332
1659
|
opName === "change" &&
|
|
1660
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
|
|
1333
1661
|
(this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))
|
|
1334
1662
|
) {
|
|
1335
1663
|
this.removePendingChange(serializedInterval);
|
|
@@ -1357,8 +1685,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1357
1685
|
// updates the local client's state to be consistent with the emitted op.
|
|
1358
1686
|
this.localCollection?.changeInterval(
|
|
1359
1687
|
localInterval,
|
|
1360
|
-
startRebased,
|
|
1361
|
-
endRebased,
|
|
1688
|
+
toOptionalSequencePlace(startRebased, startSide),
|
|
1689
|
+
toOptionalSequencePlace(endRebased, endSide),
|
|
1362
1690
|
undefined,
|
|
1363
1691
|
localSeq,
|
|
1364
1692
|
);
|
|
@@ -1377,7 +1705,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1377
1705
|
if (segoff.segment?.localRefs?.has(lref) !== true) {
|
|
1378
1706
|
return undefined;
|
|
1379
1707
|
}
|
|
1380
|
-
const newSegoff = getSlideToSegoff(
|
|
1708
|
+
const newSegoff = getSlideToSegoff(
|
|
1709
|
+
segoff,
|
|
1710
|
+
undefined,
|
|
1711
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1712
|
+
);
|
|
1381
1713
|
const value: { segment: ISegment | undefined; offset: number | undefined } | undefined =
|
|
1382
1714
|
segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
|
|
1383
1715
|
? undefined
|
|
@@ -1385,14 +1717,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1385
1717
|
return value;
|
|
1386
1718
|
}
|
|
1387
1719
|
|
|
1388
|
-
private
|
|
1389
|
-
let refType = lref.refType;
|
|
1390
|
-
refType = refType & ~ReferenceType.StayOnRemove;
|
|
1391
|
-
refType = refType | ReferenceType.SlideOnRemove;
|
|
1392
|
-
lref.refType = refType;
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
private ackInterval(interval: TInterval, op: ISequencedDocumentMessage) {
|
|
1720
|
+
private ackInterval(interval: TInterval, op: ISequencedDocumentMessage): void {
|
|
1396
1721
|
// Only SequenceIntervals need potential sliding
|
|
1397
1722
|
if (!(interval instanceof SequenceInterval)) {
|
|
1398
1723
|
return;
|
|
@@ -1413,11 +1738,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1413
1738
|
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
1414
1739
|
|
|
1415
1740
|
if (!hasPendingStartChange) {
|
|
1416
|
-
|
|
1741
|
+
setSlideOnRemove(interval.start);
|
|
1417
1742
|
}
|
|
1418
1743
|
|
|
1419
1744
|
if (!hasPendingEndChange) {
|
|
1420
|
-
|
|
1745
|
+
setSlideOnRemove(interval.end);
|
|
1421
1746
|
}
|
|
1422
1747
|
|
|
1423
1748
|
const needsStartUpdate = newStart !== undefined && !hasPendingStartChange;
|
|
@@ -1446,7 +1771,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1446
1771
|
newStart,
|
|
1447
1772
|
interval.start.refType,
|
|
1448
1773
|
op,
|
|
1774
|
+
undefined,
|
|
1775
|
+
undefined,
|
|
1449
1776
|
startReferenceSlidingPreference(interval.stickiness),
|
|
1777
|
+
startReferenceSlidingPreference(interval.stickiness) ===
|
|
1778
|
+
SlidingPreference.BACKWARD,
|
|
1450
1779
|
);
|
|
1451
1780
|
if (props) {
|
|
1452
1781
|
interval.start.addProperties(props);
|
|
@@ -1464,7 +1793,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1464
1793
|
newEnd,
|
|
1465
1794
|
interval.end.refType,
|
|
1466
1795
|
op,
|
|
1796
|
+
undefined,
|
|
1797
|
+
undefined,
|
|
1467
1798
|
endReferenceSlidingPreference(interval.stickiness),
|
|
1799
|
+
endReferenceSlidingPreference(interval.stickiness) ===
|
|
1800
|
+
SlidingPreference.FORWARD,
|
|
1468
1801
|
);
|
|
1469
1802
|
if (props) {
|
|
1470
1803
|
interval.end.addProperties(props);
|
|
@@ -1508,12 +1841,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1508
1841
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1509
1842
|
|
|
1510
1843
|
const interval: TInterval = this.localCollection.addInterval(
|
|
1511
|
-
serializedInterval.start,
|
|
1512
|
-
serializedInterval.end,
|
|
1844
|
+
toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before),
|
|
1845
|
+
toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before),
|
|
1513
1846
|
serializedInterval.intervalType,
|
|
1514
1847
|
serializedInterval.properties,
|
|
1515
1848
|
op,
|
|
1516
|
-
serializedInterval.stickiness,
|
|
1517
1849
|
);
|
|
1518
1850
|
|
|
1519
1851
|
if (interval) {
|
|
@@ -1691,8 +2023,16 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1691
2023
|
}
|
|
1692
2024
|
}
|
|
1693
2025
|
|
|
2026
|
+
function setSlideOnRemove(lref: LocalReferencePosition) {
|
|
2027
|
+
let refType = lref.refType;
|
|
2028
|
+
refType = refType & ~ReferenceType.StayOnRemove;
|
|
2029
|
+
refType = refType | ReferenceType.SlideOnRemove;
|
|
2030
|
+
lref.refType = refType;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
1694
2033
|
/**
|
|
1695
2034
|
* Information that identifies an interval within a `Sequence`.
|
|
2035
|
+
* @public
|
|
1696
2036
|
*/
|
|
1697
2037
|
export interface IntervalLocator {
|
|
1698
2038
|
/**
|
|
@@ -1710,6 +2050,7 @@ export interface IntervalLocator {
|
|
|
1710
2050
|
* @returns undefined if the reference position is not the endpoint of any interval (e.g. it was created
|
|
1711
2051
|
* on the merge tree directly by app code), otherwise an {@link IntervalLocator} for the interval this
|
|
1712
2052
|
* endpoint is a part of.
|
|
2053
|
+
* @public
|
|
1713
2054
|
*/
|
|
1714
2055
|
export function intervalLocatorFromEndpoint(
|
|
1715
2056
|
potentialEndpoint: LocalReferencePosition,
|