@fluidframework/sequence 2.0.0-internal.1.0.0.82693 → 2.0.0-internal.1.1.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.
- package/.mocharc.js +12 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +31 -4
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +84 -24
- package/dist/intervalCollection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/sharedString.d.ts +26 -1
- package/dist/sharedString.d.ts.map +1 -1
- package/dist/sharedString.js +29 -5
- package/dist/sharedString.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +31 -4
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +83 -24
- package/lib/intervalCollection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/sharedString.d.ts +26 -1
- package/lib/sharedString.d.ts.map +1 -1
- package/lib/sharedString.js +27 -4
- package/lib/sharedString.js.map +1 -1
- package/package.json +25 -20
- package/src/index.ts +2 -0
- package/src/intervalCollection.ts +131 -26
- package/src/packageVersion.ts +1 -1
- package/src/sharedString.ts +31 -4
package/lib/sharedString.js
CHANGED
|
@@ -147,6 +147,9 @@ export class SharedString extends SharedSegmentSequence {
|
|
|
147
147
|
findTile(startPos, tileLabel, preceding = true) {
|
|
148
148
|
return this.client.findTile(startPos, tileLabel, preceding);
|
|
149
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* @deprecated - use the free function `getTextAndMarkers` exported by this package instead.
|
|
152
|
+
*/
|
|
150
153
|
getTextAndMarkers(label) {
|
|
151
154
|
const segmentWindow = this.client.getCollabWindow();
|
|
152
155
|
return this.mergeTreeTextHelper.getTextAndMarkers(segmentWindow.currentSeq, segmentWindow.clientId, label);
|
|
@@ -164,13 +167,15 @@ export class SharedString extends SharedSegmentSequence {
|
|
|
164
167
|
/**
|
|
165
168
|
* Adds spaces for markers and handles, so that position calculations account for them.
|
|
166
169
|
*/
|
|
167
|
-
getTextWithPlaceholders() {
|
|
170
|
+
getTextWithPlaceholders(start, end) {
|
|
168
171
|
const segmentWindow = this.client.getCollabWindow();
|
|
169
|
-
return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, " ");
|
|
172
|
+
return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, " ", start, end);
|
|
170
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* @deprecated - use `getTextWithPlaceholders` instead.
|
|
176
|
+
*/
|
|
171
177
|
getTextRangeWithPlaceholders(start, end) {
|
|
172
|
-
|
|
173
|
-
return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, " ", start, end);
|
|
178
|
+
return this.getTextWithPlaceholders(start, end);
|
|
174
179
|
}
|
|
175
180
|
getTextRangeWithMarkers(start, end) {
|
|
176
181
|
const segmentWindow = this.client.getCollabWindow();
|
|
@@ -191,4 +196,22 @@ export class SharedString extends SharedSegmentSequence {
|
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Splits the text into regions ending with markers with the given `label`.
|
|
201
|
+
* @param sharedString - String to retrieve text and markers from
|
|
202
|
+
* @param label - label to split on
|
|
203
|
+
* @returns Two parallel lists of text and markers, split by markers with the provided `label`.
|
|
204
|
+
*
|
|
205
|
+
* For example:
|
|
206
|
+
* ```typescript
|
|
207
|
+
* // Say sharedstring has contents "hello<paragraph marker 1>world<paragraph marker 2>missing".
|
|
208
|
+
* const { parallelText, parallelMarkers } = getTextAndMarkers(sharedString, "paragraph");
|
|
209
|
+
* // parallelText === ["hello", "world"]
|
|
210
|
+
* // parallelMarkers === [<paragraph marker 1 object>, <paragraph marker 2 object>]
|
|
211
|
+
* // Note parallelText does not include "missing".
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export function getTextAndMarkers(sharedString, label) {
|
|
215
|
+
return sharedString.getTextAndMarkers(label);
|
|
216
|
+
}
|
|
194
217
|
//# sourceMappingURL=sharedString.js.map
|
package/lib/sharedString.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharedString.js","sourceRoot":"","sources":["../src/sharedString.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n ICombiningOp,\n IMergeTreeInsertMsg,\n IMergeTreeRemoveMsg,\n IMergeTreeTextHelper,\n IRelativePosition,\n ISegment,\n Marker,\n PropertySet,\n ReferencePosition,\n ReferenceType,\n TextSegment,\n} from \"@fluidframework/merge-tree\";\nimport { IFluidDataStoreRuntime, IChannelAttributes } from \"@fluidframework/datastore-definitions\";\nimport { SharedSegmentSequence } from \"./sequence\";\nimport { SharedStringFactory } from \"./sequenceFactory\";\n\n/**\n * Fluid object interface describing access methods on a SharedString\n */\nexport interface ISharedString extends SharedSegmentSequence<SharedStringSegment> {\n /**\n * Inserts the text at the position.\n * @param pos - The position to insert the text at\n * @param text - The text to insert\n * @param props - The properties of the text\n */\n insertText(pos: number, text: string, props?: PropertySet): void;\n\n /**\n * Inserts a marker at the position.\n * @param pos - The position to insert the marker at\n * @param refType - The reference type of the marker\n * @param props - The properties of the marker\n */\n insertMarker(pos: number, refType: ReferenceType, props?: PropertySet): IMergeTreeInsertMsg;\n\n /**\n * {@inheritDoc SharedSegmentSequence.posFromRelativePos}\n */\n posFromRelativePos(relativePos: IRelativePosition): number;\n}\n\nexport type SharedStringSegment = TextSegment | Marker;\n\n/**\n * The Shared String is a specialized data structure for handling collaborative\n * text. It is based on a more general Sequence data structure but has\n * additional features that make working with text easier.\n *\n * In addition to text, a Shared String can also contain markers. Markers can be\n * used to store metadata at positions within the text, like the details of an\n * image or Fluid object that should be rendered with the text.\n *\n */\nexport class SharedString extends SharedSegmentSequence<SharedStringSegment> implements ISharedString {\n /**\n * Create a new shared string.\n * @param runtime - data store runtime the new shared string belongs to\n * @param id - optional name of the shared string\n * @returns newly create shared string (but not attached yet)\n */\n public static create(runtime: IFluidDataStoreRuntime, id?: string) {\n return runtime.createChannel(id, SharedStringFactory.Type) as SharedString;\n }\n\n /**\n * Get a factory for SharedString to register with the data store.\n * @returns a factory that creates and load SharedString\n */\n public static getFactory() {\n return new SharedStringFactory();\n }\n\n public get ISharedString(): ISharedString {\n return this;\n }\n\n private readonly mergeTreeTextHelper: IMergeTreeTextHelper;\n\n constructor(document: IFluidDataStoreRuntime, public id: string, attributes: IChannelAttributes) {\n super(document, id, attributes, SharedStringFactory.segmentFromSpec);\n this.mergeTreeTextHelper = this.client.createTextHelper();\n }\n\n /**\n * Inserts a marker at a relative position.\n * @param relativePos1 - The relative position to insert the marker at\n * @param refType - The reference type of the marker\n * @param props - The properties of the marker\n */\n public insertMarkerRelative(\n relativePos1: IRelativePosition,\n refType: ReferenceType,\n props?: PropertySet) {\n const segment = new Marker(refType);\n if (props) {\n segment.addProperties(props);\n }\n\n const pos = this.posFromRelativePos(relativePos1);\n const insertOp = this.client.insertSegmentLocal(pos, segment);\n if (insertOp) {\n this.submitSequenceMessage(insertOp);\n }\n }\n\n /**\n * {@inheritDoc ISharedString.insertMarker}\n */\n public insertMarker(\n pos: number,\n refType: ReferenceType,\n props?: PropertySet): IMergeTreeInsertMsg {\n const segment = new Marker(refType);\n if (props) {\n segment.addProperties(props);\n }\n\n const insertOp = this.client.insertSegmentLocal(pos, segment);\n if (insertOp) {\n this.submitSequenceMessage(insertOp);\n }\n return insertOp;\n }\n\n /**\n * Inserts the text at the position.\n * @param relativePos1 - The relative position to insert the text at\n * @param text - The text to insert\n * @param props - The properties of text\n */\n public insertTextRelative(relativePos1: IRelativePosition, text: string, props?: PropertySet) {\n const segment = new TextSegment(text);\n if (props) {\n segment.addProperties(props);\n }\n\n const pos = this.posFromRelativePos(relativePos1);\n const insertOp = this.client.insertSegmentLocal(pos, segment);\n if (insertOp) {\n this.submitSequenceMessage(insertOp);\n }\n }\n\n /**\n * {@inheritDoc ISharedString.insertText}\n */\n public insertText(pos: number, text: string, props?: PropertySet) {\n const segment = new TextSegment(text);\n if (props) {\n segment.addProperties(props);\n }\n\n const insertOp = this.client.insertSegmentLocal(pos, segment);\n if (insertOp) {\n this.submitSequenceMessage(insertOp);\n }\n }\n\n /**\n * Replaces a range with the provided text.\n * @param start - The inclusive start of the range to replace\n * @param end - The exclusive end of the range to replace\n * @param text - The text to replace the range with\n * @param props - Optional. The properties of the replacement text\n */\n public replaceText(start: number, end: number, text: string, props?: PropertySet) {\n this.replaceRange(start, end, TextSegment.make(text, props));\n }\n\n /**\n * Removes the text in the given range.\n * @param start - The inclusive start of the range to remove\n * @param end - The exclusive end of the range to replace\n * @returns the message sent.\n */\n public removeText(start: number, end: number): IMergeTreeRemoveMsg {\n return this.removeRange(start, end);\n }\n\n /**\n * Annotates the marker with the provided properties and calls the callback on consensus.\n * @param marker - The marker to annotate\n * @param props - The properties to annotate the marker with\n * @param consensusCallback - The callback called when consensus is reached\n */\n public annotateMarkerNotifyConsensus(\n marker: Marker,\n props: PropertySet,\n callback: (m: Marker) => void) {\n const annotateOp = this.client.annotateMarkerNotifyConsensus(marker, props, callback);\n if (annotateOp) {\n this.submitSequenceMessage(annotateOp);\n }\n }\n\n /**\n * Annotates the marker with the provided properties.\n * @param marker - The marker to annotate\n * @param props - The properties to annotate the marker with\n * @param combiningOp - Optional. Specifies how to combine values for the property, such as \"incr\" for increment.\n */\n public annotateMarker(\n marker: Marker,\n props: PropertySet,\n combiningOp?: ICombiningOp) {\n const annotateOp = this.client.annotateMarker(marker, props, combiningOp);\n if (annotateOp) {\n this.submitSequenceMessage(annotateOp);\n }\n }\n\n public findTile(startPos: number | undefined, tileLabel: string, preceding = true): {\n tile: ReferencePosition;\n pos: number;\n } {\n return this.client.findTile(startPos, tileLabel, preceding);\n }\n\n public getTextAndMarkers(label: string) {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getTextAndMarkers(segmentWindow.currentSeq, segmentWindow.clientId, label);\n }\n\n /**\n * Retrieve text from the SharedString in string format.\n * @param start - The starting index of the text to retrieve, or 0 if omitted.\n * @param end - The ending index of the text to retrieve, or the end of the string if omitted\n * @returns The requested text content as a string.\n */\n public getText(start?: number, end?: number) {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, \"\", start, end);\n }\n\n /**\n * Adds spaces for markers and handles, so that position calculations account for them.\n */\n public getTextWithPlaceholders() {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, \" \");\n }\n\n public getTextRangeWithPlaceholders(start: number, end: number) {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, \" \", start, end);\n }\n\n public getTextRangeWithMarkers(start: number, end: number) {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, \"*\", start, end);\n }\n\n public getMarkerFromId(id: string): ISegment {\n return this.client.getMarkerFromId(id);\n }\n\n /**\n * Revert an op\n */\n protected rollback(content: any, localOpMetadata: unknown): void {\n if (this.client.rollback !== undefined) {\n this.client.rollback(content, localOpMetadata);\n } else {\n super.rollback(content, localOpMetadata);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sharedString.js","sourceRoot":"","sources":["../src/sharedString.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n ICombiningOp,\n IMergeTreeInsertMsg,\n IMergeTreeRemoveMsg,\n IMergeTreeTextHelper,\n IRelativePosition,\n ISegment,\n Marker,\n PropertySet,\n ReferencePosition,\n ReferenceType,\n TextSegment,\n} from \"@fluidframework/merge-tree\";\nimport { IFluidDataStoreRuntime, IChannelAttributes } from \"@fluidframework/datastore-definitions\";\nimport { SharedSegmentSequence } from \"./sequence\";\nimport { SharedStringFactory } from \"./sequenceFactory\";\n\n/**\n * Fluid object interface describing access methods on a SharedString\n */\nexport interface ISharedString extends SharedSegmentSequence<SharedStringSegment> {\n /**\n * Inserts the text at the position.\n * @param pos - The position to insert the text at\n * @param text - The text to insert\n * @param props - The properties of the text\n */\n insertText(pos: number, text: string, props?: PropertySet): void;\n\n /**\n * Inserts a marker at the position.\n * @param pos - The position to insert the marker at\n * @param refType - The reference type of the marker\n * @param props - The properties of the marker\n */\n insertMarker(pos: number, refType: ReferenceType, props?: PropertySet): IMergeTreeInsertMsg;\n\n /**\n * {@inheritDoc SharedSegmentSequence.posFromRelativePos}\n */\n posFromRelativePos(relativePos: IRelativePosition): number;\n}\n\nexport type SharedStringSegment = TextSegment | Marker;\n\n/**\n * The Shared String is a specialized data structure for handling collaborative\n * text. It is based on a more general Sequence data structure but has\n * additional features that make working with text easier.\n *\n * In addition to text, a Shared String can also contain markers. Markers can be\n * used to store metadata at positions within the text, like the details of an\n * image or Fluid object that should be rendered with the text.\n *\n */\nexport class SharedString extends SharedSegmentSequence<SharedStringSegment> implements ISharedString {\n /**\n * Create a new shared string.\n * @param runtime - data store runtime the new shared string belongs to\n * @param id - optional name of the shared string\n * @returns newly create shared string (but not attached yet)\n */\n public static create(runtime: IFluidDataStoreRuntime, id?: string) {\n return runtime.createChannel(id, SharedStringFactory.Type) as SharedString;\n }\n\n /**\n * Get a factory for SharedString to register with the data store.\n * @returns a factory that creates and load SharedString\n */\n public static getFactory() {\n return new SharedStringFactory();\n }\n\n public get ISharedString(): ISharedString {\n return this;\n }\n\n private readonly mergeTreeTextHelper: IMergeTreeTextHelper;\n\n constructor(document: IFluidDataStoreRuntime, public id: string, attributes: IChannelAttributes) {\n super(document, id, attributes, SharedStringFactory.segmentFromSpec);\n this.mergeTreeTextHelper = this.client.createTextHelper();\n }\n\n /**\n * Inserts a marker at a relative position.\n * @param relativePos1 - The relative position to insert the marker at\n * @param refType - The reference type of the marker\n * @param props - The properties of the marker\n */\n public insertMarkerRelative(\n relativePos1: IRelativePosition,\n refType: ReferenceType,\n props?: PropertySet) {\n const segment = new Marker(refType);\n if (props) {\n segment.addProperties(props);\n }\n\n const pos = this.posFromRelativePos(relativePos1);\n const insertOp = this.client.insertSegmentLocal(pos, segment);\n if (insertOp) {\n this.submitSequenceMessage(insertOp);\n }\n }\n\n /**\n * {@inheritDoc ISharedString.insertMarker}\n */\n public insertMarker(\n pos: number,\n refType: ReferenceType,\n props?: PropertySet): IMergeTreeInsertMsg {\n const segment = new Marker(refType);\n if (props) {\n segment.addProperties(props);\n }\n\n const insertOp = this.client.insertSegmentLocal(pos, segment);\n if (insertOp) {\n this.submitSequenceMessage(insertOp);\n }\n return insertOp;\n }\n\n /**\n * Inserts the text at the position.\n * @param relativePos1 - The relative position to insert the text at\n * @param text - The text to insert\n * @param props - The properties of text\n */\n public insertTextRelative(relativePos1: IRelativePosition, text: string, props?: PropertySet) {\n const segment = new TextSegment(text);\n if (props) {\n segment.addProperties(props);\n }\n\n const pos = this.posFromRelativePos(relativePos1);\n const insertOp = this.client.insertSegmentLocal(pos, segment);\n if (insertOp) {\n this.submitSequenceMessage(insertOp);\n }\n }\n\n /**\n * {@inheritDoc ISharedString.insertText}\n */\n public insertText(pos: number, text: string, props?: PropertySet) {\n const segment = new TextSegment(text);\n if (props) {\n segment.addProperties(props);\n }\n\n const insertOp = this.client.insertSegmentLocal(pos, segment);\n if (insertOp) {\n this.submitSequenceMessage(insertOp);\n }\n }\n\n /**\n * Replaces a range with the provided text.\n * @param start - The inclusive start of the range to replace\n * @param end - The exclusive end of the range to replace\n * @param text - The text to replace the range with\n * @param props - Optional. The properties of the replacement text\n */\n public replaceText(start: number, end: number, text: string, props?: PropertySet) {\n this.replaceRange(start, end, TextSegment.make(text, props));\n }\n\n /**\n * Removes the text in the given range.\n * @param start - The inclusive start of the range to remove\n * @param end - The exclusive end of the range to replace\n * @returns the message sent.\n */\n public removeText(start: number, end: number): IMergeTreeRemoveMsg {\n return this.removeRange(start, end);\n }\n\n /**\n * Annotates the marker with the provided properties and calls the callback on consensus.\n * @param marker - The marker to annotate\n * @param props - The properties to annotate the marker with\n * @param consensusCallback - The callback called when consensus is reached\n */\n public annotateMarkerNotifyConsensus(\n marker: Marker,\n props: PropertySet,\n callback: (m: Marker) => void) {\n const annotateOp = this.client.annotateMarkerNotifyConsensus(marker, props, callback);\n if (annotateOp) {\n this.submitSequenceMessage(annotateOp);\n }\n }\n\n /**\n * Annotates the marker with the provided properties.\n * @param marker - The marker to annotate\n * @param props - The properties to annotate the marker with\n * @param combiningOp - Optional. Specifies how to combine values for the property, such as \"incr\" for increment.\n */\n public annotateMarker(\n marker: Marker,\n props: PropertySet,\n combiningOp?: ICombiningOp) {\n const annotateOp = this.client.annotateMarker(marker, props, combiningOp);\n if (annotateOp) {\n this.submitSequenceMessage(annotateOp);\n }\n }\n\n public findTile(startPos: number | undefined, tileLabel: string, preceding = true): {\n tile: ReferencePosition;\n pos: number;\n } {\n return this.client.findTile(startPos, tileLabel, preceding);\n }\n\n /**\n * @deprecated - use the free function `getTextAndMarkers` exported by this package instead.\n */\n public getTextAndMarkers(label: string) {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getTextAndMarkers(segmentWindow.currentSeq, segmentWindow.clientId, label);\n }\n\n /**\n * Retrieve text from the SharedString in string format.\n * @param start - The starting index of the text to retrieve, or 0 if omitted.\n * @param end - The ending index of the text to retrieve, or the end of the string if omitted\n * @returns The requested text content as a string.\n */\n public getText(start?: number, end?: number) {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, \"\", start, end);\n }\n\n /**\n * Adds spaces for markers and handles, so that position calculations account for them.\n */\n public getTextWithPlaceholders(start?: number, end?: number) {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, \" \", start, end);\n }\n\n /**\n * @deprecated - use `getTextWithPlaceholders` instead.\n */\n public getTextRangeWithPlaceholders(start: number, end: number) {\n return this.getTextWithPlaceholders(start, end);\n }\n\n public getTextRangeWithMarkers(start: number, end: number) {\n const segmentWindow = this.client.getCollabWindow();\n return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, \"*\", start, end);\n }\n\n public getMarkerFromId(id: string): ISegment {\n return this.client.getMarkerFromId(id);\n }\n\n /**\n * Revert an op\n */\n protected rollback(content: any, localOpMetadata: unknown): void {\n if (this.client.rollback !== undefined) {\n this.client.rollback(content, localOpMetadata);\n } else {\n super.rollback(content, localOpMetadata);\n }\n }\n}\n\n/**\n * Splits the text into regions ending with markers with the given `label`.\n * @param sharedString - String to retrieve text and markers from\n * @param label - label to split on\n * @returns Two parallel lists of text and markers, split by markers with the provided `label`.\n *\n * For example:\n * ```typescript\n * // Say sharedstring has contents \"hello<paragraph marker 1>world<paragraph marker 2>missing\".\n * const { parallelText, parallelMarkers } = getTextAndMarkers(sharedString, \"paragraph\");\n * // parallelText === [\"hello\", \"world\"]\n * // parallelMarkers === [<paragraph marker 1 object>, <paragraph marker 2 object>]\n * // Note parallelText does not include \"missing\".\n * ```\n */\nexport function getTextAndMarkers(sharedString: SharedString, label: string): {\n parallelText: string[];\n parallelMarkers: Marker[];\n} {\n return sharedString.getTextAndMarkers(label);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/sequence",
|
|
3
|
-
"version": "2.0.0-internal.1.
|
|
3
|
+
"version": "2.0.0-internal.1.1.0",
|
|
4
4
|
"description": "Distributed sequence",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -30,11 +30,15 @@
|
|
|
30
30
|
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
31
31
|
"lint": "npm run eslint",
|
|
32
32
|
"lint:fix": "npm run eslint:fix",
|
|
33
|
+
"postpack": "cd dist && tar -cvf ../sequence.test-files.tar ./test",
|
|
33
34
|
"test": "npm run test:mocha",
|
|
34
35
|
"test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
|
|
35
|
-
"test:
|
|
36
|
+
"test:memory": "mocha --config src/test/memory/.mocharc.js",
|
|
37
|
+
"test:memory-profiling:report": "mocha --config src/test/memory/.mocharc.js",
|
|
38
|
+
"test:mocha": "mocha --ignore 'dist/test/memory/**/*' --recursive 'dist/test/**/*.spec.js' -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
|
|
36
39
|
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
|
|
37
40
|
"test:newsnapfiles": "node dist/test/createSnapshotFiles.js",
|
|
41
|
+
"test:stress": "cross-env FUZZ_TEST_COUNT=100 FUZZ_STRESS_RUN=true npm run test:mocha",
|
|
38
42
|
"testfarm": "node dist/test/testFarm.js",
|
|
39
43
|
"tsc": "tsc",
|
|
40
44
|
"tsfmt": "tsfmt --verify",
|
|
@@ -63,29 +67,30 @@
|
|
|
63
67
|
},
|
|
64
68
|
"dependencies": {
|
|
65
69
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
66
|
-
"@fluidframework/common-utils": "^0.
|
|
67
|
-
"@fluidframework/container-utils": "2.0.0-internal.1.
|
|
68
|
-
"@fluidframework/core-interfaces": "2.0.0-internal.1.
|
|
69
|
-
"@fluidframework/datastore-definitions": "2.0.0-internal.1.
|
|
70
|
-
"@fluidframework/merge-tree": "2.0.0-internal.1.
|
|
71
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
72
|
-
"@fluidframework/runtime-definitions": "2.0.0-internal.1.
|
|
73
|
-
"@fluidframework/runtime-utils": "2.0.0-internal.1.
|
|
74
|
-
"@fluidframework/shared-object-base": "2.0.0-internal.1.
|
|
75
|
-
"@fluidframework/telemetry-utils": "2.0.0-internal.1.
|
|
70
|
+
"@fluidframework/common-utils": "^1.0.0",
|
|
71
|
+
"@fluidframework/container-utils": "^2.0.0-internal.1.1.0",
|
|
72
|
+
"@fluidframework/core-interfaces": "^2.0.0-internal.1.1.0",
|
|
73
|
+
"@fluidframework/datastore-definitions": "^2.0.0-internal.1.1.0",
|
|
74
|
+
"@fluidframework/merge-tree": "^2.0.0-internal.1.1.0",
|
|
75
|
+
"@fluidframework/protocol-definitions": "^1.0.0",
|
|
76
|
+
"@fluidframework/runtime-definitions": "^2.0.0-internal.1.1.0",
|
|
77
|
+
"@fluidframework/runtime-utils": "^2.0.0-internal.1.1.0",
|
|
78
|
+
"@fluidframework/shared-object-base": "^2.0.0-internal.1.1.0",
|
|
79
|
+
"@fluidframework/telemetry-utils": "^2.0.0-internal.1.1.0",
|
|
76
80
|
"uuid": "^8.3.1"
|
|
77
81
|
},
|
|
78
82
|
"devDependencies": {
|
|
79
|
-
"@fluid-internal/stochastic-test-utils": "2.0.0-internal.1.
|
|
80
|
-
"@fluid-internal/test-dds-utils": "2.0.0-internal.1.
|
|
83
|
+
"@fluid-internal/stochastic-test-utils": "^2.0.0-internal.1.1.0",
|
|
84
|
+
"@fluid-internal/test-dds-utils": "^2.0.0-internal.1.1.0",
|
|
85
|
+
"@fluid-tools/benchmark": "^0.43.0",
|
|
81
86
|
"@fluidframework/build-common": "^0.24.0",
|
|
82
|
-
"@fluidframework/build-tools": "^0.3.
|
|
87
|
+
"@fluidframework/build-tools": "^0.3.1000",
|
|
83
88
|
"@fluidframework/eslint-config-fluid": "^0.28.2000",
|
|
84
|
-
"@fluidframework/gitresources": "^0.1037.1000
|
|
85
|
-
"@fluidframework/mocha-test-setup": "2.0.0-internal.1.
|
|
89
|
+
"@fluidframework/gitresources": "^0.1037.1000",
|
|
90
|
+
"@fluidframework/mocha-test-setup": "^2.0.0-internal.1.1.0",
|
|
86
91
|
"@fluidframework/sequence-previous": "npm:@fluidframework/sequence@^1.0.0",
|
|
87
|
-
"@fluidframework/server-services-client": "^0.1037.1000
|
|
88
|
-
"@fluidframework/test-runtime-utils": "2.0.0-internal.1.
|
|
92
|
+
"@fluidframework/server-services-client": "^0.1037.1000",
|
|
93
|
+
"@fluidframework/test-runtime-utils": "^2.0.0-internal.1.1.0",
|
|
89
94
|
"@microsoft/api-extractor": "^7.22.2",
|
|
90
95
|
"@rushstack/eslint-config": "^2.5.1",
|
|
91
96
|
"@types/diff": "^3.5.1",
|
|
@@ -93,7 +98,7 @@
|
|
|
93
98
|
"@types/node": "^14.18.0",
|
|
94
99
|
"@types/random-js": "^1.0.31",
|
|
95
100
|
"concurrently": "^6.2.0",
|
|
96
|
-
"copyfiles": "^2.1
|
|
101
|
+
"copyfiles": "^2.4.1",
|
|
97
102
|
"cross-env": "^7.0.2",
|
|
98
103
|
"diff": "^3.5.0",
|
|
99
104
|
"eslint": "~8.6.0",
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
UnassignedSequenceNumber,
|
|
33
33
|
maxReferencePosition,
|
|
34
34
|
createDetachedLocalReferencePosition,
|
|
35
|
+
DetachedReferencePosition,
|
|
35
36
|
} from "@fluidframework/merge-tree";
|
|
36
37
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
37
38
|
import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
@@ -99,7 +100,7 @@ function decompressInterval(interval: CompressedSerializedInterval, label?: stri
|
|
|
99
100
|
end: interval[1],
|
|
100
101
|
sequenceNumber: interval[2],
|
|
101
102
|
intervalType: interval[3],
|
|
102
|
-
properties: { ...interval[4], [reservedRangeLabelsKey]: label },
|
|
103
|
+
properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
|
|
103
104
|
};
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -191,11 +192,7 @@ export class Interval implements ISerializableInterval {
|
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
public serialize(client: Client): ISerializedInterval {
|
|
194
|
-
|
|
195
|
-
if (client) {
|
|
196
|
-
seq = client.getCurrentSeq();
|
|
197
|
-
}
|
|
198
|
-
|
|
195
|
+
const seq = client?.getCurrentSeq() ?? 0;
|
|
199
196
|
const serializedInterval: ISerializedInterval = {
|
|
200
197
|
end: this.end,
|
|
201
198
|
intervalType: 0,
|
|
@@ -431,7 +428,7 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
431
428
|
return (endPos > bstart) && (startPos < bend);
|
|
432
429
|
}
|
|
433
430
|
|
|
434
|
-
public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage) {
|
|
431
|
+
public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage, localSeq?: number) {
|
|
435
432
|
const getRefType = (baseType: ReferenceType): ReferenceType => {
|
|
436
433
|
let refType = baseType;
|
|
437
434
|
if (op === undefined) {
|
|
@@ -443,13 +440,17 @@ export class SequenceInterval implements ISerializableInterval {
|
|
|
443
440
|
|
|
444
441
|
let startRef = this.start;
|
|
445
442
|
if (start !== undefined) {
|
|
446
|
-
startRef = createPositionReference(
|
|
443
|
+
startRef = createPositionReference(
|
|
444
|
+
this.client, start, getRefType(this.start.refType), op, undefined, localSeq,
|
|
445
|
+
);
|
|
447
446
|
startRef.addProperties(this.start.properties);
|
|
448
447
|
}
|
|
449
448
|
|
|
450
449
|
let endRef = this.end;
|
|
451
450
|
if (end !== undefined) {
|
|
452
|
-
endRef = createPositionReference(
|
|
451
|
+
endRef = createPositionReference(
|
|
452
|
+
this.client, end, getRefType(this.end.refType), op, undefined, localSeq,
|
|
453
|
+
);
|
|
453
454
|
endRef.addProperties(this.end.properties);
|
|
454
455
|
}
|
|
455
456
|
|
|
@@ -479,11 +480,13 @@ function createPositionReferenceFromSegoff(
|
|
|
479
480
|
if (segoff.segment) {
|
|
480
481
|
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
|
|
481
482
|
return ref;
|
|
482
|
-
} else {
|
|
483
|
-
if (!op && !refTypeIncludesFlag(refType, ReferenceType.Transient)) {
|
|
484
|
-
throw new UsageError("Non-transient references need segment");
|
|
485
|
-
}
|
|
486
483
|
}
|
|
484
|
+
|
|
485
|
+
if (!op && !refTypeIncludesFlag(refType, ReferenceType.Transient)) {
|
|
486
|
+
// reference to segment that dne locally
|
|
487
|
+
throw new UsageError("Non-transient references need segment");
|
|
488
|
+
}
|
|
489
|
+
|
|
487
490
|
return createDetachedLocalReferencePosition(refType);
|
|
488
491
|
}
|
|
489
492
|
|
|
@@ -492,7 +495,9 @@ function createPositionReference(
|
|
|
492
495
|
pos: number,
|
|
493
496
|
refType: ReferenceType,
|
|
494
497
|
op?: ISequencedDocumentMessage,
|
|
495
|
-
fromSnapshot?: boolean
|
|
498
|
+
fromSnapshot?: boolean,
|
|
499
|
+
localSeq?: number,
|
|
500
|
+
): LocalReferencePosition {
|
|
496
501
|
let segoff;
|
|
497
502
|
if (op) {
|
|
498
503
|
assert((refType & ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
|
|
@@ -501,7 +506,7 @@ function createPositionReference(
|
|
|
501
506
|
} else {
|
|
502
507
|
assert((refType & ReferenceType.SlideOnRemove) === 0 || fromSnapshot,
|
|
503
508
|
0x2f6 /* SlideOnRemove references must be op created */);
|
|
504
|
-
segoff = client.getContainingSegment(pos);
|
|
509
|
+
segoff = client.getContainingSegment(pos, undefined, localSeq);
|
|
505
510
|
}
|
|
506
511
|
return createPositionReferenceFromSegoff(client, segoff, refType, op);
|
|
507
512
|
}
|
|
@@ -793,18 +798,23 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
793
798
|
if (!interval.properties) {
|
|
794
799
|
interval.properties = createMap<any>();
|
|
795
800
|
}
|
|
801
|
+
|
|
796
802
|
if (props) {
|
|
797
803
|
interval.addProperties(props);
|
|
798
804
|
}
|
|
799
|
-
|
|
800
|
-
// Create a new ID.
|
|
801
|
-
interval.properties[reservedIntervalIdKey] = uuid();
|
|
802
|
-
}
|
|
805
|
+
interval.properties[reservedIntervalIdKey] ??= uuid();
|
|
803
806
|
this.add(interval);
|
|
804
807
|
}
|
|
805
808
|
return interval;
|
|
806
809
|
}
|
|
807
810
|
|
|
811
|
+
private linkEndpointsToInterval(interval: TInterval): void {
|
|
812
|
+
if (interval instanceof SequenceInterval) {
|
|
813
|
+
interval.start.addProperties({ interval });
|
|
814
|
+
interval.end.addProperties({ interval });
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
808
818
|
private addIntervalToIndex(interval: TInterval) {
|
|
809
819
|
const id = interval.getIntervalId();
|
|
810
820
|
assert(id !== undefined, 0x2c0 /* "ID must be created before adding interval to collection" */);
|
|
@@ -820,6 +830,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
820
830
|
}
|
|
821
831
|
|
|
822
832
|
public add(interval: TInterval) {
|
|
833
|
+
this.linkEndpointsToInterval(interval);
|
|
823
834
|
this.addIntervalToIndex(interval);
|
|
824
835
|
this.addIntervalListeners(interval);
|
|
825
836
|
}
|
|
@@ -828,8 +839,14 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
|
|
|
828
839
|
return this.intervalIdMap.get(id);
|
|
829
840
|
}
|
|
830
841
|
|
|
831
|
-
public changeInterval(
|
|
832
|
-
|
|
842
|
+
public changeInterval(
|
|
843
|
+
interval: TInterval,
|
|
844
|
+
start: number,
|
|
845
|
+
end: number,
|
|
846
|
+
op?: ISequencedDocumentMessage,
|
|
847
|
+
localSeq?: number,
|
|
848
|
+
) {
|
|
849
|
+
const newInterval = interval.modify(this.label, start, end, op, localSeq) as TInterval | undefined;
|
|
833
850
|
if (newInterval) {
|
|
834
851
|
this.removeExistingInterval(interval);
|
|
835
852
|
this.add(newInterval);
|
|
@@ -980,6 +997,11 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
|
|
|
980
997
|
"add",
|
|
981
998
|
{
|
|
982
999
|
process: (collection, params, local, op) => {
|
|
1000
|
+
// if params is undefined, the interval was deleted during
|
|
1001
|
+
// rebasing
|
|
1002
|
+
if (!params) {
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
983
1005
|
collection.ackAdd(params, local, op);
|
|
984
1006
|
},
|
|
985
1007
|
rebase,
|
|
@@ -1001,6 +1023,11 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
|
|
|
1001
1023
|
"change",
|
|
1002
1024
|
{
|
|
1003
1025
|
process: (collection, params, local, op) => {
|
|
1026
|
+
// if params is undefined, the interval was deleted during
|
|
1027
|
+
// rebasing
|
|
1028
|
+
if (!params) {
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1004
1031
|
collection.ackChange(params, local, op);
|
|
1005
1032
|
},
|
|
1006
1033
|
rebase,
|
|
@@ -1063,7 +1090,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1063
1090
|
private savedSerializedIntervals?: ISerializedInterval[];
|
|
1064
1091
|
private localCollection: LocalIntervalCollection<TInterval>;
|
|
1065
1092
|
private onDeserialize: DeserializeCallback | undefined;
|
|
1066
|
-
private client: Client;
|
|
1093
|
+
private client: Client | undefined;
|
|
1067
1094
|
private readonly pendingChangesStart: Map<string, ISerializedInterval[]> = new Map<string, ISerializedInterval[]>();
|
|
1068
1095
|
private readonly pendingChangesEnd: Map<string, ISerializedInterval[]> = new Map<string, ISerializedInterval[]>();
|
|
1069
1096
|
|
|
@@ -1129,7 +1156,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1129
1156
|
* Gets the next local sequence number, modifying this client's collab window in doing so.
|
|
1130
1157
|
*/
|
|
1131
1158
|
private getNextLocalSeq(): number {
|
|
1132
|
-
|
|
1159
|
+
if (this.client) {
|
|
1160
|
+
return ++this.client.getCollabWindow().localSeq;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
return 0;
|
|
1133
1164
|
}
|
|
1134
1165
|
|
|
1135
1166
|
public getIntervalById(id: string) {
|
|
@@ -1411,12 +1442,22 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1411
1442
|
});
|
|
1412
1443
|
}
|
|
1413
1444
|
|
|
1414
|
-
/**
|
|
1445
|
+
/**
|
|
1446
|
+
* @internal
|
|
1447
|
+
*
|
|
1448
|
+
* Returns new interval after rebasing. If undefined, the interval was
|
|
1449
|
+
* deleted as a result of rebasing. This can occur if the interval applies
|
|
1450
|
+
* to a range that no longer exists, and the interval was unable to slide.
|
|
1451
|
+
*/
|
|
1415
1452
|
public rebaseLocalInterval(
|
|
1416
1453
|
opName: string,
|
|
1417
1454
|
serializedInterval: ISerializedInterval,
|
|
1418
1455
|
localSeq: number,
|
|
1419
|
-
) {
|
|
1456
|
+
): ISerializedInterval | undefined {
|
|
1457
|
+
if (!this.client) {
|
|
1458
|
+
// If there's no associated mergeTree client, the originally submitted op is still correct.
|
|
1459
|
+
return serializedInterval;
|
|
1460
|
+
}
|
|
1420
1461
|
if (!this.attached) {
|
|
1421
1462
|
throw new LoggingError("attachSequence must be called");
|
|
1422
1463
|
}
|
|
@@ -1428,6 +1469,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1428
1469
|
this.client.rebasePosition(end, sequenceNumber, localSeq);
|
|
1429
1470
|
|
|
1430
1471
|
const intervalId = properties?.[reservedIntervalIdKey];
|
|
1472
|
+
const localInterval = this.localCollection.getIntervalById(intervalId);
|
|
1473
|
+
|
|
1431
1474
|
const rebased: ISerializedInterval = {
|
|
1432
1475
|
start: startRebased,
|
|
1433
1476
|
end: endRebased,
|
|
@@ -1435,10 +1478,44 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1435
1478
|
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1436
1479
|
properties,
|
|
1437
1480
|
};
|
|
1481
|
+
|
|
1438
1482
|
if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
|
|
1439
1483
|
this.removePendingChange(serializedInterval);
|
|
1440
1484
|
this.addPendingChange(intervalId, rebased);
|
|
1441
1485
|
}
|
|
1486
|
+
|
|
1487
|
+
// if the interval slid off the string, rebase the op to be a noop and
|
|
1488
|
+
// delete the interval
|
|
1489
|
+
if (startRebased === DetachedReferencePosition || endRebased === DetachedReferencePosition) {
|
|
1490
|
+
if (localInterval) {
|
|
1491
|
+
this.localCollection.removeExistingInterval(localInterval);
|
|
1492
|
+
}
|
|
1493
|
+
return undefined;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
if (!localInterval) {
|
|
1497
|
+
return rebased;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
// we know we must be using `SequenceInterval` because `this.client` exists
|
|
1501
|
+
assert(
|
|
1502
|
+
localInterval instanceof SequenceInterval,
|
|
1503
|
+
0x3a0 /* localInterval must be `SequenceInterval` when used with client */,
|
|
1504
|
+
);
|
|
1505
|
+
|
|
1506
|
+
const startSegment = this.getSlideToSegment(localInterval.start);
|
|
1507
|
+
const endSegment = this.getSlideToSegment(localInterval.end);
|
|
1508
|
+
|
|
1509
|
+
// we need to slide because the reference has been removed
|
|
1510
|
+
if (startSegment || endSegment) {
|
|
1511
|
+
const newStart =
|
|
1512
|
+
startSegment && this.client.getPosition(startSegment.segment, localSeq) + startSegment.offset;
|
|
1513
|
+
const newEnd =
|
|
1514
|
+
endSegment && this.client.getPosition(endSegment.segment, localSeq) + endSegment.offset;
|
|
1515
|
+
|
|
1516
|
+
this.localCollection.changeInterval(localInterval, newStart, newEnd, undefined, localSeq);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1442
1519
|
return rebased;
|
|
1443
1520
|
}
|
|
1444
1521
|
|
|
@@ -1458,7 +1535,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1458
1535
|
}
|
|
1459
1536
|
|
|
1460
1537
|
private ackInterval(interval: TInterval, op: ISequencedDocumentMessage) {
|
|
1461
|
-
//
|
|
1538
|
+
// Only SequenceIntervals need potential sliding
|
|
1462
1539
|
if (!(interval instanceof SequenceInterval)) {
|
|
1463
1540
|
return;
|
|
1464
1541
|
}
|
|
@@ -1653,3 +1730,31 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
|
|
|
1653
1730
|
return this.localCollection.nextInterval(pos);
|
|
1654
1731
|
}
|
|
1655
1732
|
}
|
|
1733
|
+
|
|
1734
|
+
/**
|
|
1735
|
+
* Information that identifies an interval within a `Sequence`.
|
|
1736
|
+
*/
|
|
1737
|
+
export interface IntervalLocator {
|
|
1738
|
+
/**
|
|
1739
|
+
* Label for the collection the interval is a part of
|
|
1740
|
+
*/
|
|
1741
|
+
label: string;
|
|
1742
|
+
/**
|
|
1743
|
+
* Interval within that collection
|
|
1744
|
+
*/
|
|
1745
|
+
interval: SequenceInterval;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/**
|
|
1749
|
+
* Returns an object that can be used to find the interval a given LocalReferencePosition belongs to.
|
|
1750
|
+
* @returns undefined if the reference position is not the endpoint of any interval (e.g. it was created
|
|
1751
|
+
* on the merge tree directly by app code), otherwise an {@link IntervalLocator} for the interval this
|
|
1752
|
+
* endpoint is a part of.
|
|
1753
|
+
*/
|
|
1754
|
+
export function intervalLocatorFromEndpoint(potentialEndpoint: LocalReferencePosition): IntervalLocator | undefined {
|
|
1755
|
+
const {
|
|
1756
|
+
interval,
|
|
1757
|
+
[reservedRangeLabelsKey]: collectionNameArray,
|
|
1758
|
+
} = potentialEndpoint.properties ?? {};
|
|
1759
|
+
return (interval && collectionNameArray?.length === 1) ? { label: collectionNameArray[0], interval } : undefined;
|
|
1760
|
+
}
|
package/src/packageVersion.ts
CHANGED
package/src/sharedString.ts
CHANGED
|
@@ -223,6 +223,9 @@ export class SharedString extends SharedSegmentSequence<SharedStringSegment> imp
|
|
|
223
223
|
return this.client.findTile(startPos, tileLabel, preceding);
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
/**
|
|
227
|
+
* @deprecated - use the free function `getTextAndMarkers` exported by this package instead.
|
|
228
|
+
*/
|
|
226
229
|
public getTextAndMarkers(label: string) {
|
|
227
230
|
const segmentWindow = this.client.getCollabWindow();
|
|
228
231
|
return this.mergeTreeTextHelper.getTextAndMarkers(segmentWindow.currentSeq, segmentWindow.clientId, label);
|
|
@@ -242,14 +245,16 @@ export class SharedString extends SharedSegmentSequence<SharedStringSegment> imp
|
|
|
242
245
|
/**
|
|
243
246
|
* Adds spaces for markers and handles, so that position calculations account for them.
|
|
244
247
|
*/
|
|
245
|
-
public getTextWithPlaceholders() {
|
|
248
|
+
public getTextWithPlaceholders(start?: number, end?: number) {
|
|
246
249
|
const segmentWindow = this.client.getCollabWindow();
|
|
247
|
-
return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, " ");
|
|
250
|
+
return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, " ", start, end);
|
|
248
251
|
}
|
|
249
252
|
|
|
253
|
+
/**
|
|
254
|
+
* @deprecated - use `getTextWithPlaceholders` instead.
|
|
255
|
+
*/
|
|
250
256
|
public getTextRangeWithPlaceholders(start: number, end: number) {
|
|
251
|
-
|
|
252
|
-
return this.mergeTreeTextHelper.getText(segmentWindow.currentSeq, segmentWindow.clientId, " ", start, end);
|
|
257
|
+
return this.getTextWithPlaceholders(start, end);
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
public getTextRangeWithMarkers(start: number, end: number) {
|
|
@@ -272,3 +277,25 @@ export class SharedString extends SharedSegmentSequence<SharedStringSegment> imp
|
|
|
272
277
|
}
|
|
273
278
|
}
|
|
274
279
|
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Splits the text into regions ending with markers with the given `label`.
|
|
283
|
+
* @param sharedString - String to retrieve text and markers from
|
|
284
|
+
* @param label - label to split on
|
|
285
|
+
* @returns Two parallel lists of text and markers, split by markers with the provided `label`.
|
|
286
|
+
*
|
|
287
|
+
* For example:
|
|
288
|
+
* ```typescript
|
|
289
|
+
* // Say sharedstring has contents "hello<paragraph marker 1>world<paragraph marker 2>missing".
|
|
290
|
+
* const { parallelText, parallelMarkers } = getTextAndMarkers(sharedString, "paragraph");
|
|
291
|
+
* // parallelText === ["hello", "world"]
|
|
292
|
+
* // parallelMarkers === [<paragraph marker 1 object>, <paragraph marker 2 object>]
|
|
293
|
+
* // Note parallelText does not include "missing".
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
export function getTextAndMarkers(sharedString: SharedString, label: string): {
|
|
297
|
+
parallelText: string[];
|
|
298
|
+
parallelMarkers: Marker[];
|
|
299
|
+
} {
|
|
300
|
+
return sharedString.getTextAndMarkers(label);
|
|
301
|
+
}
|