@fluidframework/sequence 2.0.0-internal.1.4.2 → 2.0.0-internal.2.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/dist/defaultMap.d.ts.map +1 -1
- package/dist/defaultMap.js +1 -0
- package/dist/defaultMap.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +271 -34
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +343 -97
- package/dist/intervalCollection.js.map +1 -1
- package/dist/intervalTree.d.ts +72 -0
- package/dist/intervalTree.d.ts.map +1 -0
- package/dist/intervalTree.js +91 -0
- package/dist/intervalTree.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/sequence.d.ts +66 -15
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +73 -19
- package/dist/sequence.js.map +1 -1
- package/dist/sequenceDeltaEvent.d.ts +15 -1
- package/dist/sequenceDeltaEvent.d.ts.map +1 -1
- package/dist/sequenceDeltaEvent.js +2 -1
- package/dist/sequenceDeltaEvent.js.map +1 -1
- package/dist/sequenceFactory.d.ts +0 -89
- package/dist/sequenceFactory.d.ts.map +1 -1
- package/dist/sequenceFactory.js +2 -142
- package/dist/sequenceFactory.js.map +1 -1
- package/dist/sharedIntervalCollection.d.ts +0 -6
- package/dist/sharedIntervalCollection.d.ts.map +1 -1
- package/dist/sharedIntervalCollection.js +0 -7
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/dist/sharedSequence.d.ts +2 -2
- package/dist/sharedString.d.ts +16 -15
- package/dist/sharedString.d.ts.map +1 -1
- package/dist/sharedString.js +96 -15
- package/dist/sharedString.js.map +1 -1
- package/lib/defaultMap.d.ts.map +1 -1
- package/lib/defaultMap.js +1 -0
- package/lib/defaultMap.js.map +1 -1
- package/lib/index.d.ts +4 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -4
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +271 -34
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +341 -98
- package/lib/intervalCollection.js.map +1 -1
- package/lib/intervalTree.d.ts +72 -0
- package/lib/intervalTree.d.ts.map +1 -0
- package/lib/intervalTree.js +86 -0
- package/lib/intervalTree.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/sequence.d.ts +66 -15
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +73 -19
- package/lib/sequence.js.map +1 -1
- package/lib/sequenceDeltaEvent.d.ts +15 -1
- package/lib/sequenceDeltaEvent.d.ts.map +1 -1
- package/lib/sequenceDeltaEvent.js +2 -1
- package/lib/sequenceDeltaEvent.js.map +1 -1
- package/lib/sequenceFactory.d.ts +0 -89
- package/lib/sequenceFactory.d.ts.map +1 -1
- package/lib/sequenceFactory.js +1 -139
- package/lib/sequenceFactory.js.map +1 -1
- package/lib/sharedIntervalCollection.d.ts +0 -6
- package/lib/sharedIntervalCollection.d.ts.map +1 -1
- package/lib/sharedIntervalCollection.js +0 -7
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/lib/sharedSequence.d.ts +2 -2
- package/lib/sharedString.d.ts +16 -15
- package/lib/sharedString.d.ts.map +1 -1
- package/lib/sharedString.js +97 -16
- package/lib/sharedString.js.map +1 -1
- package/package.json +99 -27
- package/src/defaultMap.ts +3 -0
- package/src/index.ts +4 -4
- package/src/intervalCollection.ts +486 -143
- package/src/intervalTree.ts +166 -0
- package/src/packageVersion.ts +1 -1
- package/src/sequence.ts +86 -30
- package/src/sequenceDeltaEvent.ts +18 -4
- package/src/sequenceFactory.ts +2 -163
- package/src/sharedIntervalCollection.ts +0 -11
- package/src/sharedString.ts +120 -23
- package/tsconfig.json +0 -1
- package/dist/sharedNumberSequence.d.ts +0 -50
- package/dist/sharedNumberSequence.d.ts.map +0 -1
- package/dist/sharedNumberSequence.js +0 -61
- package/dist/sharedNumberSequence.js.map +0 -1
- package/dist/sharedObjectSequence.d.ts +0 -50
- package/dist/sharedObjectSequence.d.ts.map +0 -1
- package/dist/sharedObjectSequence.js +0 -61
- package/dist/sharedObjectSequence.js.map +0 -1
- package/dist/sparsematrix.d.ts +0 -152
- package/dist/sparsematrix.d.ts.map +0 -1
- package/dist/sparsematrix.js +0 -343
- package/dist/sparsematrix.js.map +0 -1
- package/lib/sharedNumberSequence.d.ts +0 -50
- package/lib/sharedNumberSequence.d.ts.map +0 -1
- package/lib/sharedNumberSequence.js +0 -57
- package/lib/sharedNumberSequence.js.map +0 -1
- package/lib/sharedObjectSequence.d.ts +0 -50
- package/lib/sharedObjectSequence.d.ts.map +0 -1
- package/lib/sharedObjectSequence.js +0 -57
- package/lib/sharedObjectSequence.js.map +0 -1
- package/lib/sparsematrix.d.ts +0 -152
- package/lib/sparsematrix.d.ts.map +0 -1
- package/lib/sparsematrix.js +0 -334
- package/lib/sparsematrix.js.map +0 -1
- package/src/sharedNumberSequence.ts +0 -62
- package/src/sharedObjectSequence.ts +0 -62
- package/src/sparsematrix.ts +0 -434
|
@@ -15,10 +15,6 @@ import {
|
|
|
15
15
|
ConflictAction,
|
|
16
16
|
createMap,
|
|
17
17
|
ICombiningOp,
|
|
18
|
-
IInterval,
|
|
19
|
-
IntervalConflictResolver,
|
|
20
|
-
IntervalNode,
|
|
21
|
-
IntervalTree,
|
|
22
18
|
ISegment,
|
|
23
19
|
MergeTreeDeltaType,
|
|
24
20
|
minReferencePosition,
|
|
@@ -45,6 +41,7 @@ import {
|
|
|
45
41
|
IValueType,
|
|
46
42
|
IValueTypeOperationValue,
|
|
47
43
|
} from "./defaultMapInterfaces";
|
|
44
|
+
import { IInterval, IntervalConflictResolver, IntervalTree, IntervalNode } from "./intervalTree";
|
|
48
45
|
|
|
49
46
|
const reservedIntervalIdKey = "intervalId";
|
|
50
47
|
|
|
@@ -67,17 +64,44 @@ export enum IntervalType {
|
|
|
67
64
|
Transient = 0x4,
|
|
68
65
|
}
|
|
69
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Serialized object representation of an interval.
|
|
69
|
+
* This representation is used for ops that create or change intervals.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
70
72
|
export interface ISerializedInterval {
|
|
73
|
+
/**
|
|
74
|
+
* Sequence number at which `start` and `end` should be interpreted
|
|
75
|
+
*
|
|
76
|
+
* @remarks - It's unclear that this is necessary to store here.
|
|
77
|
+
* This should just be the refSeq on the op that modified the interval, which should be available via other means.
|
|
78
|
+
* At the time of writing, it's not plumbed through to the reconnect/rebase code, however, which does need it.
|
|
79
|
+
*/
|
|
71
80
|
sequenceNumber: number;
|
|
81
|
+
/** Start position of the interval (inclusive) */
|
|
72
82
|
start: number;
|
|
83
|
+
/** End position of the interval (inclusive) */
|
|
73
84
|
end: number;
|
|
85
|
+
/** Interval type to create */
|
|
74
86
|
intervalType: IntervalType;
|
|
87
|
+
/** Any properties the interval has */
|
|
75
88
|
properties?: PropertySet;
|
|
76
89
|
}
|
|
77
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Represents a change that should be applied to an existing interval.
|
|
93
|
+
* Changes can modify any of start/end/properties, with `undefined` signifying no change should be made.
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
export type SerializedIntervalDelta =
|
|
97
|
+
Omit<ISerializedInterval, "start" | "end" | "properties">
|
|
98
|
+
& Partial<Pick<ISerializedInterval, "start" | "end" | "properties">>;
|
|
99
|
+
|
|
78
100
|
/**
|
|
79
101
|
* A size optimization to avoid redundantly storing keys when serializing intervals
|
|
80
|
-
* as JSON
|
|
102
|
+
* as JSON for summaries.
|
|
103
|
+
*
|
|
104
|
+
* Intervals are of the format:
|
|
81
105
|
*
|
|
82
106
|
* [start, end, sequenceNumber, intervalType, properties]
|
|
83
107
|
*/
|
|
@@ -125,11 +149,22 @@ function compressInterval(interval: ISerializedInterval): CompressedSerializedIn
|
|
|
125
149
|
}
|
|
126
150
|
|
|
127
151
|
export interface ISerializableInterval extends IInterval {
|
|
152
|
+
/** Serializable bag of properties associated with the interval. */
|
|
128
153
|
properties: PropertySet;
|
|
154
|
+
/** @internal */
|
|
129
155
|
propertyManager: PropertiesManager;
|
|
130
|
-
|
|
156
|
+
/** @internal */
|
|
157
|
+
serialize(): ISerializedInterval;
|
|
158
|
+
/** @internal */
|
|
131
159
|
addProperties(props: PropertySet, collaborating?: boolean, seq?: number):
|
|
132
160
|
PropertySet | undefined;
|
|
161
|
+
/**
|
|
162
|
+
* Gets the id associated with this interval.
|
|
163
|
+
* When the interval is used as part of an interval collection, this id can be used to modify or remove the
|
|
164
|
+
* interval.
|
|
165
|
+
* @remarks - This signature includes `undefined` strictly for backwards-compatibility reasons, as older versions
|
|
166
|
+
* of Fluid didn't always write interval ids.
|
|
167
|
+
*/
|
|
133
168
|
getIntervalId(): string | undefined;
|
|
134
169
|
}
|
|
135
170
|
|
|
@@ -148,18 +183,28 @@ export interface IIntervalHelpers<TInterval extends ISerializableInterval> {
|
|
|
148
183
|
*/
|
|
149
184
|
create(
|
|
150
185
|
label: string,
|
|
151
|
-
start: number,
|
|
152
|
-
end: number,
|
|
153
|
-
client: Client,
|
|
154
|
-
intervalType
|
|
186
|
+
start: number | undefined,
|
|
187
|
+
end: number | undefined,
|
|
188
|
+
client: Client | undefined,
|
|
189
|
+
intervalType: IntervalType,
|
|
155
190
|
op?: ISequencedDocumentMessage,
|
|
156
191
|
fromSnapshot?: boolean,
|
|
157
192
|
): TInterval;
|
|
158
193
|
}
|
|
159
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Serializable interval whose endpoints are plain-old numbers.
|
|
197
|
+
*/
|
|
160
198
|
export class Interval implements ISerializableInterval {
|
|
199
|
+
/**
|
|
200
|
+
* {@inheritDoc ISerializableInterval.properties}
|
|
201
|
+
*/
|
|
161
202
|
public properties: PropertySet;
|
|
162
|
-
|
|
203
|
+
/** @internal */
|
|
204
|
+
public auxProps: PropertySet[] | undefined;
|
|
205
|
+
/**
|
|
206
|
+
* {@inheritDoc ISerializableInterval.propertyManager}
|
|
207
|
+
*/
|
|
163
208
|
public propertyManager: PropertiesManager;
|
|
164
209
|
constructor(
|
|
165
210
|
public start: number,
|
|
@@ -174,6 +219,9 @@ export class Interval implements ISerializableInterval {
|
|
|
174
219
|
}
|
|
175
220
|
}
|
|
176
221
|
|
|
222
|
+
/**
|
|
223
|
+
* {@inheritDoc ISerializableInterval.getIntervalId}
|
|
224
|
+
*/
|
|
177
225
|
public getIntervalId(): string | undefined {
|
|
178
226
|
const id = this.properties?.[reservedIntervalIdKey];
|
|
179
227
|
if (id === undefined) {
|
|
@@ -182,10 +230,21 @@ export class Interval implements ISerializableInterval {
|
|
|
182
230
|
return `${id}`;
|
|
183
231
|
}
|
|
184
232
|
|
|
185
|
-
|
|
186
|
-
|
|
233
|
+
/**
|
|
234
|
+
* @returns an array containing any auxiliary property sets added with `addPropertySet`.
|
|
235
|
+
*/
|
|
236
|
+
public getAdditionalPropertySets(): PropertySet[] {
|
|
237
|
+
return this.auxProps ?? [];
|
|
187
238
|
}
|
|
188
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Adds an auxiliary set of properties to this interval.
|
|
242
|
+
* These properties can be recovered using `getAdditionalPropertySets`
|
|
243
|
+
* @param props - set of properties to add
|
|
244
|
+
* @remarks - This gets called as part of the default conflict resolver for `IntervalCollection<Interval>`
|
|
245
|
+
* (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
|
|
246
|
+
* This functionality seems half-baked.
|
|
247
|
+
*/
|
|
189
248
|
public addPropertySet(props: PropertySet) {
|
|
190
249
|
if (this.auxProps === undefined) {
|
|
191
250
|
this.auxProps = [];
|
|
@@ -193,12 +252,15 @@ export class Interval implements ISerializableInterval {
|
|
|
193
252
|
this.auxProps.push(props);
|
|
194
253
|
}
|
|
195
254
|
|
|
196
|
-
|
|
197
|
-
|
|
255
|
+
/**
|
|
256
|
+
* {@inheritDoc ISerializableInterval.serialize}
|
|
257
|
+
* @internal
|
|
258
|
+
*/
|
|
259
|
+
public serialize(): ISerializedInterval {
|
|
198
260
|
const serializedInterval: ISerializedInterval = {
|
|
199
261
|
end: this.end,
|
|
200
262
|
intervalType: 0,
|
|
201
|
-
sequenceNumber:
|
|
263
|
+
sequenceNumber: 0,
|
|
202
264
|
start: this.start,
|
|
203
265
|
};
|
|
204
266
|
if (this.properties) {
|
|
@@ -207,10 +269,16 @@ export class Interval implements ISerializableInterval {
|
|
|
207
269
|
return serializedInterval;
|
|
208
270
|
}
|
|
209
271
|
|
|
272
|
+
/**
|
|
273
|
+
* {@inheritDoc IInterval.clone}
|
|
274
|
+
*/
|
|
210
275
|
public clone() {
|
|
211
276
|
return new Interval(this.start, this.end, this.properties);
|
|
212
277
|
}
|
|
213
278
|
|
|
279
|
+
/**
|
|
280
|
+
* {@inheritDoc IInterval.compare}
|
|
281
|
+
*/
|
|
214
282
|
public compare(b: Interval) {
|
|
215
283
|
const startResult = this.compareStart(b);
|
|
216
284
|
if (startResult === 0) {
|
|
@@ -233,20 +301,32 @@ export class Interval implements ISerializableInterval {
|
|
|
233
301
|
}
|
|
234
302
|
}
|
|
235
303
|
|
|
304
|
+
/**
|
|
305
|
+
* {@inheritDoc IInterval.compareStart}
|
|
306
|
+
*/
|
|
236
307
|
public compareStart(b: Interval) {
|
|
237
308
|
return this.start - b.start;
|
|
238
309
|
}
|
|
239
310
|
|
|
311
|
+
/**
|
|
312
|
+
* {@inheritDoc IInterval.compareEnd}
|
|
313
|
+
*/
|
|
240
314
|
public compareEnd(b: Interval) {
|
|
241
315
|
return this.end - b.end;
|
|
242
316
|
}
|
|
243
317
|
|
|
318
|
+
/**
|
|
319
|
+
* {@inheritDoc IInterval.overlaps}
|
|
320
|
+
*/
|
|
244
321
|
public overlaps(b: Interval) {
|
|
245
322
|
const result = (this.start <= b.end) &&
|
|
246
323
|
(this.end >= b.start);
|
|
247
324
|
return result;
|
|
248
325
|
}
|
|
249
326
|
|
|
327
|
+
/**
|
|
328
|
+
* {@inheritDoc IInterval.union}
|
|
329
|
+
*/
|
|
250
330
|
public union(b: Interval) {
|
|
251
331
|
return new Interval(Math.min(this.start, b.start),
|
|
252
332
|
Math.max(this.end, b.end), this.properties);
|
|
@@ -256,6 +336,9 @@ export class Interval implements ISerializableInterval {
|
|
|
256
336
|
return this.properties;
|
|
257
337
|
}
|
|
258
338
|
|
|
339
|
+
/**
|
|
340
|
+
* {@inheritDoc ISerializableInterval.addProperties}
|
|
341
|
+
*/
|
|
259
342
|
public addProperties(
|
|
260
343
|
newProps: PropertySet,
|
|
261
344
|
collaborating: boolean = false,
|
|
@@ -268,6 +351,9 @@ export class Interval implements ISerializableInterval {
|
|
|
268
351
|
}
|
|
269
352
|
}
|
|
270
353
|
|
|
354
|
+
/**
|
|
355
|
+
* {@inheritDoc IInterval.modify}
|
|
356
|
+
*/
|
|
271
357
|
public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage) {
|
|
272
358
|
const startPos = start ?? this.start;
|
|
273
359
|
const endPos = end ?? this.end;
|
|
@@ -293,13 +379,32 @@ export class Interval implements ISerializableInterval {
|
|
|
293
379
|
}
|
|
294
380
|
}
|
|
295
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Interval impelmentation whose ends are associated with positions in a mutatable sequence.
|
|
384
|
+
* As such, when content is inserted into the middle of the interval, the interval expands to
|
|
385
|
+
* include that content.
|
|
386
|
+
*/
|
|
296
387
|
export class SequenceInterval implements ISerializableInterval {
|
|
388
|
+
/**
|
|
389
|
+
* {@inheritDoc ISerializableInterval.properties}
|
|
390
|
+
*/
|
|
297
391
|
public properties: PropertySet;
|
|
392
|
+
/**
|
|
393
|
+
* {@inheritDoc ISerializableInterval.propertyManager}
|
|
394
|
+
*/
|
|
298
395
|
public propertyManager: PropertiesManager;
|
|
299
396
|
|
|
300
397
|
constructor(
|
|
301
398
|
private readonly client: Client,
|
|
399
|
+
/**
|
|
400
|
+
* Start endpoint of this interval.
|
|
401
|
+
* @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
|
|
402
|
+
*/
|
|
302
403
|
public start: LocalReferencePosition,
|
|
404
|
+
/**
|
|
405
|
+
* End endpoint of this interval.
|
|
406
|
+
* @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
|
|
407
|
+
*/
|
|
303
408
|
public end: LocalReferencePosition,
|
|
304
409
|
public intervalType: IntervalType,
|
|
305
410
|
props?: PropertySet,
|
|
@@ -344,13 +449,17 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
344
449
|
}
|
|
345
450
|
}
|
|
346
451
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
452
|
+
/**
|
|
453
|
+
* {@inheritDoc ISerializableInterval.serialize}
|
|
454
|
+
* @internal
|
|
455
|
+
*/
|
|
456
|
+
public serialize(): ISerializedInterval {
|
|
457
|
+
const startPosition = this.client.localReferencePositionToPosition(this.start);
|
|
458
|
+
const endPosition = this.client.localReferencePositionToPosition(this.end);
|
|
350
459
|
const serializedInterval: ISerializedInterval = {
|
|
351
460
|
end: endPosition,
|
|
352
461
|
intervalType: this.intervalType,
|
|
353
|
-
sequenceNumber: client.getCurrentSeq(),
|
|
462
|
+
sequenceNumber: this.client.getCurrentSeq(),
|
|
354
463
|
start: startPosition,
|
|
355
464
|
};
|
|
356
465
|
|
|
@@ -361,10 +470,16 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
361
470
|
return serializedInterval;
|
|
362
471
|
}
|
|
363
472
|
|
|
473
|
+
/**
|
|
474
|
+
* {@inheritDoc IInterval.clone}
|
|
475
|
+
*/
|
|
364
476
|
public clone() {
|
|
365
477
|
return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties);
|
|
366
478
|
}
|
|
367
479
|
|
|
480
|
+
/**
|
|
481
|
+
* {@inheritDoc IInterval.compare}
|
|
482
|
+
*/
|
|
368
483
|
public compare(b: SequenceInterval) {
|
|
369
484
|
const startResult = this.compareStart(b);
|
|
370
485
|
if (startResult === 0) {
|
|
@@ -387,20 +502,32 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
387
502
|
}
|
|
388
503
|
}
|
|
389
504
|
|
|
505
|
+
/**
|
|
506
|
+
* {@inheritDoc IInterval.compareStart}
|
|
507
|
+
*/
|
|
390
508
|
public compareStart(b: SequenceInterval) {
|
|
391
509
|
return compareReferencePositions(this.start, b.start);
|
|
392
510
|
}
|
|
393
511
|
|
|
512
|
+
/**
|
|
513
|
+
* {@inheritDoc IInterval.compareEnd}
|
|
514
|
+
*/
|
|
394
515
|
public compareEnd(b: SequenceInterval) {
|
|
395
516
|
return compareReferencePositions(this.end, b.end);
|
|
396
517
|
}
|
|
397
518
|
|
|
519
|
+
/**
|
|
520
|
+
* {@inheritDoc IInterval.overlaps}
|
|
521
|
+
*/
|
|
398
522
|
public overlaps(b: SequenceInterval) {
|
|
399
523
|
const result = (compareReferencePositions(this.start, b.end) <= 0) &&
|
|
400
524
|
(compareReferencePositions(this.end, b.start) >= 0);
|
|
401
525
|
return result;
|
|
402
526
|
}
|
|
403
527
|
|
|
528
|
+
/**
|
|
529
|
+
* {@inheritDoc ISerializableInterval.getIntervalId}
|
|
530
|
+
*/
|
|
404
531
|
public getIntervalId(): string | undefined {
|
|
405
532
|
const id = this.properties?.[reservedIntervalIdKey];
|
|
406
533
|
if (id === undefined) {
|
|
@@ -409,11 +536,17 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
409
536
|
return `${id}`;
|
|
410
537
|
}
|
|
411
538
|
|
|
539
|
+
/**
|
|
540
|
+
* {@inheritDoc IInterval.union}
|
|
541
|
+
*/
|
|
412
542
|
public union(b: SequenceInterval) {
|
|
413
543
|
return new SequenceInterval(this.client, minReferencePosition(this.start, b.start),
|
|
414
544
|
maxReferencePosition(this.end, b.end), this.intervalType);
|
|
415
545
|
}
|
|
416
546
|
|
|
547
|
+
/**
|
|
548
|
+
* {@inheritDoc ISerializableInterval.addProperties}
|
|
549
|
+
*/
|
|
417
550
|
public addProperties(
|
|
418
551
|
newProps: PropertySet,
|
|
419
552
|
collab: boolean = false,
|
|
@@ -424,12 +557,19 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
424
557
|
return this.propertyManager.addProperties(this.properties, newProps, op, seq, collab);
|
|
425
558
|
}
|
|
426
559
|
|
|
560
|
+
/**
|
|
561
|
+
* @returns whether this interval overlaps two numerical positions.
|
|
562
|
+
* @remarks - this is currently strict overlap, which doesn't align with the endpoint treatment of`.overlaps()`
|
|
563
|
+
*/
|
|
427
564
|
public overlapsPos(bstart: number, bend: number) {
|
|
428
565
|
const startPos = this.client.localReferencePositionToPosition(this.start);
|
|
429
566
|
const endPos = this.client.localReferencePositionToPosition(this.end);
|
|
430
567
|
return (endPos > bstart) && (startPos < bend);
|
|
431
568
|
}
|
|
432
569
|
|
|
570
|
+
/**
|
|
571
|
+
* {@inheritDoc IInterval.modify}
|
|
572
|
+
*/
|
|
433
573
|
public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage, localSeq?: number) {
|
|
434
574
|
const getRefType = (baseType: ReferenceType): ReferenceType => {
|
|
435
575
|
let refType = baseType;
|
|
@@ -445,7 +585,9 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
445
585
|
startRef = createPositionReference(
|
|
446
586
|
this.client, start, getRefType(this.start.refType), op, undefined, localSeq,
|
|
447
587
|
);
|
|
448
|
-
|
|
588
|
+
if (this.start.properties) {
|
|
589
|
+
startRef.addProperties(this.start.properties);
|
|
590
|
+
}
|
|
449
591
|
}
|
|
450
592
|
|
|
451
593
|
let endRef = this.end;
|
|
@@ -453,7 +595,9 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
453
595
|
endRef = createPositionReference(
|
|
454
596
|
this.client, end, getRefType(this.end.refType), op, undefined, localSeq,
|
|
455
597
|
);
|
|
456
|
-
|
|
598
|
+
if (this.end.properties) {
|
|
599
|
+
endRef.addProperties(this.end.properties);
|
|
600
|
+
}
|
|
457
601
|
}
|
|
458
602
|
|
|
459
603
|
const newInterval = new SequenceInterval(this.client, startRef, endRef, this.intervalType);
|
|
@@ -506,21 +650,22 @@ function createPositionReference(
|
|
|
506
650
|
segoff = client.getContainingSegment(pos, op);
|
|
507
651
|
segoff = client.getSlideToSegment(segoff);
|
|
508
652
|
} else {
|
|
509
|
-
assert((refType & ReferenceType.SlideOnRemove) === 0 || fromSnapshot,
|
|
653
|
+
assert((refType & ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot,
|
|
510
654
|
0x2f6 /* SlideOnRemove references must be op created */);
|
|
511
655
|
segoff = client.getContainingSegment(pos, undefined, localSeq);
|
|
512
656
|
}
|
|
513
657
|
return createPositionReferenceFromSegoff(client, segoff, refType, op);
|
|
514
658
|
}
|
|
515
659
|
|
|
516
|
-
function createSequenceInterval(
|
|
660
|
+
export function createSequenceInterval(
|
|
517
661
|
label: string,
|
|
518
662
|
start: number,
|
|
519
663
|
end: number,
|
|
520
664
|
client: Client,
|
|
521
|
-
intervalType
|
|
665
|
+
intervalType: IntervalType,
|
|
522
666
|
op?: ISequencedDocumentMessage,
|
|
523
|
-
fromSnapshot?: boolean
|
|
667
|
+
fromSnapshot?: boolean,
|
|
668
|
+
): SequenceInterval {
|
|
524
669
|
let beginRefType = ReferenceType.RangeBegin;
|
|
525
670
|
let endRefType = ReferenceType.RangeEnd;
|
|
526
671
|
if (intervalType === IntervalType.Transient) {
|
|
@@ -551,7 +696,13 @@ function createSequenceInterval(
|
|
|
551
696
|
startLref.addProperties(rangeProp);
|
|
552
697
|
endLref.addProperties(rangeProp);
|
|
553
698
|
|
|
554
|
-
const ival = new SequenceInterval(
|
|
699
|
+
const ival = new SequenceInterval(
|
|
700
|
+
client,
|
|
701
|
+
startLref,
|
|
702
|
+
endLref,
|
|
703
|
+
intervalType,
|
|
704
|
+
rangeProp,
|
|
705
|
+
);
|
|
555
706
|
return ival;
|
|
556
707
|
}
|
|
557
708
|
|
|
@@ -565,7 +716,7 @@ export function createIntervalIndex(conflict?: IntervalConflictResolver<Interval
|
|
|
565
716
|
compareEnds: compareIntervalEnds,
|
|
566
717
|
create: createInterval,
|
|
567
718
|
};
|
|
568
|
-
const lc = new LocalIntervalCollection<Interval>(undefined, "", helpers);
|
|
719
|
+
const lc = new LocalIntervalCollection<Interval>(undefined as any as Client, "", helpers);
|
|
569
720
|
if (conflict) {
|
|
570
721
|
lc.addConflictResolver(conflict);
|
|
571
722
|
} else {
|
|
@@ -588,7 +739,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
588
739
|
private readonly label: string,
|
|
589
740
|
private readonly helpers: IIntervalHelpers<TInterval>,
|
|
590
741
|
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
591
|
-
private readonly onPositionChange?: (interval: TInterval) => void,
|
|
742
|
+
private readonly onPositionChange?: (interval: TInterval, previousInterval: TInterval) => void,
|
|
592
743
|
) {
|
|
593
744
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
594
745
|
this.endIntervalTree = new RedBlackTree<TInterval, TInterval>(helpers.compareEnds);
|
|
@@ -723,6 +874,10 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
723
874
|
}
|
|
724
875
|
}
|
|
725
876
|
|
|
877
|
+
/**
|
|
878
|
+
* @returns an array of all intervals contained in this collection that overlap the range
|
|
879
|
+
* `[startPosition, endPosition]`.
|
|
880
|
+
*/
|
|
726
881
|
public findOverlappingIntervals(startPosition: number, endPosition: number) {
|
|
727
882
|
if (endPosition < startPosition || this.intervalTree.intervals.isEmpty()) {
|
|
728
883
|
return [];
|
|
@@ -831,7 +986,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
831
986
|
this.intervalIdMap.set(id, interval);
|
|
832
987
|
}
|
|
833
988
|
|
|
834
|
-
public add(interval: TInterval) {
|
|
989
|
+
public add(interval: TInterval): void {
|
|
835
990
|
this.linkEndpointsToInterval(interval);
|
|
836
991
|
this.addIntervalToIndex(interval);
|
|
837
992
|
this.addIntervalListeners(interval);
|
|
@@ -843,8 +998,8 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
843
998
|
|
|
844
999
|
public changeInterval(
|
|
845
1000
|
interval: TInterval,
|
|
846
|
-
start: number,
|
|
847
|
-
end: number,
|
|
1001
|
+
start: number | undefined,
|
|
1002
|
+
end: number | undefined,
|
|
848
1003
|
op?: ISequencedDocumentMessage,
|
|
849
1004
|
localSeq?: number,
|
|
850
1005
|
) {
|
|
@@ -857,23 +1012,46 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
857
1012
|
}
|
|
858
1013
|
|
|
859
1014
|
public serialize(): ISerializedIntervalCollectionV2 {
|
|
860
|
-
const client = this.client;
|
|
861
1015
|
const intervals = this.intervalTree.intervals.keys();
|
|
862
|
-
|
|
863
1016
|
return {
|
|
864
1017
|
label: this.label,
|
|
865
|
-
intervals: intervals.map((interval) => compressInterval(interval.serialize(
|
|
1018
|
+
intervals: intervals.map((interval) => compressInterval(interval.serialize())),
|
|
866
1019
|
version: 2,
|
|
867
1020
|
};
|
|
868
1021
|
}
|
|
869
1022
|
|
|
870
1023
|
private addIntervalListeners(interval: TInterval) {
|
|
1024
|
+
const cloneRef = (ref: LocalReferencePosition) => {
|
|
1025
|
+
const segment = ref.getSegment();
|
|
1026
|
+
if (segment === undefined) {
|
|
1027
|
+
// Cloning is unnecessary: refs which have slid off the string entirely
|
|
1028
|
+
// never get slid back on. Creation code for refs doesn't accept undefined segment
|
|
1029
|
+
// either, so this must be special-cased.
|
|
1030
|
+
return ref;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
return this.client.createLocalReferencePosition(
|
|
1034
|
+
segment,
|
|
1035
|
+
ref.getOffset(),
|
|
1036
|
+
ReferenceType.Transient,
|
|
1037
|
+
ref.properties,
|
|
1038
|
+
);
|
|
1039
|
+
};
|
|
871
1040
|
if (interval instanceof SequenceInterval) {
|
|
1041
|
+
let previousInterval: TInterval & SequenceInterval | undefined;
|
|
872
1042
|
interval.addPositionChangeListeners(
|
|
873
|
-
() => this.removeIntervalFromIndex(interval),
|
|
874
1043
|
() => {
|
|
1044
|
+
assert(!previousInterval, 0x3f9 /* Invalid interleaving of before/after slide */);
|
|
1045
|
+
previousInterval = interval.clone() as TInterval & SequenceInterval;
|
|
1046
|
+
previousInterval.start = cloneRef(previousInterval.start);
|
|
1047
|
+
previousInterval.end = cloneRef(previousInterval.end);
|
|
1048
|
+
this.removeIntervalFromIndex(interval);
|
|
1049
|
+
},
|
|
1050
|
+
() => {
|
|
1051
|
+
assert(previousInterval !== undefined, 0x3fa /* Invalid interleaving of before/after slide */);
|
|
875
1052
|
this.addIntervalToIndex(interval);
|
|
876
|
-
this.onPositionChange?.(interval);
|
|
1053
|
+
this.onPositionChange?.(interval, previousInterval);
|
|
1054
|
+
previousInterval = undefined;
|
|
877
1055
|
},
|
|
878
1056
|
);
|
|
879
1057
|
}
|
|
@@ -886,7 +1064,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
886
1064
|
}
|
|
887
1065
|
}
|
|
888
1066
|
|
|
889
|
-
const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
|
|
1067
|
+
export const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
|
|
890
1068
|
compareReferencePositions(a.end, b.end);
|
|
891
1069
|
|
|
892
1070
|
class SequenceIntervalCollectionFactory
|
|
@@ -902,7 +1080,7 @@ class SequenceIntervalCollectionFactory
|
|
|
902
1080
|
return new IntervalCollection<SequenceInterval>(helpers, true, emitter, raw);
|
|
903
1081
|
}
|
|
904
1082
|
|
|
905
|
-
public store(value: IntervalCollection<SequenceInterval>): ISerializedIntervalCollectionV2 {
|
|
1083
|
+
public store(value: IntervalCollection<SequenceInterval>): ISerializedInterval[] | ISerializedIntervalCollectionV2 {
|
|
906
1084
|
return value.serializeInternal();
|
|
907
1085
|
}
|
|
908
1086
|
}
|
|
@@ -952,7 +1130,7 @@ class IntervalCollectionFactory
|
|
|
952
1130
|
create: createInterval,
|
|
953
1131
|
};
|
|
954
1132
|
const collection = new IntervalCollection<Interval>(helpers, false, emitter, raw);
|
|
955
|
-
collection.attachGraph(undefined, "");
|
|
1133
|
+
collection.attachGraph(undefined as any as Client, "");
|
|
956
1134
|
return collection;
|
|
957
1135
|
}
|
|
958
1136
|
|
|
@@ -982,7 +1160,7 @@ export class IntervalCollectionValueType
|
|
|
982
1160
|
private static readonly _ops = makeOpsMap<Interval>();
|
|
983
1161
|
}
|
|
984
1162
|
|
|
985
|
-
function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperation<IntervalCollection<T>>> {
|
|
1163
|
+
export function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperation<IntervalCollection<T>>> {
|
|
986
1164
|
const rebase = (
|
|
987
1165
|
collection: IntervalCollection<T>,
|
|
988
1166
|
op: IValueTypeOperationValue,
|
|
@@ -1004,6 +1182,7 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
|
|
|
1004
1182
|
if (!params) {
|
|
1005
1183
|
return;
|
|
1006
1184
|
}
|
|
1185
|
+
assert(op !== undefined, 0x3fb /* op should exist here */);
|
|
1007
1186
|
collection.ackAdd(params, local, op);
|
|
1008
1187
|
},
|
|
1009
1188
|
rebase,
|
|
@@ -1013,6 +1192,7 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
|
|
|
1013
1192
|
"delete",
|
|
1014
1193
|
{
|
|
1015
1194
|
process: (collection, params, local, op) => {
|
|
1195
|
+
assert(op !== undefined, 0x3fc /* op should exist here */);
|
|
1016
1196
|
collection.ackDelete(params, local, op);
|
|
1017
1197
|
},
|
|
1018
1198
|
rebase: (collection, op, localOpMetadata) => {
|
|
@@ -1030,6 +1210,7 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
|
|
|
1030
1210
|
if (!params) {
|
|
1031
1211
|
return;
|
|
1032
1212
|
}
|
|
1213
|
+
assert(op !== undefined, 0x3fd /* op should exist here */);
|
|
1033
1214
|
collection.ackChange(params, local, op);
|
|
1034
1215
|
},
|
|
1035
1216
|
rebase,
|
|
@@ -1039,7 +1220,8 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
|
|
|
1039
1220
|
|
|
1040
1221
|
export type DeserializeCallback = (properties: PropertySet) => void;
|
|
1041
1222
|
|
|
1042
|
-
export class IntervalCollectionIterator<TInterval extends ISerializableInterval>
|
|
1223
|
+
export class IntervalCollectionIterator<TInterval extends ISerializableInterval>
|
|
1224
|
+
implements Iterator<TInterval> {
|
|
1043
1225
|
private readonly results: TInterval[];
|
|
1044
1226
|
private index: number;
|
|
1045
1227
|
|
|
@@ -1054,43 +1236,80 @@ export class IntervalCollectionIterator<TInterval extends ISerializableInterval>
|
|
|
1054
1236
|
collection.gatherIterationResults(this.results, iteratesForward, start, end);
|
|
1055
1237
|
}
|
|
1056
1238
|
|
|
1057
|
-
public next() {
|
|
1058
|
-
let _value: TInterval | undefined;
|
|
1059
|
-
let _done: boolean = true;
|
|
1060
|
-
|
|
1239
|
+
public next(): IteratorResult<TInterval> {
|
|
1061
1240
|
if (this.index < this.results.length) {
|
|
1062
|
-
|
|
1063
|
-
|
|
1241
|
+
return {
|
|
1242
|
+
value: this.results[this.index++],
|
|
1243
|
+
done: false,
|
|
1244
|
+
};
|
|
1064
1245
|
}
|
|
1065
1246
|
|
|
1066
1247
|
return {
|
|
1067
|
-
value:
|
|
1068
|
-
done:
|
|
1248
|
+
value: undefined,
|
|
1249
|
+
done: true,
|
|
1069
1250
|
};
|
|
1070
1251
|
}
|
|
1071
1252
|
}
|
|
1072
1253
|
|
|
1254
|
+
/**
|
|
1255
|
+
* Change events emitted by `IntervalCollection`s
|
|
1256
|
+
*/
|
|
1073
1257
|
export interface IIntervalCollectionEvent<TInterval extends ISerializableInterval> extends IEvent {
|
|
1074
1258
|
/**
|
|
1075
|
-
* This event is invoked whenever the
|
|
1259
|
+
* This event is invoked whenever the endpoints of an interval may have changed.
|
|
1076
1260
|
* This can happen on:
|
|
1077
|
-
* - endpoint modification
|
|
1078
|
-
* - ack of
|
|
1079
|
-
* -
|
|
1080
|
-
* - position change due to segment sliding (will always appear as a local change)
|
|
1261
|
+
* - local endpoint modification
|
|
1262
|
+
* - ack of a remote endpoint modification
|
|
1263
|
+
* - position change due to segment sliding (slides due to mergeTree segment deletion will always appear local)
|
|
1081
1264
|
* The `interval` argument reflects the new values.
|
|
1265
|
+
* `previousInterval` contains transient `ReferencePosition`s at the same location as the interval's original
|
|
1266
|
+
* endpoints. These references should be used for position information only.
|
|
1267
|
+
* `local` reflects whether the change originated locally.
|
|
1268
|
+
* `op` is defined if and only if the server has acked this change.
|
|
1082
1269
|
*/
|
|
1083
1270
|
(event: "changeInterval",
|
|
1084
|
-
listener: (
|
|
1271
|
+
listener: (
|
|
1272
|
+
interval: TInterval,
|
|
1273
|
+
previousInterval: TInterval,
|
|
1274
|
+
local: boolean,
|
|
1275
|
+
op: ISequencedDocumentMessage | undefined
|
|
1276
|
+
) => void);
|
|
1277
|
+
/**
|
|
1278
|
+
* This event is invoked whenever an interval is added or removed from the collection.
|
|
1279
|
+
* `local` reflects whether the change originated locally.
|
|
1280
|
+
* `op` is defined if and only if the server has acked this change.
|
|
1281
|
+
*/
|
|
1085
1282
|
(event: "addInterval" | "deleteInterval",
|
|
1086
|
-
listener: (interval: TInterval, local: boolean, op: ISequencedDocumentMessage) => void);
|
|
1087
|
-
|
|
1283
|
+
listener: (interval: TInterval, local: boolean, op: ISequencedDocumentMessage | undefined) => void);
|
|
1284
|
+
/**
|
|
1285
|
+
* This event is invoked whenever an interval's properties have changed.
|
|
1286
|
+
* `interval` reflects the state of the updated properties.
|
|
1287
|
+
* `propertyDeltas` is a map-like whose keys contain all values that were changed, and whose
|
|
1288
|
+
* values contain all previous values of the property set.
|
|
1289
|
+
* This object can be used directly in a call to `changeProperties` to revert the property change if desired.
|
|
1290
|
+
* `local` reflects whether the change originated locally.
|
|
1291
|
+
* `op` is defined if and only if the server has acked this change.
|
|
1292
|
+
*/
|
|
1293
|
+
(event: "propertyChanged",
|
|
1294
|
+
listener: (
|
|
1295
|
+
interval: TInterval,
|
|
1296
|
+
propertyDeltas: PropertySet,
|
|
1297
|
+
local: boolean,
|
|
1298
|
+
op: ISequencedDocumentMessage | undefined
|
|
1299
|
+
) => void);
|
|
1088
1300
|
}
|
|
1089
1301
|
|
|
1302
|
+
/**
|
|
1303
|
+
* Collection of intervals that supports addition, modification, removal, and efficient spatial querying.
|
|
1304
|
+
* This class is not a DDS in its own right, but emits events on mutating operations such that it's possible to
|
|
1305
|
+
* integrate into a DDS.
|
|
1306
|
+
* This aligns with its usage in `SharedSegmentSequence`, which allows associating intervals to positions in the
|
|
1307
|
+
* sequence DDS which are broadcast to all other clients in an eventually consistent fashion.
|
|
1308
|
+
*/
|
|
1090
1309
|
export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
1091
1310
|
extends TypedEventEmitter<IIntervalCollectionEvent<TInterval>> {
|
|
1092
1311
|
private savedSerializedIntervals?: ISerializedInterval[];
|
|
1093
|
-
private localCollection: LocalIntervalCollection<TInterval
|
|
1312
|
+
private localCollection: LocalIntervalCollection<TInterval> | undefined;
|
|
1094
1313
|
private onDeserialize: DeserializeCallback | undefined;
|
|
1095
1314
|
private client: Client | undefined;
|
|
1096
1315
|
private readonly pendingChangesStart: Map<string, ISerializedInterval[]> = new Map<string, ISerializedInterval[]>();
|
|
@@ -1114,6 +1333,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1114
1333
|
: serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
|
|
1115
1334
|
}
|
|
1116
1335
|
|
|
1336
|
+
/** @internal */
|
|
1117
1337
|
public attachGraph(client: Client, label: string) {
|
|
1118
1338
|
if (this.attached) {
|
|
1119
1339
|
throw new LoggingError("Only supports one Sequence attach");
|
|
@@ -1129,7 +1349,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1129
1349
|
client,
|
|
1130
1350
|
label,
|
|
1131
1351
|
this.helpers,
|
|
1132
|
-
(interval) => this.
|
|
1352
|
+
(interval, previousInterval) => this.emitChange(interval, previousInterval, true),
|
|
1133
1353
|
);
|
|
1134
1354
|
if (this.savedSerializedIntervals) {
|
|
1135
1355
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
@@ -1144,7 +1364,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1144
1364
|
undefined,
|
|
1145
1365
|
true,
|
|
1146
1366
|
);
|
|
1147
|
-
|
|
1367
|
+
if (properties) {
|
|
1368
|
+
interval.addProperties(properties);
|
|
1369
|
+
}
|
|
1148
1370
|
this.localCollection.add(interval);
|
|
1149
1371
|
}
|
|
1150
1372
|
}
|
|
@@ -1162,15 +1384,43 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1162
1384
|
return 0;
|
|
1163
1385
|
}
|
|
1164
1386
|
|
|
1387
|
+
private emitChange(
|
|
1388
|
+
interval: TInterval,
|
|
1389
|
+
previousInterval: TInterval,
|
|
1390
|
+
local: boolean,
|
|
1391
|
+
op?: ISequencedDocumentMessage,
|
|
1392
|
+
): void {
|
|
1393
|
+
// Temporarily make references transient so that positional queries work (non-transient refs
|
|
1394
|
+
// on resolve to DetachedPosition on any segments that don't contain them). The original refType
|
|
1395
|
+
// is restored as single-endpoint changes re-use previous references.
|
|
1396
|
+
let startRefType: ReferenceType;
|
|
1397
|
+
let endRefType: ReferenceType;
|
|
1398
|
+
if (previousInterval instanceof SequenceInterval) {
|
|
1399
|
+
startRefType = previousInterval.start.refType;
|
|
1400
|
+
endRefType = previousInterval.end.refType;
|
|
1401
|
+
previousInterval.start.refType = ReferenceType.Transient;
|
|
1402
|
+
previousInterval.end.refType = ReferenceType.Transient;
|
|
1403
|
+
this.emit("changeInterval", interval, previousInterval, local, op);
|
|
1404
|
+
previousInterval.start.refType = startRefType;
|
|
1405
|
+
previousInterval.end.refType = endRefType;
|
|
1406
|
+
} else {
|
|
1407
|
+
this.emit("changeInterval", interval, previousInterval, local, op);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* @returns the interval in this collection that has the provided `id`.
|
|
1413
|
+
* If no interval in the collection has this `id`, returns `undefined`.
|
|
1414
|
+
*/
|
|
1165
1415
|
public getIntervalById(id: string) {
|
|
1166
|
-
if (!this.
|
|
1416
|
+
if (!this.localCollection) {
|
|
1167
1417
|
throw new LoggingError("attach must be called before accessing intervals");
|
|
1168
1418
|
}
|
|
1169
1419
|
return this.localCollection.getIntervalById(id);
|
|
1170
1420
|
}
|
|
1171
1421
|
|
|
1172
1422
|
/**
|
|
1173
|
-
*
|
|
1423
|
+
* Creates a new interval and add it to the collection.
|
|
1174
1424
|
* @param start - interval start position
|
|
1175
1425
|
* @param end - interval end position
|
|
1176
1426
|
* @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
|
|
@@ -1182,8 +1432,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1182
1432
|
end: number,
|
|
1183
1433
|
intervalType: IntervalType,
|
|
1184
1434
|
props?: PropertySet,
|
|
1185
|
-
) {
|
|
1186
|
-
if (!this.
|
|
1435
|
+
): TInterval {
|
|
1436
|
+
if (!this.localCollection) {
|
|
1187
1437
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
1188
1438
|
}
|
|
1189
1439
|
if (intervalType & IntervalType.Transient) {
|
|
@@ -1210,6 +1460,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1210
1460
|
}
|
|
1211
1461
|
|
|
1212
1462
|
private deleteExistingInterval(interval: TInterval, local: boolean, op?: ISequencedDocumentMessage) {
|
|
1463
|
+
if (!this.localCollection) {
|
|
1464
|
+
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1465
|
+
}
|
|
1213
1466
|
// The given interval is known to exist in the collection.
|
|
1214
1467
|
this.localCollection.removeExistingInterval(interval);
|
|
1215
1468
|
|
|
@@ -1219,7 +1472,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1219
1472
|
this.emitter.emit(
|
|
1220
1473
|
"delete",
|
|
1221
1474
|
undefined,
|
|
1222
|
-
interval.serialize(
|
|
1475
|
+
interval.serialize(),
|
|
1223
1476
|
{ localSeq: this.getNextLocalSeq() },
|
|
1224
1477
|
);
|
|
1225
1478
|
} else {
|
|
@@ -1232,7 +1485,15 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1232
1485
|
this.emit("deleteInterval", interval, local, op);
|
|
1233
1486
|
}
|
|
1234
1487
|
|
|
1488
|
+
/**
|
|
1489
|
+
* Removes an interval from the collection.
|
|
1490
|
+
* @param id - Id of the interval to remove
|
|
1491
|
+
* @returns the removed interval
|
|
1492
|
+
*/
|
|
1235
1493
|
public removeIntervalById(id: string) {
|
|
1494
|
+
if (!this.localCollection) {
|
|
1495
|
+
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1496
|
+
}
|
|
1236
1497
|
const interval = this.localCollection.getIntervalById(id);
|
|
1237
1498
|
if (interval) {
|
|
1238
1499
|
this.deleteExistingInterval(interval, true, undefined);
|
|
@@ -1240,6 +1501,12 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1240
1501
|
return interval;
|
|
1241
1502
|
}
|
|
1242
1503
|
|
|
1504
|
+
/**
|
|
1505
|
+
* Changes the properties on an existing interval.
|
|
1506
|
+
* @param id - Id of the interval whose properties should be changed
|
|
1507
|
+
* @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
|
|
1508
|
+
* and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
|
|
1509
|
+
*/
|
|
1243
1510
|
public changeProperties(id: string, props: PropertySet) {
|
|
1244
1511
|
if (!this.attached) {
|
|
1245
1512
|
throw new LoggingError("Attach must be called before accessing intervals");
|
|
@@ -1255,7 +1522,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1255
1522
|
if (interval) {
|
|
1256
1523
|
// Pass Unassigned as the sequence number to indicate that this is a local op that is waiting for an ack.
|
|
1257
1524
|
const deltaProps = interval.addProperties(props, true, UnassignedSequenceNumber);
|
|
1258
|
-
const serializedInterval: ISerializedInterval = interval.serialize(
|
|
1525
|
+
const serializedInterval: ISerializedInterval = interval.serialize();
|
|
1259
1526
|
|
|
1260
1527
|
// Emit a change op that will only change properties. Add the ID to
|
|
1261
1528
|
// the property bag provided by the caller.
|
|
@@ -1265,13 +1532,19 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1265
1532
|
serializedInterval.properties = props;
|
|
1266
1533
|
serializedInterval.properties[reservedIntervalIdKey] = interval.getIntervalId();
|
|
1267
1534
|
this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
|
|
1268
|
-
this.emit("propertyChanged", interval, deltaProps);
|
|
1535
|
+
this.emit("propertyChanged", interval, deltaProps, true, undefined);
|
|
1269
1536
|
}
|
|
1270
|
-
this.emit("changeInterval", interval, true, undefined);
|
|
1271
1537
|
}
|
|
1272
1538
|
|
|
1539
|
+
/**
|
|
1540
|
+
* Changes the endpoints of an existing interval.
|
|
1541
|
+
* @param id - Id of the interval to change
|
|
1542
|
+
* @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1543
|
+
* @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1544
|
+
* @returns the interval that was changed, if it existed in the collection.
|
|
1545
|
+
*/
|
|
1273
1546
|
public change(id: string, start?: number, end?: number): TInterval | undefined {
|
|
1274
|
-
if (!this.
|
|
1547
|
+
if (!this.localCollection) {
|
|
1275
1548
|
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1276
1549
|
}
|
|
1277
1550
|
|
|
@@ -1283,7 +1556,10 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1283
1556
|
const interval = this.getIntervalById(id);
|
|
1284
1557
|
if (interval) {
|
|
1285
1558
|
const newInterval = this.localCollection.changeInterval(interval, start, end);
|
|
1286
|
-
|
|
1559
|
+
if (!newInterval) {
|
|
1560
|
+
return undefined;
|
|
1561
|
+
}
|
|
1562
|
+
const serializedInterval: SerializedIntervalDelta = interval.serialize();
|
|
1287
1563
|
serializedInterval.start = start;
|
|
1288
1564
|
serializedInterval.end = end;
|
|
1289
1565
|
// Emit a property bag containing only the ID, as we don't intend for this op to change any properties.
|
|
@@ -1293,14 +1569,14 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1293
1569
|
};
|
|
1294
1570
|
this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
|
|
1295
1571
|
this.addPendingChange(id, serializedInterval);
|
|
1296
|
-
this.
|
|
1572
|
+
this.emitChange(newInterval, interval, true);
|
|
1297
1573
|
return newInterval;
|
|
1298
1574
|
}
|
|
1299
1575
|
// No interval to change
|
|
1300
1576
|
return undefined;
|
|
1301
1577
|
}
|
|
1302
1578
|
|
|
1303
|
-
private addPendingChange(id: string, serializedInterval:
|
|
1579
|
+
private addPendingChange(id: string, serializedInterval: SerializedIntervalDelta) {
|
|
1304
1580
|
if (serializedInterval.start !== undefined) {
|
|
1305
1581
|
this.addPendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
1306
1582
|
}
|
|
@@ -1311,10 +1587,10 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1311
1587
|
|
|
1312
1588
|
private addPendingChangeHelper(
|
|
1313
1589
|
id: string,
|
|
1314
|
-
pendingChanges: Map<string,
|
|
1315
|
-
serializedInterval:
|
|
1590
|
+
pendingChanges: Map<string, SerializedIntervalDelta[]>,
|
|
1591
|
+
serializedInterval: SerializedIntervalDelta,
|
|
1316
1592
|
) {
|
|
1317
|
-
let entries:
|
|
1593
|
+
let entries: SerializedIntervalDelta[] | undefined = pendingChanges.get(id);
|
|
1318
1594
|
if (!entries) {
|
|
1319
1595
|
entries = [];
|
|
1320
1596
|
pendingChanges.set(id, entries);
|
|
@@ -1322,7 +1598,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1322
1598
|
entries.push(serializedInterval);
|
|
1323
1599
|
}
|
|
1324
1600
|
|
|
1325
|
-
private removePendingChange(serializedInterval:
|
|
1601
|
+
private removePendingChange(serializedInterval: SerializedIntervalDelta) {
|
|
1326
1602
|
// Change ops always have an ID.
|
|
1327
1603
|
const id: string = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
1328
1604
|
if (serializedInterval.start !== undefined) {
|
|
@@ -1335,8 +1611,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1335
1611
|
|
|
1336
1612
|
private removePendingChangeHelper(
|
|
1337
1613
|
id: string,
|
|
1338
|
-
pendingChanges: Map<string,
|
|
1339
|
-
serializedInterval:
|
|
1614
|
+
pendingChanges: Map<string, SerializedIntervalDelta[]>,
|
|
1615
|
+
serializedInterval: SerializedIntervalDelta,
|
|
1340
1616
|
) {
|
|
1341
1617
|
const entries = pendingChanges.get(id);
|
|
1342
1618
|
if (entries) {
|
|
@@ -1363,64 +1639,72 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1363
1639
|
|
|
1364
1640
|
/** @internal */
|
|
1365
1641
|
public ackChange(serializedInterval: ISerializedInterval, local: boolean, op: ISequencedDocumentMessage) {
|
|
1366
|
-
if (!this.
|
|
1642
|
+
if (!this.localCollection) {
|
|
1367
1643
|
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1368
1644
|
}
|
|
1369
1645
|
|
|
1370
|
-
let interval: TInterval | undefined;
|
|
1371
|
-
|
|
1372
1646
|
if (local) {
|
|
1373
1647
|
// This is an ack from the server. Remove the pending change.
|
|
1374
1648
|
this.removePendingChange(serializedInterval);
|
|
1375
|
-
|
|
1376
|
-
interval = this.getIntervalById(id);
|
|
1377
|
-
if (interval) {
|
|
1378
|
-
// Let the propertyManager prune its pending change-properties set.
|
|
1379
|
-
interval.propertyManager?.ackPendingProperties(
|
|
1380
|
-
{
|
|
1381
|
-
type: MergeTreeDeltaType.ANNOTATE,
|
|
1382
|
-
props: serializedInterval.properties ?? {},
|
|
1383
|
-
});
|
|
1649
|
+
}
|
|
1384
1650
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1651
|
+
// Note that the ID is in the property bag only to allow us to find the interval.
|
|
1652
|
+
// This API cannot change the ID, and writing to the ID property will result in an exception. So we
|
|
1653
|
+
// strip it out of the properties here.
|
|
1654
|
+
const { [reservedIntervalIdKey]: id, ...newProps } = serializedInterval.properties ?? {};
|
|
1655
|
+
assert(id !== undefined, 0x3fe /* id must exist on the interval */);
|
|
1656
|
+
const interval: TInterval | undefined = this.getIntervalById(id);
|
|
1657
|
+
if (!interval) {
|
|
1658
|
+
// The interval has been removed locally; no-op.
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
if (local) {
|
|
1663
|
+
// Let the propertyManager prune its pending change-properties set.
|
|
1664
|
+
interval.propertyManager?.ackPendingProperties(
|
|
1665
|
+
{
|
|
1666
|
+
type: MergeTreeDeltaType.ANNOTATE,
|
|
1667
|
+
props: serializedInterval.properties ?? {},
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
this.ackInterval(interval, op);
|
|
1387
1671
|
} else {
|
|
1388
1672
|
// If there are pending changes with this ID, don't apply the remote start/end change, as the local ack
|
|
1389
1673
|
// should be the winning change.
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
//
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1674
|
+
let start: number | undefined;
|
|
1675
|
+
let end: number | undefined;
|
|
1676
|
+
// Track pending start/end independently of one another.
|
|
1677
|
+
if (!this.hasPendingChangeStart(id)) {
|
|
1678
|
+
start = serializedInterval.start;
|
|
1679
|
+
}
|
|
1680
|
+
if (!this.hasPendingChangeEnd(id)) {
|
|
1681
|
+
end = serializedInterval.end;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
let newInterval = interval;
|
|
1685
|
+
if (start !== undefined || end !== undefined) {
|
|
1686
|
+
// If changeInterval gives us a new interval, work with that one. Otherwise keep working with
|
|
1687
|
+
// the one we originally found in the tree.
|
|
1688
|
+
newInterval = this.localCollection.changeInterval(interval, start, end, op) ?? interval;
|
|
1689
|
+
}
|
|
1690
|
+
const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
|
|
1691
|
+
if (this.onDeserialize) {
|
|
1692
|
+
this.onDeserialize(newInterval);
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
if (newInterval !== interval) {
|
|
1696
|
+
this.emitChange(newInterval, interval, local, op);
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
const changedProperties = Object.keys(newProps).length > 0;
|
|
1700
|
+
if (changedProperties) {
|
|
1701
|
+
this.emit("propertyChanged", interval, deltaProps, local, op);
|
|
1415
1702
|
}
|
|
1416
|
-
}
|
|
1417
|
-
if (interval) {
|
|
1418
|
-
this.emit("changeInterval", interval, local, op);
|
|
1419
1703
|
}
|
|
1420
1704
|
}
|
|
1421
1705
|
|
|
1422
1706
|
public addConflictResolver(conflictResolver: IntervalConflictResolver<TInterval>): void {
|
|
1423
|
-
if (!this.
|
|
1707
|
+
if (!this.localCollection) {
|
|
1424
1708
|
throw new LoggingError("attachSequence must be called");
|
|
1425
1709
|
}
|
|
1426
1710
|
this.localCollection.addConflictResolver(conflictResolver);
|
|
@@ -1436,7 +1720,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1436
1720
|
this.onDeserialize = onDeserialize;
|
|
1437
1721
|
|
|
1438
1722
|
// Trigger the async prepare work across all values in the collection
|
|
1439
|
-
this.localCollection
|
|
1723
|
+
this.localCollection?.map((interval) => {
|
|
1440
1724
|
onDeserialize(interval);
|
|
1441
1725
|
});
|
|
1442
1726
|
}
|
|
@@ -1450,9 +1734,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1450
1734
|
*/
|
|
1451
1735
|
public rebaseLocalInterval(
|
|
1452
1736
|
opName: string,
|
|
1453
|
-
serializedInterval:
|
|
1737
|
+
serializedInterval: SerializedIntervalDelta,
|
|
1454
1738
|
localSeq: number,
|
|
1455
|
-
):
|
|
1739
|
+
): SerializedIntervalDelta | undefined {
|
|
1456
1740
|
if (!this.client) {
|
|
1457
1741
|
// If there's no associated mergeTree client, the originally submitted op is still correct.
|
|
1458
1742
|
return serializedInterval;
|
|
@@ -1468,9 +1752,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1468
1752
|
this.client.rebasePosition(end, sequenceNumber, localSeq);
|
|
1469
1753
|
|
|
1470
1754
|
const intervalId = properties?.[reservedIntervalIdKey];
|
|
1471
|
-
const localInterval = this.localCollection
|
|
1755
|
+
const localInterval = this.localCollection?.getIntervalById(intervalId);
|
|
1472
1756
|
|
|
1473
|
-
const rebased:
|
|
1757
|
+
const rebased: SerializedIntervalDelta = {
|
|
1474
1758
|
start: startRebased,
|
|
1475
1759
|
end: endRebased,
|
|
1476
1760
|
intervalType,
|
|
@@ -1487,7 +1771,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1487
1771
|
// delete the interval
|
|
1488
1772
|
if (startRebased === DetachedReferencePosition || endRebased === DetachedReferencePosition) {
|
|
1489
1773
|
if (localInterval) {
|
|
1490
|
-
this.localCollection
|
|
1774
|
+
this.localCollection?.removeExistingInterval(localInterval);
|
|
1491
1775
|
}
|
|
1492
1776
|
return undefined;
|
|
1493
1777
|
}
|
|
@@ -1508,18 +1792,24 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1508
1792
|
// we need to slide because the reference has been removed
|
|
1509
1793
|
if (startSegment || endSegment) {
|
|
1510
1794
|
const newStart =
|
|
1511
|
-
startSegment && this.client.getPosition(startSegment.segment, localSeq) + startSegment.offset;
|
|
1795
|
+
startSegment && this.client.getPosition(startSegment.segment, localSeq) + (startSegment.offset ?? 0);
|
|
1512
1796
|
const newEnd =
|
|
1513
|
-
endSegment && this.client.getPosition(endSegment.segment, localSeq) + endSegment.offset;
|
|
1797
|
+
endSegment && this.client.getPosition(endSegment.segment, localSeq) + (endSegment.offset ?? 0);
|
|
1514
1798
|
|
|
1515
|
-
this.localCollection
|
|
1799
|
+
this.localCollection?.changeInterval(localInterval, newStart, newEnd, undefined, localSeq);
|
|
1516
1800
|
}
|
|
1517
1801
|
|
|
1518
1802
|
return rebased;
|
|
1519
1803
|
}
|
|
1520
1804
|
|
|
1521
1805
|
private getSlideToSegment(lref: LocalReferencePosition) {
|
|
1806
|
+
if (!this.client) {
|
|
1807
|
+
throw new LoggingError("client does not exist");
|
|
1808
|
+
}
|
|
1522
1809
|
const segoff = { segment: lref.getSegment(), offset: lref.getOffset() };
|
|
1810
|
+
if (segoff.segment?.localRefs?.has(lref) !== true) {
|
|
1811
|
+
return undefined;
|
|
1812
|
+
}
|
|
1523
1813
|
const newSegoff = this.client.getSlideToSegment(segoff);
|
|
1524
1814
|
const value: { segment: ISegment | undefined; offset: number | undefined; } | undefined
|
|
1525
1815
|
= (segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset) ? undefined : newSegoff;
|
|
@@ -1563,28 +1853,51 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1563
1853
|
const needsEndUpdate = newEnd !== undefined && !hasPendingEndChange;
|
|
1564
1854
|
|
|
1565
1855
|
if (needsStartUpdate || needsEndUpdate) {
|
|
1856
|
+
if (!this.localCollection) {
|
|
1857
|
+
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
// `interval`'s endpoints will get modified in-place, so clone it prior to doing so for event emission.
|
|
1861
|
+
const oldInterval = interval.clone() as TInterval & SequenceInterval;
|
|
1862
|
+
|
|
1566
1863
|
// In this case, where we change the start or end of an interval,
|
|
1567
1864
|
// it is necessary to remove and re-add the interval listeners.
|
|
1568
1865
|
// This ensures that the correct listeners are added to the LocalReferencePosition.
|
|
1569
1866
|
this.localCollection.removeExistingInterval(interval);
|
|
1867
|
+
if (!this.client) {
|
|
1868
|
+
throw new LoggingError("client does not exist");
|
|
1869
|
+
}
|
|
1570
1870
|
|
|
1571
1871
|
if (needsStartUpdate) {
|
|
1572
1872
|
const props = interval.start.properties;
|
|
1573
|
-
this.client.removeLocalReferencePosition(interval.start);
|
|
1574
1873
|
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op);
|
|
1575
1874
|
if (props) {
|
|
1576
1875
|
interval.start.addProperties(props);
|
|
1577
1876
|
}
|
|
1877
|
+
const oldSeg = oldInterval.start.getSegment();
|
|
1878
|
+
// remove and rebuild start interval as transient for event
|
|
1879
|
+
this.client.removeLocalReferencePosition(oldInterval.start);
|
|
1880
|
+
oldInterval.start.refType = ReferenceType.Transient;
|
|
1881
|
+
oldSeg?.localRefs?.addLocalRef(
|
|
1882
|
+
oldInterval.start,
|
|
1883
|
+
oldInterval.start.getOffset());
|
|
1578
1884
|
}
|
|
1579
1885
|
if (needsEndUpdate) {
|
|
1580
1886
|
const props = interval.end.properties;
|
|
1581
|
-
this.client.removeLocalReferencePosition(interval.end);
|
|
1582
1887
|
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op);
|
|
1583
1888
|
if (props) {
|
|
1584
1889
|
interval.end.addProperties(props);
|
|
1585
1890
|
}
|
|
1891
|
+
// remove and rebuild end interval as transient for event
|
|
1892
|
+
const oldSeg = oldInterval.end.getSegment();
|
|
1893
|
+
this.client.removeLocalReferencePosition(oldInterval.end);
|
|
1894
|
+
oldInterval.end.refType = ReferenceType.Transient;
|
|
1895
|
+
oldSeg?.localRefs?.addLocalRef(
|
|
1896
|
+
oldInterval.end,
|
|
1897
|
+
oldInterval.end.getOffset());
|
|
1586
1898
|
}
|
|
1587
1899
|
this.localCollection.add(interval);
|
|
1900
|
+
this.emitChange(interval, oldInterval as TInterval, true, op);
|
|
1588
1901
|
}
|
|
1589
1902
|
}
|
|
1590
1903
|
|
|
@@ -1602,7 +1915,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1602
1915
|
return;
|
|
1603
1916
|
}
|
|
1604
1917
|
|
|
1605
|
-
if (!this.
|
|
1918
|
+
if (!this.localCollection) {
|
|
1606
1919
|
throw new LoggingError("attachSequence must be called");
|
|
1607
1920
|
}
|
|
1608
1921
|
|
|
@@ -1638,7 +1951,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1638
1951
|
return;
|
|
1639
1952
|
}
|
|
1640
1953
|
|
|
1641
|
-
if (!this.
|
|
1954
|
+
if (!this.localCollection) {
|
|
1642
1955
|
throw new LoggingError("attach must be called prior to deleting intervals");
|
|
1643
1956
|
}
|
|
1644
1957
|
|
|
@@ -1653,76 +1966,106 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1653
1966
|
* @internal
|
|
1654
1967
|
*/
|
|
1655
1968
|
public serializeInternal(): ISerializedIntervalCollectionV2 {
|
|
1656
|
-
if (!this.
|
|
1969
|
+
if (!this.localCollection) {
|
|
1657
1970
|
throw new LoggingError("attachSequence must be called");
|
|
1658
1971
|
}
|
|
1659
1972
|
|
|
1660
1973
|
return this.localCollection.serialize();
|
|
1661
1974
|
}
|
|
1662
1975
|
|
|
1976
|
+
/**
|
|
1977
|
+
* @returns an iterator over all intervals in this collection.
|
|
1978
|
+
*/
|
|
1663
1979
|
public [Symbol.iterator](): IntervalCollectionIterator<TInterval> {
|
|
1664
1980
|
const iterator = new IntervalCollectionIterator<TInterval>(this);
|
|
1665
1981
|
return iterator;
|
|
1666
1982
|
}
|
|
1667
1983
|
|
|
1984
|
+
/**
|
|
1985
|
+
* @returns a forward iterator over all intervals in this collection with start point equal to `startPosition`.
|
|
1986
|
+
*/
|
|
1668
1987
|
public CreateForwardIteratorWithStartPosition(startPosition: number): IntervalCollectionIterator<TInterval> {
|
|
1669
1988
|
const iterator = new IntervalCollectionIterator<TInterval>(this, true, startPosition);
|
|
1670
1989
|
return iterator;
|
|
1671
1990
|
}
|
|
1672
1991
|
|
|
1992
|
+
/**
|
|
1993
|
+
* @returns a backward iterator over all intervals in this collection with start point equal to `startPosition`.
|
|
1994
|
+
*/
|
|
1673
1995
|
public CreateBackwardIteratorWithStartPosition(startPosition: number): IntervalCollectionIterator<TInterval> {
|
|
1674
1996
|
const iterator = new IntervalCollectionIterator<TInterval>(this, false, startPosition);
|
|
1675
1997
|
return iterator;
|
|
1676
1998
|
}
|
|
1677
1999
|
|
|
2000
|
+
/**
|
|
2001
|
+
* @returns a forward iterator over all intervals in this collection with end point equal to `endPosition`.
|
|
2002
|
+
*/
|
|
1678
2003
|
public CreateForwardIteratorWithEndPosition(endPosition: number): IntervalCollectionIterator<TInterval> {
|
|
1679
2004
|
const iterator = new IntervalCollectionIterator<TInterval>(this, true, undefined, endPosition);
|
|
1680
2005
|
return iterator;
|
|
1681
2006
|
}
|
|
1682
2007
|
|
|
2008
|
+
/**
|
|
2009
|
+
* @returns a backward iterator over all intervals in this collection with end point equal to `endPosition`.
|
|
2010
|
+
*/
|
|
1683
2011
|
public CreateBackwardIteratorWithEndPosition(endPosition: number): IntervalCollectionIterator<TInterval> {
|
|
1684
2012
|
const iterator = new IntervalCollectionIterator<TInterval>(this, false, undefined, endPosition);
|
|
1685
2013
|
return iterator;
|
|
1686
2014
|
}
|
|
1687
2015
|
|
|
2016
|
+
/**
|
|
2017
|
+
* Gathers iteration results that optionally match a start/end criteria into the provided array.
|
|
2018
|
+
* @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
|
|
2019
|
+
* intervals matching the query upon edit.
|
|
2020
|
+
* @param iteratesForward - whether or not iteration should be in the forward direction
|
|
2021
|
+
* @param start - If provided, only match intervals whose start point is equal to `start`.
|
|
2022
|
+
* @param end - If provided, only match intervals whose end point is equal to `end`.
|
|
2023
|
+
*/
|
|
1688
2024
|
public gatherIterationResults(
|
|
1689
2025
|
results: TInterval[],
|
|
1690
2026
|
iteratesForward: boolean,
|
|
1691
2027
|
start?: number,
|
|
1692
2028
|
end?: number) {
|
|
1693
|
-
if (!this.
|
|
2029
|
+
if (!this.localCollection) {
|
|
1694
2030
|
return;
|
|
1695
2031
|
}
|
|
1696
2032
|
|
|
1697
2033
|
this.localCollection.gatherIterationResults(results, iteratesForward, start, end);
|
|
1698
2034
|
}
|
|
1699
2035
|
|
|
2036
|
+
/**
|
|
2037
|
+
* @returns an array of all intervals in this collection that overlap with the interval
|
|
2038
|
+
* `[startPosition, endPosition]`.
|
|
2039
|
+
*/
|
|
1700
2040
|
public findOverlappingIntervals(startPosition: number, endPosition: number): TInterval[] {
|
|
1701
|
-
if (!this.
|
|
2041
|
+
if (!this.localCollection) {
|
|
1702
2042
|
throw new LoggingError("attachSequence must be called");
|
|
1703
2043
|
}
|
|
1704
2044
|
|
|
1705
2045
|
return this.localCollection.findOverlappingIntervals(startPosition, endPosition);
|
|
1706
2046
|
}
|
|
1707
2047
|
|
|
2048
|
+
/**
|
|
2049
|
+
* Applies a function to each interval in this collection.
|
|
2050
|
+
*/
|
|
1708
2051
|
public map(fn: (interval: TInterval) => void) {
|
|
1709
|
-
if (!this.
|
|
2052
|
+
if (!this.localCollection) {
|
|
1710
2053
|
throw new LoggingError("attachSequence must be called");
|
|
1711
2054
|
}
|
|
1712
2055
|
|
|
1713
2056
|
this.localCollection.map(fn);
|
|
1714
2057
|
}
|
|
1715
2058
|
|
|
1716
|
-
public previousInterval(pos: number): TInterval {
|
|
1717
|
-
if (!this.
|
|
2059
|
+
public previousInterval(pos: number): TInterval | undefined {
|
|
2060
|
+
if (!this.localCollection) {
|
|
1718
2061
|
throw new LoggingError("attachSequence must be called");
|
|
1719
2062
|
}
|
|
1720
2063
|
|
|
1721
2064
|
return this.localCollection.previousInterval(pos);
|
|
1722
2065
|
}
|
|
1723
2066
|
|
|
1724
|
-
public nextInterval(pos: number): TInterval {
|
|
1725
|
-
if (!this.
|
|
2067
|
+
public nextInterval(pos: number): TInterval | undefined {
|
|
2068
|
+
if (!this.localCollection) {
|
|
1726
2069
|
throw new LoggingError("attachSequence must be called");
|
|
1727
2070
|
}
|
|
1728
2071
|
|