@fluidframework/merge-tree 1.2.6 → 2.0.0-dev.1.3.0.96595
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.js +12 -0
- package/README.md +2 -2
- package/dist/MergeTreeTextHelper.d.ts +23 -0
- package/dist/MergeTreeTextHelper.d.ts.map +1 -0
- package/dist/MergeTreeTextHelper.js +133 -0
- package/dist/MergeTreeTextHelper.js.map +1 -0
- package/dist/base.d.ts +2 -26
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js.map +1 -1
- package/dist/client.d.ts +27 -16
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +81 -101
- package/dist/client.js.map +1 -1
- package/dist/collections/heap.d.ts +28 -0
- package/dist/collections/heap.d.ts.map +1 -0
- package/dist/collections/heap.js +65 -0
- package/dist/collections/heap.js.map +1 -0
- package/dist/collections/index.d.ts +11 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/collections/index.js +23 -0
- package/dist/collections/index.js.map +1 -0
- package/dist/collections/intervalTree.d.ts +60 -0
- package/dist/collections/intervalTree.d.ts.map +1 -0
- package/dist/collections/intervalTree.js +99 -0
- package/dist/collections/intervalTree.js.map +1 -0
- package/dist/collections/list.d.ts +39 -0
- package/dist/collections/list.d.ts.map +1 -0
- package/dist/collections/list.js +155 -0
- package/dist/collections/list.js.map +1 -0
- package/dist/collections/rbTree.d.ts +154 -0
- package/dist/collections/rbTree.d.ts.map +1 -0
- package/dist/{collections.js → collections/rbTree.js} +15 -478
- package/dist/collections/rbTree.js.map +1 -0
- package/dist/collections/stack.d.ts +16 -0
- package/dist/collections/stack.d.ts.map +1 -0
- package/dist/collections/stack.js +30 -0
- package/dist/collections/stack.js.map +1 -0
- package/dist/collections/tst.d.ts +55 -0
- package/dist/collections/tst.d.ts.map +1 -0
- package/dist/collections/tst.js +171 -0
- package/dist/collections/tst.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/localReference.d.ts +48 -99
- package/dist/localReference.d.ts.map +1 -1
- package/dist/localReference.js +132 -169
- package/dist/localReference.js.map +1 -1
- package/dist/mergeTree.d.ts +71 -302
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +395 -642
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeDeltaCallback.d.ts +1 -1
- package/dist/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/dist/mergeTreeDeltaCallback.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +344 -0
- package/dist/mergeTreeNodes.d.ts.map +1 -0
- package/dist/mergeTreeNodes.js +383 -0
- package/dist/mergeTreeNodes.js.map +1 -0
- package/dist/mergeTreeTracking.d.ts +1 -1
- package/dist/mergeTreeTracking.d.ts.map +1 -1
- package/dist/mergeTreeTracking.js.map +1 -1
- package/dist/opBuilder.d.ts +1 -1
- package/dist/opBuilder.d.ts.map +1 -1
- package/dist/opBuilder.js.map +1 -1
- package/dist/partialLengths.d.ts +188 -18
- package/dist/partialLengths.d.ts.map +1 -1
- package/dist/partialLengths.js +495 -253
- package/dist/partialLengths.js.map +1 -1
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js.map +1 -1
- package/dist/referencePositions.d.ts +6 -26
- package/dist/referencePositions.d.ts.map +1 -1
- package/dist/referencePositions.js +3 -20
- package/dist/referencePositions.js.map +1 -1
- package/dist/segmentGroupCollection.d.ts +3 -1
- package/dist/segmentGroupCollection.d.ts.map +1 -1
- package/dist/segmentGroupCollection.js +14 -1
- package/dist/segmentGroupCollection.js.map +1 -1
- package/dist/segmentPropertiesManager.d.ts +10 -1
- package/dist/segmentPropertiesManager.d.ts.map +1 -1
- package/dist/segmentPropertiesManager.js +42 -13
- package/dist/segmentPropertiesManager.js.map +1 -1
- package/dist/snapshotChunks.d.ts +2 -1
- package/dist/snapshotChunks.d.ts.map +1 -1
- package/dist/snapshotChunks.js.map +1 -1
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/snapshotV1.d.ts +1 -1
- package/dist/snapshotV1.d.ts.map +1 -1
- package/dist/snapshotV1.js +1 -1
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.d.ts +5 -1
- package/dist/snapshotlegacy.d.ts.map +1 -1
- package/dist/snapshotlegacy.js +4 -0
- package/dist/snapshotlegacy.js.map +1 -1
- package/dist/sortedSegmentSet.d.ts +1 -1
- package/dist/sortedSegmentSet.d.ts.map +1 -1
- package/dist/sortedSegmentSet.js.map +1 -1
- package/dist/textSegment.d.ts +7 -7
- package/dist/textSegment.d.ts.map +1 -1
- package/dist/textSegment.js +3 -125
- package/dist/textSegment.js.map +1 -1
- package/{DEV.md → docs/DEV.md} +2 -2
- package/docs/Obliterate.md +639 -0
- package/{REFERENCEPOSITIONS.md → docs/REFERENCEPOSITIONS.md} +2 -2
- package/lib/MergeTreeTextHelper.d.ts +23 -0
- package/lib/MergeTreeTextHelper.d.ts.map +1 -0
- package/lib/MergeTreeTextHelper.js +129 -0
- package/lib/MergeTreeTextHelper.js.map +1 -0
- package/lib/base.d.ts +2 -26
- package/lib/base.d.ts.map +1 -1
- package/lib/base.js.map +1 -1
- package/lib/client.d.ts +27 -16
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +79 -99
- package/lib/client.js.map +1 -1
- package/lib/collections/heap.d.ts +28 -0
- package/lib/collections/heap.d.ts.map +1 -0
- package/lib/collections/heap.js +61 -0
- package/lib/collections/heap.js.map +1 -0
- package/lib/collections/index.d.ts +11 -0
- package/lib/collections/index.d.ts.map +1 -0
- package/lib/collections/index.js +11 -0
- package/lib/collections/index.js.map +1 -0
- package/lib/collections/intervalTree.d.ts +60 -0
- package/lib/collections/intervalTree.d.ts.map +1 -0
- package/lib/collections/intervalTree.js +94 -0
- package/lib/collections/intervalTree.js.map +1 -0
- package/lib/collections/list.d.ts +39 -0
- package/lib/collections/list.d.ts.map +1 -0
- package/lib/collections/list.js +149 -0
- package/lib/collections/list.js.map +1 -0
- package/lib/collections/rbTree.d.ts +154 -0
- package/lib/collections/rbTree.d.ts.map +1 -0
- package/lib/{collections.js → collections/rbTree.js} +14 -469
- package/lib/collections/rbTree.js.map +1 -0
- package/lib/collections/stack.d.ts +16 -0
- package/lib/collections/stack.d.ts.map +1 -0
- package/lib/collections/stack.js +26 -0
- package/lib/collections/stack.js.map +1 -0
- package/lib/collections/tst.d.ts +55 -0
- package/lib/collections/tst.d.ts.map +1 -0
- package/lib/collections/tst.js +167 -0
- package/lib/collections/tst.js.map +1 -0
- package/lib/index.d.ts +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/localReference.d.ts +48 -99
- package/lib/localReference.d.ts.map +1 -1
- package/lib/localReference.js +132 -170
- package/lib/localReference.js.map +1 -1
- package/lib/mergeTree.d.ts +71 -302
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +371 -607
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeDeltaCallback.d.ts +1 -1
- package/lib/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/lib/mergeTreeDeltaCallback.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +344 -0
- package/lib/mergeTreeNodes.d.ts.map +1 -0
- package/lib/mergeTreeNodes.js +369 -0
- package/lib/mergeTreeNodes.js.map +1 -0
- package/lib/mergeTreeTracking.d.ts +1 -1
- package/lib/mergeTreeTracking.d.ts.map +1 -1
- package/lib/mergeTreeTracking.js.map +1 -1
- package/lib/opBuilder.d.ts +1 -1
- package/lib/opBuilder.d.ts.map +1 -1
- package/lib/opBuilder.js.map +1 -1
- package/lib/partialLengths.d.ts +188 -18
- package/lib/partialLengths.d.ts.map +1 -1
- package/lib/partialLengths.js +491 -249
- package/lib/partialLengths.js.map +1 -1
- package/lib/properties.d.ts.map +1 -1
- package/lib/properties.js.map +1 -1
- package/lib/referencePositions.d.ts +6 -26
- package/lib/referencePositions.d.ts.map +1 -1
- package/lib/referencePositions.js +3 -20
- package/lib/referencePositions.js.map +1 -1
- package/lib/segmentGroupCollection.d.ts +3 -1
- package/lib/segmentGroupCollection.d.ts.map +1 -1
- package/lib/segmentGroupCollection.js +14 -1
- package/lib/segmentGroupCollection.js.map +1 -1
- package/lib/segmentPropertiesManager.d.ts +10 -1
- package/lib/segmentPropertiesManager.d.ts.map +1 -1
- package/lib/segmentPropertiesManager.js +42 -13
- package/lib/segmentPropertiesManager.js.map +1 -1
- package/lib/snapshotChunks.d.ts +2 -1
- package/lib/snapshotChunks.d.ts.map +1 -1
- package/lib/snapshotChunks.js.map +1 -1
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/snapshotV1.d.ts +1 -1
- package/lib/snapshotV1.d.ts.map +1 -1
- package/lib/snapshotV1.js +1 -1
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.d.ts +5 -1
- package/lib/snapshotlegacy.d.ts.map +1 -1
- package/lib/snapshotlegacy.js +4 -0
- package/lib/snapshotlegacy.js.map +1 -1
- package/lib/sortedSegmentSet.d.ts +1 -1
- package/lib/sortedSegmentSet.d.ts.map +1 -1
- package/lib/sortedSegmentSet.js.map +1 -1
- package/lib/textSegment.d.ts +7 -7
- package/lib/textSegment.d.ts.map +1 -1
- package/lib/textSegment.js +1 -122
- package/lib/textSegment.js.map +1 -1
- package/package.json +99 -20
- package/src/MergeTreeTextHelper.ts +170 -0
- package/src/base.ts +2 -35
- package/src/client.ts +91 -111
- package/src/collections/heap.ts +75 -0
- package/src/collections/index.ts +11 -0
- package/src/collections/intervalTree.ts +146 -0
- package/src/collections/list.ts +165 -0
- package/src/{collections.ts → collections/rbTree.ts} +84 -563
- package/src/collections/stack.ts +27 -0
- package/src/collections/tst.ts +212 -0
- package/src/index.ts +8 -2
- package/src/localReference.ts +152 -203
- package/src/mergeTree.ts +578 -996
- package/src/mergeTreeDeltaCallback.ts +1 -1
- package/src/mergeTreeNodes.ts +752 -0
- package/src/mergeTreeTracking.ts +1 -1
- package/src/opBuilder.ts +1 -1
- package/src/partialLengths.ts +631 -258
- package/src/properties.ts +1 -0
- package/src/referencePositions.ts +10 -44
- package/src/segmentGroupCollection.ts +17 -2
- package/src/segmentPropertiesManager.ts +46 -12
- package/src/snapshotChunks.ts +2 -1
- package/src/snapshotLoader.ts +2 -1
- package/src/snapshotV1.ts +3 -3
- package/src/snapshotlegacy.ts +6 -2
- package/src/sortedSegmentSet.ts +1 -1
- package/src/textSegment.ts +10 -157
- package/dist/collections.d.ts +0 -197
- package/dist/collections.d.ts.map +0 -1
- package/dist/collections.js.map +0 -1
- package/lib/collections.d.ts +0 -197
- package/lib/collections.d.ts.map +0 -1
- package/lib/collections.js.map +0 -1
package/src/client.ts
CHANGED
|
@@ -16,17 +16,17 @@ import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
|
16
16
|
import { IIntegerRange } from "./base";
|
|
17
17
|
import { RedBlackTree } from "./collections";
|
|
18
18
|
import { UnassignedSequenceNumber, UniversalSequenceNumber } from "./constants";
|
|
19
|
-
import {
|
|
19
|
+
import { LocalReferencePosition } from "./localReference";
|
|
20
20
|
import {
|
|
21
21
|
CollaborationWindow,
|
|
22
22
|
compareStrings,
|
|
23
23
|
IConsensusInfo,
|
|
24
|
+
IMergeNode,
|
|
24
25
|
ISegment,
|
|
25
26
|
ISegmentAction,
|
|
26
27
|
Marker,
|
|
27
|
-
MergeTree,
|
|
28
28
|
SegmentGroup,
|
|
29
|
-
} from "./
|
|
29
|
+
} from "./mergeTreeNodes";
|
|
30
30
|
import { MergeTreeDeltaCallback } from "./mergeTreeDeltaCallback";
|
|
31
31
|
import {
|
|
32
32
|
createAnnotateMarkerOp,
|
|
@@ -51,9 +51,11 @@ import {
|
|
|
51
51
|
import { PropertySet } from "./properties";
|
|
52
52
|
import { SnapshotLegacy } from "./snapshotlegacy";
|
|
53
53
|
import { SnapshotLoader } from "./snapshotLoader";
|
|
54
|
-
import {
|
|
54
|
+
import { IMergeTreeTextHelper } from "./textSegment";
|
|
55
55
|
import { SnapshotV1 } from "./snapshotV1";
|
|
56
|
-
import { ReferencePosition, RangeStackMap } from "./referencePositions";
|
|
56
|
+
import { ReferencePosition, RangeStackMap, DetachedReferencePosition } from "./referencePositions";
|
|
57
|
+
import { MergeTree } from "./mergeTree";
|
|
58
|
+
import { MergeTreeTextHelper } from "./MergeTreeTextHelper";
|
|
57
59
|
import {
|
|
58
60
|
IMergeTreeClientSequenceArgs,
|
|
59
61
|
IMergeTreeDeltaOpArgs,
|
|
@@ -88,6 +90,10 @@ export class Client {
|
|
|
88
90
|
this.mergeTree.mergeTreeMaintenanceCallback = callback;
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
/**
|
|
94
|
+
* @deprecated for internal use only. public export will be removed.
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
91
97
|
protected readonly mergeTree: MergeTree;
|
|
92
98
|
|
|
93
99
|
private readonly clientNameToIds = new RedBlackTree<string, number>(compareStrings);
|
|
@@ -167,11 +173,7 @@ export class Client {
|
|
|
167
173
|
const annotateOp =
|
|
168
174
|
createAnnotateMarkerOp(marker, props, combiningOp)!;
|
|
169
175
|
|
|
170
|
-
|
|
171
|
-
return annotateOp;
|
|
172
|
-
} else {
|
|
173
|
-
return undefined;
|
|
174
|
-
}
|
|
176
|
+
return this.applyAnnotateRangeOp({ op: annotateOp }) ? annotateOp : undefined;
|
|
175
177
|
}
|
|
176
178
|
/**
|
|
177
179
|
* Annotates the range with the provided properties
|
|
@@ -241,7 +243,7 @@ export class Client {
|
|
|
241
243
|
this.getCurrentSeq(),
|
|
242
244
|
this.getClientId());
|
|
243
245
|
|
|
244
|
-
if (pos ===
|
|
246
|
+
if (pos === DetachedReferencePosition) {
|
|
245
247
|
return undefined;
|
|
246
248
|
}
|
|
247
249
|
const op = createInsertSegmentOp(
|
|
@@ -289,9 +291,17 @@ export class Client {
|
|
|
289
291
|
* serializer which keeps track of all serialized handles.
|
|
290
292
|
*/
|
|
291
293
|
public serializeGCData(handle: IFluidHandle, handleCollectingSerializer: IFluidSerializer): void {
|
|
294
|
+
let localInserts = 0;
|
|
295
|
+
let localRemoves = 0;
|
|
292
296
|
this.mergeTree.walkAllSegments(
|
|
293
297
|
this.mergeTree.root,
|
|
294
298
|
(seg) => {
|
|
299
|
+
if (seg.seq === UnassignedSequenceNumber) {
|
|
300
|
+
localInserts++;
|
|
301
|
+
}
|
|
302
|
+
if (seg.removedSeq === UnassignedSequenceNumber) {
|
|
303
|
+
localRemoves++;
|
|
304
|
+
}
|
|
295
305
|
// Only serialize segments that have not been removed.
|
|
296
306
|
if (seg.removedSeq === undefined) {
|
|
297
307
|
handleCollectingSerializer.stringify(
|
|
@@ -301,6 +311,10 @@ export class Client {
|
|
|
301
311
|
return true;
|
|
302
312
|
},
|
|
303
313
|
);
|
|
314
|
+
|
|
315
|
+
if (localInserts > 0 || localRemoves > 0) {
|
|
316
|
+
this.logger.sendErrorEvent({ eventName: "LocalEditsInProcessGCData", localInserts, localRemoves });
|
|
317
|
+
}
|
|
304
318
|
}
|
|
305
319
|
|
|
306
320
|
public getCollabWindow(): CollaborationWindow {
|
|
@@ -312,30 +326,17 @@ export class Client {
|
|
|
312
326
|
* does not exist in this merge tree
|
|
313
327
|
* @param segment - The segment to get the position of
|
|
314
328
|
*/
|
|
315
|
-
public getPosition(segment: ISegment): number {
|
|
329
|
+
public getPosition(segment: ISegment, localSeq?: number): number {
|
|
316
330
|
if (segment?.parent === undefined) {
|
|
317
331
|
return -1;
|
|
318
332
|
}
|
|
319
|
-
return this.mergeTree.getPosition(segment, this.getCurrentSeq(), this.getClientId());
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* @deprecated - use createReferencePosition instead
|
|
323
|
-
*/
|
|
324
|
-
public addLocalReference(lref: LocalReference) {
|
|
325
|
-
return this.mergeTree.addLocalReference(lref);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* @deprecated - use removeReferencePosition instead
|
|
330
|
-
*/
|
|
331
|
-
public removeLocalReference(lref: LocalReference) {
|
|
332
|
-
return this.removeLocalReferencePosition(lref);
|
|
333
|
+
return this.mergeTree.getPosition(segment, this.getCurrentSeq(), this.getClientId(), localSeq);
|
|
333
334
|
}
|
|
334
335
|
|
|
335
336
|
public createLocalReferencePosition(
|
|
336
337
|
segment: ISegment, offset: number | undefined, refType: ReferenceType, properties: PropertySet | undefined,
|
|
337
338
|
): LocalReferencePosition {
|
|
338
|
-
return this.mergeTree.createLocalReferencePosition(segment, offset, refType, properties
|
|
339
|
+
return this.mergeTree.createLocalReferencePosition(segment, offset ?? 0, refType, properties);
|
|
339
340
|
}
|
|
340
341
|
|
|
341
342
|
public removeLocalReferencePosition(lref: LocalReferencePosition) {
|
|
@@ -359,6 +360,13 @@ export class Client {
|
|
|
359
360
|
return this.mergeTree.getMarkerFromId(id);
|
|
360
361
|
}
|
|
361
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Revert an op
|
|
365
|
+
*/
|
|
366
|
+
public rollback?(op: any, localOpMetadata: unknown) {
|
|
367
|
+
this.mergeTree.rollback(op as IMergeTreeDeltaOp, localOpMetadata as SegmentGroup);
|
|
368
|
+
}
|
|
369
|
+
|
|
362
370
|
/**
|
|
363
371
|
* Performs the remove based on the provided op
|
|
364
372
|
* @param opArgs - The ops args for the op
|
|
@@ -655,11 +663,7 @@ export class Client {
|
|
|
655
663
|
return this.clientNameToIds.get(longClientId)!.data;
|
|
656
664
|
}
|
|
657
665
|
getLongClientId(shortClientId: number) {
|
|
658
|
-
|
|
659
|
-
return this.shortClientIdMap[shortClientId];
|
|
660
|
-
} else {
|
|
661
|
-
return "original";
|
|
662
|
-
}
|
|
666
|
+
return shortClientId >= 0 ? this.shortClientIdMap[shortClientId] : "original";
|
|
663
667
|
}
|
|
664
668
|
addLongClientId(longClientId: string) {
|
|
665
669
|
this.clientNameToIds.put(longClientId, this.shortClientIdMap.length);
|
|
@@ -677,36 +681,8 @@ export class Client {
|
|
|
677
681
|
*/
|
|
678
682
|
protected findReconnectionPosition(segment: ISegment, localSeq: number) {
|
|
679
683
|
assert(localSeq <= this.mergeTree.collabWindow.localSeq, 0x032 /* "localSeq greater than collab window" */);
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
Walk the segments up to the current segment, and calculate it's
|
|
683
|
-
position taking into account local segments that were modified,
|
|
684
|
-
after the current segment.
|
|
685
|
-
|
|
686
|
-
TODO: Consider embedding this information into the tree for
|
|
687
|
-
more efficient look up of pending segment positions.
|
|
688
|
-
*/
|
|
689
|
-
this.mergeTree.walkAllSegments(this.mergeTree.root, (seg) => {
|
|
690
|
-
// If we've found the desired segment, terminate the walk and return 'segmentPosition'.
|
|
691
|
-
if (seg === segment) {
|
|
692
|
-
return false;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// Otherwise, advance segmentPosition if the segment has been inserted and not removed
|
|
696
|
-
// with respect to the given 'localSeq'.
|
|
697
|
-
//
|
|
698
|
-
// Note that all ACKed / remote ops are applied and we only need concern ourself with
|
|
699
|
-
// determining if locally pending ops fall before/after the given 'localSeq'.
|
|
700
|
-
if ((seg.localSeq === undefined || seg.localSeq <= localSeq) // Is inserted
|
|
701
|
-
&& (seg.removedSeq === undefined || seg.localRemovedSeq! > localSeq) // Not removed
|
|
702
|
-
) {
|
|
703
|
-
segmentPosition += seg.cachedLength;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
return true;
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
return segmentPosition;
|
|
684
|
+
const { currentSeq, clientId } = this.getCollabWindow();
|
|
685
|
+
return this.mergeTree.getPosition(segment, currentSeq, clientId, localSeq);
|
|
710
686
|
}
|
|
711
687
|
|
|
712
688
|
/**
|
|
@@ -716,6 +692,12 @@ export class Client {
|
|
|
716
692
|
* If the position refers to a segment/offset that was removed by some operation between `seqNumberFrom` and
|
|
717
693
|
* the current sequence number, the returned position will align with the position of a reference given
|
|
718
694
|
* `SlideOnRemove` semantics.
|
|
695
|
+
*
|
|
696
|
+
* If a reference was initially given `StayOnRemove` semantics, with intent to later change to `SlideOnRemove`,
|
|
697
|
+
* that isn't equivalent, and so local bookkeeping needs to be updated.
|
|
698
|
+
*
|
|
699
|
+
* If the position has slid off and there is no nearest position (i.e. the
|
|
700
|
+
* tree is empty), returns `DetachedReferencePosition`
|
|
719
701
|
*/
|
|
720
702
|
public rebasePosition(
|
|
721
703
|
pos: number,
|
|
@@ -723,45 +705,31 @@ export class Client {
|
|
|
723
705
|
localSeq: number,
|
|
724
706
|
): number {
|
|
725
707
|
assert(localSeq <= this.mergeTree.collabWindow.localSeq, 0x300 /* localSeq greater than collab window */);
|
|
726
|
-
|
|
727
|
-
let
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
(removedSeq !== undefined && removedSeq !== UnassignedSequenceNumber && removedSeq <= seqNumberFrom)
|
|
735
|
-
|| (localRemovedSeq !== undefined && localRemovedSeq <= localSeq);
|
|
736
|
-
|
|
737
|
-
this.mergeTree.walkAllSegments(this.mergeTree.root, (seg) => {
|
|
738
|
-
assert(seg.seq !== undefined || seg.localSeq !== undefined,
|
|
739
|
-
0x301 /* Either seq or localSeq should be defined */);
|
|
740
|
-
segment = seg;
|
|
741
|
-
|
|
742
|
-
if (isInsertedInView(seg) && !isRemovedFromView(seg)) {
|
|
743
|
-
posAccumulated += seg.cachedLength;
|
|
744
|
-
if (offset >= seg.cachedLength) {
|
|
745
|
-
offset -= seg.cachedLength;
|
|
746
|
-
}
|
|
708
|
+
const { clientId } = this.getCollabWindow();
|
|
709
|
+
let { segment, offset } = this.mergeTree.getContainingSegment(pos, seqNumberFrom, clientId, localSeq);
|
|
710
|
+
if (segment === undefined && offset === undefined) {
|
|
711
|
+
// getContainingSegment will only return non-removed segments. This means the position was past
|
|
712
|
+
// all non-removed segments in the tree, so we take the last one instead as an approximation.
|
|
713
|
+
let finalSegment: IMergeNode = this.mergeTree.root;
|
|
714
|
+
while (!finalSegment.isLeaf()) {
|
|
715
|
+
finalSegment = finalSegment.children[finalSegment.childCount - 1];
|
|
747
716
|
}
|
|
717
|
+
segment = finalSegment;
|
|
718
|
+
offset = 0;
|
|
719
|
+
}
|
|
748
720
|
|
|
749
|
-
|
|
750
|
-
return posAccumulated <= pos;
|
|
751
|
-
});
|
|
752
|
-
|
|
721
|
+
// if segment is undefined, it slid off the string
|
|
753
722
|
assert(segment !== undefined, 0x302 /* No segment found */);
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
offset = 0;
|
|
723
|
+
|
|
724
|
+
const segoff = this.getSlideToSegment({ segment, offset }) ?? segment;
|
|
725
|
+
|
|
726
|
+
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
727
|
+
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
728
|
+
return DetachedReferencePosition;
|
|
761
729
|
}
|
|
762
730
|
|
|
763
|
-
assert(0 <= offset && offset < segment.cachedLength, 0x303 /* Invalid offset */);
|
|
764
|
-
return this.findReconnectionPosition(segment, localSeq) + offset;
|
|
731
|
+
assert(offset !== undefined && 0 <= offset && offset < segment.cachedLength, 0x303 /* Invalid offset */);
|
|
732
|
+
return this.findReconnectionPosition(segoff.segment, localSeq) + segoff.offset;
|
|
765
733
|
}
|
|
766
734
|
|
|
767
735
|
private resetPendingDeltaToOps(
|
|
@@ -776,7 +744,7 @@ export class Client {
|
|
|
776
744
|
// We need to sort the segments by ordinal, as the segments are not sorted in the segment group.
|
|
777
745
|
// The reason they need them sorted, as they have the same local sequence number and which means
|
|
778
746
|
// farther segments will take into account nearer segments when calculating their position.
|
|
779
|
-
// By sorting we ensure the nearer segment will be applied and sequenced before the
|
|
747
|
+
// By sorting we ensure the nearer segment will be applied and sequenced before the farther segments
|
|
780
748
|
// so their recalculated positions will be correct.
|
|
781
749
|
for (const segment of segmentGroup.segments.sort((a, b) => a.ordinal < b.ordinal ? -1 : 1)) {
|
|
782
750
|
const segmentSegGroup = segment.segmentGroups.dequeue();
|
|
@@ -791,7 +759,8 @@ export class Client {
|
|
|
791
759
|
// if the segment has been removed, there's no need to send the annotate op
|
|
792
760
|
// unless the remove was local, in which case the annotate must have come
|
|
793
761
|
// before the remove
|
|
794
|
-
if (segment.removedSeq === undefined ||
|
|
762
|
+
if (segment.removedSeq === undefined ||
|
|
763
|
+
(segment.localRemovedSeq !== undefined && segment.removedSeq === UnassignedSequenceNumber)) {
|
|
795
764
|
newOp = createAnnotateRangeOp(
|
|
796
765
|
segmentPosition,
|
|
797
766
|
segmentPosition + segment.cachedLength,
|
|
@@ -815,7 +784,7 @@ export class Client {
|
|
|
815
784
|
break;
|
|
816
785
|
|
|
817
786
|
case MergeTreeDeltaType.REMOVE:
|
|
818
|
-
if (segment.localRemovedSeq !== undefined) {
|
|
787
|
+
if (segment.localRemovedSeq !== undefined && segment.removedSeq === UnassignedSequenceNumber) {
|
|
819
788
|
newOp = createRemoveRangeOp(
|
|
820
789
|
segmentPosition,
|
|
821
790
|
segmentPosition + segment.cachedLength);
|
|
@@ -942,8 +911,8 @@ export class Client {
|
|
|
942
911
|
}
|
|
943
912
|
|
|
944
913
|
/**
|
|
945
|
-
*
|
|
946
|
-
*
|
|
914
|
+
* Given an pending operation and segment group, regenerate the op, so it
|
|
915
|
+
* can be resubmitted
|
|
947
916
|
* @param resetOp - The op to reset
|
|
948
917
|
* @param segmentGroup - The segment group associated with the op
|
|
949
918
|
*/
|
|
@@ -979,7 +948,7 @@ export class Client {
|
|
|
979
948
|
return opList.length === 1 ? opList[0] : createGroupOp(...opList);
|
|
980
949
|
}
|
|
981
950
|
|
|
982
|
-
public createTextHelper() {
|
|
951
|
+
public createTextHelper(): IMergeTreeTextHelper {
|
|
983
952
|
return new MergeTreeTextHelper(this.mergeTree);
|
|
984
953
|
}
|
|
985
954
|
|
|
@@ -1028,18 +997,17 @@ export class Client {
|
|
|
1028
997
|
|
|
1029
998
|
return loader.initialize(storage);
|
|
1030
999
|
}
|
|
1031
|
-
|
|
1000
|
+
/**
|
|
1001
|
+
* @deprecated for internal use only. public export will be removed.
|
|
1002
|
+
* @internal
|
|
1003
|
+
*/
|
|
1032
1004
|
getStackContext(startPos: number, rangeLabels: string[]): RangeStackMap {
|
|
1033
1005
|
return this.mergeTree.getStackContext(startPos, this.getCollabWindow().clientId, rangeLabels);
|
|
1034
1006
|
}
|
|
1035
1007
|
|
|
1036
1008
|
private getLocalSequenceNumber() {
|
|
1037
1009
|
const segWindow = this.getCollabWindow();
|
|
1038
|
-
|
|
1039
|
-
return UnassignedSequenceNumber;
|
|
1040
|
-
} else {
|
|
1041
|
-
return UniversalSequenceNumber;
|
|
1042
|
-
}
|
|
1010
|
+
return segWindow.collaborating ? UnassignedSequenceNumber : UniversalSequenceNumber;
|
|
1043
1011
|
}
|
|
1044
1012
|
localTransaction(groupOp: IMergeTreeGroupMsg) {
|
|
1045
1013
|
for (const op of groupOp.ops) {
|
|
@@ -1086,9 +1054,9 @@ export class Client {
|
|
|
1086
1054
|
}
|
|
1087
1055
|
}
|
|
1088
1056
|
|
|
1089
|
-
getContainingSegment<T extends ISegment>(pos: number, op?: ISequencedDocumentMessage) {
|
|
1057
|
+
getContainingSegment<T extends ISegment>(pos: number, op?: ISequencedDocumentMessage, localSeq?: number) {
|
|
1090
1058
|
const args = this.getClientSequenceArgsForMessage(op);
|
|
1091
|
-
return this.mergeTree.getContainingSegment<T>(pos, args.referenceSequenceNumber, args.clientId);
|
|
1059
|
+
return this.mergeTree.getContainingSegment<T>(pos, args.referenceSequenceNumber, args.clientId, localSeq);
|
|
1092
1060
|
}
|
|
1093
1061
|
|
|
1094
1062
|
/**
|
|
@@ -1097,7 +1065,19 @@ export class Client {
|
|
|
1097
1065
|
* @returns - segment and offset to slide the reference to
|
|
1098
1066
|
*/
|
|
1099
1067
|
getSlideToSegment(segoff: { segment: ISegment | undefined; offset: number | undefined; }) {
|
|
1100
|
-
|
|
1068
|
+
if (segoff.segment === undefined) {
|
|
1069
|
+
return segoff;
|
|
1070
|
+
}
|
|
1071
|
+
const segment = this.mergeTree._getSlideToSegment(segoff.segment);
|
|
1072
|
+
if (segment === segoff.segment) {
|
|
1073
|
+
return segoff;
|
|
1074
|
+
}
|
|
1075
|
+
const offset =
|
|
1076
|
+
segment && segment.ordinal < segoff.segment.ordinal ? segment.cachedLength - 1 : 0;
|
|
1077
|
+
return {
|
|
1078
|
+
segment,
|
|
1079
|
+
offset,
|
|
1080
|
+
};
|
|
1101
1081
|
}
|
|
1102
1082
|
|
|
1103
1083
|
getPropertiesAtPosition(pos: number) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @deprecated for internal use only. public export will be removed.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export interface Comparer<T> {
|
|
11
|
+
compare(a: T, b: T): number;
|
|
12
|
+
min: T;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated for internal use only. public export will be removed.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export class Heap<T> {
|
|
20
|
+
private L: T[];
|
|
21
|
+
public count() {
|
|
22
|
+
return this.L.length - 1;
|
|
23
|
+
}
|
|
24
|
+
constructor(a: T[], public comp: Comparer<T>) {
|
|
25
|
+
this.L = [comp.min];
|
|
26
|
+
for (let i = 0, len = a.length; i < len; i++) {
|
|
27
|
+
this.add(a[i]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
public peek() {
|
|
31
|
+
return this.L[1];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public get() {
|
|
35
|
+
const x = this.L[1];
|
|
36
|
+
this.L[1] = this.L[this.count()];
|
|
37
|
+
this.L.pop();
|
|
38
|
+
this.fixDown(1);
|
|
39
|
+
return x;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public add(x: T) {
|
|
43
|
+
this.L.push(x);
|
|
44
|
+
this.fixup(this.count());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* eslint-disable no-bitwise */
|
|
48
|
+
private fixup(k: number) {
|
|
49
|
+
let _k = k;
|
|
50
|
+
while (_k > 1 && (this.comp.compare(this.L[_k >> 1], this.L[_k]) > 0)) {
|
|
51
|
+
const tmp = this.L[_k >> 1];
|
|
52
|
+
this.L[_k >> 1] = this.L[_k];
|
|
53
|
+
this.L[_k] = tmp;
|
|
54
|
+
_k = _k >> 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private fixDown(k: number) {
|
|
59
|
+
let _k = k;
|
|
60
|
+
while ((_k << 1) <= (this.count())) {
|
|
61
|
+
let j = _k << 1;
|
|
62
|
+
if ((j < this.count()) && (this.comp.compare(this.L[j], this.L[j + 1]) > 0)) {
|
|
63
|
+
j++;
|
|
64
|
+
}
|
|
65
|
+
if (this.comp.compare(this.L[_k], this.L[j]) <= 0) {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
const tmp = this.L[_k];
|
|
69
|
+
this.L[_k] = this.L[j];
|
|
70
|
+
this.L[j] = tmp;
|
|
71
|
+
_k = j;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/* eslint-enable no-bitwise */
|
|
75
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export * from "./heap";
|
|
7
|
+
export * from "./intervalTree";
|
|
8
|
+
export * from "./list";
|
|
9
|
+
export * from "./rbTree";
|
|
10
|
+
export * from "./stack";
|
|
11
|
+
export * from "./tst";
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
7
|
+
import {
|
|
8
|
+
IIntegerRange,
|
|
9
|
+
} from "../base";
|
|
10
|
+
import { ConflictAction, IRBAugmentation, IRBMatcher, RBNode, RBNodeActions, RedBlackTree } from "./rbTree";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated for internal use only. public export will be removed.
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export interface AugmentedIntervalNode {
|
|
17
|
+
minmax: IInterval;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated for internal use only. public export will be removed.
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export const integerRangeToString = (range: IIntegerRange) => `[${range.start},${range.end})`;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated for internal use only. public export will be removed.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export interface IInterval {
|
|
31
|
+
clone(): IInterval;
|
|
32
|
+
compare(b: IInterval): number;
|
|
33
|
+
compareStart(b: IInterval): number;
|
|
34
|
+
compareEnd(b: IInterval): number;
|
|
35
|
+
modify(
|
|
36
|
+
label: string,
|
|
37
|
+
start: number,
|
|
38
|
+
end: number,
|
|
39
|
+
op?: ISequencedDocumentMessage,
|
|
40
|
+
localSeq?: number
|
|
41
|
+
): IInterval | undefined;
|
|
42
|
+
overlaps(b: IInterval): boolean;
|
|
43
|
+
union(b: IInterval): IInterval;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const intervalComparer = (a: IInterval, b: IInterval) => a.compare(b);
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated for internal use only. public export will be removed.
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export type IntervalNode<T extends IInterval> = RBNode<T, AugmentedIntervalNode>;
|
|
52
|
+
/**
|
|
53
|
+
* @deprecated for internal use only. public export will be removed.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
export type IntervalConflictResolver<TInterval> = (a: TInterval, b: TInterval) => TInterval;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @deprecated for internal use only. public export will be removed.
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
export class IntervalTree<T extends IInterval> implements IRBAugmentation<T, AugmentedIntervalNode>,
|
|
63
|
+
IRBMatcher<T, AugmentedIntervalNode> {
|
|
64
|
+
public intervals = new RedBlackTree<T, AugmentedIntervalNode>(intervalComparer, this);
|
|
65
|
+
|
|
66
|
+
public remove(x: T) {
|
|
67
|
+
this.intervals.remove(x);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public removeExisting(x: T) {
|
|
71
|
+
this.intervals.removeExisting(x);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public put(x: T, conflict?: IntervalConflictResolver<T>) {
|
|
75
|
+
let rbConflict: ConflictAction<T, AugmentedIntervalNode> | undefined;
|
|
76
|
+
if (conflict) {
|
|
77
|
+
rbConflict = (key: T, currentKey: T) => {
|
|
78
|
+
const ival = conflict(key, currentKey);
|
|
79
|
+
return {
|
|
80
|
+
key: ival,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
this.intervals.put(x, { minmax: x.clone() }, rbConflict);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public map(fn: (x: T) => void) {
|
|
88
|
+
const actions: RBNodeActions<T, AugmentedIntervalNode> = {
|
|
89
|
+
infix: (node) => {
|
|
90
|
+
fn(node.key);
|
|
91
|
+
return true;
|
|
92
|
+
},
|
|
93
|
+
showStructure: true,
|
|
94
|
+
};
|
|
95
|
+
this.intervals.walk(actions);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public mapUntil(fn: (X: T) => boolean) {
|
|
99
|
+
const actions: RBNodeActions<T, AugmentedIntervalNode> = {
|
|
100
|
+
infix: (node) => {
|
|
101
|
+
return fn(node.key);
|
|
102
|
+
},
|
|
103
|
+
showStructure: true,
|
|
104
|
+
};
|
|
105
|
+
this.intervals.walk(actions);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public mapBackward(fn: (x: T) => void) {
|
|
109
|
+
const actions: RBNodeActions<T, AugmentedIntervalNode> = {
|
|
110
|
+
infix: (node) => {
|
|
111
|
+
fn(node.key);
|
|
112
|
+
return true;
|
|
113
|
+
},
|
|
114
|
+
showStructure: true,
|
|
115
|
+
};
|
|
116
|
+
this.intervals.walkBackward(actions);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// TODO: toString()
|
|
120
|
+
public match(x: T) {
|
|
121
|
+
return this.intervals.gather(x, this);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public matchNode(node: IntervalNode<T> | undefined, key: T) {
|
|
125
|
+
return !!node && node.key.overlaps(key);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public continueSubtree(node: IntervalNode<T> | undefined, key: T) {
|
|
129
|
+
return !!node && node.data.minmax.overlaps(key);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public update(node: IntervalNode<T>) {
|
|
133
|
+
if (node.left && node.right) {
|
|
134
|
+
node.data.minmax = node.key.union(
|
|
135
|
+
node.left.data.minmax.union(node.right.data.minmax));
|
|
136
|
+
} else {
|
|
137
|
+
if (node.left) {
|
|
138
|
+
node.data.minmax = node.key.union(node.left.data.minmax);
|
|
139
|
+
} else if (node.right) {
|
|
140
|
+
node.data.minmax = node.key.union(node.right.data.minmax);
|
|
141
|
+
} else {
|
|
142
|
+
node.data.minmax = node.key.clone();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|