@pylonsync/loro 0.22.44 → 0.22.46
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/package.json +5 -4
- package/src/prosemirror/cursors.ts +14 -0
- package/src/prosemirror/ranges.test.ts +886 -0
- package/src/prosemirror/ranges.ts +637 -0
- package/src/prosemirror/sync.ts +23 -5
package/src/prosemirror/sync.ts
CHANGED
|
@@ -346,8 +346,21 @@ function childIndexFor(node: Node, pmIndex: number): number {
|
|
|
346
346
|
* A cursor for a document position. Walks the position's path from the
|
|
347
347
|
* field's root down, so it does not depend on node identity (a replace
|
|
348
348
|
* rebuilds the nodes along its path).
|
|
349
|
+
*
|
|
350
|
+
* Inside text the cursor binds to a character: by default the one on the
|
|
351
|
+
* left, so text typed at a caret goes before it. `bind: "right"` binds to the
|
|
352
|
+
* character on the right instead, for the start of a range, so text typed
|
|
353
|
+
* just before the range stays out of it. Where there is no character on that
|
|
354
|
+
* side, it binds to the other one.
|
|
349
355
|
*/
|
|
350
|
-
export function positionToCursor(
|
|
356
|
+
export function positionToCursor(
|
|
357
|
+
root: Node,
|
|
358
|
+
pos: number,
|
|
359
|
+
doc: LoroDoc,
|
|
360
|
+
containerId: ContainerID,
|
|
361
|
+
options: { bind?: "left" | "right" } = {},
|
|
362
|
+
): Cursor | null {
|
|
363
|
+
const bind = options.bind ?? "left";
|
|
351
364
|
if (pos < 0 || pos > root.content.size) return null;
|
|
352
365
|
const $pos = root.resolve(pos);
|
|
353
366
|
let map = field(doc, containerId);
|
|
@@ -382,14 +395,14 @@ export function positionToCursor(root: Node, pos: number, doc: LoroDoc, containe
|
|
|
382
395
|
} else {
|
|
383
396
|
// The caret at the end of a text run binds to that run, so typing
|
|
384
397
|
// there continues it.
|
|
385
|
-
if (runStart >= 0 && offset <= pmOffset) return textCursor(children.get(group), offset - runStart);
|
|
398
|
+
if (runStart >= 0 && offset <= pmOffset) return textCursor(children.get(group), offset - runStart, bind);
|
|
386
399
|
runStart = -1;
|
|
387
400
|
group += 1;
|
|
388
401
|
if (offset === pmOffset) return children.getCursor(group) ?? null;
|
|
389
402
|
}
|
|
390
403
|
pmOffset += child.nodeSize;
|
|
391
404
|
}
|
|
392
|
-
if (runStart >= 0 && offset <= pmOffset) return textCursor(children.get(group), offset - runStart);
|
|
405
|
+
if (runStart >= 0 && offset <= pmOffset) return textCursor(children.get(group), offset - runStart, bind);
|
|
393
406
|
return children.getCursor(children.length) ?? null;
|
|
394
407
|
}
|
|
395
408
|
|
|
@@ -403,11 +416,16 @@ function isLowSurrogate(text: string): boolean {
|
|
|
403
416
|
* A cursor `offset` UTF-16 units into a text run, bound to the character on
|
|
404
417
|
* its left (side 1: just after it), the whole character when it is a
|
|
405
418
|
* surrogate pair. At the start of the run there is none, so it binds to the
|
|
406
|
-
* first character.
|
|
419
|
+
* first character. With `bind` "right" it binds to the character on its
|
|
420
|
+
* right (side -1: just before it) while there is one.
|
|
407
421
|
*/
|
|
408
|
-
function textCursor(text: unknown, offset: number): Cursor | null {
|
|
422
|
+
function textCursor(text: unknown, offset: number, bind: "left" | "right"): Cursor | null {
|
|
409
423
|
if (!(text instanceof LoroText)) return null;
|
|
410
424
|
if (offset <= 0) return text.getCursor(0, 0) ?? null;
|
|
425
|
+
if (bind === "right" && offset < text.length) {
|
|
426
|
+
const at = isLowSurrogate(text.slice(offset, offset + 1)) ? offset - 1 : offset;
|
|
427
|
+
return text.getCursor(at, -1) ?? null;
|
|
428
|
+
}
|
|
411
429
|
let start = Math.min(offset, text.length) - 1;
|
|
412
430
|
if (start > 0 && isLowSurrogate(text.slice(start, start + 1))) start -= 1;
|
|
413
431
|
return text.getCursor(start, 1) ?? null;
|