@fluidframework/sequence 2.0.0-internal.6.3.3 → 2.0.0-internal.7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/README.md +130 -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 +21 -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 +140 -22
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +146 -49
- package/dist/intervalCollection.js.map +1 -1
- package/dist/intervalIndex/endpointInRangeIndex.d.ts +13 -3
- package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
- package/dist/intervalIndex/endpointInRangeIndex.js +9 -6
- package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -1
- package/dist/intervalIndex/endpointIndex.d.ts +13 -2
- package/dist/intervalIndex/endpointIndex.d.ts.map +1 -1
- package/dist/intervalIndex/endpointIndex.js +7 -5
- package/dist/intervalIndex/endpointIndex.js.map +1 -1
- 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 +2 -2
- package/dist/intervalIndex/intervalIndex.js.map +1 -1
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +8 -6
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
- package/dist/intervalIndex/overlappingIntervalsIndex.js +11 -4
- package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +2 -2
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -1
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +3 -1
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +1 -1
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
- package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
- package/dist/intervalIndex/startpointInRangeIndex.d.ts +13 -3
- package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
- package/dist/intervalIndex/startpointInRangeIndex.js +9 -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 -3
- package/dist/intervals/interval.d.ts.map +1 -1
- package/dist/intervals/interval.js +14 -6
- package/dist/intervals/interval.js.map +1 -1
- package/dist/intervals/intervalUtils.d.ts +42 -20
- package/dist/intervals/intervalUtils.d.ts.map +1 -1
- package/dist/intervals/intervalUtils.js +12 -10
- package/dist/intervals/intervalUtils.js.map +1 -1
- package/dist/intervals/sequenceInterval.d.ts +30 -17
- package/dist/intervals/sequenceInterval.d.ts.map +1 -1
- package/dist/intervals/sequenceInterval.js +124 -45
- package/dist/intervals/sequenceInterval.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 +6 -17
- package/dist/revertibles.js.map +1 -1
- package/dist/sequence.d.ts +3 -2
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +46 -45
- 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.map +1 -1
- package/dist/sequenceFactory.js +1 -1
- package/dist/sequenceFactory.js.map +1 -1
- package/dist/sharedIntervalCollection.js +9 -9
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/dist/sharedSequence.js +6 -6
- package/dist/sharedSequence.js.map +1 -1
- package/dist/sharedString.d.ts +1 -1
- package/dist/sharedString.d.ts.map +1 -1
- package/dist/sharedString.js +7 -6
- 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 +21 -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 +140 -22
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +144 -50
- package/lib/intervalCollection.js.map +1 -1
- package/lib/intervalIndex/endpointInRangeIndex.d.ts +13 -3
- package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
- package/lib/intervalIndex/endpointInRangeIndex.js +9 -7
- package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -1
- package/lib/intervalIndex/endpointIndex.d.ts +13 -2
- package/lib/intervalIndex/endpointIndex.d.ts.map +1 -1
- package/lib/intervalIndex/endpointIndex.js +7 -6
- package/lib/intervalIndex/endpointIndex.js.map +1 -1
- 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 +2 -2
- package/lib/intervalIndex/intervalIndex.js.map +1 -1
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +8 -6
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
- package/lib/intervalIndex/overlappingIntervalsIndex.js +12 -5
- package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +2 -2
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -1
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +3 -1
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
- package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +1 -1
- package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
- package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
- package/lib/intervalIndex/startpointInRangeIndex.d.ts +13 -3
- package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
- package/lib/intervalIndex/startpointInRangeIndex.js +9 -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 -3
- package/lib/intervals/interval.d.ts.map +1 -1
- package/lib/intervals/interval.js +14 -6
- package/lib/intervals/interval.js.map +1 -1
- package/lib/intervals/intervalUtils.d.ts +42 -20
- package/lib/intervals/intervalUtils.d.ts.map +1 -1
- package/lib/intervals/intervalUtils.js +8 -6
- package/lib/intervals/intervalUtils.js.map +1 -1
- package/lib/intervals/sequenceInterval.d.ts +30 -17
- package/lib/intervals/sequenceInterval.d.ts.map +1 -1
- package/lib/intervals/sequenceInterval.js +125 -44
- package/lib/intervals/sequenceInterval.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 +6 -17
- package/lib/revertibles.js.map +1 -1
- package/lib/sequence.d.ts +3 -2
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +46 -45
- 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.map +1 -1
- package/lib/sequenceFactory.js +1 -1
- package/lib/sequenceFactory.js.map +1 -1
- package/lib/sharedIntervalCollection.js +9 -9
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/lib/sharedSequence.js +6 -6
- package/lib/sharedSequence.js.map +1 -1
- package/lib/sharedString.d.ts +1 -1
- package/lib/sharedString.d.ts.map +1 -1
- package/lib/sharedString.js +7 -6
- package/lib/sharedString.js.map +1 -1
- package/package.json +49 -23
- package/src/defaultMapInterfaces.ts +21 -2
- package/src/index.ts +4 -1
- package/src/intervalCollection.ts +347 -84
- package/src/intervalIndex/endpointInRangeIndex.ts +19 -11
- package/src/intervalIndex/endpointIndex.ts +16 -9
- package/src/intervalIndex/idIntervalIndex.ts +1 -1
- package/src/intervalIndex/index.ts +12 -3
- package/src/intervalIndex/intervalIndex.ts +2 -2
- package/src/intervalIndex/overlappingIntervalsIndex.ts +31 -15
- package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +4 -1
- package/src/intervalIndex/sequenceIntervalIndexes.ts +1 -1
- package/src/intervalIndex/startpointInRangeIndex.ts +19 -17
- package/src/intervals/interval.ts +30 -8
- package/src/intervals/intervalUtils.ts +51 -28
- package/src/intervals/sequenceInterval.ts +197 -49
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +8 -33
- package/src/sequence.ts +5 -2
- package/src/sequenceDeltaEvent.ts +11 -3
- package/src/sequenceFactory.ts +1 -1
- package/src/sharedString.ts +2 -1
|
@@ -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,61 @@ 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
|
+
*/
|
|
92
|
+
export type SequencePlace = number | "start" | "end" | InteriorSequencePlace;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* A sequence place that does not refer to the special endpoint segments.
|
|
96
|
+
*
|
|
97
|
+
* See {@link SequencePlace} for additional context.
|
|
98
|
+
*/
|
|
99
|
+
export interface InteriorSequencePlace {
|
|
100
|
+
pos: number;
|
|
101
|
+
side: Side;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Defines a side relative to a character in a sequence.
|
|
106
|
+
*
|
|
107
|
+
* @remarks See {@link SequencePlace} for additional context on usage.
|
|
108
|
+
*/
|
|
109
|
+
export enum Side {
|
|
110
|
+
Before = 0,
|
|
111
|
+
After = 1,
|
|
112
|
+
}
|
|
113
|
+
|
|
65
114
|
const reservedIntervalIdKey = "intervalId";
|
|
66
115
|
|
|
67
116
|
export interface ISerializedIntervalCollectionV2 {
|
|
@@ -70,6 +119,13 @@ export interface ISerializedIntervalCollectionV2 {
|
|
|
70
119
|
intervals: CompressedSerializedInterval[];
|
|
71
120
|
}
|
|
72
121
|
|
|
122
|
+
export function sidesFromStickiness(stickiness: IntervalStickiness) {
|
|
123
|
+
const startSide = (stickiness & IntervalStickiness.START) !== 0 ? Side.After : Side.Before;
|
|
124
|
+
const endSide = (stickiness & IntervalStickiness.END) !== 0 ? Side.Before : Side.After;
|
|
125
|
+
|
|
126
|
+
return { startSide, endSide };
|
|
127
|
+
}
|
|
128
|
+
|
|
73
129
|
/**
|
|
74
130
|
* Decompress an interval after loading a summary from JSON. The exact format
|
|
75
131
|
* of this compression is unspecified and subject to change
|
|
@@ -78,13 +134,17 @@ function decompressInterval(
|
|
|
78
134
|
interval: CompressedSerializedInterval,
|
|
79
135
|
label?: string,
|
|
80
136
|
): ISerializedInterval {
|
|
137
|
+
const stickiness = interval[5] ?? IntervalStickiness.END;
|
|
138
|
+
const { startSide, endSide } = sidesFromStickiness(stickiness);
|
|
81
139
|
return {
|
|
82
140
|
start: interval[0],
|
|
83
141
|
end: interval[1],
|
|
84
142
|
sequenceNumber: interval[2],
|
|
85
143
|
intervalType: interval[3],
|
|
86
144
|
properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
|
|
87
|
-
stickiness
|
|
145
|
+
stickiness,
|
|
146
|
+
startSide,
|
|
147
|
+
endSide,
|
|
88
148
|
};
|
|
89
149
|
}
|
|
90
150
|
|
|
@@ -95,7 +155,7 @@ function decompressInterval(
|
|
|
95
155
|
function compressInterval(interval: ISerializedInterval): CompressedSerializedInterval {
|
|
96
156
|
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
97
157
|
|
|
98
|
-
|
|
158
|
+
let base: CompressedSerializedInterval = [
|
|
99
159
|
start,
|
|
100
160
|
end,
|
|
101
161
|
sequenceNumber,
|
|
@@ -106,18 +166,69 @@ function compressInterval(interval: ISerializedInterval): CompressedSerializedIn
|
|
|
106
166
|
];
|
|
107
167
|
|
|
108
168
|
if (interval.stickiness !== undefined && interval.stickiness !== IntervalStickiness.END) {
|
|
109
|
-
|
|
169
|
+
// reassignment to make it easier for typescript to reason about types
|
|
170
|
+
base = [...base, interval.stickiness];
|
|
110
171
|
}
|
|
111
172
|
|
|
112
173
|
return base;
|
|
113
174
|
}
|
|
114
175
|
|
|
176
|
+
export function endpointPosAndSide(
|
|
177
|
+
start: SequencePlace | undefined,
|
|
178
|
+
end: SequencePlace | undefined,
|
|
179
|
+
) {
|
|
180
|
+
const startIsPlainEndpoint = typeof start === "number" || start === "start" || start === "end";
|
|
181
|
+
const endIsPlainEndpoint = typeof end === "number" || end === "start" || end === "end";
|
|
182
|
+
|
|
183
|
+
const startSide = startIsPlainEndpoint ? Side.Before : start?.side;
|
|
184
|
+
const endSide = endIsPlainEndpoint ? Side.Before : end?.side;
|
|
185
|
+
|
|
186
|
+
const startPos = startIsPlainEndpoint ? start : start?.pos;
|
|
187
|
+
const endPos = endIsPlainEndpoint ? end : end?.pos;
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
startSide,
|
|
191
|
+
endSide,
|
|
192
|
+
startPos,
|
|
193
|
+
endPos,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function toSequencePlace(pos: number | "start" | "end", side: Side): SequencePlace {
|
|
198
|
+
return typeof pos === "number" ? { pos, side } : pos;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function toOptionalSequencePlace(
|
|
202
|
+
pos: number | "start" | "end" | undefined,
|
|
203
|
+
side: Side = Side.Before,
|
|
204
|
+
): SequencePlace | undefined {
|
|
205
|
+
return typeof pos === "number" ? { pos, side } : pos;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function computeStickinessFromSide(
|
|
209
|
+
startPos: number | "start" | "end" | undefined = -1,
|
|
210
|
+
startSide: Side = Side.Before,
|
|
211
|
+
endPos: number | "start" | "end" | undefined = -1,
|
|
212
|
+
endSide: Side = Side.Before,
|
|
213
|
+
): IntervalStickiness {
|
|
214
|
+
let stickiness: IntervalStickiness = IntervalStickiness.NONE;
|
|
215
|
+
|
|
216
|
+
if (startSide === Side.After || startPos === "start") {
|
|
217
|
+
stickiness |= IntervalStickiness.START;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (endSide === Side.Before || endPos === "end") {
|
|
221
|
+
stickiness |= IntervalStickiness.END;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return stickiness as IntervalStickiness;
|
|
225
|
+
}
|
|
226
|
+
|
|
115
227
|
export function createIntervalIndex() {
|
|
116
228
|
const helpers: IIntervalHelpers<Interval> = {
|
|
117
|
-
compareEnds: (a: Interval, b: Interval) => a.end - b.end,
|
|
118
229
|
create: createInterval,
|
|
119
230
|
};
|
|
120
|
-
const lc = new LocalIntervalCollection<Interval>(undefined as any as Client, "", helpers);
|
|
231
|
+
const lc = new LocalIntervalCollection<Interval>(undefined as any as Client, "", helpers, {});
|
|
121
232
|
return lc;
|
|
122
233
|
}
|
|
123
234
|
|
|
@@ -132,15 +243,16 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
132
243
|
private readonly client: Client,
|
|
133
244
|
private readonly label: string,
|
|
134
245
|
private readonly helpers: IIntervalHelpers<TInterval>,
|
|
246
|
+
private readonly options: Partial<SequenceOptions>,
|
|
135
247
|
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
136
248
|
private readonly onPositionChange?: (
|
|
137
249
|
interval: TInterval,
|
|
138
250
|
previousInterval: TInterval,
|
|
139
251
|
) => void,
|
|
140
252
|
) {
|
|
141
|
-
this.overlappingIntervalsIndex =
|
|
253
|
+
this.overlappingIntervalsIndex = new OverlappingIntervalsIndex(client, helpers);
|
|
142
254
|
this.idIntervalIndex = createIdIntervalIndex<TInterval>();
|
|
143
|
-
this.endIntervalIndex =
|
|
255
|
+
this.endIntervalIndex = new EndpointIndex(client, helpers);
|
|
144
256
|
this.indexes = new Set([
|
|
145
257
|
this.overlappingIntervalsIndex,
|
|
146
258
|
this.idIntervalIndex,
|
|
@@ -148,7 +260,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
148
260
|
]);
|
|
149
261
|
}
|
|
150
262
|
|
|
151
|
-
public createLegacyId(start: number, end: number): string {
|
|
263
|
+
public createLegacyId(start: number | "start" | "end", end: number | "start" | "end"): string {
|
|
152
264
|
// Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
|
|
153
265
|
// without ID's.
|
|
154
266
|
return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
|
|
@@ -203,11 +315,10 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
203
315
|
}
|
|
204
316
|
|
|
205
317
|
public createInterval(
|
|
206
|
-
start:
|
|
207
|
-
end:
|
|
318
|
+
start: SequencePlace,
|
|
319
|
+
end: SequencePlace,
|
|
208
320
|
intervalType: IntervalType,
|
|
209
321
|
op?: ISequencedDocumentMessage,
|
|
210
|
-
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
211
322
|
): TInterval {
|
|
212
323
|
return this.helpers.create(
|
|
213
324
|
this.label,
|
|
@@ -217,19 +328,18 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
217
328
|
intervalType,
|
|
218
329
|
op,
|
|
219
330
|
undefined,
|
|
220
|
-
|
|
331
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
221
332
|
);
|
|
222
333
|
}
|
|
223
334
|
|
|
224
335
|
public addInterval(
|
|
225
|
-
start:
|
|
226
|
-
end:
|
|
336
|
+
start: SequencePlace,
|
|
337
|
+
end: SequencePlace,
|
|
227
338
|
intervalType: IntervalType,
|
|
228
339
|
props?: PropertySet,
|
|
229
340
|
op?: ISequencedDocumentMessage,
|
|
230
|
-
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
231
341
|
) {
|
|
232
|
-
const interval: TInterval = this.createInterval(start, end, intervalType, op
|
|
342
|
+
const interval: TInterval = this.createInterval(start, end, intervalType, op);
|
|
233
343
|
if (interval) {
|
|
234
344
|
if (!interval.properties) {
|
|
235
345
|
interval.properties = createMap<any>();
|
|
@@ -276,14 +386,19 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
276
386
|
|
|
277
387
|
public changeInterval(
|
|
278
388
|
interval: TInterval,
|
|
279
|
-
start:
|
|
280
|
-
end:
|
|
389
|
+
start: SequencePlace | undefined,
|
|
390
|
+
end: SequencePlace | undefined,
|
|
281
391
|
op?: ISequencedDocumentMessage,
|
|
282
392
|
localSeq?: number,
|
|
283
393
|
) {
|
|
284
|
-
const newInterval = interval.modify(
|
|
285
|
-
|
|
286
|
-
|
|
394
|
+
const newInterval = interval.modify(
|
|
395
|
+
this.label,
|
|
396
|
+
start,
|
|
397
|
+
end,
|
|
398
|
+
op,
|
|
399
|
+
localSeq,
|
|
400
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
401
|
+
) as TInterval | undefined;
|
|
287
402
|
if (newInterval) {
|
|
288
403
|
this.removeExistingInterval(interval);
|
|
289
404
|
this.add(newInterval);
|
|
@@ -317,6 +432,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
317
432
|
ReferenceType.Transient,
|
|
318
433
|
ref.properties,
|
|
319
434
|
ref.slidingPreference,
|
|
435
|
+
ref.canSlideToEndpoint,
|
|
320
436
|
);
|
|
321
437
|
};
|
|
322
438
|
if (interval instanceof SequenceInterval) {
|
|
@@ -410,7 +526,6 @@ class IntervalCollectionFactory implements IValueFactory<IntervalCollection<Inte
|
|
|
410
526
|
options?: Partial<SequenceOptions>,
|
|
411
527
|
): IntervalCollection<Interval> {
|
|
412
528
|
const helpers: IIntervalHelpers<Interval> = {
|
|
413
|
-
compareEnds: (a: Interval, b: Interval) => a.end - b.end,
|
|
414
529
|
create: createInterval,
|
|
415
530
|
};
|
|
416
531
|
const collection = new IntervalCollection<Interval>(helpers, false, emitter, raw, options);
|
|
@@ -612,7 +727,7 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
|
|
|
612
727
|
* All intervals which are part of this collection will be added to the index, and the index will automatically
|
|
613
728
|
* be updated when this collection updates due to local or remote changes.
|
|
614
729
|
*
|
|
615
|
-
* @remarks
|
|
730
|
+
* @remarks After attaching an index to an interval collection, applications should typically store this
|
|
616
731
|
* index somewhere in their in-memory data model for future reference and querying.
|
|
617
732
|
*/
|
|
618
733
|
attachIndex(index: IntervalIndex<TInterval>): void;
|
|
@@ -621,7 +736,7 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
|
|
|
621
736
|
* All intervals which are part of this collection will be removed from the index, and updates to this collection
|
|
622
737
|
* due to local or remote changes will no longer incur updates to the index.
|
|
623
738
|
*
|
|
624
|
-
* @returns
|
|
739
|
+
* @returns `false` if the target index cannot be found in the indexes, otherwise remove all intervals in the index and return `true`.
|
|
625
740
|
*/
|
|
626
741
|
detachIndex(index: IntervalIndex<TInterval>): boolean;
|
|
627
742
|
/**
|
|
@@ -631,21 +746,82 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
|
|
|
631
746
|
getIntervalById(id: string): TInterval | undefined;
|
|
632
747
|
/**
|
|
633
748
|
* Creates a new interval and add it to the collection.
|
|
634
|
-
* @param start - interval start position
|
|
635
|
-
* @param end - interval end position
|
|
636
|
-
* @param intervalType - type of the interval. All intervals are SlideOnRemove.
|
|
749
|
+
* @param start - interval start position
|
|
750
|
+
* @param end - interval end position
|
|
751
|
+
* @param intervalType - type of the interval. All intervals are SlideOnRemove.
|
|
752
|
+
* Intervals may not be Transient.
|
|
637
753
|
* @param props - properties of the interval
|
|
638
|
-
* @
|
|
639
|
-
* @
|
|
640
|
-
*
|
|
641
|
-
*
|
|
754
|
+
* @returns The created interval
|
|
755
|
+
* @remarks See documentation on {@link SequenceInterval} for comments on
|
|
756
|
+
* interval endpoint semantics: there are subtleties with how the current
|
|
757
|
+
* half-open behavior is represented.
|
|
758
|
+
*
|
|
759
|
+
* Note that intervals may behave unexpectedly if the entire contents
|
|
760
|
+
* of the string are deleted. In this case, it is possible for one endpoint
|
|
761
|
+
* of the interval to become detached, while the other remains on the string.
|
|
762
|
+
*
|
|
763
|
+
* By adjusting the `side` and `pos` values of the `start` and `end` parameters,
|
|
764
|
+
* it is possible to control whether the interval expands to include content
|
|
765
|
+
* inserted at its start or end.
|
|
766
|
+
*
|
|
767
|
+
* See {@link SequencePlace} for more details on the model.
|
|
768
|
+
*
|
|
769
|
+
* @example
|
|
770
|
+
*
|
|
771
|
+
* Given the string "ABCD":
|
|
772
|
+
*
|
|
773
|
+
*```typescript
|
|
774
|
+
* // Refers to "BC". If any content is inserted before B or after C, this
|
|
775
|
+
* // interval will include that content
|
|
776
|
+
* //
|
|
777
|
+
* // Picture:
|
|
778
|
+
* // \{start\} - A[- B - C -]D - \{end\}
|
|
779
|
+
* // \{start\} - A - B - C - D - \{end\}
|
|
780
|
+
* collection.add(\{ pos: 0, side: Side.After \}, \{ pos: 3, side: Side.Before \}, IntervalType.SlideOnRemove);
|
|
781
|
+
* // Equivalent to specifying the same positions and Side.Before.
|
|
782
|
+
* // Refers to "ABC". Content inserted after C will be included in the
|
|
783
|
+
* // interval, but content inserted before A will not.
|
|
784
|
+
* // \{start\} -[A - B - C -]D - \{end\}
|
|
785
|
+
* // \{start\} - A - B - C - D - \{end\}
|
|
786
|
+
* collection.add(0, 3, IntervalType.SlideOnRemove);
|
|
787
|
+
*```
|
|
788
|
+
*
|
|
789
|
+
* In the case of the first example, if text is deleted,
|
|
790
|
+
*
|
|
791
|
+
* ```typescript
|
|
792
|
+
* // Delete the character "B"
|
|
793
|
+
* string.removeRange(1, 2);
|
|
794
|
+
* ```
|
|
795
|
+
*
|
|
796
|
+
* The start point of the interval will slide to the position immediately
|
|
797
|
+
* before "C", and the same will be true.
|
|
798
|
+
*
|
|
799
|
+
* ```
|
|
800
|
+
* \{start\} - A[- C -]D - \{end\}
|
|
801
|
+
* ```
|
|
802
|
+
*
|
|
803
|
+
* In this case, text inserted immediately before "C" would be included in
|
|
804
|
+
* the interval.
|
|
805
|
+
*
|
|
806
|
+
* ```typescript
|
|
807
|
+
* string.insertText(1, "EFG");
|
|
808
|
+
* ```
|
|
809
|
+
*
|
|
810
|
+
* With the string now being,
|
|
811
|
+
*
|
|
812
|
+
* ```
|
|
813
|
+
* \{start\} - A[- E - F - G - C -]D - \{end\}
|
|
814
|
+
* ```
|
|
815
|
+
*
|
|
816
|
+
* @privateRemarks TODO: ADO:5205 the above comment regarding behavior in
|
|
817
|
+
* the case that the entire interval has been deleted should be resolved at
|
|
818
|
+
* the same time as this ticket
|
|
642
819
|
*/
|
|
643
820
|
add(
|
|
644
|
-
start:
|
|
645
|
-
end:
|
|
821
|
+
start: SequencePlace,
|
|
822
|
+
end: SequencePlace,
|
|
646
823
|
intervalType: IntervalType,
|
|
647
824
|
props?: PropertySet,
|
|
648
|
-
stickiness?: IntervalStickiness,
|
|
649
825
|
): TInterval;
|
|
650
826
|
/**
|
|
651
827
|
* Removes an interval from the collection.
|
|
@@ -663,11 +839,11 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
|
|
|
663
839
|
/**
|
|
664
840
|
* Changes the endpoints of an existing interval.
|
|
665
841
|
* @param id - Id of the interval to change
|
|
666
|
-
* @param start - New start value
|
|
667
|
-
* @param end - New end value
|
|
842
|
+
* @param start - New start value. This can be the existing position to keep it unchanged.
|
|
843
|
+
* @param end - New end value. This can be the existing position to keep it unchanged.
|
|
668
844
|
* @returns the interval that was changed, if it existed in the collection.
|
|
669
845
|
*/
|
|
670
|
-
change(id: string, start
|
|
846
|
+
change(id: string, start: SequencePlace, end: SequencePlace): TInterval | undefined;
|
|
671
847
|
|
|
672
848
|
attachDeserializer(onDeserialize: DeserializeCallback): void;
|
|
673
849
|
/**
|
|
@@ -810,13 +986,18 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
810
986
|
}
|
|
811
987
|
|
|
812
988
|
private rebasePositionWithSegmentSlide(
|
|
813
|
-
pos: number,
|
|
989
|
+
pos: number | "start" | "end",
|
|
814
990
|
seqNumberFrom: number,
|
|
815
991
|
localSeq: number,
|
|
816
|
-
): number | undefined {
|
|
992
|
+
): number | "start" | "end" | undefined {
|
|
817
993
|
if (!this.client) {
|
|
818
994
|
throw new LoggingError("mergeTree client must exist");
|
|
819
995
|
}
|
|
996
|
+
|
|
997
|
+
if (pos === "start" || pos === "end") {
|
|
998
|
+
return pos;
|
|
999
|
+
}
|
|
1000
|
+
|
|
820
1001
|
const { clientId } = this.client.getCollabWindow();
|
|
821
1002
|
const { segment, offset } = this.client.getContainingSegment(
|
|
822
1003
|
pos,
|
|
@@ -830,7 +1011,12 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
830
1011
|
// if segment is undefined, it slid off the string
|
|
831
1012
|
assert(segment !== undefined, 0x54e /* No segment found */);
|
|
832
1013
|
|
|
833
|
-
const segoff =
|
|
1014
|
+
const segoff =
|
|
1015
|
+
getSlideToSegoff(
|
|
1016
|
+
{ segment, offset },
|
|
1017
|
+
undefined,
|
|
1018
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1019
|
+
) ?? segment;
|
|
834
1020
|
|
|
835
1021
|
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
836
1022
|
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
@@ -894,12 +1080,28 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
894
1080
|
client,
|
|
895
1081
|
label,
|
|
896
1082
|
this.helpers,
|
|
1083
|
+
this.options,
|
|
897
1084
|
(interval, previousInterval) => this.emitChange(interval, previousInterval, true, true),
|
|
898
1085
|
);
|
|
899
1086
|
if (this.savedSerializedIntervals) {
|
|
900
1087
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
901
1088
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
902
|
-
const {
|
|
1089
|
+
const {
|
|
1090
|
+
start: startPos,
|
|
1091
|
+
end: endPos,
|
|
1092
|
+
intervalType,
|
|
1093
|
+
properties,
|
|
1094
|
+
startSide,
|
|
1095
|
+
endSide,
|
|
1096
|
+
} = serializedInterval;
|
|
1097
|
+
const start =
|
|
1098
|
+
typeof startPos === "number" && startSide !== undefined
|
|
1099
|
+
? { pos: startPos, side: startSide }
|
|
1100
|
+
: startPos;
|
|
1101
|
+
const end =
|
|
1102
|
+
typeof endPos === "number" && endSide !== undefined
|
|
1103
|
+
? { pos: endPos, side: endSide }
|
|
1104
|
+
: endPos;
|
|
903
1105
|
const interval = this.helpers.create(
|
|
904
1106
|
label,
|
|
905
1107
|
start,
|
|
@@ -908,7 +1110,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
908
1110
|
intervalType,
|
|
909
1111
|
undefined,
|
|
910
1112
|
true,
|
|
911
|
-
|
|
1113
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
912
1114
|
);
|
|
913
1115
|
if (properties) {
|
|
914
1116
|
interval.addProperties(properties);
|
|
@@ -965,15 +1167,25 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
965
1167
|
return this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
966
1168
|
}
|
|
967
1169
|
|
|
1170
|
+
private assertStickinessEnabled(start: SequencePlace, end: SequencePlace) {
|
|
1171
|
+
if (
|
|
1172
|
+
!(typeof start === "number" && typeof end === "number") &&
|
|
1173
|
+
!this.options.intervalStickinessEnabled
|
|
1174
|
+
) {
|
|
1175
|
+
throw new UsageError(
|
|
1176
|
+
"attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag",
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
968
1181
|
/**
|
|
969
1182
|
* {@inheritdoc IIntervalCollection.add}
|
|
970
1183
|
*/
|
|
971
1184
|
public add(
|
|
972
|
-
start:
|
|
973
|
-
end:
|
|
1185
|
+
start: SequencePlace,
|
|
1186
|
+
end: SequencePlace,
|
|
974
1187
|
intervalType: IntervalType,
|
|
975
1188
|
props?: PropertySet,
|
|
976
|
-
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
977
1189
|
): TInterval {
|
|
978
1190
|
if (!this.localCollection) {
|
|
979
1191
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
@@ -981,29 +1193,42 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
981
1193
|
if (intervalType & IntervalType.Transient) {
|
|
982
1194
|
throw new LoggingError("Can not add transient intervals");
|
|
983
1195
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1196
|
+
|
|
1197
|
+
const { startSide, endSide, startPos, endPos } = endpointPosAndSide(start, end);
|
|
1198
|
+
|
|
1199
|
+
assert(
|
|
1200
|
+
startPos !== undefined &&
|
|
1201
|
+
endPos !== undefined &&
|
|
1202
|
+
startSide !== undefined &&
|
|
1203
|
+
endSide !== undefined,
|
|
1204
|
+
0x793 /* start and end cannot be undefined because they were not passed in as undefined */,
|
|
1205
|
+
);
|
|
1206
|
+
|
|
1207
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
1208
|
+
|
|
1209
|
+
this.assertStickinessEnabled(start, end);
|
|
989
1210
|
|
|
990
1211
|
const interval: TInterval = this.localCollection.addInterval(
|
|
991
|
-
|
|
992
|
-
|
|
1212
|
+
toSequencePlace(startPos, startSide),
|
|
1213
|
+
toSequencePlace(endPos, endSide),
|
|
993
1214
|
intervalType,
|
|
994
1215
|
props,
|
|
995
|
-
undefined,
|
|
996
|
-
stickiness,
|
|
997
1216
|
);
|
|
998
1217
|
|
|
999
1218
|
if (interval) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1219
|
+
if (!this.isCollaborating && interval instanceof SequenceInterval) {
|
|
1220
|
+
setSlideOnRemove(interval.start);
|
|
1221
|
+
setSlideOnRemove(interval.end);
|
|
1222
|
+
}
|
|
1223
|
+
const serializedInterval: ISerializedInterval = {
|
|
1224
|
+
start: startPos,
|
|
1225
|
+
end: endPos,
|
|
1002
1226
|
intervalType,
|
|
1003
1227
|
properties: interval.properties,
|
|
1004
1228
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1005
|
-
start,
|
|
1006
1229
|
stickiness,
|
|
1230
|
+
startSide,
|
|
1231
|
+
endSide,
|
|
1007
1232
|
};
|
|
1008
1233
|
const localSeq = this.getNextLocalSeq();
|
|
1009
1234
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1080,8 +1305,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1080
1305
|
|
|
1081
1306
|
const interval = this.getIntervalById(id);
|
|
1082
1307
|
if (interval) {
|
|
1083
|
-
|
|
1084
|
-
|
|
1308
|
+
const deltaProps = interval.addProperties(
|
|
1309
|
+
props,
|
|
1310
|
+
true,
|
|
1311
|
+
this.isCollaborating ? UnassignedSequenceNumber : UniversalSequenceNumber,
|
|
1312
|
+
);
|
|
1085
1313
|
const serializedInterval: ISerializedInterval = interval.serialize();
|
|
1086
1314
|
|
|
1087
1315
|
// Emit a change op that will only change properties. Add the ID to
|
|
@@ -1101,7 +1329,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1101
1329
|
/**
|
|
1102
1330
|
* {@inheritdoc IIntervalCollection.change}
|
|
1103
1331
|
*/
|
|
1104
|
-
public change(id: string, start
|
|
1332
|
+
public change(id: string, start: SequencePlace, end: SequencePlace): TInterval | undefined {
|
|
1105
1333
|
if (!this.localCollection) {
|
|
1106
1334
|
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1107
1335
|
}
|
|
@@ -1117,9 +1345,18 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1117
1345
|
if (!newInterval) {
|
|
1118
1346
|
return undefined;
|
|
1119
1347
|
}
|
|
1348
|
+
if (!this.isCollaborating && newInterval instanceof SequenceInterval) {
|
|
1349
|
+
setSlideOnRemove(newInterval.start);
|
|
1350
|
+
setSlideOnRemove(newInterval.end);
|
|
1351
|
+
}
|
|
1120
1352
|
const serializedInterval: SerializedIntervalDelta = interval.serialize();
|
|
1121
|
-
|
|
1122
|
-
|
|
1353
|
+
const { startPos, startSide, endPos, endSide } = endpointPosAndSide(start, end);
|
|
1354
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
1355
|
+
serializedInterval.start = startPos;
|
|
1356
|
+
serializedInterval.end = endPos;
|
|
1357
|
+
serializedInterval.startSide = startSide;
|
|
1358
|
+
serializedInterval.endSide = endSide;
|
|
1359
|
+
serializedInterval.stickiness = stickiness;
|
|
1123
1360
|
// Emit a property bag containing only the ID, as we don't intend for this op to change any properties.
|
|
1124
1361
|
serializedInterval.properties = {
|
|
1125
1362
|
[reservedIntervalIdKey]: interval.getIntervalId(),
|
|
@@ -1135,7 +1372,14 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1135
1372
|
return undefined;
|
|
1136
1373
|
}
|
|
1137
1374
|
|
|
1375
|
+
private get isCollaborating(): boolean {
|
|
1376
|
+
return this.client?.getCollabWindow().collaborating ?? false;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1138
1379
|
private addPendingChange(id: string, serializedInterval: SerializedIntervalDelta) {
|
|
1380
|
+
if (!this.isCollaborating) {
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1139
1383
|
if (serializedInterval.start !== undefined) {
|
|
1140
1384
|
this.addPendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
1141
1385
|
}
|
|
@@ -1241,8 +1485,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1241
1485
|
} else {
|
|
1242
1486
|
// If there are pending changes with this ID, don't apply the remote start/end change, as the local ack
|
|
1243
1487
|
// should be the winning change.
|
|
1244
|
-
let start: number | undefined;
|
|
1245
|
-
let end: number | undefined;
|
|
1488
|
+
let start: number | "start" | "end" | undefined;
|
|
1489
|
+
let end: number | "start" | "end" | undefined;
|
|
1246
1490
|
// Track pending start/end independently of one another.
|
|
1247
1491
|
if (!this.hasPendingChangeStart(id)) {
|
|
1248
1492
|
start = serializedInterval.start;
|
|
@@ -1256,7 +1500,12 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1256
1500
|
// If changeInterval gives us a new interval, work with that one. Otherwise keep working with
|
|
1257
1501
|
// the one we originally found in the tree.
|
|
1258
1502
|
newInterval =
|
|
1259
|
-
this.localCollection.changeInterval(
|
|
1503
|
+
this.localCollection.changeInterval(
|
|
1504
|
+
interval,
|
|
1505
|
+
toOptionalSequencePlace(start, serializedInterval.startSide),
|
|
1506
|
+
toOptionalSequencePlace(end, serializedInterval.endSide),
|
|
1507
|
+
op,
|
|
1508
|
+
) ?? interval;
|
|
1260
1509
|
}
|
|
1261
1510
|
const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
|
|
1262
1511
|
if (this.onDeserialize) {
|
|
@@ -1312,7 +1561,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1312
1561
|
throw new LoggingError("attachSequence must be called");
|
|
1313
1562
|
}
|
|
1314
1563
|
|
|
1315
|
-
const { intervalType, properties } = serializedInterval;
|
|
1564
|
+
const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
|
|
1316
1565
|
|
|
1317
1566
|
const { start: startRebased, end: endRebased } =
|
|
1318
1567
|
this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
|
|
@@ -1326,6 +1575,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1326
1575
|
intervalType,
|
|
1327
1576
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1328
1577
|
properties,
|
|
1578
|
+
stickiness,
|
|
1579
|
+
startSide,
|
|
1580
|
+
endSide,
|
|
1329
1581
|
};
|
|
1330
1582
|
|
|
1331
1583
|
if (
|
|
@@ -1357,8 +1609,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1357
1609
|
// updates the local client's state to be consistent with the emitted op.
|
|
1358
1610
|
this.localCollection?.changeInterval(
|
|
1359
1611
|
localInterval,
|
|
1360
|
-
startRebased,
|
|
1361
|
-
endRebased,
|
|
1612
|
+
toOptionalSequencePlace(startRebased, startSide),
|
|
1613
|
+
toOptionalSequencePlace(endRebased, endSide),
|
|
1362
1614
|
undefined,
|
|
1363
1615
|
localSeq,
|
|
1364
1616
|
);
|
|
@@ -1377,7 +1629,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1377
1629
|
if (segoff.segment?.localRefs?.has(lref) !== true) {
|
|
1378
1630
|
return undefined;
|
|
1379
1631
|
}
|
|
1380
|
-
const newSegoff = getSlideToSegoff(
|
|
1632
|
+
const newSegoff = getSlideToSegoff(
|
|
1633
|
+
segoff,
|
|
1634
|
+
undefined,
|
|
1635
|
+
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1636
|
+
);
|
|
1381
1637
|
const value: { segment: ISegment | undefined; offset: number | undefined } | undefined =
|
|
1382
1638
|
segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
|
|
1383
1639
|
? undefined
|
|
@@ -1385,14 +1641,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1385
1641
|
return value;
|
|
1386
1642
|
}
|
|
1387
1643
|
|
|
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) {
|
|
1644
|
+
private ackInterval(interval: TInterval, op: ISequencedDocumentMessage): void {
|
|
1396
1645
|
// Only SequenceIntervals need potential sliding
|
|
1397
1646
|
if (!(interval instanceof SequenceInterval)) {
|
|
1398
1647
|
return;
|
|
@@ -1413,11 +1662,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1413
1662
|
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
1414
1663
|
|
|
1415
1664
|
if (!hasPendingStartChange) {
|
|
1416
|
-
|
|
1665
|
+
setSlideOnRemove(interval.start);
|
|
1417
1666
|
}
|
|
1418
1667
|
|
|
1419
1668
|
if (!hasPendingEndChange) {
|
|
1420
|
-
|
|
1669
|
+
setSlideOnRemove(interval.end);
|
|
1421
1670
|
}
|
|
1422
1671
|
|
|
1423
1672
|
const needsStartUpdate = newStart !== undefined && !hasPendingStartChange;
|
|
@@ -1446,7 +1695,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1446
1695
|
newStart,
|
|
1447
1696
|
interval.start.refType,
|
|
1448
1697
|
op,
|
|
1698
|
+
undefined,
|
|
1699
|
+
undefined,
|
|
1449
1700
|
startReferenceSlidingPreference(interval.stickiness),
|
|
1701
|
+
startReferenceSlidingPreference(interval.stickiness) ===
|
|
1702
|
+
SlidingPreference.BACKWARD,
|
|
1450
1703
|
);
|
|
1451
1704
|
if (props) {
|
|
1452
1705
|
interval.start.addProperties(props);
|
|
@@ -1464,7 +1717,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1464
1717
|
newEnd,
|
|
1465
1718
|
interval.end.refType,
|
|
1466
1719
|
op,
|
|
1720
|
+
undefined,
|
|
1721
|
+
undefined,
|
|
1467
1722
|
endReferenceSlidingPreference(interval.stickiness),
|
|
1723
|
+
endReferenceSlidingPreference(interval.stickiness) ===
|
|
1724
|
+
SlidingPreference.FORWARD,
|
|
1468
1725
|
);
|
|
1469
1726
|
if (props) {
|
|
1470
1727
|
interval.end.addProperties(props);
|
|
@@ -1508,12 +1765,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1508
1765
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1509
1766
|
|
|
1510
1767
|
const interval: TInterval = this.localCollection.addInterval(
|
|
1511
|
-
serializedInterval.start,
|
|
1512
|
-
serializedInterval.end,
|
|
1768
|
+
toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before),
|
|
1769
|
+
toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before),
|
|
1513
1770
|
serializedInterval.intervalType,
|
|
1514
1771
|
serializedInterval.properties,
|
|
1515
1772
|
op,
|
|
1516
|
-
serializedInterval.stickiness,
|
|
1517
1773
|
);
|
|
1518
1774
|
|
|
1519
1775
|
if (interval) {
|
|
@@ -1691,6 +1947,13 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1691
1947
|
}
|
|
1692
1948
|
}
|
|
1693
1949
|
|
|
1950
|
+
function setSlideOnRemove(lref: LocalReferencePosition) {
|
|
1951
|
+
let refType = lref.refType;
|
|
1952
|
+
refType = refType & ~ReferenceType.StayOnRemove;
|
|
1953
|
+
refType = refType | ReferenceType.SlideOnRemove;
|
|
1954
|
+
lref.refType = refType;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1694
1957
|
/**
|
|
1695
1958
|
* Information that identifies an interval within a `Sequence`.
|
|
1696
1959
|
*/
|