json-joy
Version:
Collection of libraries for building collaborative editing apps.
322 lines (321 loc) • 14.7 kB
TypeScript
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;
}