@fluidframework/sequence 1.2.7 → 2.0.0-dev.1.3.0.96595
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/.mocharc.js +12 -0
- package/README.md +19 -18
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +39 -17
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +108 -67
- package/dist/intervalCollection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/sequence.d.ts +13 -22
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +11 -32
- package/dist/sequence.js.map +1 -1
- package/dist/sequenceDeltaEvent.d.ts +0 -6
- package/dist/sequenceDeltaEvent.d.ts.map +1 -1
- package/dist/sequenceDeltaEvent.js +0 -1
- package/dist/sequenceDeltaEvent.js.map +1 -1
- package/dist/sharedIntervalCollection.d.ts +5 -5
- package/dist/sharedIntervalCollection.js +5 -5
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/dist/sharedString.d.ts +30 -1
- package/dist/sharedString.d.ts.map +1 -1
- package/dist/sharedString.js +40 -5
- package/dist/sharedString.js.map +1 -1
- package/dist/sparsematrix.d.ts +28 -15
- package/dist/sparsematrix.d.ts.map +1 -1
- package/dist/sparsematrix.js +24 -13
- package/dist/sparsematrix.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +39 -17
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +107 -67
- package/lib/intervalCollection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/sequence.d.ts +13 -22
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +12 -33
- package/lib/sequence.js.map +1 -1
- package/lib/sequenceDeltaEvent.d.ts +0 -6
- package/lib/sequenceDeltaEvent.d.ts.map +1 -1
- package/lib/sequenceDeltaEvent.js +0 -1
- package/lib/sequenceDeltaEvent.js.map +1 -1
- package/lib/sharedIntervalCollection.d.ts +5 -5
- package/lib/sharedIntervalCollection.js +5 -5
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/lib/sharedString.d.ts +30 -1
- package/lib/sharedString.d.ts.map +1 -1
- package/lib/sharedString.js +38 -4
- package/lib/sharedString.js.map +1 -1
- package/lib/sparsematrix.d.ts +28 -15
- package/lib/sparsematrix.d.ts.map +1 -1
- package/lib/sparsematrix.js +24 -13
- package/lib/sparsematrix.js.map +1 -1
- package/package.json +70 -25
- package/src/index.ts +3 -1
- package/src/intervalCollection.ts +169 -85
- package/src/packageVersion.ts +1 -1
- package/src/sequence.ts +12 -41
- package/src/sequenceDeltaEvent.ts +0 -7
- package/src/sharedIntervalCollection.ts +5 -5
- package/src/sharedString.ts +44 -6
- package/src/sparsematrix.ts +48 -35
|
@@ -11,6 +11,7 @@ import { UsageError } from "@fluidframework/container-utils";
|
|
|
11
11
|
import {
|
|
12
12
|
addProperties,
|
|
13
13
|
Client,
|
|
14
|
+
compareReferencePositions,
|
|
14
15
|
ConflictAction,
|
|
15
16
|
createMap,
|
|
16
17
|
ICombiningOp,
|
|
@@ -19,15 +20,19 @@ import {
|
|
|
19
20
|
IntervalNode,
|
|
20
21
|
IntervalTree,
|
|
21
22
|
ISegment,
|
|
22
|
-
LocalReference,
|
|
23
23
|
MergeTreeDeltaType,
|
|
24
|
+
minReferencePosition,
|
|
24
25
|
PropertiesManager,
|
|
25
26
|
PropertySet,
|
|
26
27
|
RedBlackTree,
|
|
28
|
+
LocalReferencePosition,
|
|
27
29
|
ReferenceType,
|
|
28
30
|
refTypeIncludesFlag,
|
|
29
31
|
reservedRangeLabelsKey,
|
|
30
32
|
UnassignedSequenceNumber,
|
|
33
|
+
maxReferencePosition,
|
|
34
|
+
createDetachedLocalReferencePosition,
|
|
35
|
+
DetachedReferencePosition,
|
|
31
36
|
} from "@fluidframework/merge-tree";
|
|
32
37
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
33
38
|
import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
@@ -46,6 +51,7 @@ const reservedIntervalIdKey = "intervalId";
|
|
|
46
51
|
export enum IntervalType {
|
|
47
52
|
Simple = 0x0,
|
|
48
53
|
Nest = 0x1,
|
|
54
|
+
|
|
49
55
|
/**
|
|
50
56
|
* SlideOnRemove indicates that the ends of the interval will slide if the segment
|
|
51
57
|
* they reference is removed and acked.
|
|
@@ -53,9 +59,10 @@ export enum IntervalType {
|
|
|
53
59
|
* SlideOnRemove is the default interval behavior and does not need to be specified.
|
|
54
60
|
*/
|
|
55
61
|
SlideOnRemove = 0x2, // SlideOnRemove is default behavior - all intervals are SlideOnRemove
|
|
62
|
+
|
|
56
63
|
/**
|
|
57
|
-
* @internal
|
|
58
64
|
* A temporary interval, used internally
|
|
65
|
+
* @internal
|
|
59
66
|
*/
|
|
60
67
|
Transient = 0x4,
|
|
61
68
|
}
|
|
@@ -95,7 +102,7 @@ function decompressInterval(interval: CompressedSerializedInterval, label?: stri
|
|
|
95
102
|
end: interval[1],
|
|
96
103
|
sequenceNumber: interval[2],
|
|
97
104
|
intervalType: interval[3],
|
|
98
|
-
properties: { ...interval[4], [reservedRangeLabelsKey]: label },
|
|
105
|
+
properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
|
|
99
106
|
};
|
|
100
107
|
}
|
|
101
108
|
|
|
@@ -187,11 +194,7 @@ export class Interval implements ISerializableInterval {
|
|
|
187
194
|
}
|
|
188
195
|
|
|
189
196
|
public serialize(client: Client): ISerializedInterval {
|
|
190
|
-
|
|
191
|
-
if (client) {
|
|
192
|
-
seq = client.getCurrentSeq();
|
|
193
|
-
}
|
|
194
|
-
|
|
197
|
+
const seq = client?.getCurrentSeq() ?? 0;
|
|
195
198
|
const serializedInterval: ISerializedInterval = {
|
|
196
199
|
end: this.end,
|
|
197
200
|
intervalType: 0,
|
|
@@ -295,8 +298,9 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
295
298
|
public propertyManager: PropertiesManager;
|
|
296
299
|
|
|
297
300
|
constructor(
|
|
298
|
-
|
|
299
|
-
public
|
|
301
|
+
private readonly client: Client,
|
|
302
|
+
public start: LocalReferencePosition,
|
|
303
|
+
public end: LocalReferencePosition,
|
|
300
304
|
public intervalType: IntervalType,
|
|
301
305
|
props?: PropertySet,
|
|
302
306
|
) {
|
|
@@ -311,8 +315,8 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
311
315
|
private callbacks?: Record<"beforePositionChange" | "afterPositionChange", () => void>;
|
|
312
316
|
|
|
313
317
|
/**
|
|
314
|
-
* @internal
|
|
315
318
|
* Subscribes to position change events on this interval if there are no current listeners.
|
|
319
|
+
* @internal
|
|
316
320
|
*/
|
|
317
321
|
public addPositionChangeListeners(beforePositionChange: () => void, afterPositionChange: () => void): void {
|
|
318
322
|
if (this.callbacks === undefined) {
|
|
@@ -329,8 +333,8 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
329
333
|
}
|
|
330
334
|
|
|
331
335
|
/**
|
|
332
|
-
* @internal
|
|
333
336
|
* Removes the currently subscribed position change listeners.
|
|
337
|
+
* @internal
|
|
334
338
|
*/
|
|
335
339
|
public removePositionChangeListeners(): void {
|
|
336
340
|
if (this.callbacks) {
|
|
@@ -341,8 +345,8 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
341
345
|
}
|
|
342
346
|
|
|
343
347
|
public serialize(client: Client): ISerializedInterval {
|
|
344
|
-
const startPosition = this.start
|
|
345
|
-
const endPosition = this.end
|
|
348
|
+
const startPosition = client.localReferencePositionToPosition(this.start);
|
|
349
|
+
const endPosition = client.localReferencePositionToPosition(this.end);
|
|
346
350
|
const serializedInterval: ISerializedInterval = {
|
|
347
351
|
end: endPosition,
|
|
348
352
|
intervalType: this.intervalType,
|
|
@@ -358,7 +362,7 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
358
362
|
}
|
|
359
363
|
|
|
360
364
|
public clone() {
|
|
361
|
-
return new SequenceInterval(this.start, this.end, this.intervalType, this.properties);
|
|
365
|
+
return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties);
|
|
362
366
|
}
|
|
363
367
|
|
|
364
368
|
public compare(b: SequenceInterval) {
|
|
@@ -384,16 +388,16 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
384
388
|
}
|
|
385
389
|
|
|
386
390
|
public compareStart(b: SequenceInterval) {
|
|
387
|
-
return this.start
|
|
391
|
+
return compareReferencePositions(this.start, b.start);
|
|
388
392
|
}
|
|
389
393
|
|
|
390
394
|
public compareEnd(b: SequenceInterval) {
|
|
391
|
-
return this.end
|
|
395
|
+
return compareReferencePositions(this.end, b.end);
|
|
392
396
|
}
|
|
393
397
|
|
|
394
398
|
public overlaps(b: SequenceInterval) {
|
|
395
|
-
const result = (this.start
|
|
396
|
-
(this.end
|
|
399
|
+
const result = (compareReferencePositions(this.start, b.end) <= 0) &&
|
|
400
|
+
(compareReferencePositions(this.end, b.start) >= 0);
|
|
397
401
|
return result;
|
|
398
402
|
}
|
|
399
403
|
|
|
@@ -406,8 +410,8 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
406
410
|
}
|
|
407
411
|
|
|
408
412
|
public union(b: SequenceInterval) {
|
|
409
|
-
return new SequenceInterval(this.start
|
|
410
|
-
this.end
|
|
413
|
+
return new SequenceInterval(this.client, minReferencePosition(this.start, b.start),
|
|
414
|
+
maxReferencePosition(this.end, b.end), this.intervalType);
|
|
411
415
|
}
|
|
412
416
|
|
|
413
417
|
public addProperties(
|
|
@@ -421,12 +425,12 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
421
425
|
}
|
|
422
426
|
|
|
423
427
|
public overlapsPos(bstart: number, bend: number) {
|
|
424
|
-
const startPos = this.
|
|
425
|
-
const endPos = this.
|
|
428
|
+
const startPos = this.client.localReferencePositionToPosition(this.start);
|
|
429
|
+
const endPos = this.client.localReferencePositionToPosition(this.end);
|
|
426
430
|
return (endPos > bstart) && (startPos < bend);
|
|
427
431
|
}
|
|
428
432
|
|
|
429
|
-
public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage) {
|
|
433
|
+
public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage, localSeq?: number) {
|
|
430
434
|
const getRefType = (baseType: ReferenceType): ReferenceType => {
|
|
431
435
|
let refType = baseType;
|
|
432
436
|
if (op === undefined) {
|
|
@@ -438,20 +442,21 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
438
442
|
|
|
439
443
|
let startRef = this.start;
|
|
440
444
|
if (start !== undefined) {
|
|
441
|
-
startRef = createPositionReference(
|
|
445
|
+
startRef = createPositionReference(
|
|
446
|
+
this.client, start, getRefType(this.start.refType), op, undefined, localSeq,
|
|
447
|
+
);
|
|
442
448
|
startRef.addProperties(this.start.properties);
|
|
443
449
|
}
|
|
444
450
|
|
|
445
451
|
let endRef = this.end;
|
|
446
452
|
if (end !== undefined) {
|
|
447
|
-
endRef = createPositionReference(
|
|
453
|
+
endRef = createPositionReference(
|
|
454
|
+
this.client, end, getRefType(this.end.refType), op, undefined, localSeq,
|
|
455
|
+
);
|
|
448
456
|
endRef.addProperties(this.end.properties);
|
|
449
457
|
}
|
|
450
458
|
|
|
451
|
-
|
|
452
|
-
endRef.pairedRef = startRef;
|
|
453
|
-
|
|
454
|
-
const newInterval = new SequenceInterval(startRef, endRef, this.intervalType);
|
|
459
|
+
const newInterval = new SequenceInterval(this.client, startRef, endRef, this.intervalType);
|
|
455
460
|
if (this.properties) {
|
|
456
461
|
newInterval.initializeProperties();
|
|
457
462
|
this.propertyManager.copyTo(this.properties, newInterval.properties, newInterval.propertyManager);
|
|
@@ -473,16 +478,18 @@ function createPositionReferenceFromSegoff(
|
|
|
473
478
|
client: Client,
|
|
474
479
|
segoff: { segment: ISegment | undefined; offset: number | undefined; },
|
|
475
480
|
refType: ReferenceType,
|
|
476
|
-
op?: ISequencedDocumentMessage):
|
|
481
|
+
op?: ISequencedDocumentMessage): LocalReferencePosition {
|
|
477
482
|
if (segoff.segment) {
|
|
478
483
|
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
|
|
479
|
-
return ref
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
484
|
+
return ref;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (!op && !refTypeIncludesFlag(refType, ReferenceType.Transient)) {
|
|
488
|
+
// reference to segment that dne locally
|
|
489
|
+
throw new UsageError("Non-transient references need segment");
|
|
485
490
|
}
|
|
491
|
+
|
|
492
|
+
return createDetachedLocalReferencePosition(refType);
|
|
486
493
|
}
|
|
487
494
|
|
|
488
495
|
function createPositionReference(
|
|
@@ -490,7 +497,9 @@ function createPositionReference(
|
|
|
490
497
|
pos: number,
|
|
491
498
|
refType: ReferenceType,
|
|
492
499
|
op?: ISequencedDocumentMessage,
|
|
493
|
-
fromSnapshot?: boolean
|
|
500
|
+
fromSnapshot?: boolean,
|
|
501
|
+
localSeq?: number,
|
|
502
|
+
): LocalReferencePosition {
|
|
494
503
|
let segoff;
|
|
495
504
|
if (op) {
|
|
496
505
|
assert((refType & ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
|
|
@@ -499,7 +508,7 @@ function createPositionReference(
|
|
|
499
508
|
} else {
|
|
500
509
|
assert((refType & ReferenceType.SlideOnRemove) === 0 || fromSnapshot,
|
|
501
510
|
0x2f6 /* SlideOnRemove references must be op created */);
|
|
502
|
-
segoff = client.getContainingSegment(pos);
|
|
511
|
+
segoff = client.getContainingSegment(pos, undefined, localSeq);
|
|
503
512
|
}
|
|
504
513
|
return createPositionReferenceFromSegoff(client, segoff, refType, op);
|
|
505
514
|
}
|
|
@@ -536,15 +545,13 @@ function createSequenceInterval(
|
|
|
536
545
|
|
|
537
546
|
const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot);
|
|
538
547
|
const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot);
|
|
539
|
-
startLref.pairedRef = endLref;
|
|
540
|
-
endLref.pairedRef = startLref;
|
|
541
548
|
const rangeProp = {
|
|
542
549
|
[reservedRangeLabelsKey]: [label],
|
|
543
550
|
};
|
|
544
551
|
startLref.addProperties(rangeProp);
|
|
545
552
|
endLref.addProperties(rangeProp);
|
|
546
553
|
|
|
547
|
-
const ival = new SequenceInterval(startLref, endLref, intervalType, rangeProp);
|
|
554
|
+
const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp);
|
|
548
555
|
return ival;
|
|
549
556
|
}
|
|
550
557
|
|
|
@@ -793,18 +800,23 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
793
800
|
if (!interval.properties) {
|
|
794
801
|
interval.properties = createMap<any>();
|
|
795
802
|
}
|
|
803
|
+
|
|
796
804
|
if (props) {
|
|
797
805
|
interval.addProperties(props);
|
|
798
806
|
}
|
|
799
|
-
|
|
800
|
-
// Create a new ID.
|
|
801
|
-
interval.properties[reservedIntervalIdKey] = uuid();
|
|
802
|
-
}
|
|
807
|
+
interval.properties[reservedIntervalIdKey] ??= uuid();
|
|
803
808
|
this.add(interval);
|
|
804
809
|
}
|
|
805
810
|
return interval;
|
|
806
811
|
}
|
|
807
812
|
|
|
813
|
+
private linkEndpointsToInterval(interval: TInterval): void {
|
|
814
|
+
if (interval instanceof SequenceInterval) {
|
|
815
|
+
interval.start.addProperties({ interval });
|
|
816
|
+
interval.end.addProperties({ interval });
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
808
820
|
private addIntervalToIndex(interval: TInterval) {
|
|
809
821
|
const id = interval.getIntervalId();
|
|
810
822
|
assert(id !== undefined, 0x2c0 /* "ID must be created before adding interval to collection" */);
|
|
@@ -820,6 +832,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
820
832
|
}
|
|
821
833
|
|
|
822
834
|
public add(interval: TInterval) {
|
|
835
|
+
this.linkEndpointsToInterval(interval);
|
|
823
836
|
this.addIntervalToIndex(interval);
|
|
824
837
|
this.addIntervalListeners(interval);
|
|
825
838
|
}
|
|
@@ -828,8 +841,14 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
828
841
|
return this.intervalIdMap.get(id);
|
|
829
842
|
}
|
|
830
843
|
|
|
831
|
-
public changeInterval(
|
|
832
|
-
|
|
844
|
+
public changeInterval(
|
|
845
|
+
interval: TInterval,
|
|
846
|
+
start: number,
|
|
847
|
+
end: number,
|
|
848
|
+
op?: ISequencedDocumentMessage,
|
|
849
|
+
localSeq?: number,
|
|
850
|
+
) {
|
|
851
|
+
const newInterval = interval.modify(this.label, start, end, op, localSeq) as TInterval | undefined;
|
|
833
852
|
if (newInterval) {
|
|
834
853
|
this.removeExistingInterval(interval);
|
|
835
854
|
this.add(newInterval);
|
|
@@ -867,7 +886,8 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
867
886
|
}
|
|
868
887
|
}
|
|
869
888
|
|
|
870
|
-
const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
|
|
889
|
+
const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
|
|
890
|
+
compareReferencePositions(a.end, b.end);
|
|
871
891
|
|
|
872
892
|
class SequenceIntervalCollectionFactory
|
|
873
893
|
implements IValueFactory<IntervalCollection<SequenceInterval>> {
|
|
@@ -979,6 +999,11 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
|
|
|
979
999
|
"add",
|
|
980
1000
|
{
|
|
981
1001
|
process: (collection, params, local, op) => {
|
|
1002
|
+
// if params is undefined, the interval was deleted during
|
|
1003
|
+
// rebasing
|
|
1004
|
+
if (!params) {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
982
1007
|
collection.ackAdd(params, local, op);
|
|
983
1008
|
},
|
|
984
1009
|
rebase,
|
|
@@ -1000,6 +1025,11 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
|
|
|
1000
1025
|
"change",
|
|
1001
1026
|
{
|
|
1002
1027
|
process: (collection, params, local, op) => {
|
|
1028
|
+
// if params is undefined, the interval was deleted during
|
|
1029
|
+
// rebasing
|
|
1030
|
+
if (!params) {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1003
1033
|
collection.ackChange(params, local, op);
|
|
1004
1034
|
},
|
|
1005
1035
|
rebase,
|
|
@@ -1062,7 +1092,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1062
1092
|
private savedSerializedIntervals?: ISerializedInterval[];
|
|
1063
1093
|
private localCollection: LocalIntervalCollection<TInterval>;
|
|
1064
1094
|
private onDeserialize: DeserializeCallback | undefined;
|
|
1065
|
-
private client: Client;
|
|
1095
|
+
private client: Client | undefined;
|
|
1066
1096
|
private readonly pendingChangesStart: Map<string, ISerializedInterval[]> = new Map<string, ISerializedInterval[]>();
|
|
1067
1097
|
private readonly pendingChangesEnd: Map<string, ISerializedInterval[]> = new Map<string, ISerializedInterval[]>();
|
|
1068
1098
|
|
|
@@ -1079,12 +1109,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1079
1109
|
) {
|
|
1080
1110
|
super();
|
|
1081
1111
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
this.savedSerializedIntervals =
|
|
1086
|
-
serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
|
|
1087
|
-
}
|
|
1112
|
+
this.savedSerializedIntervals = Array.isArray(serializedIntervals)
|
|
1113
|
+
? serializedIntervals
|
|
1114
|
+
: serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
|
|
1088
1115
|
}
|
|
1089
1116
|
|
|
1090
1117
|
public attachGraph(client: Client, label: string) {
|
|
@@ -1128,7 +1155,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1128
1155
|
* Gets the next local sequence number, modifying this client's collab window in doing so.
|
|
1129
1156
|
*/
|
|
1130
1157
|
private getNextLocalSeq(): number {
|
|
1131
|
-
|
|
1158
|
+
if (this.client) {
|
|
1159
|
+
return ++this.client.getCollabWindow().localSeq;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
return 0;
|
|
1132
1163
|
}
|
|
1133
1164
|
|
|
1134
1165
|
public getIntervalById(id: string) {
|
|
@@ -1330,11 +1361,6 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1330
1361
|
return entries && entries.length !== 0;
|
|
1331
1362
|
}
|
|
1332
1363
|
|
|
1333
|
-
/** @deprecated - use ackChange */
|
|
1334
|
-
public changeInterval(serializedInterval: ISerializedInterval, local: boolean, op: ISequencedDocumentMessage) {
|
|
1335
|
-
return this.ackChange(serializedInterval, local, op);
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
1364
|
/** @internal */
|
|
1339
1365
|
public ackChange(serializedInterval: ISerializedInterval, local: boolean, op: ISequencedDocumentMessage) {
|
|
1340
1366
|
if (!this.attached) {
|
|
@@ -1415,12 +1441,22 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1415
1441
|
});
|
|
1416
1442
|
}
|
|
1417
1443
|
|
|
1418
|
-
/**
|
|
1444
|
+
/**
|
|
1445
|
+
* Returns new interval after rebasing. If undefined, the interval was
|
|
1446
|
+
* deleted as a result of rebasing. This can occur if the interval applies
|
|
1447
|
+
* to a range that no longer exists, and the interval was unable to slide.
|
|
1448
|
+
*
|
|
1449
|
+
* @internal
|
|
1450
|
+
*/
|
|
1419
1451
|
public rebaseLocalInterval(
|
|
1420
1452
|
opName: string,
|
|
1421
1453
|
serializedInterval: ISerializedInterval,
|
|
1422
1454
|
localSeq: number,
|
|
1423
|
-
) {
|
|
1455
|
+
): ISerializedInterval | undefined {
|
|
1456
|
+
if (!this.client) {
|
|
1457
|
+
// If there's no associated mergeTree client, the originally submitted op is still correct.
|
|
1458
|
+
return serializedInterval;
|
|
1459
|
+
}
|
|
1424
1460
|
if (!this.attached) {
|
|
1425
1461
|
throw new LoggingError("attachSequence must be called");
|
|
1426
1462
|
}
|
|
@@ -1432,6 +1468,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1432
1468
|
this.client.rebasePosition(end, sequenceNumber, localSeq);
|
|
1433
1469
|
|
|
1434
1470
|
const intervalId = properties?.[reservedIntervalIdKey];
|
|
1471
|
+
const localInterval = this.localCollection.getIntervalById(intervalId);
|
|
1472
|
+
|
|
1435
1473
|
const rebased: ISerializedInterval = {
|
|
1436
1474
|
start: startRebased,
|
|
1437
1475
|
end: endRebased,
|
|
@@ -1439,22 +1477,56 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1439
1477
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1440
1478
|
properties,
|
|
1441
1479
|
};
|
|
1480
|
+
|
|
1442
1481
|
if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
|
|
1443
1482
|
this.removePendingChange(serializedInterval);
|
|
1444
1483
|
this.addPendingChange(intervalId, rebased);
|
|
1445
1484
|
}
|
|
1485
|
+
|
|
1486
|
+
// if the interval slid off the string, rebase the op to be a noop and
|
|
1487
|
+
// delete the interval
|
|
1488
|
+
if (startRebased === DetachedReferencePosition || endRebased === DetachedReferencePosition) {
|
|
1489
|
+
if (localInterval) {
|
|
1490
|
+
this.localCollection.removeExistingInterval(localInterval);
|
|
1491
|
+
}
|
|
1492
|
+
return undefined;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
if (!localInterval) {
|
|
1496
|
+
return rebased;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
// we know we must be using `SequenceInterval` because `this.client` exists
|
|
1500
|
+
assert(
|
|
1501
|
+
localInterval instanceof SequenceInterval,
|
|
1502
|
+
0x3a0 /* localInterval must be `SequenceInterval` when used with client */,
|
|
1503
|
+
);
|
|
1504
|
+
|
|
1505
|
+
const startSegment = this.getSlideToSegment(localInterval.start);
|
|
1506
|
+
const endSegment = this.getSlideToSegment(localInterval.end);
|
|
1507
|
+
|
|
1508
|
+
// we need to slide because the reference has been removed
|
|
1509
|
+
if (startSegment || endSegment) {
|
|
1510
|
+
const newStart =
|
|
1511
|
+
startSegment && this.client.getPosition(startSegment.segment, localSeq) + startSegment.offset;
|
|
1512
|
+
const newEnd =
|
|
1513
|
+
endSegment && this.client.getPosition(endSegment.segment, localSeq) + endSegment.offset;
|
|
1514
|
+
|
|
1515
|
+
this.localCollection.changeInterval(localInterval, newStart, newEnd, undefined, localSeq);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1446
1518
|
return rebased;
|
|
1447
1519
|
}
|
|
1448
1520
|
|
|
1449
|
-
private getSlideToSegment(lref:
|
|
1450
|
-
const segoff = { segment: lref.
|
|
1521
|
+
private getSlideToSegment(lref: LocalReferencePosition) {
|
|
1522
|
+
const segoff = { segment: lref.getSegment(), offset: lref.getOffset() };
|
|
1451
1523
|
const newSegoff = this.client.getSlideToSegment(segoff);
|
|
1452
1524
|
const value: { segment: ISegment | undefined; offset: number | undefined; } | undefined
|
|
1453
1525
|
= (segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset) ? undefined : newSegoff;
|
|
1454
1526
|
return value;
|
|
1455
1527
|
}
|
|
1456
1528
|
|
|
1457
|
-
private setSlideOnRemove(lref:
|
|
1529
|
+
private setSlideOnRemove(lref: LocalReferencePosition) {
|
|
1458
1530
|
let refType = lref.refType;
|
|
1459
1531
|
refType = refType & ~ReferenceType.StayOnRemove;
|
|
1460
1532
|
refType = refType | ReferenceType.SlideOnRemove;
|
|
@@ -1462,7 +1534,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1462
1534
|
}
|
|
1463
1535
|
|
|
1464
1536
|
private ackInterval(interval: TInterval, op: ISequencedDocumentMessage) {
|
|
1465
|
-
//
|
|
1537
|
+
// Only SequenceIntervals need potential sliding
|
|
1466
1538
|
if (!(interval instanceof SequenceInterval)) {
|
|
1467
1539
|
return;
|
|
1468
1540
|
}
|
|
@@ -1493,7 +1565,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1493
1565
|
if (needsStartUpdate || needsEndUpdate) {
|
|
1494
1566
|
// In this case, where we change the start or end of an interval,
|
|
1495
1567
|
// it is necessary to remove and re-add the interval listeners.
|
|
1496
|
-
// This ensures that the correct listeners are added to the
|
|
1568
|
+
// This ensures that the correct listeners are added to the LocalReferencePosition.
|
|
1497
1569
|
this.localCollection.removeExistingInterval(interval);
|
|
1498
1570
|
|
|
1499
1571
|
if (needsStartUpdate) {
|
|
@@ -1516,14 +1588,6 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1516
1588
|
}
|
|
1517
1589
|
}
|
|
1518
1590
|
|
|
1519
|
-
/** @deprecated - use ackAdd */
|
|
1520
|
-
public addInternal(
|
|
1521
|
-
serializedInterval: ISerializedInterval,
|
|
1522
|
-
local: boolean,
|
|
1523
|
-
op: ISequencedDocumentMessage) {
|
|
1524
|
-
return this.ackAdd(serializedInterval, local, op);
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
1591
|
/** @internal */
|
|
1528
1592
|
public ackAdd(
|
|
1529
1593
|
serializedInterval: ISerializedInterval,
|
|
@@ -1562,14 +1626,6 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1562
1626
|
return interval;
|
|
1563
1627
|
}
|
|
1564
1628
|
|
|
1565
|
-
/** @deprecated - use ackDelete */
|
|
1566
|
-
public deleteInterval(
|
|
1567
|
-
serializedInterval: ISerializedInterval,
|
|
1568
|
-
local: boolean,
|
|
1569
|
-
op: ISequencedDocumentMessage): void {
|
|
1570
|
-
return this.ackDelete(serializedInterval, local, op);
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
1629
|
/** @internal */
|
|
1574
1630
|
public ackDelete(
|
|
1575
1631
|
serializedInterval: ISerializedInterval,
|
|
@@ -1673,3 +1729,31 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1673
1729
|
return this.localCollection.nextInterval(pos);
|
|
1674
1730
|
}
|
|
1675
1731
|
}
|
|
1732
|
+
|
|
1733
|
+
/**
|
|
1734
|
+
* Information that identifies an interval within a `Sequence`.
|
|
1735
|
+
*/
|
|
1736
|
+
export interface IntervalLocator {
|
|
1737
|
+
/**
|
|
1738
|
+
* Label for the collection the interval is a part of
|
|
1739
|
+
*/
|
|
1740
|
+
label: string;
|
|
1741
|
+
/**
|
|
1742
|
+
* Interval within that collection
|
|
1743
|
+
*/
|
|
1744
|
+
interval: SequenceInterval;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
/**
|
|
1748
|
+
* Returns an object that can be used to find the interval a given LocalReferencePosition belongs to.
|
|
1749
|
+
* @returns undefined if the reference position is not the endpoint of any interval (e.g. it was created
|
|
1750
|
+
* on the merge tree directly by app code), otherwise an {@link IntervalLocator} for the interval this
|
|
1751
|
+
* endpoint is a part of.
|
|
1752
|
+
*/
|
|
1753
|
+
export function intervalLocatorFromEndpoint(potentialEndpoint: LocalReferencePosition): IntervalLocator | undefined {
|
|
1754
|
+
const {
|
|
1755
|
+
interval,
|
|
1756
|
+
[reservedRangeLabelsKey]: collectionNameArray,
|
|
1757
|
+
} = potentialEndpoint.properties ?? {};
|
|
1758
|
+
return (interval && collectionNameArray?.length === 1) ? { label: collectionNameArray[0], interval } : undefined;
|
|
1759
|
+
}
|
package/src/packageVersion.ts
CHANGED
package/src/sequence.ts
CHANGED
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
IRelativePosition,
|
|
30
30
|
ISegment,
|
|
31
31
|
ISegmentAction,
|
|
32
|
-
LocalReference,
|
|
33
32
|
LocalReferencePosition,
|
|
34
33
|
matchProperties,
|
|
35
34
|
MergeTreeDeltaType,
|
|
@@ -67,7 +66,7 @@ const contentPath = "content";
|
|
|
67
66
|
/**
|
|
68
67
|
* Events emitted in response to changes to the sequence data.
|
|
69
68
|
*
|
|
70
|
-
*
|
|
69
|
+
* @remarks
|
|
71
70
|
*
|
|
72
71
|
* The following is the list of events emitted.
|
|
73
72
|
*
|
|
@@ -298,20 +297,6 @@ export abstract class SharedSegmentSequence<T extends ISegment>
|
|
|
298
297
|
return this.client.getRangeExtentsOfPosition(pos);
|
|
299
298
|
}
|
|
300
299
|
|
|
301
|
-
/**
|
|
302
|
-
* @deprecated - use createLocalReferencePosition
|
|
303
|
-
*/
|
|
304
|
-
public createPositionReference(
|
|
305
|
-
segment: T,
|
|
306
|
-
offset: number,
|
|
307
|
-
refType: ReferenceType): LocalReference {
|
|
308
|
-
const lref = new LocalReference(this.client, segment, offset, refType);
|
|
309
|
-
if (refType !== ReferenceType.Transient) {
|
|
310
|
-
this.addLocalReference(lref);
|
|
311
|
-
}
|
|
312
|
-
return lref;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
300
|
public createLocalReferencePosition(
|
|
316
301
|
segment: T,
|
|
317
302
|
offset: number,
|
|
@@ -324,13 +309,6 @@ export abstract class SharedSegmentSequence<T extends ISegment>
|
|
|
324
309
|
properties);
|
|
325
310
|
}
|
|
326
311
|
|
|
327
|
-
/**
|
|
328
|
-
* @deprecated - use localReferencePositionToPosition
|
|
329
|
-
*/
|
|
330
|
-
public localRefToPos(localRef: LocalReference) {
|
|
331
|
-
return this.client.localReferencePositionToPosition(localRef);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
312
|
public localReferencePositionToPosition(lref: ReferencePosition): number {
|
|
335
313
|
return this.client.localReferencePositionToPosition(lref);
|
|
336
314
|
}
|
|
@@ -377,20 +355,6 @@ export abstract class SharedSegmentSequence<T extends ISegment>
|
|
|
377
355
|
}
|
|
378
356
|
}
|
|
379
357
|
|
|
380
|
-
/**
|
|
381
|
-
* @deprecated - use createLocalReferencePosition
|
|
382
|
-
*/
|
|
383
|
-
public addLocalReference(lref: LocalReference) {
|
|
384
|
-
return this.client.addLocalReference(lref);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* @deprecated - use removeLocalReferencePosition
|
|
389
|
-
*/
|
|
390
|
-
public removeLocalReference(lref: LocalReference) {
|
|
391
|
-
return this.client.removeLocalReferencePosition(lref);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
358
|
public removeLocalReferencePosition(lref: LocalReferencePosition) {
|
|
395
359
|
return this.client.removeLocalReferencePosition(lref);
|
|
396
360
|
}
|
|
@@ -424,6 +388,10 @@ export abstract class SharedSegmentSequence<T extends ISegment>
|
|
|
424
388
|
return this.client.walkSegments<TClientData>(handler, start, end, accum, splitRange);
|
|
425
389
|
}
|
|
426
390
|
|
|
391
|
+
/**
|
|
392
|
+
* @deprecated for internal use only. public export will be removed.
|
|
393
|
+
* @internal
|
|
394
|
+
*/
|
|
427
395
|
public getStackContext(startPos: number, rangeLabels: string[]): RangeStackMap {
|
|
428
396
|
return this.client.getStackContext(startPos, rangeLabels);
|
|
429
397
|
}
|
|
@@ -440,8 +408,8 @@ export abstract class SharedSegmentSequence<T extends ISegment>
|
|
|
440
408
|
}
|
|
441
409
|
|
|
442
410
|
/**
|
|
443
|
-
* @deprecated
|
|
444
|
-
* are supported. Use
|
|
411
|
+
* @deprecated `IntervalCollection`s are created on a first-write wins basis, and concurrent creates
|
|
412
|
+
* are supported. Use {@link SharedSegmentSequence.getIntervalCollection} instead.
|
|
445
413
|
*/
|
|
446
414
|
public async waitIntervalCollection(
|
|
447
415
|
label: string,
|
|
@@ -454,12 +422,15 @@ export abstract class SharedSegmentSequence<T extends ISegment>
|
|
|
454
422
|
}
|
|
455
423
|
|
|
456
424
|
/**
|
|
457
|
-
* @returns
|
|
458
|
-
*
|
|
425
|
+
* @returns An iterable object that enumerates the IntervalCollection labels.
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```typescript
|
|
459
429
|
* const iter = this.getIntervalCollectionKeys();
|
|
460
430
|
* for (key of iter)
|
|
461
431
|
* const collection = this.getIntervalCollection(key);
|
|
462
432
|
* ...
|
|
433
|
+
* ```
|
|
463
434
|
*/
|
|
464
435
|
public getIntervalCollectionLabels(): IterableIterator<string> {
|
|
465
436
|
return this.intervalCollections.keys();
|