@fluidframework/sequence 2.0.0-dev.4.4.0.162574 → 2.0.0-dev.5.3.2.178189
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -0
- package/dist/defaultMap.d.ts +3 -2
- package/dist/defaultMap.d.ts.map +1 -1
- package/dist/defaultMap.js +4 -3
- package/dist/defaultMap.js.map +1 -1
- package/dist/defaultMapInterfaces.d.ts +12 -1
- package/dist/defaultMapInterfaces.d.ts.map +1 -1
- package/dist/defaultMapInterfaces.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -3
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +240 -78
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +313 -190
- package/dist/intervalCollection.js.map +1 -1
- package/dist/intervalIndex/index.d.ts +8 -0
- package/dist/intervalIndex/index.d.ts.map +1 -0
- package/dist/intervalIndex/index.js +12 -0
- package/dist/intervalIndex/index.js.map +1 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +32 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.js +103 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +8 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +33 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +33 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.js +7 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.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/revertibles.d.ts +104 -0
- package/dist/revertibles.d.ts.map +1 -0
- package/dist/revertibles.js +414 -0
- package/dist/revertibles.js.map +1 -0
- package/dist/sequence.d.ts +4 -4
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +3 -3
- package/dist/sequence.js.map +1 -1
- package/dist/sharedIntervalCollection.d.ts +3 -3
- package/dist/sharedIntervalCollection.d.ts.map +1 -1
- package/dist/sharedIntervalCollection.js +1 -1
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/lib/defaultMap.d.ts +3 -2
- package/lib/defaultMap.d.ts.map +1 -1
- package/lib/defaultMap.js +4 -3
- package/lib/defaultMap.js.map +1 -1
- package/lib/defaultMapInterfaces.d.ts +12 -1
- package/lib/defaultMapInterfaces.d.ts.map +1 -1
- package/lib/defaultMapInterfaces.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +240 -78
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +310 -190
- package/lib/intervalCollection.js.map +1 -1
- package/lib/intervalIndex/index.d.ts +8 -0
- package/lib/intervalIndex/index.d.ts.map +1 -0
- package/lib/intervalIndex/index.js +7 -0
- package/lib/intervalIndex/index.js.map +1 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +32 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.js +98 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +8 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +29 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +33 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.js +6 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.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/revertibles.d.ts +104 -0
- package/lib/revertibles.d.ts.map +1 -0
- package/lib/revertibles.js +404 -0
- package/lib/revertibles.js.map +1 -0
- package/lib/sequence.d.ts +4 -4
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +3 -3
- package/lib/sequence.js.map +1 -1
- package/lib/sharedIntervalCollection.d.ts +3 -3
- package/lib/sharedIntervalCollection.d.ts.map +1 -1
- package/lib/sharedIntervalCollection.js +1 -1
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/package.json +22 -24
- package/src/defaultMap.ts +4 -1
- package/src/defaultMapInterfaces.ts +13 -1
- package/src/index.ts +27 -5
- package/src/intervalCollection.ts +660 -216
- package/src/intervalIndex/index.ts +11 -0
- package/src/intervalIndex/overlappingIntervalsIndex.ts +166 -0
- package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +71 -0
- package/src/intervalIndex/sequenceIntervalIndexes.ts +32 -0
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +626 -0
- package/src/sequence.ts +12 -2
- package/src/sharedIntervalCollection.ts +4 -2
|
@@ -15,15 +15,26 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
15
15
|
return t;
|
|
16
16
|
};
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.intervalLocatorFromEndpoint = exports.IntervalCollection = exports.
|
|
18
|
+
exports.intervalLocatorFromEndpoint = exports.IntervalCollection = exports.makeOpsMap = exports.IntervalCollectionValueType = exports.SequenceIntervalCollectionValueType = exports.intervalHelpers = exports.sequenceIntervalHelpers = exports.compareSequenceIntervalStarts = exports.compareSequenceIntervalEnds = exports.LocalIntervalCollection = exports.createStartpointInRangeIndex = exports.createEndpointInRangeIndex = exports.createIntervalIndex = exports.createSequenceInterval = exports.createPositionReferenceFromSegoff = exports.SequenceInterval = exports.Interval = exports.IntervalStickiness = exports.IntervalType = exports.IntervalOpType = void 0;
|
|
19
19
|
/* eslint-disable no-bitwise */
|
|
20
20
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
21
21
|
const container_utils_1 = require("@fluidframework/container-utils");
|
|
22
22
|
const merge_tree_1 = require("@fluidframework/merge-tree");
|
|
23
23
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
24
24
|
const uuid_1 = require("uuid");
|
|
25
|
-
const
|
|
25
|
+
const intervalIndex_1 = require("./intervalIndex");
|
|
26
26
|
const reservedIntervalIdKey = "intervalId";
|
|
27
|
+
/**
|
|
28
|
+
* Values are used in persisted formats (ops) and revertibles.
|
|
29
|
+
* @alpha
|
|
30
|
+
*/
|
|
31
|
+
exports.IntervalOpType = {
|
|
32
|
+
ADD: "add",
|
|
33
|
+
DELETE: "delete",
|
|
34
|
+
CHANGE: "change",
|
|
35
|
+
PROPERTY_CHANGED: "propertyChanged",
|
|
36
|
+
POSITION_REMOVE: "positionRemove",
|
|
37
|
+
};
|
|
27
38
|
var IntervalType;
|
|
28
39
|
(function (IntervalType) {
|
|
29
40
|
IntervalType[IntervalType["Simple"] = 0] = "Simple";
|
|
@@ -31,7 +42,7 @@ var IntervalType;
|
|
|
31
42
|
/**
|
|
32
43
|
* SlideOnRemove indicates that the ends of the interval will slide if the segment
|
|
33
44
|
* they reference is removed and acked.
|
|
34
|
-
* See `packages\dds\merge-tree\REFERENCEPOSITIONS.md` for details
|
|
45
|
+
* See `packages\dds\merge-tree\docs\REFERENCEPOSITIONS.md` for details
|
|
35
46
|
* SlideOnRemove is the default interval behavior and does not need to be specified.
|
|
36
47
|
*/
|
|
37
48
|
IntervalType[IntervalType["SlideOnRemove"] = 2] = "SlideOnRemove";
|
|
@@ -52,6 +63,7 @@ function decompressInterval(interval, label) {
|
|
|
52
63
|
sequenceNumber: interval[2],
|
|
53
64
|
intervalType: interval[3],
|
|
54
65
|
properties: Object.assign(Object.assign({}, interval[4]), { [merge_tree_1.reservedRangeLabelsKey]: [label] }),
|
|
66
|
+
stickiness: interval[5],
|
|
55
67
|
};
|
|
56
68
|
}
|
|
57
69
|
/**
|
|
@@ -60,14 +72,57 @@ function decompressInterval(interval, label) {
|
|
|
60
72
|
*/
|
|
61
73
|
function compressInterval(interval) {
|
|
62
74
|
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
63
|
-
|
|
75
|
+
const base = [
|
|
64
76
|
start,
|
|
65
77
|
end,
|
|
66
78
|
sequenceNumber,
|
|
67
79
|
intervalType,
|
|
68
80
|
Object.assign(Object.assign({}, properties), { [merge_tree_1.reservedRangeLabelsKey]: undefined }),
|
|
69
81
|
];
|
|
82
|
+
if (interval.stickiness !== undefined && interval.stickiness !== exports.IntervalStickiness.END) {
|
|
83
|
+
base.push(interval.stickiness);
|
|
84
|
+
}
|
|
85
|
+
return base;
|
|
86
|
+
}
|
|
87
|
+
function startReferenceSlidingPreference(stickiness) {
|
|
88
|
+
// if any start stickiness, prefer sliding backwards
|
|
89
|
+
return (stickiness & exports.IntervalStickiness.START) !== 0
|
|
90
|
+
? merge_tree_1.SlidingPreference.BACKWARD
|
|
91
|
+
: merge_tree_1.SlidingPreference.FORWARD;
|
|
92
|
+
}
|
|
93
|
+
function endReferenceSlidingPreference(stickiness) {
|
|
94
|
+
// if any end stickiness, prefer sliding forwards
|
|
95
|
+
return (stickiness & exports.IntervalStickiness.END) !== 0
|
|
96
|
+
? merge_tree_1.SlidingPreference.FORWARD
|
|
97
|
+
: merge_tree_1.SlidingPreference.BACKWARD;
|
|
70
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Determines how an interval should expand when segments are inserted adjacent
|
|
101
|
+
* to the range it spans
|
|
102
|
+
*
|
|
103
|
+
* Note that interval stickiness is currently an experimental feature and must
|
|
104
|
+
* be explicitly enabled with the `intervalStickinessEnabled` flag
|
|
105
|
+
*/
|
|
106
|
+
exports.IntervalStickiness = {
|
|
107
|
+
/**
|
|
108
|
+
* Interval does not expand to include adjacent segments
|
|
109
|
+
*/
|
|
110
|
+
NONE: 0b00,
|
|
111
|
+
/**
|
|
112
|
+
* Interval expands to include segments inserted adjacent to the start
|
|
113
|
+
*/
|
|
114
|
+
START: 0b01,
|
|
115
|
+
/**
|
|
116
|
+
* Interval expands to include segments inserted adjacent to the end
|
|
117
|
+
*
|
|
118
|
+
* This is the default stickiness
|
|
119
|
+
*/
|
|
120
|
+
END: 0b10,
|
|
121
|
+
/**
|
|
122
|
+
* Interval expands to include all segments inserted adjacent to it
|
|
123
|
+
*/
|
|
124
|
+
FULL: 0b11,
|
|
125
|
+
};
|
|
71
126
|
/**
|
|
72
127
|
* Serializable interval whose endpoints are plain-old numbers.
|
|
73
128
|
*/
|
|
@@ -101,7 +156,7 @@ class Interval {
|
|
|
101
156
|
* Adds an auxiliary set of properties to this interval.
|
|
102
157
|
* These properties can be recovered using `getAdditionalPropertySets`
|
|
103
158
|
* @param props - set of properties to add
|
|
104
|
-
* @remarks - This gets called as part of the default conflict resolver for `
|
|
159
|
+
* @remarks - This gets called as part of the default conflict resolver for `IIntervalCollection<Interval>`
|
|
105
160
|
* (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
|
|
106
161
|
* This functionality seems half-baked.
|
|
107
162
|
*/
|
|
@@ -227,7 +282,7 @@ class Interval {
|
|
|
227
282
|
}
|
|
228
283
|
exports.Interval = Interval;
|
|
229
284
|
/**
|
|
230
|
-
* Interval
|
|
285
|
+
* Interval implementation whose ends are associated with positions in a mutatable sequence.
|
|
231
286
|
* As such, when content is inserted into the middle of the interval, the interval expands to
|
|
232
287
|
* include that content.
|
|
233
288
|
*
|
|
@@ -258,11 +313,12 @@ class SequenceInterval {
|
|
|
258
313
|
* End endpoint of this interval.
|
|
259
314
|
* @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
|
|
260
315
|
*/
|
|
261
|
-
end, intervalType, props) {
|
|
316
|
+
end, intervalType, props, stickiness = exports.IntervalStickiness.END) {
|
|
262
317
|
this.client = client;
|
|
263
318
|
this.start = start;
|
|
264
319
|
this.end = end;
|
|
265
320
|
this.intervalType = intervalType;
|
|
321
|
+
this.stickiness = stickiness;
|
|
266
322
|
this.propertyManager = new merge_tree_1.PropertiesManager();
|
|
267
323
|
this.properties = {};
|
|
268
324
|
if (props) {
|
|
@@ -314,13 +370,16 @@ class SequenceInterval {
|
|
|
314
370
|
if (this.properties) {
|
|
315
371
|
serializedInterval.properties = this.properties;
|
|
316
372
|
}
|
|
373
|
+
if (this.stickiness !== exports.IntervalStickiness.END) {
|
|
374
|
+
serializedInterval.stickiness = this.stickiness;
|
|
375
|
+
}
|
|
317
376
|
return serializedInterval;
|
|
318
377
|
}
|
|
319
378
|
/**
|
|
320
379
|
* {@inheritDoc IInterval.clone}
|
|
321
380
|
*/
|
|
322
381
|
clone() {
|
|
323
|
-
return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties);
|
|
382
|
+
return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties, this.stickiness);
|
|
324
383
|
}
|
|
325
384
|
/**
|
|
326
385
|
* {@inheritDoc IInterval.compare}
|
|
@@ -404,7 +463,7 @@ class SequenceInterval {
|
|
|
404
463
|
* {@inheritDoc IInterval.modify}
|
|
405
464
|
* @deprecated - This API was never intended to be public and will be marked internal in a future release.
|
|
406
465
|
*/
|
|
407
|
-
modify(label, start, end, op, localSeq) {
|
|
466
|
+
modify(label, start, end, op, localSeq, stickiness = exports.IntervalStickiness.END) {
|
|
408
467
|
const getRefType = (baseType) => {
|
|
409
468
|
let refType = baseType;
|
|
410
469
|
if (op === undefined) {
|
|
@@ -415,14 +474,14 @@ class SequenceInterval {
|
|
|
415
474
|
};
|
|
416
475
|
let startRef = this.start;
|
|
417
476
|
if (start !== undefined) {
|
|
418
|
-
startRef = createPositionReference(this.client, start, getRefType(this.start.refType), op, undefined, localSeq);
|
|
477
|
+
startRef = createPositionReference(this.client, start, getRefType(this.start.refType), op, undefined, localSeq, startReferenceSlidingPreference(stickiness));
|
|
419
478
|
if (this.start.properties) {
|
|
420
479
|
startRef.addProperties(this.start.properties);
|
|
421
480
|
}
|
|
422
481
|
}
|
|
423
482
|
let endRef = this.end;
|
|
424
483
|
if (end !== undefined) {
|
|
425
|
-
endRef = createPositionReference(this.client, end, getRefType(this.end.refType), op, undefined, localSeq);
|
|
484
|
+
endRef = createPositionReference(this.client, end, getRefType(this.end.refType), op, undefined, localSeq, endReferenceSlidingPreference(stickiness));
|
|
426
485
|
if (this.end.properties) {
|
|
427
486
|
endRef.addProperties(this.end.properties);
|
|
428
487
|
}
|
|
@@ -444,9 +503,9 @@ class SequenceInterval {
|
|
|
444
503
|
}
|
|
445
504
|
}
|
|
446
505
|
exports.SequenceInterval = SequenceInterval;
|
|
447
|
-
function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot) {
|
|
506
|
+
function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot, slidingPreference) {
|
|
448
507
|
if (segoff.segment) {
|
|
449
|
-
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
|
|
508
|
+
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined, slidingPreference);
|
|
450
509
|
return ref;
|
|
451
510
|
}
|
|
452
511
|
// Creating references on detached segments is allowed for:
|
|
@@ -462,7 +521,8 @@ function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq
|
|
|
462
521
|
}
|
|
463
522
|
return (0, merge_tree_1.createDetachedLocalReferencePosition)(refType);
|
|
464
523
|
}
|
|
465
|
-
|
|
524
|
+
exports.createPositionReferenceFromSegoff = createPositionReferenceFromSegoff;
|
|
525
|
+
function createPositionReference(client, pos, refType, op, fromSnapshot, localSeq, slidingPreference) {
|
|
466
526
|
let segoff;
|
|
467
527
|
if (op) {
|
|
468
528
|
(0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
|
|
@@ -470,15 +530,15 @@ function createPositionReference(client, pos, refType, op, fromSnapshot, localSe
|
|
|
470
530
|
referenceSequenceNumber: op.referenceSequenceNumber,
|
|
471
531
|
clientId: op.clientId,
|
|
472
532
|
});
|
|
473
|
-
segoff =
|
|
533
|
+
segoff = (0, merge_tree_1.getSlideToSegoff)(segoff);
|
|
474
534
|
}
|
|
475
535
|
else {
|
|
476
536
|
(0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
|
|
477
537
|
segoff = client.getContainingSegment(pos, undefined, localSeq);
|
|
478
538
|
}
|
|
479
|
-
return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot);
|
|
539
|
+
return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot, slidingPreference);
|
|
480
540
|
}
|
|
481
|
-
function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot) {
|
|
541
|
+
function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot, stickiness = exports.IntervalStickiness.END) {
|
|
482
542
|
let beginRefType = merge_tree_1.ReferenceType.RangeBegin;
|
|
483
543
|
let endRefType = merge_tree_1.ReferenceType.RangeEnd;
|
|
484
544
|
if (intervalType === IntervalType.Transient) {
|
|
@@ -502,14 +562,14 @@ function createSequenceInterval(label, start, end, client, intervalType, op, fro
|
|
|
502
562
|
endRefType |= merge_tree_1.ReferenceType.StayOnRemove;
|
|
503
563
|
}
|
|
504
564
|
}
|
|
505
|
-
const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot);
|
|
506
|
-
const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot);
|
|
565
|
+
const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot, undefined, startReferenceSlidingPreference(stickiness));
|
|
566
|
+
const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot, undefined, endReferenceSlidingPreference(stickiness));
|
|
507
567
|
const rangeProp = {
|
|
508
568
|
[merge_tree_1.reservedRangeLabelsKey]: [label],
|
|
509
569
|
};
|
|
510
570
|
startLref.addProperties(rangeProp);
|
|
511
571
|
endLref.addProperties(rangeProp);
|
|
512
|
-
const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp);
|
|
572
|
+
const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp, stickiness);
|
|
513
573
|
return ival;
|
|
514
574
|
}
|
|
515
575
|
exports.createSequenceInterval = createSequenceInterval;
|
|
@@ -522,98 +582,6 @@ function createIntervalIndex() {
|
|
|
522
582
|
return lc;
|
|
523
583
|
}
|
|
524
584
|
exports.createIntervalIndex = createIntervalIndex;
|
|
525
|
-
class OverlappingIntervalsIndex {
|
|
526
|
-
constructor(client, helpers) {
|
|
527
|
-
this.client = client;
|
|
528
|
-
this.helpers = helpers;
|
|
529
|
-
this.intervalTree = new intervalTree_1.IntervalTree();
|
|
530
|
-
}
|
|
531
|
-
map(fn) {
|
|
532
|
-
this.intervalTree.map(fn);
|
|
533
|
-
}
|
|
534
|
-
mapUntil(fn) {
|
|
535
|
-
this.intervalTree.mapUntil(fn);
|
|
536
|
-
}
|
|
537
|
-
gatherIterationResults(results, iteratesForward, start, end) {
|
|
538
|
-
if (this.intervalTree.intervals.isEmpty()) {
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
if (start === undefined && end === undefined) {
|
|
542
|
-
// No start/end provided. Gather the whole tree in the specified order.
|
|
543
|
-
if (iteratesForward) {
|
|
544
|
-
this.intervalTree.map((interval) => {
|
|
545
|
-
results.push(interval);
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
this.intervalTree.mapBackward((interval) => {
|
|
550
|
-
results.push(interval);
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
555
|
-
const transientInterval = this.helpers.create("transient", start, end, this.client, IntervalType.Transient);
|
|
556
|
-
if (start === undefined) {
|
|
557
|
-
// Only end position provided. Since the tree is not sorted by end position,
|
|
558
|
-
// walk the whole tree in the specified order, gathering intervals that match the end.
|
|
559
|
-
if (iteratesForward) {
|
|
560
|
-
this.intervalTree.map((interval) => {
|
|
561
|
-
if (transientInterval.compareEnd(interval) === 0) {
|
|
562
|
-
results.push(interval);
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
this.intervalTree.mapBackward((interval) => {
|
|
568
|
-
if (transientInterval.compareEnd(interval) === 0) {
|
|
569
|
-
results.push(interval);
|
|
570
|
-
}
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
else {
|
|
575
|
-
// Start and (possibly) end provided. Walk the subtrees that may contain
|
|
576
|
-
// this start position.
|
|
577
|
-
const compareFn = end === undefined
|
|
578
|
-
? (node) => {
|
|
579
|
-
return transientInterval.compareStart(node.key);
|
|
580
|
-
}
|
|
581
|
-
: (node) => {
|
|
582
|
-
return transientInterval.compare(node.key);
|
|
583
|
-
};
|
|
584
|
-
const continueLeftFn = (cmpResult) => cmpResult <= 0;
|
|
585
|
-
const continueRightFn = (cmpResult) => cmpResult >= 0;
|
|
586
|
-
const actionFn = (node) => {
|
|
587
|
-
results.push(node.key);
|
|
588
|
-
};
|
|
589
|
-
if (iteratesForward) {
|
|
590
|
-
this.intervalTree.intervals.walkExactMatchesForward(compareFn, actionFn, continueLeftFn, continueRightFn);
|
|
591
|
-
}
|
|
592
|
-
else {
|
|
593
|
-
this.intervalTree.intervals.walkExactMatchesBackward(compareFn, actionFn, continueLeftFn, continueRightFn);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
/**
|
|
599
|
-
* @returns an array of all intervals contained in this collection that overlap the range
|
|
600
|
-
* `[startPosition, endPosition)`.
|
|
601
|
-
*/
|
|
602
|
-
findOverlappingIntervals(startPosition, endPosition) {
|
|
603
|
-
if (endPosition < startPosition || this.intervalTree.intervals.isEmpty()) {
|
|
604
|
-
return [];
|
|
605
|
-
}
|
|
606
|
-
const transientInterval = this.helpers.create("transient", startPosition, endPosition, this.client, IntervalType.Transient);
|
|
607
|
-
const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
|
|
608
|
-
return overlappingIntervalNodes.map((node) => node.key);
|
|
609
|
-
}
|
|
610
|
-
remove(interval) {
|
|
611
|
-
this.intervalTree.removeExisting(interval);
|
|
612
|
-
}
|
|
613
|
-
add(interval) {
|
|
614
|
-
this.intervalTree.put(interval);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
585
|
class IdIntervalIndex {
|
|
618
586
|
constructor() {
|
|
619
587
|
this.intervalIdMap = new Map();
|
|
@@ -669,6 +637,119 @@ class EndpointIndex {
|
|
|
669
637
|
this.endIntervalTree.remove(interval);
|
|
670
638
|
}
|
|
671
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* Interface for intervals that have comparison override properties.
|
|
642
|
+
*/
|
|
643
|
+
const forceCompare = Symbol();
|
|
644
|
+
/**
|
|
645
|
+
* Compares two objects based on their comparison override properties.
|
|
646
|
+
* @returns A number indicating the order of the intervals (negative for a is lower than b, 0 for tie, positive for a is greater than b).
|
|
647
|
+
*/
|
|
648
|
+
function compareOverrideables(a, b) {
|
|
649
|
+
var _a, _b;
|
|
650
|
+
const forceCompareA = (_a = a[forceCompare]) !== null && _a !== void 0 ? _a : 0;
|
|
651
|
+
const forceCompareB = (_b = b[forceCompare]) !== null && _b !== void 0 ? _b : 0;
|
|
652
|
+
return forceCompareA - forceCompareB;
|
|
653
|
+
}
|
|
654
|
+
class EndpointInRangeIndex {
|
|
655
|
+
constructor(helpers, client) {
|
|
656
|
+
this.helpers = helpers;
|
|
657
|
+
this.client = client;
|
|
658
|
+
this.intervalTree = new merge_tree_1.RedBlackTree((a, b) => {
|
|
659
|
+
const compareEndsResult = helpers.compareEnds(a, b);
|
|
660
|
+
if (compareEndsResult !== 0) {
|
|
661
|
+
return compareEndsResult;
|
|
662
|
+
}
|
|
663
|
+
const overrideablesComparison = compareOverrideables(a, b);
|
|
664
|
+
if (overrideablesComparison !== 0) {
|
|
665
|
+
return overrideablesComparison;
|
|
666
|
+
}
|
|
667
|
+
const aId = a.getIntervalId();
|
|
668
|
+
const bId = b.getIntervalId();
|
|
669
|
+
if (aId !== undefined && bId !== undefined) {
|
|
670
|
+
return aId.localeCompare(bId);
|
|
671
|
+
}
|
|
672
|
+
return 0;
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
add(interval) {
|
|
676
|
+
this.intervalTree.put(interval, interval);
|
|
677
|
+
}
|
|
678
|
+
remove(interval) {
|
|
679
|
+
this.intervalTree.remove(interval);
|
|
680
|
+
}
|
|
681
|
+
findIntervalsWithEndpointInRange(start, end) {
|
|
682
|
+
if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
|
|
683
|
+
return [];
|
|
684
|
+
}
|
|
685
|
+
const results = [];
|
|
686
|
+
const action = (node) => {
|
|
687
|
+
results.push(node.data);
|
|
688
|
+
return true;
|
|
689
|
+
};
|
|
690
|
+
const transientStartInterval = this.helpers.create("transient", start, start, this.client, IntervalType.Transient);
|
|
691
|
+
const transientEndInterval = this.helpers.create("transient", end, end, this.client, IntervalType.Transient);
|
|
692
|
+
// Add comparison overrides to the transient intervals
|
|
693
|
+
transientStartInterval[forceCompare] = -1;
|
|
694
|
+
transientEndInterval[forceCompare] = 1;
|
|
695
|
+
this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
|
|
696
|
+
return results;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
class StartpointInRangeIndex {
|
|
700
|
+
constructor(helpers, client) {
|
|
701
|
+
this.helpers = helpers;
|
|
702
|
+
this.client = client;
|
|
703
|
+
this.intervalTree = new merge_tree_1.RedBlackTree((a, b) => {
|
|
704
|
+
(0, common_utils_1.assert)(typeof helpers.compareStarts === "function", 0x6d1 /* compareStarts does not exist in the helpers */);
|
|
705
|
+
const compareStartsResult = helpers.compareStarts(a, b);
|
|
706
|
+
if (compareStartsResult !== 0) {
|
|
707
|
+
return compareStartsResult;
|
|
708
|
+
}
|
|
709
|
+
const overrideablesComparison = compareOverrideables(a, b);
|
|
710
|
+
if (overrideablesComparison !== 0) {
|
|
711
|
+
return overrideablesComparison;
|
|
712
|
+
}
|
|
713
|
+
const aId = a.getIntervalId();
|
|
714
|
+
const bId = b.getIntervalId();
|
|
715
|
+
if (aId !== undefined && bId !== undefined) {
|
|
716
|
+
return aId.localeCompare(bId);
|
|
717
|
+
}
|
|
718
|
+
return 0;
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
add(interval) {
|
|
722
|
+
this.intervalTree.put(interval, interval);
|
|
723
|
+
}
|
|
724
|
+
remove(interval) {
|
|
725
|
+
this.intervalTree.remove(interval);
|
|
726
|
+
}
|
|
727
|
+
findIntervalsWithStartpointInRange(start, end) {
|
|
728
|
+
if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
|
|
729
|
+
return [];
|
|
730
|
+
}
|
|
731
|
+
const results = [];
|
|
732
|
+
const action = (node) => {
|
|
733
|
+
results.push(node.data);
|
|
734
|
+
return true;
|
|
735
|
+
};
|
|
736
|
+
const transientStartInterval = this.helpers.create("transient", start, start, this.client, IntervalType.Transient);
|
|
737
|
+
const transientEndInterval = this.helpers.create("transient", end, end, this.client, IntervalType.Transient);
|
|
738
|
+
// Add comparison overrides to the transient intervals
|
|
739
|
+
transientStartInterval[forceCompare] = -1;
|
|
740
|
+
transientEndInterval[forceCompare] = 1;
|
|
741
|
+
this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
|
|
742
|
+
return results;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function createEndpointInRangeIndex(helpers, client) {
|
|
746
|
+
return new EndpointInRangeIndex(helpers, client);
|
|
747
|
+
}
|
|
748
|
+
exports.createEndpointInRangeIndex = createEndpointInRangeIndex;
|
|
749
|
+
function createStartpointInRangeIndex(helpers, client) {
|
|
750
|
+
return new StartpointInRangeIndex(helpers, client);
|
|
751
|
+
}
|
|
752
|
+
exports.createStartpointInRangeIndex = createStartpointInRangeIndex;
|
|
672
753
|
class LocalIntervalCollection {
|
|
673
754
|
constructor(client, label, helpers,
|
|
674
755
|
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
@@ -677,14 +758,14 @@ class LocalIntervalCollection {
|
|
|
677
758
|
this.label = label;
|
|
678
759
|
this.helpers = helpers;
|
|
679
760
|
this.onPositionChange = onPositionChange;
|
|
680
|
-
this.overlappingIntervalsIndex =
|
|
761
|
+
this.overlappingIntervalsIndex = (0, intervalIndex_1.createOverlappingIntervalsIndex)(client, helpers);
|
|
681
762
|
this.idIntervalIndex = new IdIntervalIndex();
|
|
682
763
|
this.endIntervalIndex = new EndpointIndex(client, helpers);
|
|
683
|
-
this.indexes = [
|
|
764
|
+
this.indexes = new Set([
|
|
684
765
|
this.overlappingIntervalsIndex,
|
|
685
766
|
this.idIntervalIndex,
|
|
686
767
|
this.endIntervalIndex,
|
|
687
|
-
];
|
|
768
|
+
]);
|
|
688
769
|
}
|
|
689
770
|
createLegacyId(start, end) {
|
|
690
771
|
// Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
|
|
@@ -724,22 +805,35 @@ class LocalIntervalCollection {
|
|
|
724
805
|
index.remove(interval);
|
|
725
806
|
}
|
|
726
807
|
}
|
|
808
|
+
appendIndex(index) {
|
|
809
|
+
this.indexes.add(index);
|
|
810
|
+
}
|
|
811
|
+
removeIndex(index) {
|
|
812
|
+
return this.indexes.delete(index);
|
|
813
|
+
}
|
|
727
814
|
removeExistingInterval(interval) {
|
|
728
815
|
this.removeIntervalFromIndexes(interval);
|
|
729
816
|
this.removeIntervalListeners(interval);
|
|
730
817
|
}
|
|
731
|
-
createInterval(start, end, intervalType, op) {
|
|
732
|
-
return this.helpers.create(this.label, start, end, this.client, intervalType, op);
|
|
818
|
+
createInterval(start, end, intervalType, op, stickiness = exports.IntervalStickiness.END) {
|
|
819
|
+
return this.helpers.create(this.label, start, end, this.client, intervalType, op, undefined, stickiness);
|
|
733
820
|
}
|
|
734
|
-
addInterval(start, end, intervalType, props, op) {
|
|
821
|
+
addInterval(start, end, intervalType, props, op, stickiness = exports.IntervalStickiness.END) {
|
|
735
822
|
var _a;
|
|
736
823
|
var _b;
|
|
737
|
-
const interval = this.createInterval(start, end, intervalType, op);
|
|
824
|
+
const interval = this.createInterval(start, end, intervalType, op, stickiness);
|
|
738
825
|
if (interval) {
|
|
739
826
|
if (!interval.properties) {
|
|
740
827
|
interval.properties = (0, merge_tree_1.createMap)();
|
|
741
828
|
}
|
|
742
829
|
if (props) {
|
|
830
|
+
// This check is intended to prevent scenarios where a random interval is created and then
|
|
831
|
+
// inserted into a collection. The aim is to ensure that the collection is created first
|
|
832
|
+
// then the user can create/add intervals based on the collection
|
|
833
|
+
if (props[merge_tree_1.reservedRangeLabelsKey] !== undefined &&
|
|
834
|
+
props[merge_tree_1.reservedRangeLabelsKey][0] !== this.label) {
|
|
835
|
+
throw new telemetry_utils_1.LoggingError("Adding an interval that belongs to another interval collection is not permitted");
|
|
836
|
+
}
|
|
743
837
|
interval.addProperties(props);
|
|
744
838
|
}
|
|
745
839
|
(_a = (_b = interval.properties)[reservedIntervalIdKey]) !== null && _a !== void 0 ? _a : (_b[reservedIntervalIdKey] = (0, uuid_1.v4)());
|
|
@@ -787,7 +881,7 @@ class LocalIntervalCollection {
|
|
|
787
881
|
// either, so this must be special-cased.
|
|
788
882
|
return ref;
|
|
789
883
|
}
|
|
790
|
-
return this.client.createLocalReferencePosition(segment, ref.getOffset(), merge_tree_1.ReferenceType.Transient, ref.properties);
|
|
884
|
+
return this.client.createLocalReferencePosition(segment, ref.getOffset(), merge_tree_1.ReferenceType.Transient, ref.properties, ref.slidingPreference);
|
|
791
885
|
};
|
|
792
886
|
if (interval instanceof SequenceInterval) {
|
|
793
887
|
let previousInterval;
|
|
@@ -823,13 +917,21 @@ exports.LocalIntervalCollection = LocalIntervalCollection;
|
|
|
823
917
|
LocalIntervalCollection.legacyIdPrefix = "legacy";
|
|
824
918
|
const compareSequenceIntervalEnds = (a, b) => (0, merge_tree_1.compareReferencePositions)(a.end, b.end);
|
|
825
919
|
exports.compareSequenceIntervalEnds = compareSequenceIntervalEnds;
|
|
920
|
+
const compareSequenceIntervalStarts = (a, b) => (0, merge_tree_1.compareReferencePositions)(a.start, b.start);
|
|
921
|
+
exports.compareSequenceIntervalStarts = compareSequenceIntervalStarts;
|
|
922
|
+
exports.sequenceIntervalHelpers = {
|
|
923
|
+
compareEnds: exports.compareSequenceIntervalEnds,
|
|
924
|
+
compareStarts: exports.compareSequenceIntervalStarts,
|
|
925
|
+
create: createSequenceInterval,
|
|
926
|
+
};
|
|
927
|
+
exports.intervalHelpers = {
|
|
928
|
+
compareEnds: (a, b) => a.end - b.end,
|
|
929
|
+
compareStarts: (a, b) => a.start - b.start,
|
|
930
|
+
create: createInterval,
|
|
931
|
+
};
|
|
826
932
|
class SequenceIntervalCollectionFactory {
|
|
827
|
-
load(emitter, raw = []) {
|
|
828
|
-
|
|
829
|
-
compareEnds: exports.compareSequenceIntervalEnds,
|
|
830
|
-
create: createSequenceInterval,
|
|
831
|
-
};
|
|
832
|
-
return new IntervalCollection(helpers, true, emitter, raw);
|
|
933
|
+
load(emitter, raw = [], options) {
|
|
934
|
+
return new IntervalCollection(exports.sequenceIntervalHelpers, true, emitter, raw, options);
|
|
833
935
|
}
|
|
834
936
|
store(value) {
|
|
835
937
|
return value.serializeInternal();
|
|
@@ -851,7 +953,7 @@ SequenceIntervalCollectionValueType.Name = "sharedStringIntervalCollection";
|
|
|
851
953
|
SequenceIntervalCollectionValueType._factory = new SequenceIntervalCollectionFactory();
|
|
852
954
|
SequenceIntervalCollectionValueType._ops = makeOpsMap();
|
|
853
955
|
const compareIntervalEnds = (a, b) => a.end - b.end;
|
|
854
|
-
function createInterval(label, start, end, client) {
|
|
956
|
+
function createInterval(label, start, end, client, intervalType, op, fromSnapshot) {
|
|
855
957
|
const rangeProp = {};
|
|
856
958
|
if (label && label.length > 0) {
|
|
857
959
|
rangeProp[merge_tree_1.reservedRangeLabelsKey] = [label];
|
|
@@ -859,12 +961,12 @@ function createInterval(label, start, end, client) {
|
|
|
859
961
|
return new Interval(start, end, rangeProp);
|
|
860
962
|
}
|
|
861
963
|
class IntervalCollectionFactory {
|
|
862
|
-
load(emitter, raw = []) {
|
|
964
|
+
load(emitter, raw = [], options) {
|
|
863
965
|
const helpers = {
|
|
864
966
|
compareEnds: compareIntervalEnds,
|
|
865
967
|
create: createInterval,
|
|
866
968
|
};
|
|
867
|
-
const collection = new IntervalCollection(helpers, false, emitter, raw);
|
|
969
|
+
const collection = new IntervalCollection(helpers, false, emitter, raw, options);
|
|
868
970
|
collection.attachGraph(undefined, "");
|
|
869
971
|
return collection;
|
|
870
972
|
}
|
|
@@ -896,7 +998,7 @@ function makeOpsMap() {
|
|
|
896
998
|
};
|
|
897
999
|
return new Map([
|
|
898
1000
|
[
|
|
899
|
-
|
|
1001
|
+
exports.IntervalOpType.ADD,
|
|
900
1002
|
{
|
|
901
1003
|
process: (collection, params, local, op, localOpMetadata) => {
|
|
902
1004
|
// if params is undefined, the interval was deleted during
|
|
@@ -911,7 +1013,7 @@ function makeOpsMap() {
|
|
|
911
1013
|
},
|
|
912
1014
|
],
|
|
913
1015
|
[
|
|
914
|
-
|
|
1016
|
+
exports.IntervalOpType.DELETE,
|
|
915
1017
|
{
|
|
916
1018
|
process: (collection, params, local, op) => {
|
|
917
1019
|
(0, common_utils_1.assert)(op !== undefined, 0x3fc /* op should exist here */);
|
|
@@ -924,7 +1026,7 @@ function makeOpsMap() {
|
|
|
924
1026
|
},
|
|
925
1027
|
],
|
|
926
1028
|
[
|
|
927
|
-
|
|
1029
|
+
exports.IntervalOpType.CHANGE,
|
|
928
1030
|
{
|
|
929
1031
|
process: (collection, params, local, op, localOpMetadata) => {
|
|
930
1032
|
// if params is undefined, the interval was deleted during
|
|
@@ -960,21 +1062,17 @@ class IntervalCollectionIterator {
|
|
|
960
1062
|
};
|
|
961
1063
|
}
|
|
962
1064
|
}
|
|
963
|
-
exports.IntervalCollectionIterator = IntervalCollectionIterator;
|
|
964
1065
|
/**
|
|
965
|
-
*
|
|
966
|
-
* This class is not a DDS in its own right, but emits events on mutating operations such that it's possible to
|
|
967
|
-
* integrate into a DDS.
|
|
968
|
-
* This aligns with its usage in `SharedSegmentSequence`, which allows associating intervals to positions in the
|
|
969
|
-
* sequence DDS which are broadcast to all other clients in an eventually consistent fashion.
|
|
1066
|
+
* {@inheritdoc IIntervalCollection}
|
|
970
1067
|
*/
|
|
971
1068
|
class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
972
1069
|
/** @internal */
|
|
973
|
-
constructor(helpers, requiresClient, emitter, serializedIntervals) {
|
|
1070
|
+
constructor(helpers, requiresClient, emitter, serializedIntervals, options = {}) {
|
|
974
1071
|
super();
|
|
975
1072
|
this.helpers = helpers;
|
|
976
1073
|
this.requiresClient = requiresClient;
|
|
977
1074
|
this.emitter = emitter;
|
|
1075
|
+
this.options = options;
|
|
978
1076
|
this.localSeqToSerializedInterval = new Map();
|
|
979
1077
|
this.localSeqToRebasedInterval = new Map();
|
|
980
1078
|
this.pendingChangesStart = new Map();
|
|
@@ -986,6 +1084,36 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
986
1084
|
get attached() {
|
|
987
1085
|
return !!this.localCollection;
|
|
988
1086
|
}
|
|
1087
|
+
/**
|
|
1088
|
+
* {@inheritdoc IIntervalCollection.attachIndex}
|
|
1089
|
+
*/
|
|
1090
|
+
attachIndex(index) {
|
|
1091
|
+
var _a;
|
|
1092
|
+
if (!this.attached) {
|
|
1093
|
+
throw new telemetry_utils_1.LoggingError("The local interval collection must exist");
|
|
1094
|
+
}
|
|
1095
|
+
for (const interval of this) {
|
|
1096
|
+
index.add(interval);
|
|
1097
|
+
}
|
|
1098
|
+
(_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.appendIndex(index);
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* {@inheritdoc IIntervalCollection.detachIndex}
|
|
1102
|
+
*/
|
|
1103
|
+
detachIndex(index) {
|
|
1104
|
+
var _a;
|
|
1105
|
+
if (!this.attached) {
|
|
1106
|
+
throw new telemetry_utils_1.LoggingError("The local interval collection must exist");
|
|
1107
|
+
}
|
|
1108
|
+
// Avoid removing intervals if the index does not exist
|
|
1109
|
+
if (!((_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.removeIndex(index))) {
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
for (const interval of this) {
|
|
1113
|
+
index.remove(interval);
|
|
1114
|
+
}
|
|
1115
|
+
return true;
|
|
1116
|
+
}
|
|
989
1117
|
rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
|
|
990
1118
|
var _a;
|
|
991
1119
|
if (!this.client) {
|
|
@@ -998,7 +1126,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
998
1126
|
}, localSeq);
|
|
999
1127
|
// if segment is undefined, it slid off the string
|
|
1000
1128
|
(0, common_utils_1.assert)(segment !== undefined, 0x54e /* No segment found */);
|
|
1001
|
-
const segoff = (_a =
|
|
1129
|
+
const segoff = (_a = (0, merge_tree_1.getSlideToSegoff)({ segment, offset })) !== null && _a !== void 0 ? _a : segment;
|
|
1002
1130
|
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
1003
1131
|
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
1004
1132
|
return merge_tree_1.DetachedReferencePosition;
|
|
@@ -1037,12 +1165,12 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1037
1165
|
}
|
|
1038
1166
|
});
|
|
1039
1167
|
}
|
|
1040
|
-
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true));
|
|
1168
|
+
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
|
|
1041
1169
|
if (this.savedSerializedIntervals) {
|
|
1042
1170
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
1043
1171
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1044
|
-
const { start, end, intervalType, properties } = serializedInterval;
|
|
1045
|
-
const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true);
|
|
1172
|
+
const { start, end, intervalType, properties, stickiness } = serializedInterval;
|
|
1173
|
+
const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true, stickiness);
|
|
1046
1174
|
if (properties) {
|
|
1047
1175
|
interval.addProperties(properties);
|
|
1048
1176
|
}
|
|
@@ -1060,7 +1188,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1060
1188
|
}
|
|
1061
1189
|
return 0;
|
|
1062
1190
|
}
|
|
1063
|
-
emitChange(interval, previousInterval, local, op) {
|
|
1191
|
+
emitChange(interval, previousInterval, local, slide, op) {
|
|
1064
1192
|
// Temporarily make references transient so that positional queries work (non-transient refs
|
|
1065
1193
|
// on resolve to DetachedPosition on any segments that don't contain them). The original refType
|
|
1066
1194
|
// is restored as single-endpoint changes re-use previous references.
|
|
@@ -1071,17 +1199,16 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1071
1199
|
endRefType = previousInterval.end.refType;
|
|
1072
1200
|
previousInterval.start.refType = merge_tree_1.ReferenceType.Transient;
|
|
1073
1201
|
previousInterval.end.refType = merge_tree_1.ReferenceType.Transient;
|
|
1074
|
-
this.emit("changeInterval", interval, previousInterval, local, op);
|
|
1202
|
+
this.emit("changeInterval", interval, previousInterval, local, op, slide);
|
|
1075
1203
|
previousInterval.start.refType = startRefType;
|
|
1076
1204
|
previousInterval.end.refType = endRefType;
|
|
1077
1205
|
}
|
|
1078
1206
|
else {
|
|
1079
|
-
this.emit("changeInterval", interval, previousInterval, local, op);
|
|
1207
|
+
this.emit("changeInterval", interval, previousInterval, local, op, slide);
|
|
1080
1208
|
}
|
|
1081
1209
|
}
|
|
1082
1210
|
/**
|
|
1083
|
-
* @
|
|
1084
|
-
* If no interval in the collection has this `id`, returns `undefined`.
|
|
1211
|
+
* {@inheritdoc IIntervalCollection.getIntervalById}
|
|
1085
1212
|
*/
|
|
1086
1213
|
getIntervalById(id) {
|
|
1087
1214
|
if (!this.localCollection) {
|
|
@@ -1090,16 +1217,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1090
1217
|
return this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
1091
1218
|
}
|
|
1092
1219
|
/**
|
|
1093
|
-
*
|
|
1094
|
-
* @param start - interval start position (inclusive)
|
|
1095
|
-
* @param end - interval end position (exclusive)
|
|
1096
|
-
* @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
|
|
1097
|
-
* @param props - properties of the interval
|
|
1098
|
-
* @returns - the created interval
|
|
1099
|
-
* @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
|
|
1100
|
-
* with how the current half-open behavior is represented.
|
|
1220
|
+
* {@inheritdoc IIntervalCollection.add}
|
|
1101
1221
|
*/
|
|
1102
|
-
add(start, end, intervalType, props) {
|
|
1222
|
+
add(start, end, intervalType, props, stickiness = exports.IntervalStickiness.END) {
|
|
1103
1223
|
var _a, _b;
|
|
1104
1224
|
if (!this.localCollection) {
|
|
1105
1225
|
throw new telemetry_utils_1.LoggingError("attach must be called prior to adding intervals");
|
|
@@ -1107,7 +1227,10 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1107
1227
|
if (intervalType & IntervalType.Transient) {
|
|
1108
1228
|
throw new telemetry_utils_1.LoggingError("Can not add transient intervals");
|
|
1109
1229
|
}
|
|
1110
|
-
|
|
1230
|
+
if (stickiness !== exports.IntervalStickiness.END && !this.options.intervalStickinessEnabled) {
|
|
1231
|
+
throw new container_utils_1.UsageError("attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag");
|
|
1232
|
+
}
|
|
1233
|
+
const interval = this.localCollection.addInterval(start, end, intervalType, props, undefined, stickiness);
|
|
1111
1234
|
if (interval) {
|
|
1112
1235
|
const serializedInterval = {
|
|
1113
1236
|
end,
|
|
@@ -1115,6 +1238,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1115
1238
|
properties: interval.properties,
|
|
1116
1239
|
sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
|
|
1117
1240
|
start,
|
|
1241
|
+
stickiness,
|
|
1118
1242
|
};
|
|
1119
1243
|
const localSeq = this.getNextLocalSeq();
|
|
1120
1244
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1146,9 +1270,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1146
1270
|
this.emit("deleteInterval", interval, local, op);
|
|
1147
1271
|
}
|
|
1148
1272
|
/**
|
|
1149
|
-
*
|
|
1150
|
-
* @param id - Id of the interval to remove
|
|
1151
|
-
* @returns the removed interval
|
|
1273
|
+
* {@inheritdoc IIntervalCollection.removeIntervalById}
|
|
1152
1274
|
*/
|
|
1153
1275
|
removeIntervalById(id) {
|
|
1154
1276
|
if (!this.localCollection) {
|
|
@@ -1161,10 +1283,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1161
1283
|
return interval;
|
|
1162
1284
|
}
|
|
1163
1285
|
/**
|
|
1164
|
-
*
|
|
1165
|
-
* @param id - Id of the interval whose properties should be changed
|
|
1166
|
-
* @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
|
|
1167
|
-
* and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
|
|
1286
|
+
* {@inheritdoc IIntervalCollection.changeProperties}
|
|
1168
1287
|
*/
|
|
1169
1288
|
changeProperties(id, props) {
|
|
1170
1289
|
if (!this.attached) {
|
|
@@ -1176,6 +1295,11 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1176
1295
|
if (!props) {
|
|
1177
1296
|
throw new telemetry_utils_1.LoggingError("changeProperties should be called with a property set");
|
|
1178
1297
|
}
|
|
1298
|
+
// prevent the overwriting of an interval label, it should remain unchanged
|
|
1299
|
+
// once it has been inserted into the collection.
|
|
1300
|
+
if (props[merge_tree_1.reservedRangeLabelsKey] !== undefined) {
|
|
1301
|
+
throw new telemetry_utils_1.LoggingError("The label property should not be modified once inserted to the collection");
|
|
1302
|
+
}
|
|
1179
1303
|
const interval = this.getIntervalById(id);
|
|
1180
1304
|
if (interval) {
|
|
1181
1305
|
// Pass Unassigned as the sequence number to indicate that this is a local op that is waiting for an ack.
|
|
@@ -1194,11 +1318,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1194
1318
|
}
|
|
1195
1319
|
}
|
|
1196
1320
|
/**
|
|
1197
|
-
*
|
|
1198
|
-
* @param id - Id of the interval to change
|
|
1199
|
-
* @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1200
|
-
* @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1201
|
-
* @returns the interval that was changed, if it existed in the collection.
|
|
1321
|
+
* {@inheritdoc IIntervalCollection.change}
|
|
1202
1322
|
*/
|
|
1203
1323
|
change(id, start, end) {
|
|
1204
1324
|
if (!this.localCollection) {
|
|
@@ -1225,7 +1345,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1225
1345
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
1226
1346
|
this.emitter.emit("change", undefined, serializedInterval, { localSeq });
|
|
1227
1347
|
this.addPendingChange(id, serializedInterval);
|
|
1228
|
-
this.emitChange(newInterval, interval, true);
|
|
1348
|
+
this.emitChange(newInterval, interval, true, false);
|
|
1229
1349
|
return newInterval;
|
|
1230
1350
|
}
|
|
1231
1351
|
// No interval to change
|
|
@@ -1333,7 +1453,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1333
1453
|
this.onDeserialize(newInterval);
|
|
1334
1454
|
}
|
|
1335
1455
|
if (newInterval !== interval) {
|
|
1336
|
-
this.emitChange(newInterval, interval, local, op);
|
|
1456
|
+
this.emitChange(newInterval, interval, local, false, op);
|
|
1337
1457
|
}
|
|
1338
1458
|
const changedProperties = Object.keys(newProps).length > 0;
|
|
1339
1459
|
if (changedProperties) {
|
|
@@ -1353,6 +1473,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1353
1473
|
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1354
1474
|
}
|
|
1355
1475
|
}
|
|
1476
|
+
/**
|
|
1477
|
+
* {@inheritdoc IIntervalCollection.attachDeserializer}
|
|
1478
|
+
*/
|
|
1356
1479
|
attachDeserializer(onDeserialize) {
|
|
1357
1480
|
// If no deserializer is specified can skip all processing work
|
|
1358
1481
|
if (!onDeserialize) {
|
|
@@ -1423,7 +1546,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1423
1546
|
if (((_b = (_a = segoff.segment) === null || _a === void 0 ? void 0 : _a.localRefs) === null || _b === void 0 ? void 0 : _b.has(lref)) !== true) {
|
|
1424
1547
|
return undefined;
|
|
1425
1548
|
}
|
|
1426
|
-
const newSegoff =
|
|
1549
|
+
const newSegoff = (0, merge_tree_1.getSlideToSegoff)(segoff);
|
|
1427
1550
|
const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
|
|
1428
1551
|
? undefined
|
|
1429
1552
|
: newSegoff;
|
|
@@ -1473,7 +1596,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1473
1596
|
}
|
|
1474
1597
|
if (needsStartUpdate) {
|
|
1475
1598
|
const props = interval.start.properties;
|
|
1476
|
-
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op);
|
|
1599
|
+
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op, startReferenceSlidingPreference(interval.stickiness));
|
|
1477
1600
|
if (props) {
|
|
1478
1601
|
interval.start.addProperties(props);
|
|
1479
1602
|
}
|
|
@@ -1485,7 +1608,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1485
1608
|
}
|
|
1486
1609
|
if (needsEndUpdate) {
|
|
1487
1610
|
const props = interval.end.properties;
|
|
1488
|
-
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op);
|
|
1611
|
+
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op, endReferenceSlidingPreference(interval.stickiness));
|
|
1489
1612
|
if (props) {
|
|
1490
1613
|
interval.end.addProperties(props);
|
|
1491
1614
|
}
|
|
@@ -1496,7 +1619,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1496
1619
|
(_b = oldSeg === null || oldSeg === void 0 ? void 0 : oldSeg.localRefs) === null || _b === void 0 ? void 0 : _b.addLocalRef(oldInterval.end, oldInterval.end.getOffset());
|
|
1497
1620
|
}
|
|
1498
1621
|
this.localCollection.add(interval);
|
|
1499
|
-
this.emitChange(interval, oldInterval, true, op);
|
|
1622
|
+
this.emitChange(interval, oldInterval, true, true, op);
|
|
1500
1623
|
}
|
|
1501
1624
|
}
|
|
1502
1625
|
/** @internal */
|
|
@@ -1516,7 +1639,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1516
1639
|
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1517
1640
|
}
|
|
1518
1641
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1519
|
-
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op);
|
|
1642
|
+
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op, serializedInterval.stickiness);
|
|
1520
1643
|
if (interval) {
|
|
1521
1644
|
if (this.onDeserialize) {
|
|
1522
1645
|
this.onDeserialize(interval);
|
|
@@ -1559,40 +1682,35 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1559
1682
|
return iterator;
|
|
1560
1683
|
}
|
|
1561
1684
|
/**
|
|
1562
|
-
* @
|
|
1685
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithStartPosition}
|
|
1563
1686
|
*/
|
|
1564
1687
|
CreateForwardIteratorWithStartPosition(startPosition) {
|
|
1565
1688
|
const iterator = new IntervalCollectionIterator(this, true, startPosition);
|
|
1566
1689
|
return iterator;
|
|
1567
1690
|
}
|
|
1568
1691
|
/**
|
|
1569
|
-
* @
|
|
1692
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithStartPosition}
|
|
1570
1693
|
*/
|
|
1571
1694
|
CreateBackwardIteratorWithStartPosition(startPosition) {
|
|
1572
1695
|
const iterator = new IntervalCollectionIterator(this, false, startPosition);
|
|
1573
1696
|
return iterator;
|
|
1574
1697
|
}
|
|
1575
1698
|
/**
|
|
1576
|
-
* @
|
|
1699
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithEndPosition}
|
|
1577
1700
|
*/
|
|
1578
1701
|
CreateForwardIteratorWithEndPosition(endPosition) {
|
|
1579
1702
|
const iterator = new IntervalCollectionIterator(this, true, undefined, endPosition);
|
|
1580
1703
|
return iterator;
|
|
1581
1704
|
}
|
|
1582
1705
|
/**
|
|
1583
|
-
* @
|
|
1706
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithEndPosition}
|
|
1584
1707
|
*/
|
|
1585
1708
|
CreateBackwardIteratorWithEndPosition(endPosition) {
|
|
1586
1709
|
const iterator = new IntervalCollectionIterator(this, false, undefined, endPosition);
|
|
1587
1710
|
return iterator;
|
|
1588
1711
|
}
|
|
1589
1712
|
/**
|
|
1590
|
-
*
|
|
1591
|
-
* @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
|
|
1592
|
-
* intervals matching the query upon edit.
|
|
1593
|
-
* @param iteratesForward - whether or not iteration should be in the forward direction
|
|
1594
|
-
* @param start - If provided, only match intervals whose start point is equal to `start`.
|
|
1595
|
-
* @param end - If provided, only match intervals whose end point is equal to `end`.
|
|
1713
|
+
* {@inheritdoc IIntervalCollection.gatherIterationResults}
|
|
1596
1714
|
*/
|
|
1597
1715
|
gatherIterationResults(results, iteratesForward, start, end) {
|
|
1598
1716
|
if (!this.localCollection) {
|
|
@@ -1601,8 +1719,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1601
1719
|
this.localCollection.overlappingIntervalsIndex.gatherIterationResults(results, iteratesForward, start, end);
|
|
1602
1720
|
}
|
|
1603
1721
|
/**
|
|
1604
|
-
* @
|
|
1605
|
-
* `[startPosition, endPosition]`.
|
|
1722
|
+
* {@inheritdoc IIntervalCollection.findOverlappingIntervals}
|
|
1606
1723
|
*/
|
|
1607
1724
|
findOverlappingIntervals(startPosition, endPosition) {
|
|
1608
1725
|
if (!this.localCollection) {
|
|
@@ -1611,7 +1728,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1611
1728
|
return this.localCollection.overlappingIntervalsIndex.findOverlappingIntervals(startPosition, endPosition);
|
|
1612
1729
|
}
|
|
1613
1730
|
/**
|
|
1614
|
-
*
|
|
1731
|
+
* {@inheritdoc IIntervalCollection.map}
|
|
1615
1732
|
*/
|
|
1616
1733
|
map(fn) {
|
|
1617
1734
|
if (!this.localCollection) {
|
|
@@ -1621,12 +1738,18 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
1621
1738
|
fn(interval);
|
|
1622
1739
|
}
|
|
1623
1740
|
}
|
|
1741
|
+
/**
|
|
1742
|
+
* {@inheritdoc IIntervalCollection.previousInterval}
|
|
1743
|
+
*/
|
|
1624
1744
|
previousInterval(pos) {
|
|
1625
1745
|
if (!this.localCollection) {
|
|
1626
1746
|
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1627
1747
|
}
|
|
1628
1748
|
return this.localCollection.endIntervalIndex.previousInterval(pos);
|
|
1629
1749
|
}
|
|
1750
|
+
/**
|
|
1751
|
+
* {@inheritdoc IIntervalCollection.nextInterval}
|
|
1752
|
+
*/
|
|
1630
1753
|
nextInterval(pos) {
|
|
1631
1754
|
if (!this.localCollection) {
|
|
1632
1755
|
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|