Skip to content

Commit 0af5609

Browse files
committed
feat(json-crdt-extensions): 🎸 make Point generic
1 parent 75a1d51 commit 0af5609

File tree

4 files changed

+54
-54
lines changed

4 files changed

+54
-54
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class Peritext implements Printable {
4242
* @returns The point.
4343
*/
4444
public point(id: ITimestampStruct = this.str.id, anchor: Anchor = Anchor.After): Point {
45-
return new Point(this, id, anchor);
45+
return new Point(this, this.str, id, anchor);
4646
}
4747

4848
/**

src/json-crdt-extensions/peritext/point/Point.ts

Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import {compare, type ITimestampStruct, toDisplayString, equal, tick, containsId
22
import {Anchor} from '../constants';
33
import {ChunkSlice} from '../util/ChunkSlice';
44
import {updateId} from '../../../json-crdt/hash';
5+
import type {AbstractRga, Chunk} from '../../../json-crdt/nodes/rga';
56
import type {Stateful} from '../types';
67
import type {Peritext} from '../Peritext';
78
import type {Printable} from '../../../util/print/types';
8-
import type {StringChunk} from '../util/types';
99

1010
/**
1111
* A "point" in a rich-text Peritext document. It is a combination of a
@@ -23,9 +23,10 @@ import type {StringChunk} from '../util/types';
2323
* after the last character, and even after any deleted characters at the end
2424
* of the string.
2525
*/
26-
export class Point implements Pick<Stateful, 'refresh'>, Printable {
26+
export class Point<T = string> implements Pick<Stateful, 'refresh'>, Printable {
2727
constructor(
2828
protected readonly txt: Peritext,
29+
protected readonly rga: AbstractRga<T>,
2930
public id: ITimestampStruct,
3031
public anchor: Anchor,
3132
) {}
@@ -36,7 +37,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
3637
*
3738
* @param point Point to copy.
3839
*/
39-
public set(point: Point): void {
40+
public set(point: Point<T>): void {
4041
this.id = point.id;
4142
this.anchor = point.anchor;
4243
}
@@ -46,8 +47,8 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
4647
*
4748
* @returns Returns a new point with the same ID and anchor as this point.
4849
*/
49-
public clone(): Point {
50-
return new Point(this.txt, this.id, this.anchor);
50+
public clone(): Point<T> {
51+
return new Point(this.txt, this.rga, this.id, this.anchor);
5152
}
5253

5354
/**
@@ -60,7 +61,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
6061
* than the other point, and 1 if this point is greater than the other
6162
* point.
6263
*/
63-
public compare(other: Point): -1 | 0 | 1 {
64+
public compare(other: Point<T>): -1 | 0 | 1 {
6465
const cmp = compare(this.id, other.id);
6566
if (cmp !== 0) return cmp;
6667
return (this.anchor - other.anchor) as -1 | 0 | 1;
@@ -76,7 +77,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
7677
* less than the other point, and positive if this point is greater
7778
* than the other point.
7879
*/
79-
public compareSpatial(other: Point): number {
80+
public compareSpatial(other: Point<T>): number {
8081
const thisId = this.id;
8182
const otherId = other.id;
8283
if (this.isAbs()) {
@@ -93,22 +94,22 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
9394
let chunk = this.chunk();
9495
if (!chunk) return cmp0;
9596
if (containsId(chunk.id, chunk.span, otherId)) return thisId.time - otherId.time;
96-
const str = this.txt.str;
97-
chunk = str.next(chunk);
97+
const rga = this.rga;
98+
chunk = rga.next(chunk);
9899
while (chunk) {
99100
if (containsId(chunk.id, chunk.span, otherId)) return -1;
100-
chunk = str.next(chunk);
101+
chunk = rga.next(chunk);
101102
}
102103
return 1;
103104
}
104105

105-
private _chunk: StringChunk | undefined;
106+
private _chunk: Chunk<T> | undefined;
106107

107108
/**
108109
* @returns Returns the chunk that contains the character referenced by the
109110
* point, or `undefined` if the chunk is not found.
110111
*/
111-
public chunk(): StringChunk | undefined {
112+
public chunk(): Chunk<T> | undefined {
112113
let chunk = this._chunk;
113114
const id = this.id;
114115
if (chunk) {
@@ -117,7 +118,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
117118
const idTime = id.time;
118119
if (id.sid === chunkId.sid && idTime >= chunkIdTime && idTime < chunkIdTime + chunk.span) return chunk;
119120
}
120-
this._chunk = chunk = this.txt.str.findById(this.id);
121+
this._chunk = chunk = this.rga.findById(this.id);
121122
return chunk;
122123
}
123124

@@ -127,7 +128,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
127128
public pos(): number {
128129
const chunk = this.chunk();
129130
if (!chunk) return -1;
130-
const pos = this.txt.str.pos(chunk);
131+
const pos = this.rga.pos(chunk);
131132
if (chunk.del) return pos;
132133
return pos + this.id.time - chunk.id.time;
133134
}
@@ -147,7 +148,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
147148
*/
148149
public viewPos(): number {
149150
const pos = this.pos();
150-
if (pos < 0) return this.isAbsStart() ? 0 : this.txt.str.length();
151+
if (pos < 0) return this.isAbsStart() ? 0 : this.rga.length();
151152
return this.anchor === Anchor.Before ? pos : pos + 1;
152153
}
153154

@@ -162,17 +163,16 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
162163
public nextId(move: number = 1): ITimestampStruct | undefined {
163164
if (this.isAbsEnd()) return;
164165
let remaining: number = move;
165-
const {id, txt} = this;
166-
const str = txt.str;
167-
let chunk: StringChunk | undefined;
166+
const {id, rga} = this;
167+
let chunk: Chunk<T> | undefined;
168168
if (this.isAbsStart()) {
169-
chunk = str.first();
170-
while (chunk && chunk.del) chunk = str.next(chunk);
169+
chunk = rga.first();
170+
while (chunk && chunk.del) chunk = rga.next(chunk);
171171
if (!chunk) return;
172172
const span = chunk.span;
173173
if (remaining <= span) return tick(chunk.id, remaining - 1);
174174
remaining -= span;
175-
chunk = str.next(chunk);
175+
chunk = rga.next(chunk);
176176
} else {
177177
chunk = this.chunk();
178178
if (!chunk) return undefined;
@@ -182,19 +182,19 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
182182
if (offset + remaining < span) return tick(id, remaining);
183183
else remaining -= span - offset - 1;
184184
}
185-
chunk = str.next(chunk);
185+
chunk = rga.next(chunk);
186186
}
187-
let lastVisibleChunk: StringChunk | undefined;
187+
let lastVisibleChunk: Chunk<T> | undefined;
188188
while (chunk && remaining >= 0) {
189189
if (chunk.del) {
190-
chunk = str.next(chunk);
190+
chunk = rga.next(chunk);
191191
continue;
192192
}
193193
lastVisibleChunk = chunk;
194194
const span = chunk.span;
195195
if (remaining <= span) return remaining > 1 ? tick(chunk.id, remaining - 1) : chunk.id;
196196
remaining -= span;
197-
chunk = str.next(chunk);
197+
chunk = rga.next(chunk);
198198
}
199199
if (remaining > 0) return;
200200
return lastVisibleChunk ? tick(lastVisibleChunk.id, lastVisibleChunk.span - 1) : undefined;
@@ -208,27 +208,26 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
208208
public prevId(move: number = 1): ITimestampStruct | undefined {
209209
if (this.isAbsStart()) return;
210210
let remaining: number = move;
211-
const {id, txt} = this;
212-
const str = txt.str;
211+
const {id, rga} = this;
213212
let chunk = this.chunk();
214-
if (!chunk) return str.id;
213+
if (!chunk) return rga.id;
215214
if (!chunk.del) {
216215
const offset = id.time - chunk.id.time;
217216
if (offset >= remaining) return tick(id, -remaining);
218217
remaining -= offset;
219218
}
220-
chunk = str.prev(chunk);
219+
chunk = rga.prev(chunk);
221220
while (chunk) {
222221
if (chunk.del) {
223-
chunk = str.prev(chunk);
222+
chunk = rga.prev(chunk);
224223
continue;
225224
}
226225
const span = chunk.span;
227226
if (remaining <= span) {
228227
return tick(chunk.id, span - remaining);
229228
}
230229
remaining -= span;
231-
chunk = str.prev(chunk);
230+
chunk = rga.prev(chunk);
232231
}
233232
return;
234233
}
@@ -239,10 +238,10 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
239238
*
240239
* @returns A character slice to the left of the point.
241240
*/
242-
public leftChar(): ChunkSlice | undefined {
243-
const str = this.txt.str;
241+
public leftChar(): ChunkSlice<T> | undefined {
242+
const rga = this.rga;
244243
if (this.isAbsEnd()) {
245-
const res = str.findChunk(str.length() - 1);
244+
const res = rga.findChunk(rga.length() - 1);
246245
if (!res) return;
247246
return new ChunkSlice(res[0], res[1], 1);
248247
}
@@ -261,10 +260,10 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
261260
*
262261
* @returns A character slice to the right of the point.
263262
*/
264-
public rightChar(): ChunkSlice | undefined {
265-
const str = this.txt.str;
263+
public rightChar(): ChunkSlice<T> | undefined {
264+
const rga = this.rga;
266265
if (this.isAbsStart()) {
267-
const res = str.findChunk(0);
266+
const res = rga.findChunk(0);
268267
if (!res) return;
269268
return new ChunkSlice(res[0], res[1], 1);
270269
}
@@ -285,7 +284,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
285284
* @returns Returns `true` if the point is an absolute point.
286285
*/
287286
public isAbs(): boolean {
288-
return equal(this.id, this.txt.str.id);
287+
return equal(this.id, this.rga.id);
289288
}
290289

291290
/**
@@ -311,7 +310,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
311310
*/
312311
public isRelStart(): boolean {
313312
if (this.anchor !== Anchor.Before) return false;
314-
const id = this.txt.str.find(0);
313+
const id = this.rga.find(0);
315314
return !!id && equal(this.id, id);
316315
}
317316

@@ -322,26 +321,26 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
322321
*/
323322
public isRelEnd(): boolean {
324323
if (this.anchor !== Anchor.After) return false;
325-
const str = this.txt.str;
326-
const length = str.length();
324+
const rga = this.rga;
325+
const length = rga.length();
327326
if (length === 0) return false;
328-
const id = str.find(length - 1);
327+
const id = rga.find(length - 1);
329328
return !!id && equal(this.id, id);
330329
}
331330

332331
/**
333332
* Sets the point to the absolute start of the string.
334333
*/
335334
public refAbsStart(): void {
336-
this.id = this.txt.str.id;
335+
this.id = this.rga.id;
337336
this.anchor = Anchor.After;
338337
}
339338

340339
/**
341340
* Sets the point to the absolute end of the string.
342341
*/
343342
public refAbsEnd(): void {
344-
this.id = this.txt.str.id;
343+
this.id = this.rga.id;
345344
this.anchor = Anchor.Before;
346345
}
347346

@@ -371,7 +370,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
371370
const chunk = this.chunk();
372371
if (!chunk) {
373372
if (this.isAbsStart()) {
374-
const id = this.txt.str.find(0);
373+
const id = this.rga.find(0);
375374
if (id) {
376375
this.id = id;
377376
this.anchor = Anchor.Before;
@@ -382,7 +381,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
382381
}
383382
if (!chunk.del && this.anchor === Anchor.Before) return;
384383
this.anchor = Anchor.Before;
385-
this.id = this.nextId() || this.txt.str.id;
384+
this.id = this.nextId() || this.rga.id;
386385
}
387386

388387
/**
@@ -395,10 +394,10 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
395394
const chunk = this.chunk();
396395
if (!chunk) {
397396
if (this.isAbsEnd()) {
398-
const str = this.txt.str;
399-
const length = str.length();
397+
const rga = this.rga;
398+
const length = rga.length();
400399
if (length !== 0) {
401-
const id = str.find(length - 1);
400+
const id = rga.find(length - 1);
402401
if (id) {
403402
this.id = id;
404403
this.anchor = Anchor.After;
@@ -410,7 +409,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
410409
}
411410
if (!chunk.del && this.anchor === Anchor.After) return;
412411
this.anchor = Anchor.After;
413-
this.id = this.prevId() || this.txt.str.id;
412+
this.id = this.prevId() || this.rga.id;
414413
}
415414

416415
/**

src/json-crdt-extensions/peritext/slice/Range.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ export class Range implements Printable {
148148
/** @todo Can this be moved to Cursor? */
149149
public setCaret(after: ITimestampStruct, shift: number = 0): void {
150150
const id = shift ? tick(after, shift) : after;
151-
const caretAfter = new Point(this.txt, id, Anchor.After);
151+
const txt = this.txt;
152+
const caretAfter = new Point(txt, txt.str, id, Anchor.After);
152153
this.set(caretAfter);
153154
}
154155

src/json-crdt-extensions/peritext/slice/Slices.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ export class Slices implements Stateful, Printable {
7575
const id2 = (tuple.get(2)!.view() || id1) as ITimestampStruct;
7676
if (!(id1 instanceof Timestamp)) throw new Error('INVALID_ID');
7777
if (!(id2 instanceof Timestamp)) throw new Error('INVALID_ID');
78-
const p1 = new Point(txt, id1, anchor1);
79-
const p2 = new Point(txt, id2, anchor2);
78+
const p1 = new Point(txt, txt.str, id1, anchor1);
79+
const p2 = new Point(txt, txt.str, id2, anchor2);
8080
const type = tuple.get(3)!.view() as SliceType;
8181
const slice =
8282
behavior === SliceBehavior.Split

0 commit comments

Comments
 (0)