Skip to content
95 changes: 89 additions & 6 deletions src/json-crdt-extensions/peritext/Peritext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import type {Model} from '../../json-crdt/model';
import type {Printable} from '../../util/print/types';
import type {SliceType} from './types';
import type {PersistedSlice} from './slice/PersistedSlice';
import {CONST} from '../../json-hash';

/**
* Context for a Peritext instance. Contains all the data and methods needed to
* interact with the text.
*/
export class Peritext implements Printable {
public readonly slices: Slices;
public readonly editor: Editor;
Expand All @@ -25,35 +28,97 @@ export class Peritext implements Printable {
this.editor = new Editor(this);
}

public point(id: ITimestampStruct, anchor: Anchor = Anchor.After): Point {
public strApi() {
return this.model.api.wrap(this.str);
}

// ------------------------------------------------------------------- Points

/**
* Creates a point at a character ID.
*
* @param id Character ID to which the point should be attached.
* @param anchor Whether the point should be before or after the character.
* @returns The point.
*/
public point(id: ITimestampStruct = this.str.id, anchor: Anchor = Anchor.After): Point {
return new Point(this, id, anchor);
}

/**
* Creates a point at a view position in the text. The `pos` argument specifies
* the position of the character, not the gap between characters.
*
* @param pos Position of the character in the text.
* @param anchor Whether the point should attach before or after a character.
* @returns The point.
*/
public pointAt(pos: number, anchor: Anchor = Anchor.Before): Point {
// TODO: Provide ability to attach to the beginning of the text?
// TODO: Provide ability to attach to the end of the text?
const str = this.str;
const id = str.find(pos);
if (!id) return this.point(str.id, Anchor.After);
return this.point(id, anchor);
}

public pointAtStart(): Point {
/**
* Creates a point which is attached to the start of the text, before the
* first character.
*
* @returns A point at the start of the text.
*/
public pointAbsStart(): Point {
return this.point(this.str.id, Anchor.After);
}

public pointAtEnd(): Point {
/**
* Creates a point which is attached to the end of the text, after the last
* character.
*
* @returns A point at the end of the text.
*/
public pointAbsEnd(): Point {
return this.point(this.str.id, Anchor.Before);
}

// ------------------------------------------------------------------- Ranges

/**
* Creates a range from two points. The points can be in any order.
*
* @param p1 Point
* @param p2 Point
* @returns A range with points in correct order.
*/
public rangeFromPoints(p1: Point, p2: Point): Range {
return Range.from(this, p1, p2);
}

/**
* Creates a range from two points, the points have to be in the correct order.
*
* @param start Start point of the range, must be before or equal to end.
* @param end End point of the range, must be after or equal to start.
* @returns A range with the given start and end points.
*/
public range(start: Point, end: Point): Range {
return new Range(this, start, end);
}

/**
* Creates a range from a view position and a length.
*
* @param start Position in the text.
* @param length Length of the range.
* @returns A range from the given position with the given length.
*/
public rangeAt(start: number, length: number = 0): Range {
const str = this.str;
if (!length) {
const startId = !start ? str.id : str.find(start - 1) || str.id;
const point = this.point(startId, Anchor.After);
return this.range(point, point);
return this.range(point, point.clone());
}
const startId = str.find(start) || str.id;
const endId = str.find(start + length - 1) || startId;
Expand All @@ -62,11 +127,27 @@ export class Peritext implements Printable {
return this.range(startEndpoint, endEndpoint);
}

// --------------------------------------------------------------- Insertions

/**
* Insert plain text at a view position in the text.
*
* @param pos View position in the text.
* @param text Text to insert.
*/
public insAt(pos: number, text: string): void {
const str = this.model.api.wrap(this.str);
const str = this.strApi();
str.ins(pos, text);
}

/**
* Insert plain text after a character referenced by its ID and return the
* ID of the insertion operation.
*
* @param after Character ID after which the text should be inserted.
* @param text Text to insert.
* @returns ID of the insertion operation.
*/
public ins(after: ITimestampStruct, text: string): ITimestampStruct {
if (!text) throw new Error('NO_TEXT');
const api = this.model.api;
Expand All @@ -87,6 +168,8 @@ export class Peritext implements Printable {
return slice;
}

// ---------------------------------------------------------------- Deletions

public delSlice(sliceId: ITimestampStruct): void {
this.slices.del(sliceId);
}
Expand Down
8 changes: 8 additions & 0 deletions src/json-crdt-extensions/peritext/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,12 @@ export class Editor implements Printable {
public insertSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice {
return this.txt.insSlice(this.cursor, SliceBehavior.Stack, type, data);
}

public insertOverwriteSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice {
return this.txt.insSlice(this.cursor, SliceBehavior.Overwrite, type, data);
}

public insertEraseSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice {
return this.txt.insSlice(this.cursor, SliceBehavior.Erase, type, data);
}
}
Loading