UNPKG

position-strings

Version:

Lexicographically-ordered position strings for collaborative lists and text

77 lines (73 loc) 2.78 kB
import { findPosition } from "./find_position"; import { PositionSource } from "./position_source"; import { precond } from "./util"; /** * Utilities for working with cursors in a collaborative list * or text string. * * A *cursor* points to a particular spot in a list, in between * two list elements (or text characters). This class handles * cursors for lists that use our position strings. * * A cursor is represented as a string. * Specifically, it is the position of the element * to its left, or `PositionSource.FIRST` if it is at the beginning * of the list. If that position is later deleted, the cursor stays the * same, but its index shifts to next element on its left. * * You can use cursor strings as ordinary cursors, selection endpoints, * range endpoints for a comment or formatting span, etc. */ export class Cursors { private constructor() { // Not instantiable. } /** * Returns the cursor at `index` within the given list of positions. Invert with `Cursors.toIndex`. * * That is, the cursor is between the list elements at `index - 1` and `index`. * * If this method is inconvenient (e.g., the positions are in a database * instead of an array), you can instead run the following algorithm yourself: * - If `index` is 0, return `PositionSource.FIRST = ""`. * - Else return `positions[index - 1]`. * * @param positions The target list's positions, in lexicographic order. * There should be no duplicate positions. */ static fromIndex(index: number, positions: ArrayLike<string>): string { precond( index >= 0 && index <= positions.length, "Index out of bounds:", index, positions.length ); return index === 0 ? PositionSource.FIRST : positions[index - 1]; } /** * Returns the current index of `cursor` within the given list of * positions. Inverse of `Cursors.fromIndex`. * * That is, the cursor is between the list elements at `index - 1` and `index`. * * If this method is inconvenient (e.g., the positions are in a database * instead of an array), you can instead compute * `index` by finding the number of positions less than * or equal to `position`. * For example, in SQL, use: * ```sql * SELECT COUNT(*) FROM table WHERE position <= $position * ``` * * See also: `findPosition`. * * @param positions The target list's positions, in lexicographic order. * There should be no duplicate positions. */ static toIndex(cursor: string, positions: ArrayLike<string>): number { const { index, isPresent } = findPosition(cursor, positions); // findPosition gives < elements, but we want <= elements. // So if there's an == element, add 1. return isPresent ? index + 1 : index; } }