Skip to content

Commit ce831fd

Browse files
authored
Merge pull request #579 from streamich/slices-3
Peritext slice and marker improvements
2 parents c89a75b + 15a7024 commit ce831fd

File tree

7 files changed

+813
-134
lines changed

7 files changed

+813
-134
lines changed

src/json-crdt-extensions/peritext/Peritext.ts

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ import type {Model} from '../../json-crdt/model';
1010
import type {Printable} from '../../util/print/types';
1111
import type {SliceType} from './types';
1212
import type {PersistedSlice} from './slice/PersistedSlice';
13-
import {CONST} from '../../json-hash';
1413

14+
/**
15+
* Context for a Peritext instance. Contains all the data and methods needed to
16+
* interact with the text.
17+
*/
1518
export class Peritext implements Printable {
1619
public readonly slices: Slices;
1720
public readonly editor: Editor;
@@ -25,35 +28,97 @@ export class Peritext implements Printable {
2528
this.editor = new Editor(this);
2629
}
2730

28-
public point(id: ITimestampStruct, anchor: Anchor = Anchor.After): Point {
31+
public strApi() {
32+
return this.model.api.wrap(this.str);
33+
}
34+
35+
// ------------------------------------------------------------------- Points
36+
37+
/**
38+
* Creates a point at a character ID.
39+
*
40+
* @param id Character ID to which the point should be attached.
41+
* @param anchor Whether the point should be before or after the character.
42+
* @returns The point.
43+
*/
44+
public point(id: ITimestampStruct = this.str.id, anchor: Anchor = Anchor.After): Point {
2945
return new Point(this, id, anchor);
3046
}
3147

48+
/**
49+
* Creates a point at a view position in the text. The `pos` argument specifies
50+
* the position of the character, not the gap between characters.
51+
*
52+
* @param pos Position of the character in the text.
53+
* @param anchor Whether the point should attach before or after a character.
54+
* @returns The point.
55+
*/
3256
public pointAt(pos: number, anchor: Anchor = Anchor.Before): Point {
57+
// TODO: Provide ability to attach to the beginning of the text?
58+
// TODO: Provide ability to attach to the end of the text?
3359
const str = this.str;
3460
const id = str.find(pos);
3561
if (!id) return this.point(str.id, Anchor.After);
3662
return this.point(id, anchor);
3763
}
3864

39-
public pointAtStart(): Point {
65+
/**
66+
* Creates a point which is attached to the start of the text, before the
67+
* first character.
68+
*
69+
* @returns A point at the start of the text.
70+
*/
71+
public pointAbsStart(): Point {
4072
return this.point(this.str.id, Anchor.After);
4173
}
4274

43-
public pointAtEnd(): Point {
75+
/**
76+
* Creates a point which is attached to the end of the text, after the last
77+
* character.
78+
*
79+
* @returns A point at the end of the text.
80+
*/
81+
public pointAbsEnd(): Point {
4482
return this.point(this.str.id, Anchor.Before);
4583
}
4684

85+
// ------------------------------------------------------------------- Ranges
86+
87+
/**
88+
* Creates a range from two points. The points can be in any order.
89+
*
90+
* @param p1 Point
91+
* @param p2 Point
92+
* @returns A range with points in correct order.
93+
*/
94+
public rangeFromPoints(p1: Point, p2: Point): Range {
95+
return Range.from(this, p1, p2);
96+
}
97+
98+
/**
99+
* Creates a range from two points, the points have to be in the correct order.
100+
*
101+
* @param start Start point of the range, must be before or equal to end.
102+
* @param end End point of the range, must be after or equal to start.
103+
* @returns A range with the given start and end points.
104+
*/
47105
public range(start: Point, end: Point): Range {
48106
return new Range(this, start, end);
49107
}
50108

109+
/**
110+
* Creates a range from a view position and a length.
111+
*
112+
* @param start Position in the text.
113+
* @param length Length of the range.
114+
* @returns A range from the given position with the given length.
115+
*/
51116
public rangeAt(start: number, length: number = 0): Range {
52117
const str = this.str;
53118
if (!length) {
54119
const startId = !start ? str.id : str.find(start - 1) || str.id;
55120
const point = this.point(startId, Anchor.After);
56-
return this.range(point, point);
121+
return this.range(point, point.clone());
57122
}
58123
const startId = str.find(start) || str.id;
59124
const endId = str.find(start + length - 1) || startId;
@@ -62,11 +127,27 @@ export class Peritext implements Printable {
62127
return this.range(startEndpoint, endEndpoint);
63128
}
64129

130+
// --------------------------------------------------------------- Insertions
131+
132+
/**
133+
* Insert plain text at a view position in the text.
134+
*
135+
* @param pos View position in the text.
136+
* @param text Text to insert.
137+
*/
65138
public insAt(pos: number, text: string): void {
66-
const str = this.model.api.wrap(this.str);
139+
const str = this.strApi();
67140
str.ins(pos, text);
68141
}
69142

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

171+
// ---------------------------------------------------------------- Deletions
172+
90173
public delSlice(sliceId: ITimestampStruct): void {
91174
this.slices.del(sliceId);
92175
}

src/json-crdt-extensions/peritext/editor/Editor.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,12 @@ export class Editor implements Printable {
122122
public insertSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice {
123123
return this.txt.insSlice(this.cursor, SliceBehavior.Stack, type, data);
124124
}
125+
126+
public insertOverwriteSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice {
127+
return this.txt.insSlice(this.cursor, SliceBehavior.Overwrite, type, data);
128+
}
129+
130+
public insertEraseSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice {
131+
return this.txt.insSlice(this.cursor, SliceBehavior.Erase, type, data);
132+
}
125133
}

0 commit comments

Comments
 (0)