UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

322 lines (321 loc) 14.7 kB
import { Cursor } from './Cursor'; import { EditorSlices } from './EditorSlices'; import { CommonSliceType, type SliceTypeSteps, type SliceType, type SliceTypeStep } from '../slice'; import { ValueSyncStore } from '../../../util/events/sync-store'; import { UndefEndIter, type UndefIterator } from '../../../util/iterator'; import { type ITimespanStruct } from '../../../json-crdt-patch'; import { CursorAnchor } from '../slice/constants'; import type { Point } from '../rga/Point'; import type { Range } from '../rga/Range'; import type { Printable } from 'tree-dump'; import type { Peritext } from '../Peritext'; import type { MarkerSlice } from '../slice/MarkerSlice'; import type { SliceRegistry } from '../registry/SliceRegistry'; import type { CharIterator, CharPredicate, EditorPosition, TextRangeUnit, ViewStyle, ViewRange, EditorUi, EditorSelection } from './types'; export declare class Editor<T = string> implements Printable { readonly txt: Peritext<T>; readonly saved: EditorSlices<T>; readonly extra: EditorSlices<T>; readonly local: EditorSlices<T>; /** * Formatting which will be applied to the next inserted text. This is a * temporary store for formatting which is not yet applied to the text, but * will be if the cursor is not moved. */ readonly pending: ValueSyncStore<Map<string | number, unknown>>; registry: SliceRegistry; constructor(txt: Peritext<T>); text(): string; addCursor(range?: Range<T>, anchor?: CursorAnchor): Cursor<T>; /** * Cursor is the the current user selection. It can be a caret or a range. If * range is collapsed to a single point, it is a *caret*. * * Returns the first cursor in the text and removes all other cursors. If * there is no cursor, creates one and inserts it at the start of the text. * To work with multiple cursors, use `.cursors()` method. */ get cursor(): Cursor<T>; cursors0(): UndefIterator<Cursor<T>>; mainCursor(): Cursor<T> | undefined; cursors(): UndefEndIter<Cursor<T>>; forCursor(callback: (cursor: Cursor<T>) => void): void; /** * @returns Returns `true` if there is at least one cursor in the document. */ hasCursor(): boolean; /** * Returns the first cursor in the document, if any. * * @returns Returns the first cursor in the document, or `undefined` if there * are no cursors. */ getCursor(): undefined | Cursor<T>; /** * @returns Returns the exact number of cursors in the document. */ cursorCount(): number; /** * Returns relative count of cursors (cardinality). * * @returns 0 if there are no cursors, 1 if there is exactly one cursor, 2 if * there are more than one cursor. */ cursorCard(): 0 | 1 | 2; delCursor(cursor: Cursor<T>): void; delCursors(): void; /** * Ensures there is no range selection. If user has selected a range, * the contents is removed and the cursor is set at the start of the range * as caret. */ collapseCursor(cursor: Range<T>): void; collapseCursors(): void; /** * Ensures there is exactly one cursor. If the cursor is a range, contents * inside the range is deleted and cursor is collapsed to a single point. * * @returns A single cursor collapsed to a single point. */ caret(): Cursor<T>; /** * Insert inline text at current cursor position. If cursor selects a range, * the range is removed and the text is inserted at the start of the range. */ insert0(range: Range<T>, text: string): ITimespanStruct | undefined; /** * Inserts text at the cursor positions and collapses cursors, if necessary. * Then applies any pending inline formatting to the inserted text. */ insert(text: string, ranges?: IterableIterator<Range<T>> | Range<T>[]): ITimespanStruct[]; /** * Deletes the previous character at current cursor positions. If cursors * select a range, deletes the whole range. */ del(step?: number): void; delRange(range: Range<T>): void; /** * Deletes one or more units of text in all cursors. If cursor is a range, * deletes the whole range. * * @param step Number of units to delete. * @param unit A unit of deletion: "char", "word", "line". */ delete(step: number, unit: 'char' | 'word' | 'line'): void; /** * Returns an iterator through visible text, one `step`, one character at a * time, starting from a given {@link Point}. * * @param start The starting point. * @param step Number of visible characters to skip. * @returns The next visible character iterator. */ walk(start: Point<T>, step?: number): CharIterator<T>; /** * Returns a forward iterator through visible text, one character at a time, * starting from a given {@link Point}. * * @param start The starting point. * @param chunk Chunk to start from. * @returns The next visible character iterator. */ fwd(start: Point<T>): CharIterator<T>; /** * Returns a backward iterator through visible text, one character at a time, * starting from a given {@link Point}. * * @param start The starting point. * @param chunk Chunk to start from. * @returns The previous visible character iterator. */ bwd(start: Point<T>): CharIterator<T>; /** * Skips a word in an arbitrary direction. A word is defined by the `predicate` * function, which returns `true` if the character is part of the word. * * @param iterator Character iterator. * @param predicate Predicate function to match characters, returns `true` if * the character is part of the word. * @param firstLetterFound Whether the first letter has already been found. If * not, will skip any characters until the first letter, which is matched * by the `predicate` is found. * @returns Point after the last skipped character. */ private skipWord; /** * Hard skips line, skips to the next "\n" newline character. * * @param iterator Character iterator. * @returns Point after the last skipped character. */ private skipLine; /** * End of word iterator (eow). Skips a word forward. A word is defined by the * `predicate` function, which returns `true` if the character is part of the * word. * * @param point Point from which to start skipping. * @param predicate Character class to skip. * @param firstLetterFound Whether the first letter has already been found. If * not, will skip any characters until the first letter, which is * matched by the `predicate` is found. * @returns Point after the last character skipped. */ eow(point: Point<T>, predicate?: CharPredicate<string>, firstLetterFound?: boolean): Point<T>; /** * Beginning of word iterator (bow). Skips a word backward. A word is defined * by the `predicate` function, which returns `true` if the character is part * of the word. * * @param point Point from which to start skipping. * @param predicate Character class to skip. * @param firstLetterFound Whether the first letter has already been found. If * not, will skip any characters until the first letter, which is * matched by the `predicate` is found. * @returns Point after the last character skipped. */ bow(point: Point<T>, predicate?: CharPredicate<string>, firstLetterFound?: boolean): Point<T>; /** Find end of line, starting from given point. */ eol(point: Point<T>): Point<T>; /** Find beginning of line, starting from given point. */ bol(point: Point<T>): Point<T>; /** * Find end of block, starting from given point. Overlay should be refreshed * before calling this method. */ eob(point: Point<T>): Point<T>; /** * Find beginning of block, starting from given point. Overlay should be * refreshed before calling this method. */ bob(point: Point<T>): Point<T>; /** * Move a point given number of steps in a specified direction. The unit of * one move step is defined by the `unit` parameter. * * @param point The point to start from. * @param steps Number of steps to move. Negative number moves backward. * @param unit The unit of move per step: "char", "word", "line", etc. * @returns The destination point after the move. */ skip(point: Point<T>, steps: number, unit: TextRangeUnit, ui?: EditorUi<T>): Point<T>; /** * Move all cursors given number of units. * * @param steps Number of steps to move. * @param unit The unit of move per step: "char", "word", "line". * @param endpoint 0 for "focus", 1 for "anchor", 2 for both. * @param collapse Whether to collapse the range to a single point. */ move(steps: number, unit: TextRangeUnit, endpoint?: 0 | 1 | 2, collapse?: boolean, ui?: EditorUi<T>): void; /** * Leaves only the first cursor, and sets it selection to the whole text. * * @returns Returns `true` if the selection was successful. */ selectAll(): boolean; /** * Selects a word by extending the selection to the left and right of the point. * * @param point Point to the right of which is the starting character of the word. * @returns Range which contains the word. */ rangeWord(point: Point<T>): Range<T> | undefined; /** * Returns a range by expanding the selection to the left and right of the * given point. * * @param point Point from which to start range expansion. * @param unit Unit of the range expansion. * @returns Range which contains the specified unit. */ range(point: Point<T>, unit: TextRangeUnit, ui?: EditorUi<T>): Range<T> | undefined; select(unit: TextRangeUnit, ui?: EditorUi<T>): void; selectAt(at: EditorPosition<T>, unit: TextRangeUnit | '', ui?: EditorUi<T>): void; eraseFormatting(store?: EditorSlices<T>, selection?: Range<T>[] | IterableIterator<Range<T>>): void; clearFormatting(store?: EditorSlices<T>, selection?: Range<T>[] | IterableIterator<Range<T>>): void; protected toggleRangeExclFmt(range: Range<T>, type: CommonSliceType | string | number, data?: unknown, store?: EditorSlices<T>): void; toggleExclFmt(type: CommonSliceType | string | number, data?: unknown, store?: EditorSlices<T>, selection?: Range<T>[] | IterableIterator<Range<T>>): void; /** * Returns block split marker of the block inside which the point is located. * * @param point The point to get the marker at. * @returns The split marker at the point, if any. */ getMarker(point: Point<T>): MarkerSlice<T> | undefined; /** * Returns the block type at the given point. Block type is a nested array of * tags, e.g. `['p']`, `['blockquote', 'p']`, `['ul', 'li', 'p']`, etc. * * @param point The point to get the block type at. * @returns Current block type at the point. */ getBlockType(point: Point<T>): [type: SliceTypeSteps, marker?: MarkerSlice<T> | undefined]; /** * Insert a block split at the start of the document. The start of the * document is defined as immediately after all deleted characters starting * from the beginning of the document, or as the ABS start of the document if * there are no deleted characters. * * @param type The type of the marker. * @returns The inserted marker slice. */ insStartMarker(type: SliceType): MarkerSlice<T>; /** * Find the block split marker which contains the point and sets the block * type of the marker. If there is no block split marker at the point, a new * block split marker is inserted at the beginning of the document with the * specified block type. * * @param point The point at which to set the block type. * @param type The new block type. * @returns The marker slice at the point, or a new marker slice if there is none. */ setBlockType(point: Point<T>, type: SliceType): MarkerSlice<T>; getContainerPath(steps: SliceTypeSteps): SliceTypeSteps; getDeepestCommonContainer(steps1: SliceTypeSteps, steps2: SliceTypeSteps): number; /** * @param at Point at which split block split happens. * @param slices The slices set to use. * @returns True if a marker was inserted, false if it was updated. */ splitAt(at: Point<T>, slices?: EditorSlices<T>): boolean; split(type?: SliceType, data?: unknown, selection?: Range<T>[] | IterableIterator<Range<T>>, slices?: EditorSlices<T>): void; setStartMarker(type: SliceType, data?: unknown, slices?: EditorSlices<T>): MarkerSlice<T>; tglMarkerAt(point: Point<T>, type: SliceType, data?: unknown, slices?: EditorSlices<T>, def?: SliceTypeStep): void; updMarkerAt(point: Point<T>, type: SliceType, data?: unknown, slices?: EditorSlices<T>): void; /** * Toggle the type of a block split between the slice type and the default * (paragraph) block type. * * @param type Slice type to toggle. * @param data Custom data of the slice. */ tglMarker(type: SliceType, data?: unknown, selection?: Range<T>[] | IterableIterator<Range<T>>, slices?: EditorSlices<T>, def?: SliceTypeStep): void; /** * Update the type of a block split at all cursor positions. * * @param type Slice type to set. * @param data Custom data of the slice. * @param slices The slices set to use, if new marker is inserted at the start * of the document. */ updMarker(type: SliceType, data?: unknown, selection?: Range<T>[] | IterableIterator<Range<T>>, slices?: EditorSlices<T>): void; delMarker(selection?: Range<T>[] | IterableIterator<Range<T>>): void; export(range: Range<T>): ViewRange; /** * "Copy formatting-only", copies inline formatting applied to the selected * range. * * @param range Range copy formatting from, normally a single visible character. * @returns A list of serializable inline formatting applied to the selected range. */ exportStyle(range: Range<T>): ViewStyle[]; import(pos: number, view: ViewRange): number; importStyle(range: Range<T>, formatting: ViewStyle[]): void; pos2point(at: EditorPosition<T>): Point<T>; sel2range(at: EditorSelection<T>): [range: Range<T>, anchor: CursorAnchor]; end(): Point<T>; start(): Point<T>; toString(tab?: string): string; }