@fluidframework/sequence 2.0.0-internal.6.3.3 → 2.0.0-internal.7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +130 -0
  3. package/dist/defaultMap.d.ts +1 -1
  4. package/dist/defaultMap.d.ts.map +1 -1
  5. package/dist/defaultMap.js +6 -6
  6. package/dist/defaultMap.js.map +1 -1
  7. package/dist/defaultMapInterfaces.d.ts +21 -2
  8. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  9. package/dist/defaultMapInterfaces.js.map +1 -1
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +2 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/intervalCollection.d.ts +140 -22
  15. package/dist/intervalCollection.d.ts.map +1 -1
  16. package/dist/intervalCollection.js +146 -49
  17. package/dist/intervalCollection.js.map +1 -1
  18. package/dist/intervalIndex/endpointInRangeIndex.d.ts +13 -3
  19. package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
  20. package/dist/intervalIndex/endpointInRangeIndex.js +9 -6
  21. package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -1
  22. package/dist/intervalIndex/endpointIndex.d.ts +13 -2
  23. package/dist/intervalIndex/endpointIndex.d.ts.map +1 -1
  24. package/dist/intervalIndex/endpointIndex.js +7 -5
  25. package/dist/intervalIndex/endpointIndex.js.map +1 -1
  26. package/dist/intervalIndex/idIntervalIndex.js.map +1 -1
  27. package/dist/intervalIndex/index.d.ts +4 -4
  28. package/dist/intervalIndex/index.d.ts.map +1 -1
  29. package/dist/intervalIndex/index.js +5 -1
  30. package/dist/intervalIndex/index.js.map +1 -1
  31. package/dist/intervalIndex/intervalIndex.d.ts +2 -2
  32. package/dist/intervalIndex/intervalIndex.js.map +1 -1
  33. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +8 -6
  34. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  35. package/dist/intervalIndex/overlappingIntervalsIndex.js +11 -4
  36. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  37. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +2 -2
  38. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -1
  39. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +3 -1
  40. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
  41. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +1 -1
  42. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
  43. package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
  44. package/dist/intervalIndex/startpointInRangeIndex.d.ts +13 -3
  45. package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
  46. package/dist/intervalIndex/startpointInRangeIndex.js +9 -8
  47. package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -1
  48. package/dist/intervalTree.d.ts +1 -1
  49. package/dist/intervalTree.d.ts.map +1 -1
  50. package/dist/intervals/interval.d.ts +4 -3
  51. package/dist/intervals/interval.d.ts.map +1 -1
  52. package/dist/intervals/interval.js +14 -6
  53. package/dist/intervals/interval.js.map +1 -1
  54. package/dist/intervals/intervalUtils.d.ts +42 -20
  55. package/dist/intervals/intervalUtils.d.ts.map +1 -1
  56. package/dist/intervals/intervalUtils.js +12 -10
  57. package/dist/intervals/intervalUtils.js.map +1 -1
  58. package/dist/intervals/sequenceInterval.d.ts +30 -17
  59. package/dist/intervals/sequenceInterval.d.ts.map +1 -1
  60. package/dist/intervals/sequenceInterval.js +124 -45
  61. package/dist/intervals/sequenceInterval.js.map +1 -1
  62. package/dist/packageVersion.d.ts +1 -1
  63. package/dist/packageVersion.js +1 -1
  64. package/dist/packageVersion.js.map +1 -1
  65. package/dist/revertibles.d.ts +3 -15
  66. package/dist/revertibles.d.ts.map +1 -1
  67. package/dist/revertibles.js +6 -17
  68. package/dist/revertibles.js.map +1 -1
  69. package/dist/sequence.d.ts +3 -2
  70. package/dist/sequence.d.ts.map +1 -1
  71. package/dist/sequence.js +46 -45
  72. package/dist/sequence.js.map +1 -1
  73. package/dist/sequenceDeltaEvent.d.ts +8 -3
  74. package/dist/sequenceDeltaEvent.d.ts.map +1 -1
  75. package/dist/sequenceDeltaEvent.js.map +1 -1
  76. package/dist/sequenceFactory.js +1 -1
  77. package/dist/sequenceFactory.js.map +1 -1
  78. package/dist/sharedIntervalCollection.js +9 -9
  79. package/dist/sharedIntervalCollection.js.map +1 -1
  80. package/dist/sharedSequence.js +6 -6
  81. package/dist/sharedSequence.js.map +1 -1
  82. package/dist/sharedString.d.ts +1 -1
  83. package/dist/sharedString.d.ts.map +1 -1
  84. package/dist/sharedString.js +7 -6
  85. package/dist/sharedString.js.map +1 -1
  86. package/dist/tsdoc-metadata.json +1 -1
  87. package/lib/defaultMap.d.ts +1 -1
  88. package/lib/defaultMap.d.ts.map +1 -1
  89. package/lib/defaultMap.js +6 -6
  90. package/lib/defaultMap.js.map +1 -1
  91. package/lib/defaultMapInterfaces.d.ts +21 -2
  92. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  93. package/lib/defaultMapInterfaces.js.map +1 -1
  94. package/lib/index.d.ts +2 -2
  95. package/lib/index.d.ts.map +1 -1
  96. package/lib/index.js +1 -1
  97. package/lib/index.js.map +1 -1
  98. package/lib/intervalCollection.d.ts +140 -22
  99. package/lib/intervalCollection.d.ts.map +1 -1
  100. package/lib/intervalCollection.js +144 -50
  101. package/lib/intervalCollection.js.map +1 -1
  102. package/lib/intervalIndex/endpointInRangeIndex.d.ts +13 -3
  103. package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
  104. package/lib/intervalIndex/endpointInRangeIndex.js +9 -7
  105. package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -1
  106. package/lib/intervalIndex/endpointIndex.d.ts +13 -2
  107. package/lib/intervalIndex/endpointIndex.d.ts.map +1 -1
  108. package/lib/intervalIndex/endpointIndex.js +7 -6
  109. package/lib/intervalIndex/endpointIndex.js.map +1 -1
  110. package/lib/intervalIndex/idIntervalIndex.js.map +1 -1
  111. package/lib/intervalIndex/index.d.ts +4 -4
  112. package/lib/intervalIndex/index.d.ts.map +1 -1
  113. package/lib/intervalIndex/index.js +4 -4
  114. package/lib/intervalIndex/index.js.map +1 -1
  115. package/lib/intervalIndex/intervalIndex.d.ts +2 -2
  116. package/lib/intervalIndex/intervalIndex.js.map +1 -1
  117. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +8 -6
  118. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  119. package/lib/intervalIndex/overlappingIntervalsIndex.js +12 -5
  120. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  121. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +2 -2
  122. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -1
  123. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +3 -1
  124. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
  125. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +1 -1
  126. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
  127. package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
  128. package/lib/intervalIndex/startpointInRangeIndex.d.ts +13 -3
  129. package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
  130. package/lib/intervalIndex/startpointInRangeIndex.js +9 -9
  131. package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -1
  132. package/lib/intervalTree.d.ts +1 -1
  133. package/lib/intervalTree.d.ts.map +1 -1
  134. package/lib/intervals/interval.d.ts +4 -3
  135. package/lib/intervals/interval.d.ts.map +1 -1
  136. package/lib/intervals/interval.js +14 -6
  137. package/lib/intervals/interval.js.map +1 -1
  138. package/lib/intervals/intervalUtils.d.ts +42 -20
  139. package/lib/intervals/intervalUtils.d.ts.map +1 -1
  140. package/lib/intervals/intervalUtils.js +8 -6
  141. package/lib/intervals/intervalUtils.js.map +1 -1
  142. package/lib/intervals/sequenceInterval.d.ts +30 -17
  143. package/lib/intervals/sequenceInterval.d.ts.map +1 -1
  144. package/lib/intervals/sequenceInterval.js +125 -44
  145. package/lib/intervals/sequenceInterval.js.map +1 -1
  146. package/lib/packageVersion.d.ts +1 -1
  147. package/lib/packageVersion.js +1 -1
  148. package/lib/packageVersion.js.map +1 -1
  149. package/lib/revertibles.d.ts +3 -15
  150. package/lib/revertibles.d.ts.map +1 -1
  151. package/lib/revertibles.js +6 -17
  152. package/lib/revertibles.js.map +1 -1
  153. package/lib/sequence.d.ts +3 -2
  154. package/lib/sequence.d.ts.map +1 -1
  155. package/lib/sequence.js +46 -45
  156. package/lib/sequence.js.map +1 -1
  157. package/lib/sequenceDeltaEvent.d.ts +8 -3
  158. package/lib/sequenceDeltaEvent.d.ts.map +1 -1
  159. package/lib/sequenceDeltaEvent.js.map +1 -1
  160. package/lib/sequenceFactory.js +1 -1
  161. package/lib/sequenceFactory.js.map +1 -1
  162. package/lib/sharedIntervalCollection.js +9 -9
  163. package/lib/sharedIntervalCollection.js.map +1 -1
  164. package/lib/sharedSequence.js +6 -6
  165. package/lib/sharedSequence.js.map +1 -1
  166. package/lib/sharedString.d.ts +1 -1
  167. package/lib/sharedString.d.ts.map +1 -1
  168. package/lib/sharedString.js +7 -6
  169. package/lib/sharedString.js.map +1 -1
  170. package/package.json +49 -23
  171. package/src/defaultMapInterfaces.ts +21 -2
  172. package/src/index.ts +4 -1
  173. package/src/intervalCollection.ts +347 -84
  174. package/src/intervalIndex/endpointInRangeIndex.ts +19 -11
  175. package/src/intervalIndex/endpointIndex.ts +16 -9
  176. package/src/intervalIndex/idIntervalIndex.ts +1 -1
  177. package/src/intervalIndex/index.ts +12 -3
  178. package/src/intervalIndex/intervalIndex.ts +2 -2
  179. package/src/intervalIndex/overlappingIntervalsIndex.ts +31 -15
  180. package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +4 -1
  181. package/src/intervalIndex/sequenceIntervalIndexes.ts +1 -1
  182. package/src/intervalIndex/startpointInRangeIndex.ts +19 -17
  183. package/src/intervals/interval.ts +30 -8
  184. package/src/intervals/intervalUtils.ts +51 -28
  185. package/src/intervals/sequenceInterval.ts +197 -49
  186. package/src/packageVersion.ts +1 -1
  187. package/src/revertibles.ts +8 -33
  188. package/src/sequence.ts +5 -2
  189. package/src/sequenceDeltaEvent.ts +11 -3
  190. package/src/sequenceFactory.ts +1 -1
  191. package/src/sharedString.ts +2 -1
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  /* eslint-disable no-bitwise */
7
+ /* eslint-disable import/no-deprecated */
7
8
 
8
9
  import {
9
10
  Client,
@@ -26,6 +27,13 @@ import {
26
27
  import { assert } from "@fluidframework/core-utils";
27
28
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
28
29
  import { UsageError } from "@fluidframework/telemetry-utils";
30
+ import {
31
+ SequencePlace,
32
+ Side,
33
+ computeStickinessFromSide,
34
+ endpointPosAndSide,
35
+ sidesFromStickiness,
36
+ } from "../intervalCollection";
29
37
  import {
30
38
  IIntervalHelpers,
31
39
  ISerializableInterval,
@@ -38,14 +46,42 @@ import {
38
46
 
39
47
  const reservedIntervalIdKey = "intervalId";
40
48
 
49
+ function compareSides(sideA: Side, sideB: Side): number {
50
+ if (sideA === sideB) {
51
+ return 0;
52
+ }
53
+
54
+ if (sideA === Side.Before) {
55
+ return 1;
56
+ }
57
+
58
+ return -1;
59
+ }
60
+
61
+ function minSide(sideA: Side, sideB: Side): Side {
62
+ if (sideA === Side.After && sideB === Side.After) {
63
+ return Side.After;
64
+ }
65
+
66
+ return Side.Before;
67
+ }
68
+
69
+ function maxSide(sideA: Side, sideB: Side): Side {
70
+ if (sideA === Side.Before && sideB === Side.Before) {
71
+ return Side.Before;
72
+ }
73
+
74
+ return Side.After;
75
+ }
76
+
41
77
  /**
42
78
  * Interval implementation whose ends are associated with positions in a mutatable sequence.
43
79
  * As such, when content is inserted into the middle of the interval, the interval expands to
44
80
  * include that content.
45
81
  *
46
- * @remarks - The endpoint's position should be treated exclusively to get reasonable behavior--i.e.
47
- * an interval referring to "hello" in "hello world" should have a start position of 0 and an end
48
- * position of 5.
82
+ * @remarks The endpoints' positions should be treated exclusively to get
83
+ * reasonable behavior. E.g., an interval referring to "hello" in "hello world"
84
+ * should have a start position of 0 and an end position of 5.
49
85
  *
50
86
  * To see why, consider what happens if "llo wor" is removed from the string to make "held".
51
87
  * The interval's startpoint remains on the "h" (it isn't altered), but the interval's endpoint
@@ -55,9 +91,15 @@ const reservedIntervalIdKey = "intervalId";
55
91
  * If the interval endpoint was treated inclusively, the interval would now refer to "hel", which
56
92
  * is undesirable.
57
93
  *
58
- * Since the end of an interval is treated exclusively but cannot be greater than or equal to the
59
- * length of the associated sequence, application models which leverage interval collections should
60
- * consider inserting a marker at the end of the sequence to represent the end of the content.
94
+ * Since the endpoints of an interval are treated exclusively but cannot be greater
95
+ * than or equal to the length of the associated sequence, there exist special
96
+ * endpoint segments, "start" and "end", which represent the position immediately
97
+ * before or immediately after the string respectively.
98
+ *
99
+ * If a `SequenceInterval` is created on a sequence with the
100
+ * `mergeTreeReferencesCanSlideToEndpoint` feature flag set to true, the endpoints
101
+ * of the interval that are exclusive will have the ability to slide to these
102
+ * special endpoint segments.
61
103
  */
62
104
  export class SequenceInterval implements ISerializableInterval {
63
105
  /**
@@ -70,21 +112,36 @@ export class SequenceInterval implements ISerializableInterval {
70
112
  */
71
113
  public propertyManager: PropertiesManager;
72
114
 
115
+ /**
116
+ * @internal
117
+ */
118
+ public get stickiness(): IntervalStickiness {
119
+ const startSegment = this.start.getSegment();
120
+ const endSegment = this.end.getSegment();
121
+ return computeStickinessFromSide(
122
+ startSegment?.endpointType,
123
+ this.startSide,
124
+ endSegment?.endpointType,
125
+ this.endSide,
126
+ );
127
+ }
128
+
73
129
  constructor(
74
130
  private readonly client: Client,
75
131
  /**
76
132
  * Start endpoint of this interval.
77
- * @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
133
+ * @remarks This endpoint can be resolved into a character position using the SharedString it's a part of.
78
134
  */
79
135
  public start: LocalReferencePosition,
80
136
  /**
81
137
  * End endpoint of this interval.
82
- * @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
138
+ * @remarks This endpoint can be resolved into a character position using the SharedString it's a part of.
83
139
  */
84
140
  public end: LocalReferencePosition,
85
141
  public intervalType: IntervalType,
86
142
  props?: PropertySet,
87
- public readonly stickiness: IntervalStickiness = IntervalStickiness.END,
143
+ public readonly startSide: Side = Side.Before,
144
+ public readonly endSide: Side = Side.Before,
88
145
  ) {
89
146
  this.propertyManager = new PropertiesManager();
90
147
  this.properties = {};
@@ -136,19 +193,20 @@ export class SequenceInterval implements ISerializableInterval {
136
193
  public serialize(): ISerializedInterval {
137
194
  const startPosition = this.client.localReferencePositionToPosition(this.start);
138
195
  const endPosition = this.client.localReferencePositionToPosition(this.end);
196
+ const { startSide, endSide } = sidesFromStickiness(this.stickiness);
139
197
  const serializedInterval: ISerializedInterval = {
140
198
  end: endPosition,
141
199
  intervalType: this.intervalType,
142
200
  sequenceNumber: this.client.getCurrentSeq(),
143
201
  start: startPosition,
202
+ stickiness: this.stickiness,
203
+ startSide,
204
+ endSide,
144
205
  };
145
206
 
146
207
  if (this.properties) {
147
208
  serializedInterval.properties = this.properties;
148
209
  }
149
- if (this.stickiness !== IntervalStickiness.END) {
150
- serializedInterval.stickiness = this.stickiness;
151
- }
152
210
 
153
211
  return serializedInterval;
154
212
  }
@@ -163,7 +221,8 @@ export class SequenceInterval implements ISerializableInterval {
163
221
  this.end,
164
222
  this.intervalType,
165
223
  this.properties,
166
- this.stickiness,
224
+ this.startSide,
225
+ this.endSide,
167
226
  );
168
227
  }
169
228
 
@@ -196,14 +255,26 @@ export class SequenceInterval implements ISerializableInterval {
196
255
  * {@inheritDoc IInterval.compareStart}
197
256
  */
198
257
  public compareStart(b: SequenceInterval) {
199
- return compareReferencePositions(this.start, b.start);
258
+ const dist = compareReferencePositions(this.start, b.start);
259
+
260
+ if (dist === 0) {
261
+ return compareSides(this.startSide, b.startSide);
262
+ }
263
+
264
+ return dist;
200
265
  }
201
266
 
202
267
  /**
203
268
  * {@inheritDoc IInterval.compareEnd}
204
269
  */
205
- public compareEnd(b: SequenceInterval) {
206
- return compareReferencePositions(this.end, b.end);
270
+ public compareEnd(b: SequenceInterval): number {
271
+ const dist = compareReferencePositions(this.end, b.end);
272
+
273
+ if (dist === 0) {
274
+ return compareSides(b.endSide, this.endSide);
275
+ }
276
+
277
+ return dist;
207
278
  }
208
279
 
209
280
  /**
@@ -230,11 +301,33 @@ export class SequenceInterval implements ISerializableInterval {
230
301
  * @internal
231
302
  */
232
303
  public union(b: SequenceInterval) {
304
+ const newStart = minReferencePosition(this.start, b.start);
305
+ const newEnd = maxReferencePosition(this.end, b.end);
306
+
307
+ let startSide: Side;
308
+
309
+ if (this.start === b.start) {
310
+ startSide = minSide(this.startSide, b.startSide);
311
+ } else {
312
+ startSide = this.start === newStart ? this.startSide : b.startSide;
313
+ }
314
+
315
+ let endSide: Side;
316
+
317
+ if (this.end === b.end) {
318
+ endSide = maxSide(this.endSide, b.endSide);
319
+ } else {
320
+ endSide = this.end === newEnd ? this.endSide : b.endSide;
321
+ }
322
+
233
323
  return new SequenceInterval(
234
324
  this.client,
235
- minReferencePosition(this.start, b.start),
236
- maxReferencePosition(this.end, b.end),
325
+ newStart,
326
+ newEnd,
237
327
  this.intervalType,
328
+ undefined,
329
+ startSide,
330
+ endSide,
238
331
  );
239
332
  }
240
333
 
@@ -267,12 +360,19 @@ export class SequenceInterval implements ISerializableInterval {
267
360
  */
268
361
  public modify(
269
362
  label: string,
270
- start: number,
271
- end: number,
363
+ start: SequencePlace | undefined,
364
+ end: SequencePlace | undefined,
272
365
  op?: ISequencedDocumentMessage,
273
366
  localSeq?: number,
274
- stickiness: IntervalStickiness = IntervalStickiness.END,
367
+ useNewSlidingBehavior: boolean = false,
275
368
  ) {
369
+ const { startSide, endSide, startPos, endPos } = endpointPosAndSide(start, end);
370
+ const stickiness = computeStickinessFromSide(
371
+ startPos ?? this.start.getSegment()?.endpointType,
372
+ startSide ?? this.startSide,
373
+ endPos ?? this.end.getSegment()?.endpointType,
374
+ endSide ?? this.endSide,
375
+ );
276
376
  const getRefType = (baseType: ReferenceType): ReferenceType => {
277
377
  let refType = baseType;
278
378
  if (op === undefined) {
@@ -283,15 +383,17 @@ export class SequenceInterval implements ISerializableInterval {
283
383
  };
284
384
 
285
385
  let startRef = this.start;
286
- if (start !== undefined) {
386
+ if (startPos !== undefined) {
287
387
  startRef = createPositionReference(
288
388
  this.client,
289
- start,
389
+ startPos,
290
390
  getRefType(this.start.refType),
291
391
  op,
292
392
  undefined,
293
393
  localSeq,
294
394
  startReferenceSlidingPreference(stickiness),
395
+ startReferenceSlidingPreference(stickiness) === SlidingPreference.BACKWARD,
396
+ useNewSlidingBehavior,
295
397
  );
296
398
  if (this.start.properties) {
297
399
  startRef.addProperties(this.start.properties);
@@ -299,22 +401,32 @@ export class SequenceInterval implements ISerializableInterval {
299
401
  }
300
402
 
301
403
  let endRef = this.end;
302
- if (end !== undefined) {
404
+ if (endPos !== undefined) {
303
405
  endRef = createPositionReference(
304
406
  this.client,
305
- end,
407
+ endPos,
306
408
  getRefType(this.end.refType),
307
409
  op,
308
410
  undefined,
309
411
  localSeq,
310
412
  endReferenceSlidingPreference(stickiness),
413
+ endReferenceSlidingPreference(stickiness) === SlidingPreference.FORWARD,
414
+ useNewSlidingBehavior,
311
415
  );
312
416
  if (this.end.properties) {
313
417
  endRef.addProperties(this.end.properties);
314
418
  }
315
419
  }
316
420
 
317
- const newInterval = new SequenceInterval(this.client, startRef, endRef, this.intervalType);
421
+ const newInterval = new SequenceInterval(
422
+ this.client,
423
+ startRef,
424
+ endRef,
425
+ this.intervalType,
426
+ undefined,
427
+ startSide ?? this.startSide,
428
+ endSide ?? this.endSide,
429
+ );
318
430
  if (this.properties) {
319
431
  newInterval.initializeProperties();
320
432
  this.propertyManager.copyTo(
@@ -338,13 +450,25 @@ export class SequenceInterval implements ISerializableInterval {
338
450
 
339
451
  export function createPositionReferenceFromSegoff(
340
452
  client: Client,
341
- segoff: { segment: ISegment | undefined; offset: number | undefined },
453
+ segoff: { segment: ISegment | undefined; offset: number | undefined } | "start" | "end",
342
454
  refType: ReferenceType,
343
455
  op?: ISequencedDocumentMessage,
344
456
  localSeq?: number,
345
457
  fromSnapshot?: boolean,
346
458
  slidingPreference?: SlidingPreference,
459
+ canSlideToEndpoint?: boolean,
347
460
  ): LocalReferencePosition {
461
+ if (segoff === "start" || segoff === "end") {
462
+ return client.createLocalReferencePosition(
463
+ segoff,
464
+ undefined,
465
+ refType,
466
+ undefined,
467
+ slidingPreference,
468
+ canSlideToEndpoint,
469
+ );
470
+ }
471
+
348
472
  if (segoff.segment) {
349
473
  const ref = client.createLocalReferencePosition(
350
474
  segoff.segment,
@@ -352,6 +476,7 @@ export function createPositionReferenceFromSegoff(
352
476
  refType,
353
477
  undefined,
354
478
  slidingPreference,
479
+ canSlideToEndpoint,
355
480
  );
356
481
  return ref;
357
482
  }
@@ -375,30 +500,40 @@ export function createPositionReferenceFromSegoff(
375
500
 
376
501
  function createPositionReference(
377
502
  client: Client,
378
- pos: number,
503
+ pos: number | "start" | "end",
379
504
  refType: ReferenceType,
380
505
  op?: ISequencedDocumentMessage,
381
506
  fromSnapshot?: boolean,
382
507
  localSeq?: number,
383
508
  slidingPreference?: SlidingPreference,
509
+ exclusive: boolean = false,
510
+ useNewSlidingBehavior: boolean = false,
384
511
  ): LocalReferencePosition {
385
512
  let segoff;
513
+
386
514
  if (op) {
387
515
  assert(
388
516
  (refType & ReferenceType.SlideOnRemove) !== 0,
389
517
  0x2f5 /* op create references must be SlideOnRemove */,
390
518
  );
391
- segoff = client.getContainingSegment(pos, {
392
- referenceSequenceNumber: op.referenceSequenceNumber,
393
- clientId: op.clientId,
394
- });
395
- segoff = getSlideToSegoff(segoff);
519
+ if (pos === "start" || pos === "end") {
520
+ segoff = pos;
521
+ } else {
522
+ segoff = client.getContainingSegment(pos, {
523
+ referenceSequenceNumber: op.referenceSequenceNumber,
524
+ clientId: op.clientId,
525
+ });
526
+ segoff = getSlideToSegoff(segoff, undefined, useNewSlidingBehavior);
527
+ }
396
528
  } else {
397
529
  assert(
398
530
  (refType & ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot,
399
531
  0x2f6 /* SlideOnRemove references must be op created */,
400
532
  );
401
- segoff = client.getContainingSegment(pos, undefined, localSeq);
533
+ segoff =
534
+ pos === "start" || pos === "end"
535
+ ? pos
536
+ : client.getContainingSegment(pos, undefined, localSeq);
402
537
  }
403
538
 
404
539
  return createPositionReferenceFromSegoff(
@@ -409,19 +544,32 @@ function createPositionReference(
409
544
  localSeq,
410
545
  fromSnapshot,
411
546
  slidingPreference,
547
+ exclusive,
412
548
  );
413
549
  }
414
550
 
415
551
  export function createSequenceInterval(
416
552
  label: string,
417
- start: number,
418
- end: number,
553
+ start: SequencePlace | undefined,
554
+ end: SequencePlace | undefined,
419
555
  client: Client,
420
556
  intervalType: IntervalType,
421
557
  op?: ISequencedDocumentMessage,
422
558
  fromSnapshot?: boolean,
423
- stickiness: IntervalStickiness = IntervalStickiness.END,
559
+ useNewSlidingBehavior: boolean = false,
424
560
  ): SequenceInterval {
561
+ const { startPos, startSide, endPos, endSide } = endpointPosAndSide(
562
+ start ?? "start",
563
+ end ?? "end",
564
+ );
565
+ assert(
566
+ startPos !== undefined &&
567
+ endPos !== undefined &&
568
+ startSide !== undefined &&
569
+ endSide !== undefined,
570
+ 0x794 /* start and end cannot be undefined because they were not passed in as undefined */,
571
+ );
572
+ const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
425
573
  let beginRefType = ReferenceType.RangeBegin;
426
574
  let endRefType = ReferenceType.RangeEnd;
427
575
  if (intervalType === IntervalType.Transient) {
@@ -435,7 +583,7 @@ export function createSequenceInterval(
435
583
  // All non-transient interval references must eventually be SlideOnRemove
436
584
  // To ensure eventual consistency, they must start as StayOnRemove when
437
585
  // pending (created locally and creation op is not acked)
438
- if (op || fromSnapshot) {
586
+ if (op ?? fromSnapshot) {
439
587
  beginRefType |= ReferenceType.SlideOnRemove;
440
588
  endRefType |= ReferenceType.SlideOnRemove;
441
589
  } else {
@@ -446,22 +594,26 @@ export function createSequenceInterval(
446
594
 
447
595
  const startLref = createPositionReference(
448
596
  client,
449
- start,
597
+ startPos,
450
598
  beginRefType,
451
599
  op,
452
600
  fromSnapshot,
453
601
  undefined,
454
602
  startReferenceSlidingPreference(stickiness),
603
+ startReferenceSlidingPreference(stickiness) === SlidingPreference.BACKWARD,
604
+ useNewSlidingBehavior,
455
605
  );
456
606
 
457
607
  const endLref = createPositionReference(
458
608
  client,
459
- end,
609
+ endPos,
460
610
  endRefType,
461
611
  op,
462
612
  fromSnapshot,
463
613
  undefined,
464
614
  endReferenceSlidingPreference(stickiness),
615
+ endReferenceSlidingPreference(stickiness) === SlidingPreference.FORWARD,
616
+ useNewSlidingBehavior,
465
617
  );
466
618
 
467
619
  const rangeProp = {
@@ -476,19 +628,15 @@ export function createSequenceInterval(
476
628
  endLref,
477
629
  intervalType,
478
630
  rangeProp,
479
- stickiness,
631
+ startSide,
632
+ endSide,
480
633
  );
481
634
  return ival;
482
635
  }
483
636
 
484
- export const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
485
- compareReferencePositions(a.end, b.end);
486
-
487
- export const compareSequenceIntervalStarts = (a: SequenceInterval, b: SequenceInterval): number =>
488
- compareReferencePositions(a.start, b.start);
489
-
637
+ /**
638
+ * @deprecated The methods within have substitutions
639
+ */
490
640
  export const sequenceIntervalHelpers: IIntervalHelpers<SequenceInterval> = {
491
- compareEnds: compareSequenceIntervalEnds,
492
- compareStarts: compareSequenceIntervalStarts,
493
641
  create: createSequenceInterval,
494
642
  };
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/sequence";
9
- export const pkgVersion = "2.0.0-internal.6.3.3";
9
+ export const pkgVersion = "2.0.0-internal.7.0.0";
@@ -27,9 +27,6 @@ import { ISequenceDeltaRange, SequenceDeltaEvent } from "./sequenceDeltaEvent";
27
27
  /**
28
28
  * Data for undoing edits on SharedStrings and Intervals.
29
29
  *
30
- * Revertibles are new and require the option mergeTreeUseNewLengthCalculations to
31
- * be set as true on the underlying merge tree in order to function correctly.
32
- *
33
30
  * @alpha
34
31
  */
35
32
  export type SharedStringRevertible = MergeTreeDeltaRevertible | IntervalRevertible;
@@ -41,9 +38,6 @@ type IntervalOpType = typeof IntervalOpType[keyof typeof IntervalOpType];
41
38
  /**
42
39
  * Data for undoing edits affecting Intervals.
43
40
  *
44
- * Revertibles are new and require the option mergeTreeUseNewLengthCalculations to
45
- * be set as true on the underlying merge tree in order to function correctly.
46
- *
47
41
  * @alpha
48
42
  */
49
43
  export type IntervalRevertible =
@@ -271,9 +265,6 @@ function addIfRevertibleRef(
271
265
  * Create revertibles for SharedStringDeltas, handling indirectly modified intervals
272
266
  * (e.g. reverting remove of a range that contains an interval will move the interval back)
273
267
  *
274
- * Revertibles are new and require the option mergeTreeUseNewLengthCalculations to
275
- * be set as true on the underlying merge tree in order to function correctly.
276
- *
277
268
  * @alpha
278
269
  */
279
270
  export function appendSharedStringDeltaToRevertibles(
@@ -537,27 +528,14 @@ function revertLocalSequenceRemove(
537
528
  const intervalId = getUpdatedId(intervalInfo.intervalId);
538
529
  const interval = intervalCollection.getIntervalById(intervalId);
539
530
  if (interval !== undefined) {
540
- const newStart = newEndpointPosition(
541
- intervalInfo.startOffset,
542
- restoredRanges,
543
- sharedString,
544
- );
545
- const newEnd = newEndpointPosition(
546
- intervalInfo.endOffset,
547
- restoredRanges,
548
- sharedString,
549
- );
550
- // only move interval if start <= end
551
- if (
552
- (newStart === undefined &&
553
- newEnd !== undefined &&
554
- sharedString.localReferencePositionToPosition(interval.start) <= newEnd) ||
555
- (newEnd === undefined &&
556
- newStart !== undefined &&
557
- sharedString.localReferencePositionToPosition(interval.end) >= newStart) ||
558
- (newStart !== undefined && newEnd !== undefined && newStart <= newEnd)
559
- ) {
560
- intervalCollection.change(intervalId, newStart, newEnd);
531
+ const start =
532
+ newEndpointPosition(intervalInfo.startOffset, restoredRanges, sharedString) ??
533
+ sharedString.localReferencePositionToPosition(interval.start);
534
+ const end =
535
+ newEndpointPosition(intervalInfo.endOffset, restoredRanges, sharedString) ??
536
+ sharedString.localReferencePositionToPosition(interval.end);
537
+ if (start <= end) {
538
+ intervalCollection.change(intervalId, start, end);
561
539
  }
562
540
  }
563
541
  });
@@ -597,9 +575,6 @@ function revertLocalSequenceRemove(
597
575
  /**
598
576
  * Invoke revertibles to reverse prior edits
599
577
  *
600
- * Revertibles are new and require the option mergeTreeUseNewLengthCalculations to
601
- * be set as true on the underlying merge tree in order to function correctly.
602
- *
603
578
  * @alpha
604
579
  */
605
580
  export function revertSharedStringRevertibles(
package/src/sequence.ts CHANGED
@@ -340,6 +340,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
340
340
  refType: ReferenceType,
341
341
  properties: PropertySet | undefined,
342
342
  slidingPreference?: SlidingPreference,
343
+ canSlideToEndpoint?: boolean,
343
344
  ): LocalReferencePosition {
344
345
  return this.client.createLocalReferencePosition(
345
346
  segment,
@@ -347,6 +348,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
347
348
  refType,
348
349
  properties,
349
350
  slidingPreference,
351
+ canSlideToEndpoint,
350
352
  );
351
353
  }
352
354
 
@@ -452,7 +454,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
452
454
  }
453
455
 
454
456
  /**
455
- * @returns - The most recent sequence number which has been acked by the server and processed by this
457
+ * @returns The most recent sequence number which has been acked by the server and processed by this
456
458
  * SharedSegmentSequence.
457
459
  */
458
460
  public getCurrentSeq() {
@@ -489,6 +491,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
489
491
  * @returns An iterable object that enumerates the IntervalCollection labels.
490
492
  *
491
493
  * @example
494
+ *
492
495
  * ```typescript
493
496
  * const iter = this.getIntervalCollectionKeys();
494
497
  * for (key of iter)
@@ -704,7 +707,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
704
707
  * {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
705
708
  */
706
709
  protected applyStashedOp(content: any): unknown {
707
- return this.client.applyStashedOp(content);
710
+ return this.client.applyStashedOp(parseHandles(content, this.serializer));
708
711
  }
709
712
 
710
713
  private summarizeMergeTree(serializer: IFluidSerializer): ISummaryTreeWithStats {
@@ -147,25 +147,33 @@ export interface ISequenceDeltaRange<
147
147
  > {
148
148
  /**
149
149
  * The type of operation that changed this range.
150
- * @remarks - Consuming code should typically compare this to the enum values defined in
150
+ *
151
+ * @remarks Consuming code should typically compare this to the enum values defined in
151
152
  * `MergeTreeDeltaOperationTypes`.
152
153
  */
153
154
  operation: TOperation;
155
+
154
156
  /**
155
157
  * The index of the start of the range.
156
158
  */
157
159
  position: number;
160
+
158
161
  /**
159
162
  * The segment that corresponds to the range.
160
163
  */
161
164
  segment: ISegment;
165
+
162
166
  /**
163
167
  * Deltas object which contains all modified properties with their previous values.
164
168
  * Since `undefined` doesn't survive a round-trip through JSON serialization, the old value being absent
165
169
  * is instead encoded with `null`.
166
- * @remarks - This object is motivated by undo/redo scenarios, and provides a convenient "inverse op" to apply to
170
+ *
171
+ * @remarks This object is motivated by undo/redo scenarios, and provides a convenient "inverse op" to apply to
167
172
  * undo a property change.
168
- * @example - If a segment initially had properties `{ foo: "1", bar: 2 }` and it was annotated with
173
+ *
174
+ * @example
175
+ *
176
+ * If a segment initially had properties `{ foo: "1", bar: 2 }` and it was annotated with
169
177
  * `{ foo: 3, baz: 5 }`, the corresponding event would have a `propertyDeltas` of `{ foo: "1", baz: null }`.
170
178
  */
171
179
  propertyDeltas: PropertySet;
@@ -15,7 +15,7 @@ import { SharedString, SharedStringSegment } from "./sharedString";
15
15
 
16
16
  export class SharedStringFactory implements IChannelFactory {
17
17
  // TODO rename back to https://graph.microsoft.com/types/mergeTree/string once paparazzi is able to dynamically
18
- // load code
18
+ // load code (UPDATE: paparazzi is gone... anything to do here?)
19
19
  public static Type = "https://graph.microsoft.com/types/mergeTree";
20
20
 
21
21
  public static readonly Attributes: IChannelAttributes = {
@@ -406,7 +406,8 @@ const gatherTextAndMarkers: ISegmentAction<ITextAndMarkerAccumulator> = (
406
406
  if (placeholder && placeholder.length > 0) {
407
407
  const placeholderText =
408
408
  placeholder === "*"
409
- ? `\n${segment.toString()}`
409
+ ? // eslint-disable-next-line @typescript-eslint/no-base-to-string
410
+ `\n${segment.toString()}`
410
411
  : placeholder.repeat(segment.cachedLength);
411
412
  textSegment.text += placeholderText;
412
413
  } else {