@fluidframework/sequence 2.0.0-dev.4.4.0.162253 → 2.0.0-dev.5.2.0.169897
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 +20 -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 +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +231 -59
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +288 -86
- package/dist/intervalCollection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/revertibles.d.ts +104 -0
- package/dist/revertibles.d.ts.map +1 -0
- package/dist/revertibles.js +374 -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 +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +231 -59
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +286 -86
- package/lib/intervalCollection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/revertibles.d.ts +104 -0
- package/lib/revertibles.d.ts.map +1 -0
- package/lib/revertibles.js +364 -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 +20 -22
- package/src/defaultMap.ts +4 -1
- package/src/defaultMapInterfaces.ts +13 -1
- package/src/index.ts +21 -5
- package/src/intervalCollection.ts +619 -73
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +572 -0
- package/src/sequence.ts +12 -2
- package/src/sharedIntervalCollection.ts +4 -2
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
maxReferencePosition,
|
|
29
29
|
createDetachedLocalReferencePosition,
|
|
30
30
|
DetachedReferencePosition,
|
|
31
|
+
SlidingPreference,
|
|
32
|
+
PropertyAction,
|
|
31
33
|
} from "@fluidframework/merge-tree";
|
|
32
34
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
33
35
|
import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
@@ -39,11 +41,24 @@ import {
|
|
|
39
41
|
IValueOperation,
|
|
40
42
|
IValueType,
|
|
41
43
|
IValueTypeOperationValue,
|
|
44
|
+
SequenceOptions,
|
|
42
45
|
} from "./defaultMapInterfaces";
|
|
43
46
|
import { IInterval, IntervalConflictResolver, IntervalTree, IntervalNode } from "./intervalTree";
|
|
44
47
|
|
|
45
48
|
const reservedIntervalIdKey = "intervalId";
|
|
46
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Values are used in persisted formats (ops) and revertibles.
|
|
52
|
+
* @alpha
|
|
53
|
+
*/
|
|
54
|
+
export const IntervalOpType = {
|
|
55
|
+
ADD: "add",
|
|
56
|
+
DELETE: "delete",
|
|
57
|
+
CHANGE: "change",
|
|
58
|
+
PROPERTY_CHANGED: "propertyChanged",
|
|
59
|
+
POSITION_REMOVE: "positionRemove",
|
|
60
|
+
} as const;
|
|
61
|
+
|
|
47
62
|
export enum IntervalType {
|
|
48
63
|
Simple = 0x0,
|
|
49
64
|
Nest = 0x1,
|
|
@@ -51,7 +66,7 @@ export enum IntervalType {
|
|
|
51
66
|
/**
|
|
52
67
|
* SlideOnRemove indicates that the ends of the interval will slide if the segment
|
|
53
68
|
* they reference is removed and acked.
|
|
54
|
-
* See `packages\dds\merge-tree\REFERENCEPOSITIONS.md` for details
|
|
69
|
+
* See `packages\dds\merge-tree\docs\REFERENCEPOSITIONS.md` for details
|
|
55
70
|
* SlideOnRemove is the default interval behavior and does not need to be specified.
|
|
56
71
|
*/
|
|
57
72
|
SlideOnRemove = 0x2, // SlideOnRemove is default behavior - all intervals are SlideOnRemove
|
|
@@ -83,6 +98,7 @@ export interface ISerializedInterval {
|
|
|
83
98
|
end: number;
|
|
84
99
|
/** Interval type to create */
|
|
85
100
|
intervalType: IntervalType;
|
|
101
|
+
stickiness?: IntervalStickiness;
|
|
86
102
|
/** Any properties the interval has */
|
|
87
103
|
properties?: PropertySet;
|
|
88
104
|
}
|
|
@@ -101,13 +117,12 @@ export type SerializedIntervalDelta = Omit<ISerializedInterval, "start" | "end"
|
|
|
101
117
|
*
|
|
102
118
|
* Intervals are of the format:
|
|
103
119
|
*
|
|
104
|
-
* [start, end, sequenceNumber, intervalType, properties]
|
|
120
|
+
* [start, end, sequenceNumber, intervalType, properties, stickiness?]
|
|
105
121
|
*/
|
|
106
|
-
export type CompressedSerializedInterval =
|
|
122
|
+
export type CompressedSerializedInterval =
|
|
123
|
+
| [number, number, number, IntervalType, PropertySet, IntervalStickiness]
|
|
124
|
+
| [number, number, number, IntervalType, PropertySet];
|
|
107
125
|
|
|
108
|
-
/**
|
|
109
|
-
* @internal
|
|
110
|
-
*/
|
|
111
126
|
export interface ISerializedIntervalCollectionV2 {
|
|
112
127
|
label: string;
|
|
113
128
|
version: 2;
|
|
@@ -128,6 +143,7 @@ function decompressInterval(
|
|
|
128
143
|
sequenceNumber: interval[2],
|
|
129
144
|
intervalType: interval[3],
|
|
130
145
|
properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
|
|
146
|
+
stickiness: interval[5],
|
|
131
147
|
};
|
|
132
148
|
}
|
|
133
149
|
|
|
@@ -138,7 +154,7 @@ function decompressInterval(
|
|
|
138
154
|
function compressInterval(interval: ISerializedInterval): CompressedSerializedInterval {
|
|
139
155
|
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
140
156
|
|
|
141
|
-
|
|
157
|
+
const base: CompressedSerializedInterval = [
|
|
142
158
|
start,
|
|
143
159
|
end,
|
|
144
160
|
sequenceNumber,
|
|
@@ -147,6 +163,26 @@ function compressInterval(interval: ISerializedInterval): CompressedSerializedIn
|
|
|
147
163
|
// in the `label` field of the summary
|
|
148
164
|
{ ...properties, [reservedRangeLabelsKey]: undefined },
|
|
149
165
|
];
|
|
166
|
+
|
|
167
|
+
if (interval.stickiness !== undefined && interval.stickiness !== IntervalStickiness.END) {
|
|
168
|
+
base.push(interval.stickiness);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return base;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function startReferenceSlidingPreference(stickiness: IntervalStickiness): SlidingPreference {
|
|
175
|
+
// if any start stickiness, prefer sliding backwards
|
|
176
|
+
return (stickiness & IntervalStickiness.START) !== 0
|
|
177
|
+
? SlidingPreference.BACKWARD
|
|
178
|
+
: SlidingPreference.FORWARD;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function endReferenceSlidingPreference(stickiness: IntervalStickiness): SlidingPreference {
|
|
182
|
+
// if any end stickiness, prefer sliding forwards
|
|
183
|
+
return (stickiness & IntervalStickiness.END) !== 0
|
|
184
|
+
? SlidingPreference.FORWARD
|
|
185
|
+
: SlidingPreference.BACKWARD;
|
|
150
186
|
}
|
|
151
187
|
|
|
152
188
|
export interface ISerializableInterval extends IInterval {
|
|
@@ -172,14 +208,18 @@ export interface ISerializableInterval extends IInterval {
|
|
|
172
208
|
getIntervalId(): string | undefined;
|
|
173
209
|
}
|
|
174
210
|
|
|
211
|
+
/**
|
|
212
|
+
* @sealed
|
|
213
|
+
*/
|
|
175
214
|
export interface IIntervalHelpers<TInterval extends ISerializableInterval> {
|
|
176
215
|
compareEnds(a: TInterval, b: TInterval): number;
|
|
216
|
+
compareStarts?(a: TInterval, b: TInterval): number;
|
|
177
217
|
/**
|
|
178
218
|
*
|
|
179
219
|
* @param label - label of the interval collection this interval is being added to. This parameter is
|
|
180
220
|
* irrelevant for transient intervals.
|
|
181
221
|
* @param start - numerical start position of the interval
|
|
182
|
-
* @param end -
|
|
222
|
+
* @param end - numerical end position of the interval
|
|
183
223
|
* @param client - client creating the interval
|
|
184
224
|
* @param intervalType - Type of interval to create. Default is SlideOnRemove
|
|
185
225
|
* @param op - If this create came from a remote client, op that created it. Default is undefined (i.e. local)
|
|
@@ -193,9 +233,50 @@ export interface IIntervalHelpers<TInterval extends ISerializableInterval> {
|
|
|
193
233
|
intervalType: IntervalType,
|
|
194
234
|
op?: ISequencedDocumentMessage,
|
|
195
235
|
fromSnapshot?: boolean,
|
|
236
|
+
stickiness?: IntervalStickiness,
|
|
196
237
|
): TInterval;
|
|
197
238
|
}
|
|
198
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Determines how an interval should expand when segments are inserted adjacent
|
|
242
|
+
* to the range it spans
|
|
243
|
+
*
|
|
244
|
+
* Note that interval stickiness is currently an experimental feature and must
|
|
245
|
+
* be explicitly enabled with the `intervalStickinessEnabled` flag
|
|
246
|
+
*/
|
|
247
|
+
export const IntervalStickiness = {
|
|
248
|
+
/**
|
|
249
|
+
* Interval does not expand to include adjacent segments
|
|
250
|
+
*/
|
|
251
|
+
NONE: 0b00,
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Interval expands to include segments inserted adjacent to the start
|
|
255
|
+
*/
|
|
256
|
+
START: 0b01,
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Interval expands to include segments inserted adjacent to the end
|
|
260
|
+
*
|
|
261
|
+
* This is the default stickiness
|
|
262
|
+
*/
|
|
263
|
+
END: 0b10,
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Interval expands to include all segments inserted adjacent to it
|
|
267
|
+
*/
|
|
268
|
+
FULL: 0b11,
|
|
269
|
+
} as const;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Determines how an interval should expand when segments are inserted adjacent
|
|
273
|
+
* to the range it spans
|
|
274
|
+
*
|
|
275
|
+
* Note that interval stickiness is currently an experimental feature and must
|
|
276
|
+
* be explicitly enabled with the `intervalStickinessEnabled` flag
|
|
277
|
+
*/
|
|
278
|
+
export type IntervalStickiness = typeof IntervalStickiness[keyof typeof IntervalStickiness];
|
|
279
|
+
|
|
199
280
|
/**
|
|
200
281
|
* Serializable interval whose endpoints are plain-old numbers.
|
|
201
282
|
*/
|
|
@@ -240,7 +321,7 @@ export class Interval implements ISerializableInterval {
|
|
|
240
321
|
* Adds an auxiliary set of properties to this interval.
|
|
241
322
|
* These properties can be recovered using `getAdditionalPropertySets`
|
|
242
323
|
* @param props - set of properties to add
|
|
243
|
-
* @remarks - This gets called as part of the default conflict resolver for `
|
|
324
|
+
* @remarks - This gets called as part of the default conflict resolver for `IIntervalCollection<Interval>`
|
|
244
325
|
* (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
|
|
245
326
|
* This functionality seems half-baked.
|
|
246
327
|
*/
|
|
@@ -394,7 +475,7 @@ export class Interval implements ISerializableInterval {
|
|
|
394
475
|
}
|
|
395
476
|
|
|
396
477
|
/**
|
|
397
|
-
* Interval
|
|
478
|
+
* Interval implementation whose ends are associated with positions in a mutatable sequence.
|
|
398
479
|
* As such, when content is inserted into the middle of the interval, the interval expands to
|
|
399
480
|
* include that content.
|
|
400
481
|
*
|
|
@@ -439,6 +520,7 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
439
520
|
public end: LocalReferencePosition,
|
|
440
521
|
public intervalType: IntervalType,
|
|
441
522
|
props?: PropertySet,
|
|
523
|
+
public readonly stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
442
524
|
) {
|
|
443
525
|
this.propertyManager = new PropertiesManager();
|
|
444
526
|
this.properties = {};
|
|
@@ -500,6 +582,9 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
500
582
|
if (this.properties) {
|
|
501
583
|
serializedInterval.properties = this.properties;
|
|
502
584
|
}
|
|
585
|
+
if (this.stickiness !== IntervalStickiness.END) {
|
|
586
|
+
serializedInterval.stickiness = this.stickiness;
|
|
587
|
+
}
|
|
503
588
|
|
|
504
589
|
return serializedInterval;
|
|
505
590
|
}
|
|
@@ -514,6 +599,7 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
514
599
|
this.end,
|
|
515
600
|
this.intervalType,
|
|
516
601
|
this.properties,
|
|
602
|
+
this.stickiness,
|
|
517
603
|
);
|
|
518
604
|
}
|
|
519
605
|
|
|
@@ -621,6 +707,7 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
621
707
|
end: number,
|
|
622
708
|
op?: ISequencedDocumentMessage,
|
|
623
709
|
localSeq?: number,
|
|
710
|
+
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
624
711
|
) {
|
|
625
712
|
const getRefType = (baseType: ReferenceType): ReferenceType => {
|
|
626
713
|
let refType = baseType;
|
|
@@ -640,6 +727,7 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
640
727
|
op,
|
|
641
728
|
undefined,
|
|
642
729
|
localSeq,
|
|
730
|
+
startReferenceSlidingPreference(stickiness),
|
|
643
731
|
);
|
|
644
732
|
if (this.start.properties) {
|
|
645
733
|
startRef.addProperties(this.start.properties);
|
|
@@ -655,6 +743,7 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
655
743
|
op,
|
|
656
744
|
undefined,
|
|
657
745
|
localSeq,
|
|
746
|
+
endReferenceSlidingPreference(stickiness),
|
|
658
747
|
);
|
|
659
748
|
if (this.end.properties) {
|
|
660
749
|
endRef.addProperties(this.end.properties);
|
|
@@ -690,6 +779,7 @@ function createPositionReferenceFromSegoff(
|
|
|
690
779
|
op?: ISequencedDocumentMessage,
|
|
691
780
|
localSeq?: number,
|
|
692
781
|
fromSnapshot?: boolean,
|
|
782
|
+
slidingPreference?: SlidingPreference,
|
|
693
783
|
): LocalReferencePosition {
|
|
694
784
|
if (segoff.segment) {
|
|
695
785
|
const ref = client.createLocalReferencePosition(
|
|
@@ -697,6 +787,7 @@ function createPositionReferenceFromSegoff(
|
|
|
697
787
|
segoff.offset,
|
|
698
788
|
refType,
|
|
699
789
|
undefined,
|
|
790
|
+
slidingPreference,
|
|
700
791
|
);
|
|
701
792
|
return ref;
|
|
702
793
|
}
|
|
@@ -725,6 +816,7 @@ function createPositionReference(
|
|
|
725
816
|
op?: ISequencedDocumentMessage,
|
|
726
817
|
fromSnapshot?: boolean,
|
|
727
818
|
localSeq?: number,
|
|
819
|
+
slidingPreference?: SlidingPreference,
|
|
728
820
|
): LocalReferencePosition {
|
|
729
821
|
let segoff;
|
|
730
822
|
if (op) {
|
|
@@ -744,7 +836,16 @@ function createPositionReference(
|
|
|
744
836
|
);
|
|
745
837
|
segoff = client.getContainingSegment(pos, undefined, localSeq);
|
|
746
838
|
}
|
|
747
|
-
|
|
839
|
+
|
|
840
|
+
return createPositionReferenceFromSegoff(
|
|
841
|
+
client,
|
|
842
|
+
segoff,
|
|
843
|
+
refType,
|
|
844
|
+
op,
|
|
845
|
+
localSeq,
|
|
846
|
+
fromSnapshot,
|
|
847
|
+
slidingPreference,
|
|
848
|
+
);
|
|
748
849
|
}
|
|
749
850
|
|
|
750
851
|
export function createSequenceInterval(
|
|
@@ -755,6 +856,7 @@ export function createSequenceInterval(
|
|
|
755
856
|
intervalType: IntervalType,
|
|
756
857
|
op?: ISequencedDocumentMessage,
|
|
757
858
|
fromSnapshot?: boolean,
|
|
859
|
+
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
758
860
|
): SequenceInterval {
|
|
759
861
|
let beginRefType = ReferenceType.RangeBegin;
|
|
760
862
|
let endRefType = ReferenceType.RangeEnd;
|
|
@@ -778,15 +880,38 @@ export function createSequenceInterval(
|
|
|
778
880
|
}
|
|
779
881
|
}
|
|
780
882
|
|
|
781
|
-
const startLref = createPositionReference(
|
|
782
|
-
|
|
883
|
+
const startLref = createPositionReference(
|
|
884
|
+
client,
|
|
885
|
+
start,
|
|
886
|
+
beginRefType,
|
|
887
|
+
op,
|
|
888
|
+
fromSnapshot,
|
|
889
|
+
undefined,
|
|
890
|
+
startReferenceSlidingPreference(stickiness),
|
|
891
|
+
);
|
|
892
|
+
const endLref = createPositionReference(
|
|
893
|
+
client,
|
|
894
|
+
end,
|
|
895
|
+
endRefType,
|
|
896
|
+
op,
|
|
897
|
+
fromSnapshot,
|
|
898
|
+
undefined,
|
|
899
|
+
endReferenceSlidingPreference(stickiness),
|
|
900
|
+
);
|
|
783
901
|
const rangeProp = {
|
|
784
902
|
[reservedRangeLabelsKey]: [label],
|
|
785
903
|
};
|
|
786
904
|
startLref.addProperties(rangeProp);
|
|
787
905
|
endLref.addProperties(rangeProp);
|
|
788
906
|
|
|
789
|
-
const ival = new SequenceInterval(
|
|
907
|
+
const ival = new SequenceInterval(
|
|
908
|
+
client,
|
|
909
|
+
startLref,
|
|
910
|
+
endLref,
|
|
911
|
+
intervalType,
|
|
912
|
+
rangeProp,
|
|
913
|
+
stickiness,
|
|
914
|
+
);
|
|
790
915
|
return ival;
|
|
791
916
|
}
|
|
792
917
|
|
|
@@ -1036,12 +1161,229 @@ class EndpointIndex<TInterval extends ISerializableInterval> implements Interval
|
|
|
1036
1161
|
}
|
|
1037
1162
|
}
|
|
1038
1163
|
|
|
1164
|
+
/**
|
|
1165
|
+
* Collection of intervals.
|
|
1166
|
+
*
|
|
1167
|
+
* Provide additional APIs to support efficiently querying a collection of intervals whose endpoints fall within a specified range.
|
|
1168
|
+
*/
|
|
1169
|
+
export interface IEndpointInRangeIndex<TInterval extends ISerializableInterval>
|
|
1170
|
+
extends IntervalIndex<TInterval> {
|
|
1171
|
+
/**
|
|
1172
|
+
* @returns an array of all intervals contained in this collection whose endpoints locate in the range [start, end] (includes both ends)
|
|
1173
|
+
*/
|
|
1174
|
+
findIntervalsWithEndpointInRange(start: number, end: number);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* Collection of intervals.
|
|
1179
|
+
*
|
|
1180
|
+
* Provide additional APIs to support efficiently querying a collection of intervals whose startpoints fall within a specified range.
|
|
1181
|
+
*/
|
|
1182
|
+
export interface IStartpointInRangeIndex<TInterval extends ISerializableInterval>
|
|
1183
|
+
extends IntervalIndex<TInterval> {
|
|
1184
|
+
/**
|
|
1185
|
+
* @returns an array of all intervals contained in this collection whose startpoints locate in the range [start, end] (includes both ends)
|
|
1186
|
+
*/
|
|
1187
|
+
findIntervalsWithStartpointInRange(start: number, end: number);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* Interface for intervals that have comparison override properties.
|
|
1192
|
+
*/
|
|
1193
|
+
const forceCompare = Symbol();
|
|
1194
|
+
|
|
1195
|
+
interface HasComparisonOverride {
|
|
1196
|
+
[forceCompare]: number;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* Compares two objects based on their comparison override properties.
|
|
1201
|
+
* @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).
|
|
1202
|
+
*/
|
|
1203
|
+
function compareOverrideables(
|
|
1204
|
+
a: Partial<HasComparisonOverride>,
|
|
1205
|
+
b: Partial<HasComparisonOverride>,
|
|
1206
|
+
): number {
|
|
1207
|
+
const forceCompareA = a[forceCompare] ?? 0;
|
|
1208
|
+
const forceCompareB = b[forceCompare] ?? 0;
|
|
1209
|
+
|
|
1210
|
+
return forceCompareA - forceCompareB;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
class EndpointInRangeIndex<TInterval extends ISerializableInterval>
|
|
1214
|
+
implements IEndpointInRangeIndex<TInterval>
|
|
1215
|
+
{
|
|
1216
|
+
private readonly intervalTree;
|
|
1217
|
+
|
|
1218
|
+
constructor(
|
|
1219
|
+
private readonly helpers: IIntervalHelpers<TInterval>,
|
|
1220
|
+
private readonly client: Client,
|
|
1221
|
+
) {
|
|
1222
|
+
this.intervalTree = new RedBlackTree<TInterval, TInterval>((a: TInterval, b: TInterval) => {
|
|
1223
|
+
const compareEndsResult = helpers.compareEnds(a, b);
|
|
1224
|
+
if (compareEndsResult !== 0) {
|
|
1225
|
+
return compareEndsResult;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
const overrideablesComparison = compareOverrideables(
|
|
1229
|
+
a as Partial<HasComparisonOverride>,
|
|
1230
|
+
b as Partial<HasComparisonOverride>,
|
|
1231
|
+
);
|
|
1232
|
+
if (overrideablesComparison !== 0) {
|
|
1233
|
+
return overrideablesComparison;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
const aId = a.getIntervalId();
|
|
1237
|
+
const bId = b.getIntervalId();
|
|
1238
|
+
if (aId !== undefined && bId !== undefined) {
|
|
1239
|
+
return aId.localeCompare(bId);
|
|
1240
|
+
}
|
|
1241
|
+
return 0;
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
public add(interval: TInterval): void {
|
|
1246
|
+
this.intervalTree.put(interval, interval);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
public remove(interval: TInterval): void {
|
|
1250
|
+
this.intervalTree.remove(interval);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
public findIntervalsWithEndpointInRange(start: number, end: number) {
|
|
1254
|
+
if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
|
|
1255
|
+
return [];
|
|
1256
|
+
}
|
|
1257
|
+
const results: TInterval[] = [];
|
|
1258
|
+
const action: PropertyAction<TInterval, TInterval> = (node) => {
|
|
1259
|
+
results.push(node.data);
|
|
1260
|
+
return true;
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
const transientStartInterval = this.helpers.create(
|
|
1264
|
+
"transient",
|
|
1265
|
+
start,
|
|
1266
|
+
start,
|
|
1267
|
+
this.client,
|
|
1268
|
+
IntervalType.Transient,
|
|
1269
|
+
);
|
|
1270
|
+
|
|
1271
|
+
const transientEndInterval = this.helpers.create(
|
|
1272
|
+
"transient",
|
|
1273
|
+
end,
|
|
1274
|
+
end,
|
|
1275
|
+
this.client,
|
|
1276
|
+
IntervalType.Transient,
|
|
1277
|
+
);
|
|
1278
|
+
|
|
1279
|
+
// Add comparison overrides to the transient intervals
|
|
1280
|
+
(transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
|
|
1281
|
+
(transientEndInterval as Partial<HasComparisonOverride>)[forceCompare] = 1;
|
|
1282
|
+
|
|
1283
|
+
this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
|
|
1284
|
+
return results;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
class StartpointInRangeIndex<TInterval extends ISerializableInterval>
|
|
1289
|
+
implements IStartpointInRangeIndex<TInterval>
|
|
1290
|
+
{
|
|
1291
|
+
private readonly intervalTree;
|
|
1292
|
+
|
|
1293
|
+
constructor(
|
|
1294
|
+
private readonly helpers: IIntervalHelpers<TInterval>,
|
|
1295
|
+
private readonly client: Client,
|
|
1296
|
+
) {
|
|
1297
|
+
this.intervalTree = new RedBlackTree<TInterval, TInterval>((a: TInterval, b: TInterval) => {
|
|
1298
|
+
assert(
|
|
1299
|
+
typeof helpers.compareStarts === "function",
|
|
1300
|
+
0x6d1 /* compareStarts does not exist in the helpers */,
|
|
1301
|
+
);
|
|
1302
|
+
|
|
1303
|
+
const compareStartsResult = helpers.compareStarts(a, b);
|
|
1304
|
+
if (compareStartsResult !== 0) {
|
|
1305
|
+
return compareStartsResult;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
const overrideablesComparison = compareOverrideables(
|
|
1309
|
+
a as Partial<HasComparisonOverride>,
|
|
1310
|
+
b as Partial<HasComparisonOverride>,
|
|
1311
|
+
);
|
|
1312
|
+
if (overrideablesComparison !== 0) {
|
|
1313
|
+
return overrideablesComparison;
|
|
1314
|
+
}
|
|
1315
|
+
const aId = a.getIntervalId();
|
|
1316
|
+
const bId = b.getIntervalId();
|
|
1317
|
+
if (aId !== undefined && bId !== undefined) {
|
|
1318
|
+
return aId.localeCompare(bId);
|
|
1319
|
+
}
|
|
1320
|
+
return 0;
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
public add(interval: TInterval): void {
|
|
1325
|
+
this.intervalTree.put(interval, interval);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
public remove(interval: TInterval): void {
|
|
1329
|
+
this.intervalTree.remove(interval);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
public findIntervalsWithStartpointInRange(start: number, end: number) {
|
|
1333
|
+
if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
|
|
1334
|
+
return [];
|
|
1335
|
+
}
|
|
1336
|
+
const results: TInterval[] = [];
|
|
1337
|
+
const action: PropertyAction<TInterval, TInterval> = (node) => {
|
|
1338
|
+
results.push(node.data);
|
|
1339
|
+
return true;
|
|
1340
|
+
};
|
|
1341
|
+
|
|
1342
|
+
const transientStartInterval = this.helpers.create(
|
|
1343
|
+
"transient",
|
|
1344
|
+
start,
|
|
1345
|
+
start,
|
|
1346
|
+
this.client,
|
|
1347
|
+
IntervalType.Transient,
|
|
1348
|
+
);
|
|
1349
|
+
|
|
1350
|
+
const transientEndInterval = this.helpers.create(
|
|
1351
|
+
"transient",
|
|
1352
|
+
end,
|
|
1353
|
+
end,
|
|
1354
|
+
this.client,
|
|
1355
|
+
IntervalType.Transient,
|
|
1356
|
+
);
|
|
1357
|
+
|
|
1358
|
+
// Add comparison overrides to the transient intervals
|
|
1359
|
+
(transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
|
|
1360
|
+
(transientEndInterval as Partial<HasComparisonOverride>)[forceCompare] = 1;
|
|
1361
|
+
|
|
1362
|
+
this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
|
|
1363
|
+
return results;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
export function createEndpointInRangeIndex<TInterval extends ISerializableInterval>(
|
|
1368
|
+
helpers: IIntervalHelpers<TInterval>,
|
|
1369
|
+
client: Client,
|
|
1370
|
+
): IEndpointInRangeIndex<TInterval> {
|
|
1371
|
+
return new EndpointInRangeIndex<TInterval>(helpers, client);
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
export function createStartpointInRangeIndex<TInterval extends ISerializableInterval>(
|
|
1375
|
+
helpers: IIntervalHelpers<TInterval>,
|
|
1376
|
+
client: Client,
|
|
1377
|
+
): IStartpointInRangeIndex<TInterval> {
|
|
1378
|
+
return new StartpointInRangeIndex<TInterval>(helpers, client);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1039
1381
|
export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
1040
1382
|
private static readonly legacyIdPrefix = "legacy";
|
|
1041
1383
|
public readonly overlappingIntervalsIndex: OverlappingIntervalsIndex<TInterval>;
|
|
1042
1384
|
public readonly idIntervalIndex: IdIntervalIndex<TInterval>;
|
|
1043
1385
|
public readonly endIntervalIndex: EndpointIndex<TInterval>;
|
|
1044
|
-
private readonly indexes: IntervalIndex<TInterval
|
|
1386
|
+
private readonly indexes: Set<IntervalIndex<TInterval>>;
|
|
1045
1387
|
|
|
1046
1388
|
constructor(
|
|
1047
1389
|
private readonly client: Client,
|
|
@@ -1056,11 +1398,11 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
1056
1398
|
this.overlappingIntervalsIndex = new OverlappingIntervalsIndex(client, helpers);
|
|
1057
1399
|
this.idIntervalIndex = new IdIntervalIndex();
|
|
1058
1400
|
this.endIntervalIndex = new EndpointIndex(client, helpers);
|
|
1059
|
-
this.indexes = [
|
|
1401
|
+
this.indexes = new Set([
|
|
1060
1402
|
this.overlappingIntervalsIndex,
|
|
1061
1403
|
this.idIntervalIndex,
|
|
1062
1404
|
this.endIntervalIndex,
|
|
1063
|
-
];
|
|
1405
|
+
]);
|
|
1064
1406
|
}
|
|
1065
1407
|
|
|
1066
1408
|
public createLegacyId(start: number, end: number): string {
|
|
@@ -1104,6 +1446,14 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
1104
1446
|
}
|
|
1105
1447
|
}
|
|
1106
1448
|
|
|
1449
|
+
public appendIndex(index: IntervalIndex<TInterval>) {
|
|
1450
|
+
this.indexes.add(index);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
public removeIndex(index: IntervalIndex<TInterval>): boolean {
|
|
1454
|
+
return this.indexes.delete(index);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1107
1457
|
public removeExistingInterval(interval: TInterval) {
|
|
1108
1458
|
this.removeIntervalFromIndexes(interval);
|
|
1109
1459
|
this.removeIntervalListeners(interval);
|
|
@@ -1114,8 +1464,18 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
1114
1464
|
end: number,
|
|
1115
1465
|
intervalType: IntervalType,
|
|
1116
1466
|
op?: ISequencedDocumentMessage,
|
|
1467
|
+
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
1117
1468
|
): TInterval {
|
|
1118
|
-
return this.helpers.create(
|
|
1469
|
+
return this.helpers.create(
|
|
1470
|
+
this.label,
|
|
1471
|
+
start,
|
|
1472
|
+
end,
|
|
1473
|
+
this.client,
|
|
1474
|
+
intervalType,
|
|
1475
|
+
op,
|
|
1476
|
+
undefined,
|
|
1477
|
+
stickiness,
|
|
1478
|
+
);
|
|
1119
1479
|
}
|
|
1120
1480
|
|
|
1121
1481
|
public addInterval(
|
|
@@ -1124,8 +1484,9 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
1124
1484
|
intervalType: IntervalType,
|
|
1125
1485
|
props?: PropertySet,
|
|
1126
1486
|
op?: ISequencedDocumentMessage,
|
|
1487
|
+
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
1127
1488
|
) {
|
|
1128
|
-
const interval: TInterval = this.createInterval(start, end, intervalType, op);
|
|
1489
|
+
const interval: TInterval = this.createInterval(start, end, intervalType, op, stickiness);
|
|
1129
1490
|
if (interval) {
|
|
1130
1491
|
if (!interval.properties) {
|
|
1131
1492
|
interval.properties = createMap<any>();
|
|
@@ -1201,6 +1562,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
1201
1562
|
ref.getOffset(),
|
|
1202
1563
|
ReferenceType.Transient,
|
|
1203
1564
|
ref.properties,
|
|
1565
|
+
ref.slidingPreference,
|
|
1204
1566
|
);
|
|
1205
1567
|
};
|
|
1206
1568
|
if (interval instanceof SequenceInterval) {
|
|
@@ -1243,18 +1605,36 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
1243
1605
|
export const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
|
|
1244
1606
|
compareReferencePositions(a.end, b.end);
|
|
1245
1607
|
|
|
1608
|
+
export const compareSequenceIntervalStarts = (a: SequenceInterval, b: SequenceInterval): number =>
|
|
1609
|
+
compareReferencePositions(a.start, b.start);
|
|
1610
|
+
|
|
1611
|
+
export const sequenceIntervalHelpers: IIntervalHelpers<SequenceInterval> = {
|
|
1612
|
+
compareEnds: compareSequenceIntervalEnds,
|
|
1613
|
+
compareStarts: compareSequenceIntervalStarts,
|
|
1614
|
+
create: createSequenceInterval,
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
export const intervalHelpers: IIntervalHelpers<Interval> = {
|
|
1618
|
+
compareEnds: (a: Interval, b: Interval) => a.end - b.end,
|
|
1619
|
+
compareStarts: (a: Interval, b: Interval) => a.start - b.start,
|
|
1620
|
+
create: createInterval,
|
|
1621
|
+
};
|
|
1622
|
+
|
|
1246
1623
|
class SequenceIntervalCollectionFactory
|
|
1247
1624
|
implements IValueFactory<IntervalCollection<SequenceInterval>>
|
|
1248
1625
|
{
|
|
1249
1626
|
public load(
|
|
1250
1627
|
emitter: IValueOpEmitter,
|
|
1251
1628
|
raw: ISerializedInterval[] | ISerializedIntervalCollectionV2 = [],
|
|
1629
|
+
options?: Partial<SequenceOptions>,
|
|
1252
1630
|
): IntervalCollection<SequenceInterval> {
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1631
|
+
return new IntervalCollection<SequenceInterval>(
|
|
1632
|
+
sequenceIntervalHelpers,
|
|
1633
|
+
true,
|
|
1634
|
+
emitter,
|
|
1635
|
+
raw,
|
|
1636
|
+
options,
|
|
1637
|
+
);
|
|
1258
1638
|
}
|
|
1259
1639
|
|
|
1260
1640
|
public store(
|
|
@@ -1289,7 +1669,15 @@ export class SequenceIntervalCollectionValueType
|
|
|
1289
1669
|
|
|
1290
1670
|
const compareIntervalEnds = (a: Interval, b: Interval) => a.end - b.end;
|
|
1291
1671
|
|
|
1292
|
-
function createInterval(
|
|
1672
|
+
function createInterval(
|
|
1673
|
+
label: string,
|
|
1674
|
+
start: number,
|
|
1675
|
+
end: number,
|
|
1676
|
+
client: Client,
|
|
1677
|
+
intervalType?: IntervalType,
|
|
1678
|
+
op?: ISequencedDocumentMessage,
|
|
1679
|
+
fromSnapshot?: boolean,
|
|
1680
|
+
): Interval {
|
|
1293
1681
|
const rangeProp: PropertySet = {};
|
|
1294
1682
|
|
|
1295
1683
|
if (label && label.length > 0) {
|
|
@@ -1303,12 +1691,13 @@ class IntervalCollectionFactory implements IValueFactory<IntervalCollection<Inte
|
|
|
1303
1691
|
public load(
|
|
1304
1692
|
emitter: IValueOpEmitter,
|
|
1305
1693
|
raw: ISerializedInterval[] | ISerializedIntervalCollectionV2 = [],
|
|
1694
|
+
options?: Partial<SequenceOptions>,
|
|
1306
1695
|
): IntervalCollection<Interval> {
|
|
1307
1696
|
const helpers: IIntervalHelpers<Interval> = {
|
|
1308
1697
|
compareEnds: compareIntervalEnds,
|
|
1309
1698
|
create: createInterval,
|
|
1310
1699
|
};
|
|
1311
|
-
const collection = new IntervalCollection<Interval>(helpers, false, emitter, raw);
|
|
1700
|
+
const collection = new IntervalCollection<Interval>(helpers, false, emitter, raw, options);
|
|
1312
1701
|
collection.attachGraph(undefined as any as Client, "");
|
|
1313
1702
|
return collection;
|
|
1314
1703
|
}
|
|
@@ -1355,7 +1744,7 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
|
|
|
1355
1744
|
|
|
1356
1745
|
return new Map<string, IValueOperation<IntervalCollection<T>>>([
|
|
1357
1746
|
[
|
|
1358
|
-
|
|
1747
|
+
IntervalOpType.ADD,
|
|
1359
1748
|
{
|
|
1360
1749
|
process: (collection, params, local, op, localOpMetadata) => {
|
|
1361
1750
|
// if params is undefined, the interval was deleted during
|
|
@@ -1370,7 +1759,7 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
|
|
|
1370
1759
|
},
|
|
1371
1760
|
],
|
|
1372
1761
|
[
|
|
1373
|
-
|
|
1762
|
+
IntervalOpType.DELETE,
|
|
1374
1763
|
{
|
|
1375
1764
|
process: (collection, params, local, op) => {
|
|
1376
1765
|
assert(op !== undefined, 0x3fc /* op should exist here */);
|
|
@@ -1383,7 +1772,7 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
|
|
|
1383
1772
|
},
|
|
1384
1773
|
],
|
|
1385
1774
|
[
|
|
1386
|
-
|
|
1775
|
+
IntervalOpType.CHANGE,
|
|
1387
1776
|
{
|
|
1388
1777
|
process: (collection, params, local, op, localOpMetadata) => {
|
|
1389
1778
|
// if params is undefined, the interval was deleted during
|
|
@@ -1402,7 +1791,7 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
|
|
|
1402
1791
|
|
|
1403
1792
|
export type DeserializeCallback = (properties: PropertySet) => void;
|
|
1404
1793
|
|
|
1405
|
-
|
|
1794
|
+
class IntervalCollectionIterator<TInterval extends ISerializableInterval>
|
|
1406
1795
|
implements Iterator<TInterval>
|
|
1407
1796
|
{
|
|
1408
1797
|
private readonly results: TInterval[];
|
|
@@ -1495,14 +1884,137 @@ export interface IIntervalCollectionEvent<TInterval extends ISerializableInterva
|
|
|
1495
1884
|
|
|
1496
1885
|
/**
|
|
1497
1886
|
* Collection of intervals that supports addition, modification, removal, and efficient spatial querying.
|
|
1498
|
-
*
|
|
1499
|
-
* integrate into a DDS.
|
|
1500
|
-
* This aligns with its usage in `SharedSegmentSequence`, which allows associating intervals to positions in the
|
|
1501
|
-
* sequence DDS which are broadcast to all other clients in an eventually consistent fashion.
|
|
1887
|
+
* Changes to this collection will be incur updates on collaborating clients (i.e. they are not local-only).
|
|
1502
1888
|
*/
|
|
1503
|
-
export
|
|
1504
|
-
IIntervalCollectionEvent<TInterval
|
|
1505
|
-
|
|
1889
|
+
export interface IIntervalCollection<TInterval extends ISerializableInterval>
|
|
1890
|
+
extends TypedEventEmitter<IIntervalCollectionEvent<TInterval>> {
|
|
1891
|
+
readonly attached: boolean;
|
|
1892
|
+
/**
|
|
1893
|
+
* Attaches an index to this collection.
|
|
1894
|
+
* All intervals which are part of this collection will be added to the index, and the index will automatically
|
|
1895
|
+
* be updated when this collection updates due to local or remote changes.
|
|
1896
|
+
*
|
|
1897
|
+
* @remarks - After attaching an index to an interval collection, applications should typically store this
|
|
1898
|
+
* index somewhere in their in-memory data model for future reference and querying.
|
|
1899
|
+
*/
|
|
1900
|
+
attachIndex(index: IntervalIndex<TInterval>): void;
|
|
1901
|
+
/**
|
|
1902
|
+
* Detaches an index from this collection.
|
|
1903
|
+
* All intervals which are part of this collection will be removed from the index, and updates to this collection
|
|
1904
|
+
* due to local or remote changes will no longer incur updates to the index.
|
|
1905
|
+
*
|
|
1906
|
+
* @returns - Return false if the target index cannot be found in the indexes, otherwise remove all intervals in the index and return true
|
|
1907
|
+
*/
|
|
1908
|
+
detachIndex(index: IntervalIndex<TInterval>): boolean;
|
|
1909
|
+
/**
|
|
1910
|
+
* @returns the interval in this collection that has the provided `id`.
|
|
1911
|
+
* If no interval in the collection has this `id`, returns `undefined`.
|
|
1912
|
+
*/
|
|
1913
|
+
getIntervalById(id: string): TInterval | undefined;
|
|
1914
|
+
/**
|
|
1915
|
+
* Creates a new interval and add it to the collection.
|
|
1916
|
+
* @param start - interval start position (inclusive)
|
|
1917
|
+
* @param end - interval end position (exclusive)
|
|
1918
|
+
* @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
|
|
1919
|
+
* @param props - properties of the interval
|
|
1920
|
+
* @param stickiness - {@link (IntervalStickiness:type)} to apply to the added interval.
|
|
1921
|
+
* @returns - the created interval
|
|
1922
|
+
* @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
|
|
1923
|
+
* with how the current half-open behavior is represented.
|
|
1924
|
+
*/
|
|
1925
|
+
add(
|
|
1926
|
+
start: number,
|
|
1927
|
+
end: number,
|
|
1928
|
+
intervalType: IntervalType,
|
|
1929
|
+
props?: PropertySet,
|
|
1930
|
+
stickiness?: IntervalStickiness,
|
|
1931
|
+
): TInterval;
|
|
1932
|
+
/**
|
|
1933
|
+
* Removes an interval from the collection.
|
|
1934
|
+
* @param id - Id of the interval to remove
|
|
1935
|
+
* @returns the removed interval
|
|
1936
|
+
*/
|
|
1937
|
+
removeIntervalById(id: string): TInterval | undefined;
|
|
1938
|
+
/**
|
|
1939
|
+
* Changes the properties on an existing interval.
|
|
1940
|
+
* @param id - Id of the interval whose properties should be changed
|
|
1941
|
+
* @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
|
|
1942
|
+
* and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
|
|
1943
|
+
*/
|
|
1944
|
+
changeProperties(id: string, props: PropertySet);
|
|
1945
|
+
/**
|
|
1946
|
+
* Changes the endpoints of an existing interval.
|
|
1947
|
+
* @param id - Id of the interval to change
|
|
1948
|
+
* @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1949
|
+
* @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1950
|
+
* @returns the interval that was changed, if it existed in the collection.
|
|
1951
|
+
*/
|
|
1952
|
+
change(id: string, start?: number, end?: number): TInterval | undefined;
|
|
1953
|
+
|
|
1954
|
+
attachDeserializer(onDeserialize: DeserializeCallback): void;
|
|
1955
|
+
/**
|
|
1956
|
+
* @returns an iterator over all intervals in this collection.
|
|
1957
|
+
*/
|
|
1958
|
+
[Symbol.iterator](): Iterator<TInterval>;
|
|
1959
|
+
|
|
1960
|
+
/**
|
|
1961
|
+
* @returns a forward iterator over all intervals in this collection with start point equal to `startPosition`.
|
|
1962
|
+
*/
|
|
1963
|
+
CreateForwardIteratorWithStartPosition(startPosition: number): Iterator<TInterval>;
|
|
1964
|
+
|
|
1965
|
+
/**
|
|
1966
|
+
* @returns a backward iterator over all intervals in this collection with start point equal to `startPosition`.
|
|
1967
|
+
*/
|
|
1968
|
+
CreateBackwardIteratorWithStartPosition(startPosition: number): Iterator<TInterval>;
|
|
1969
|
+
|
|
1970
|
+
/**
|
|
1971
|
+
* @returns a forward iterator over all intervals in this collection with end point equal to `endPosition`.
|
|
1972
|
+
*/
|
|
1973
|
+
CreateForwardIteratorWithEndPosition(endPosition: number): Iterator<TInterval>;
|
|
1974
|
+
|
|
1975
|
+
/**
|
|
1976
|
+
* @returns a backward iterator over all intervals in this collection with end point equal to `endPosition`.
|
|
1977
|
+
*/
|
|
1978
|
+
CreateBackwardIteratorWithEndPosition(endPosition: number): Iterator<TInterval>;
|
|
1979
|
+
|
|
1980
|
+
/**
|
|
1981
|
+
* Gathers iteration results that optionally match a start/end criteria into the provided array.
|
|
1982
|
+
* @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
|
|
1983
|
+
* intervals matching the query upon edit.
|
|
1984
|
+
* @param iteratesForward - whether or not iteration should be in the forward direction
|
|
1985
|
+
* @param start - If provided, only match intervals whose start point is equal to `start`.
|
|
1986
|
+
* @param end - If provided, only match intervals whose end point is equal to `end`.
|
|
1987
|
+
*/
|
|
1988
|
+
gatherIterationResults(
|
|
1989
|
+
results: TInterval[],
|
|
1990
|
+
iteratesForward: boolean,
|
|
1991
|
+
start?: number,
|
|
1992
|
+
end?: number,
|
|
1993
|
+
): void;
|
|
1994
|
+
|
|
1995
|
+
/**
|
|
1996
|
+
* @returns an array of all intervals in this collection that overlap with the interval
|
|
1997
|
+
* `[startPosition, endPosition]`.
|
|
1998
|
+
*/
|
|
1999
|
+
findOverlappingIntervals(startPosition: number, endPosition: number): TInterval[];
|
|
2000
|
+
|
|
2001
|
+
/**
|
|
2002
|
+
* Applies a function to each interval in this collection.
|
|
2003
|
+
*/
|
|
2004
|
+
map(fn: (interval: TInterval) => void): void;
|
|
2005
|
+
|
|
2006
|
+
previousInterval(pos: number): TInterval | undefined;
|
|
2007
|
+
|
|
2008
|
+
nextInterval(pos: number): TInterval | undefined;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
/**
|
|
2012
|
+
* {@inheritdoc IIntervalCollection}
|
|
2013
|
+
*/
|
|
2014
|
+
export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
2015
|
+
extends TypedEventEmitter<IIntervalCollectionEvent<TInterval>>
|
|
2016
|
+
implements IIntervalCollection<TInterval>
|
|
2017
|
+
{
|
|
1506
2018
|
private savedSerializedIntervals?: ISerializedInterval[];
|
|
1507
2019
|
private localCollection: LocalIntervalCollection<TInterval> | undefined;
|
|
1508
2020
|
private onDeserialize: DeserializeCallback | undefined;
|
|
@@ -1534,6 +2046,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1534
2046
|
private readonly requiresClient: boolean,
|
|
1535
2047
|
private readonly emitter: IValueOpEmitter,
|
|
1536
2048
|
serializedIntervals: ISerializedInterval[] | ISerializedIntervalCollectionV2,
|
|
2049
|
+
private readonly options: Partial<SequenceOptions> = {},
|
|
1537
2050
|
) {
|
|
1538
2051
|
super();
|
|
1539
2052
|
|
|
@@ -1544,6 +2057,40 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1544
2057
|
);
|
|
1545
2058
|
}
|
|
1546
2059
|
|
|
2060
|
+
/**
|
|
2061
|
+
* {@inheritdoc IIntervalCollection.attachIndex}
|
|
2062
|
+
*/
|
|
2063
|
+
public attachIndex(index: IntervalIndex<TInterval>): void {
|
|
2064
|
+
if (!this.attached) {
|
|
2065
|
+
throw new LoggingError("The local interval collection must exist");
|
|
2066
|
+
}
|
|
2067
|
+
for (const interval of this) {
|
|
2068
|
+
index.add(interval);
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
this.localCollection?.appendIndex(index);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
/**
|
|
2075
|
+
* {@inheritdoc IIntervalCollection.detachIndex}
|
|
2076
|
+
*/
|
|
2077
|
+
public detachIndex(index: IntervalIndex<TInterval>): boolean {
|
|
2078
|
+
if (!this.attached) {
|
|
2079
|
+
throw new LoggingError("The local interval collection must exist");
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
// Avoid removing intervals if the index does not exist
|
|
2083
|
+
if (!this.localCollection?.removeIndex(index)) {
|
|
2084
|
+
return false;
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
for (const interval of this) {
|
|
2088
|
+
index.remove(interval);
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
return true;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
1547
2094
|
private rebasePositionWithSegmentSlide(
|
|
1548
2095
|
pos: number,
|
|
1549
2096
|
seqNumberFrom: number,
|
|
@@ -1634,7 +2181,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1634
2181
|
if (this.savedSerializedIntervals) {
|
|
1635
2182
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
1636
2183
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1637
|
-
const { start, end, intervalType, properties } = serializedInterval;
|
|
2184
|
+
const { start, end, intervalType, properties, stickiness } = serializedInterval;
|
|
1638
2185
|
const interval = this.helpers.create(
|
|
1639
2186
|
label,
|
|
1640
2187
|
start,
|
|
@@ -1643,6 +2190,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1643
2190
|
intervalType,
|
|
1644
2191
|
undefined,
|
|
1645
2192
|
true,
|
|
2193
|
+
stickiness,
|
|
1646
2194
|
);
|
|
1647
2195
|
if (properties) {
|
|
1648
2196
|
interval.addProperties(properties);
|
|
@@ -1689,8 +2237,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1689
2237
|
}
|
|
1690
2238
|
|
|
1691
2239
|
/**
|
|
1692
|
-
* @
|
|
1693
|
-
* If no interval in the collection has this `id`, returns `undefined`.
|
|
2240
|
+
* {@inheritdoc IIntervalCollection.getIntervalById}
|
|
1694
2241
|
*/
|
|
1695
2242
|
public getIntervalById(id: string) {
|
|
1696
2243
|
if (!this.localCollection) {
|
|
@@ -1700,20 +2247,14 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1700
2247
|
}
|
|
1701
2248
|
|
|
1702
2249
|
/**
|
|
1703
|
-
*
|
|
1704
|
-
* @param start - interval start position (inclusive)
|
|
1705
|
-
* @param end - interval end position (exclusive)
|
|
1706
|
-
* @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
|
|
1707
|
-
* @param props - properties of the interval
|
|
1708
|
-
* @returns - the created interval
|
|
1709
|
-
* @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
|
|
1710
|
-
* with how the current half-open behavior is represented.
|
|
2250
|
+
* {@inheritdoc IIntervalCollection.add}
|
|
1711
2251
|
*/
|
|
1712
2252
|
public add(
|
|
1713
2253
|
start: number,
|
|
1714
2254
|
end: number,
|
|
1715
2255
|
intervalType: IntervalType,
|
|
1716
2256
|
props?: PropertySet,
|
|
2257
|
+
stickiness: IntervalStickiness = IntervalStickiness.END,
|
|
1717
2258
|
): TInterval {
|
|
1718
2259
|
if (!this.localCollection) {
|
|
1719
2260
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
@@ -1721,12 +2262,19 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1721
2262
|
if (intervalType & IntervalType.Transient) {
|
|
1722
2263
|
throw new LoggingError("Can not add transient intervals");
|
|
1723
2264
|
}
|
|
2265
|
+
if (stickiness !== IntervalStickiness.END && !this.options.intervalStickinessEnabled) {
|
|
2266
|
+
throw new UsageError(
|
|
2267
|
+
"attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag",
|
|
2268
|
+
);
|
|
2269
|
+
}
|
|
1724
2270
|
|
|
1725
2271
|
const interval: TInterval = this.localCollection.addInterval(
|
|
1726
2272
|
start,
|
|
1727
2273
|
end,
|
|
1728
2274
|
intervalType,
|
|
1729
2275
|
props,
|
|
2276
|
+
undefined,
|
|
2277
|
+
stickiness,
|
|
1730
2278
|
);
|
|
1731
2279
|
|
|
1732
2280
|
if (interval) {
|
|
@@ -1736,6 +2284,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1736
2284
|
properties: interval.properties,
|
|
1737
2285
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1738
2286
|
start,
|
|
2287
|
+
stickiness,
|
|
1739
2288
|
};
|
|
1740
2289
|
const localSeq = this.getNextLocalSeq();
|
|
1741
2290
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1776,9 +2325,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1776
2325
|
}
|
|
1777
2326
|
|
|
1778
2327
|
/**
|
|
1779
|
-
*
|
|
1780
|
-
* @param id - Id of the interval to remove
|
|
1781
|
-
* @returns the removed interval
|
|
2328
|
+
* {@inheritdoc IIntervalCollection.removeIntervalById}
|
|
1782
2329
|
*/
|
|
1783
2330
|
public removeIntervalById(id: string) {
|
|
1784
2331
|
if (!this.localCollection) {
|
|
@@ -1792,10 +2339,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1792
2339
|
}
|
|
1793
2340
|
|
|
1794
2341
|
/**
|
|
1795
|
-
*
|
|
1796
|
-
* @param id - Id of the interval whose properties should be changed
|
|
1797
|
-
* @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
|
|
1798
|
-
* and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
|
|
2342
|
+
* {@inheritdoc IIntervalCollection.changeProperties}
|
|
1799
2343
|
*/
|
|
1800
2344
|
public changeProperties(id: string, props: PropertySet) {
|
|
1801
2345
|
if (!this.attached) {
|
|
@@ -1829,11 +2373,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
1829
2373
|
}
|
|
1830
2374
|
|
|
1831
2375
|
/**
|
|
1832
|
-
*
|
|
1833
|
-
* @param id - Id of the interval to change
|
|
1834
|
-
* @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1835
|
-
* @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1836
|
-
* @returns the interval that was changed, if it existed in the collection.
|
|
2376
|
+
* {@inheritdoc IIntervalCollection.change}
|
|
1837
2377
|
*/
|
|
1838
2378
|
public change(id: string, start?: number, end?: number): TInterval | undefined {
|
|
1839
2379
|
if (!this.localCollection) {
|
|
@@ -2021,6 +2561,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2021
2561
|
}
|
|
2022
2562
|
}
|
|
2023
2563
|
|
|
2564
|
+
/**
|
|
2565
|
+
* {@inheritdoc IIntervalCollection.attachDeserializer}
|
|
2566
|
+
*/
|
|
2024
2567
|
public attachDeserializer(onDeserialize: DeserializeCallback): void {
|
|
2025
2568
|
// If no deserializer is specified can skip all processing work
|
|
2026
2569
|
if (!onDeserialize) {
|
|
@@ -2188,6 +2731,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2188
2731
|
newStart,
|
|
2189
2732
|
interval.start.refType,
|
|
2190
2733
|
op,
|
|
2734
|
+
startReferenceSlidingPreference(interval.stickiness),
|
|
2191
2735
|
);
|
|
2192
2736
|
if (props) {
|
|
2193
2737
|
interval.start.addProperties(props);
|
|
@@ -2205,6 +2749,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2205
2749
|
newEnd,
|
|
2206
2750
|
interval.end.refType,
|
|
2207
2751
|
op,
|
|
2752
|
+
endReferenceSlidingPreference(interval.stickiness),
|
|
2208
2753
|
);
|
|
2209
2754
|
if (props) {
|
|
2210
2755
|
interval.end.addProperties(props);
|
|
@@ -2253,6 +2798,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2253
2798
|
serializedInterval.intervalType,
|
|
2254
2799
|
serializedInterval.properties,
|
|
2255
2800
|
op,
|
|
2801
|
+
serializedInterval.stickiness,
|
|
2256
2802
|
);
|
|
2257
2803
|
|
|
2258
2804
|
if (interval) {
|
|
@@ -2310,7 +2856,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2310
2856
|
}
|
|
2311
2857
|
|
|
2312
2858
|
/**
|
|
2313
|
-
* @
|
|
2859
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithStartPosition}
|
|
2314
2860
|
*/
|
|
2315
2861
|
public CreateForwardIteratorWithStartPosition(
|
|
2316
2862
|
startPosition: number,
|
|
@@ -2320,7 +2866,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2320
2866
|
}
|
|
2321
2867
|
|
|
2322
2868
|
/**
|
|
2323
|
-
* @
|
|
2869
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithStartPosition}
|
|
2324
2870
|
*/
|
|
2325
2871
|
public CreateBackwardIteratorWithStartPosition(
|
|
2326
2872
|
startPosition: number,
|
|
@@ -2330,7 +2876,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2330
2876
|
}
|
|
2331
2877
|
|
|
2332
2878
|
/**
|
|
2333
|
-
* @
|
|
2879
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithEndPosition}
|
|
2334
2880
|
*/
|
|
2335
2881
|
public CreateForwardIteratorWithEndPosition(
|
|
2336
2882
|
endPosition: number,
|
|
@@ -2345,7 +2891,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2345
2891
|
}
|
|
2346
2892
|
|
|
2347
2893
|
/**
|
|
2348
|
-
* @
|
|
2894
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithEndPosition}
|
|
2349
2895
|
*/
|
|
2350
2896
|
public CreateBackwardIteratorWithEndPosition(
|
|
2351
2897
|
endPosition: number,
|
|
@@ -2360,12 +2906,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2360
2906
|
}
|
|
2361
2907
|
|
|
2362
2908
|
/**
|
|
2363
|
-
*
|
|
2364
|
-
* @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
|
|
2365
|
-
* intervals matching the query upon edit.
|
|
2366
|
-
* @param iteratesForward - whether or not iteration should be in the forward direction
|
|
2367
|
-
* @param start - If provided, only match intervals whose start point is equal to `start`.
|
|
2368
|
-
* @param end - If provided, only match intervals whose end point is equal to `end`.
|
|
2909
|
+
* {@inheritdoc IIntervalCollection.gatherIterationResults}
|
|
2369
2910
|
*/
|
|
2370
2911
|
public gatherIterationResults(
|
|
2371
2912
|
results: TInterval[],
|
|
@@ -2386,8 +2927,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2386
2927
|
}
|
|
2387
2928
|
|
|
2388
2929
|
/**
|
|
2389
|
-
* @
|
|
2390
|
-
* `[startPosition, endPosition]`.
|
|
2930
|
+
* {@inheritdoc IIntervalCollection.findOverlappingIntervals}
|
|
2391
2931
|
*/
|
|
2392
2932
|
public findOverlappingIntervals(startPosition: number, endPosition: number): TInterval[] {
|
|
2393
2933
|
if (!this.localCollection) {
|
|
@@ -2401,7 +2941,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2401
2941
|
}
|
|
2402
2942
|
|
|
2403
2943
|
/**
|
|
2404
|
-
*
|
|
2944
|
+
* {@inheritdoc IIntervalCollection.map}
|
|
2405
2945
|
*/
|
|
2406
2946
|
public map(fn: (interval: TInterval) => void) {
|
|
2407
2947
|
if (!this.localCollection) {
|
|
@@ -2413,6 +2953,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2413
2953
|
}
|
|
2414
2954
|
}
|
|
2415
2955
|
|
|
2956
|
+
/**
|
|
2957
|
+
* {@inheritdoc IIntervalCollection.previousInterval}
|
|
2958
|
+
*/
|
|
2416
2959
|
public previousInterval(pos: number): TInterval | undefined {
|
|
2417
2960
|
if (!this.localCollection) {
|
|
2418
2961
|
throw new LoggingError("attachSequence must be called");
|
|
@@ -2421,6 +2964,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
|
|
|
2421
2964
|
return this.localCollection.endIntervalIndex.previousInterval(pos);
|
|
2422
2965
|
}
|
|
2423
2966
|
|
|
2967
|
+
/**
|
|
2968
|
+
* {@inheritdoc IIntervalCollection.nextInterval}
|
|
2969
|
+
*/
|
|
2424
2970
|
public nextInterval(pos: number): TInterval | undefined {
|
|
2425
2971
|
if (!this.localCollection) {
|
|
2426
2972
|
throw new LoggingError("attachSequence must be called");
|