Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 2x 2x 123x 123x 123x 123x 123x 145x 145x 145x 102x 145x 145x 145x 145x 145x 94x 94x 94x 51x 145x 123x 123x 2x 211x 211x 174x 174x 174x 174x 174x 2x 105x 105x 105x 104x 104x 104x 104x 122x 122x 122x 122x 89x 89x 89x 33x 104x | import {JsonCrdtDataType} from 'json-joy/lib/json-crdt-patch/constants';
import * as id from './id';
import type {ITimestampStruct, StrApi, StrNode, Model} from 'json-joy/lib/json-crdt';
import type {PresenceIdShorthand, PresencePoint, RgaSelection, PresenceCursor} from './types';
export type StrSelectionStrict = [anchor: number, focus?: number];
export type StrSelection =
/** A single *caret* selection. */
| number
/** A *range* selection, with an anchor and focus. */
| StrSelectionStrict;
/**
* Converts offset-based string selections to CRDT ID-based selections. The
* offset-based selections are relative to the current state of the string, and
* will be converted to stable CRDT ID-based selections, which are stable across
* concurrent edits.
*
* @param str The "str" node instance.
* @param selections Collection of offset-based selections in the string.
* @returns RGA selection entry, where all offset-based selection converted to
* stable CRDT ID-based selections.
*/
export const toDto = (str: StrApi, selections: StrSelection[]): RgaSelection => {
const clock = str.api.model.clock;
const sid = clock.sid;
const nodeId: PresenceIdShorthand = id.toDto(sid, str.node.id);
const cursors: PresenceCursor[] = [];
for (const selection of selections) {
let anchor: number = 0,
focus: number = -1;
if (typeof selection === 'number') anchor = selection;
else [anchor, focus = anchor] = selection;
if (focus === anchor) focus = -1;
let cursor: PresenceCursor;
try {
const anchorId: ITimestampStruct = str.findId(anchor - 1);
const anchorPoint: PresencePoint = [id.toDto(sid, anchorId)];
if (focus >= 0) {
const focusId: ITimestampStruct = str.findId(focus - 1);
const focusPoint: PresencePoint = [id.toDto(sid, focusId)];
cursor = [anchorPoint, focusPoint];
} else {
cursor = [anchorPoint];
}
} catch {
cursor = [[id.toDto(sid, str.node.id)]];
}
cursors.push(cursor);
}
const selection: RgaSelection = ['', '', sid, clock.time, {}, JsonCrdtDataType.str, nodeId, cursors];
return selection;
};
/**
* Convert a CRDT ID to a view offset (the cursor position after that
* character). Returns 0 when the ID matches the node itself (i.e. before
* the first character).
*/
const findOffset = (str: StrNode, tsId: ITimestampStruct): number => {
const nodeId = str.id;
if (nodeId.sid === tsId.sid && nodeId.time === tsId.time) return 0;
const chunk = str.findById(tsId);
Iif (!chunk) return 0;
const pos = str.pos(chunk);
const charIndex = pos + (chunk.del ? 0 : tsId.time - chunk.id.time);
return charIndex + 1;
};
/**
* Converts a CRDT ID-based RGA selection back to offset-based string
* selections. This is the inverse of {@link toDto}.
*
* @param model The JSON CRDT model containing the "str" node.
* @param selection The RGA selection DTO to convert.
* @returns Collection of offset-based selections, or an empty array if the
* node is not found or is not a "str" node.
*/
export const fromDto = (model: Model<any>, selection: RgaSelection): StrSelectionStrict[] => {
const [_documentId, _uiLocationId, sid, _time, _meta, type, nodeIdDto, cursors] = selection;
const result: StrSelectionStrict[] = [];
if (type !== JsonCrdtDataType.str) return result;
const nodeId = id.fromDto(sid, nodeIdDto);
const str = model.index.get(nodeId) as StrNode | undefined;
Iif (!str || str.name() !== 'str') return result;
for (const cursor of cursors) {
const [anchorPointDto, focusPointDto] = cursor;
const anchorId = id.fromDto(sid, anchorPointDto[0]);
const anchorOffset = findOffset(str, anchorId);
if (focusPointDto) {
const focusId = id.fromDto(sid, focusPointDto[0]);
const focusOffset = findOffset(str, focusId);
result.push([anchorOffset, focusOffset]);
} else {
result.push([anchorOffset]);
}
}
return result;
};
|