json-joy
Version:
Collection of libraries for building collaborative editing apps.
127 lines (126 loc) • 5.67 kB
TypeScript
import { type ITimestampStruct, type ITimespanStruct } from '../../../json-crdt-patch/clock';
/**
* @category CRDT Node
*/
export interface Chunk<T> {
/** Unique sortable ID of this chunk and its span. */
id: ITimestampStruct;
/** Length of the logical clock interval of this chunk. */
span: number;
/** Whether this chunk is deleted. */
del: boolean;
/** Actual content of the chunk, may be undefined if chunk is deleted. */
data: T | undefined;
/** Length of content in this subtree (this node and its children). */
len: number;
/** Parent. */
p: Chunk<T> | undefined;
/** Left. */
l: Chunk<T> | undefined;
/** Right. */
r: Chunk<T> | undefined;
/** Parent 2. */
p2: Chunk<T> | undefined;
/** Left 2. */
l2: Chunk<T> | undefined;
/** Right 2. */
r2: Chunk<T> | undefined;
/** Split link, next chunk which was split from this chunk. */
s: Chunk<T> | undefined;
/** Add more content to this chunk. */
merge(content: T): void;
/**
* Split this chunk after given clock ticks. Minimal `ticks` is 1.
*/
split(ticks: number): Chunk<T>;
/** Mark chunk as deleted. */
delete(): void;
/** Return a deep copy of itself. */
clone(): Chunk<T>;
/** Return the data of the chunk, if not deleted. */
view(): T & {
slice: (start: number, end: number) => T;
};
}
/**
* @category CRDT Node
*/
export declare abstract class AbstractRga<T> {
readonly id: ITimestampStruct;
root: Chunk<T> | undefined;
ids: Chunk<T> | undefined;
count: number;
abstract view(): unknown;
protected abstract createChunk(id: ITimestampStruct, content: T | undefined): Chunk<T>;
protected abstract onChange(): void;
constructor(id: ITimestampStruct);
ins(after: ITimestampStruct, id: ITimestampStruct, content: T): void;
insAt(position: number, id: ITimestampStruct, content: T): ITimestampStruct | undefined;
protected insAfterRoot(after: ITimestampStruct, id: ITimestampStruct, content: T): void;
protected insAfterChunk(after: ITimestampStruct, chunk: Chunk<T>, chunkOffset: number, id: ITimestampStruct, content: T): void;
delete(spans: ITimespanStruct[]): void;
protected deleteSpan(span: ITimespanStruct): void;
find(position: number): undefined | ITimestampStruct;
findChunk(position: number): undefined | [chunk: Chunk<T>, offset: number];
findInterval(position: number, length: number): ITimespanStruct[];
/** Rename to .rangeX() method? */
findInterval2(from: ITimestampStruct, to: ITimestampStruct): ITimespanStruct[];
/**
* @note All ".rangeX()" method are not performance optimized. For hot paths
* it is better to hand craft the loop.
*
* @param startChunk Chunk from which to start the range. If undefined, the
* chunk containing `from` will be used. This is an optimization
* to avoid a lookup.
* @param from ID of the first element in the range.
* @param to ID of the last element in the range.
* @param callback Function to call for each chunk slice in the range. If it
* returns truthy value, the iteration will stop.
* @returns Reference to the last chunk in the range.
*/
range0(startChunk: Chunk<T> | undefined, from: ITimestampStruct, to: ITimestampStruct, callback: (chunk: Chunk<T>, off: number, len: number) => boolean | void): Chunk<T> | void;
first(): Chunk<T> | undefined;
last(): Chunk<T> | undefined;
lastId(): ITimestampStruct | undefined;
/** @todo Maybe use implementation from tree utils, if does not impact performance. */
/** @todo Or better remove this method completely, as it does not require "this". */
next(curr: Chunk<T>): Chunk<T> | undefined;
/** @todo Maybe use implementation from tree utils, if does not impact performance. */
/** @todo Or better remove this method completely, as it does not require "this". */
prev(curr: Chunk<T>): Chunk<T> | undefined;
/** Content length. */
length(): number;
/** Number of chunks. */
size(): number;
/** Returns the position of the first element in the chunk. */
pos(chunk: Chunk<T>): number;
setRoot(chunk: Chunk<T>): void;
insertBefore(chunk: Chunk<T>, before: Chunk<T>): void;
insertAfter(chunk: Chunk<T>, after: Chunk<T>): void;
protected insertAfterRef(chunk: Chunk<T>, ref: ITimestampStruct, left: Chunk<T>): void;
protected mergeContent(chunk: Chunk<T>, content: T): void;
protected insertInside(chunk: Chunk<T>, at: Chunk<T>, offset: number): void;
protected split(chunk: Chunk<T>, ticks: number): Chunk<T>;
protected mergeTombstones(ch1: Chunk<T>, ch2: Chunk<T>): boolean;
protected mergeTombstones2(start: Chunk<T>, end: Chunk<T>): void;
removeTombstones(): void;
deleteChunk(chunk: Chunk<T>): void;
insertId(chunk: Chunk<T>): void;
insertIdFast(chunk: Chunk<T>): void;
protected deleteId(chunk: Chunk<T>): void;
findById(after: ITimestampStruct): Chunk<T> | undefined;
/**
* @param id ID of character to start the search from.
* @returns Previous ID in the RGA sequence.
*/
prevId(id: ITimestampStruct): ITimestampStruct | undefined;
spanView(span: ITimespanStruct): T[];
splay(chunk: Chunk<T>): void;
iterator(): () => Chunk<T> | undefined;
ingest(size: number, next: () => Chunk<T>): void;
private _ingest;
protected toStringName(): string;
toString(tab?: string): string;
protected printChunk(tab: string, chunk: Chunk<T>): string;
protected formatChunk(chunk: Chunk<T>): string;
}