@@ -10,9 +10,9 @@ import type {StringChunk} from '../util/types';
1010/**
1111 * A "point" in a rich-text Peritext document. It is a combination of a
1212 * character ID and an anchor. Anchor specifies the side of the character to
13- * which the point is attached. For example, a point with an anchor "before"
14- * points just before the character, while a point with an anchor "after" points
15- * just after the character.
13+ * which the point is attached. For example, a point with an anchor "before" .▢
14+ * points just before the character, while a point with an anchor "after" ▢.
15+ * points just after the character.
1616 */
1717export class Point implements Pick < Stateful , 'refresh' > , Printable {
1818 constructor (
@@ -21,15 +21,33 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
2121 public anchor : Anchor ,
2222 ) { }
2323
24+ /**
25+ * Overwrites the internal state of this point with the state of the given
26+ * point.
27+ *
28+ * @param point Point to copy.
29+ */
2430 public set ( point : Point ) : void {
2531 this . id = point . id ;
2632 this . anchor = point . anchor ;
2733 }
2834
35+ /**
36+ * Creates a copy of this point.
37+ *
38+ * @returns Returns a new point with the same ID and anchor as this point.
39+ */
2940 public clone ( ) : Point {
3041 return new Point ( this . txt , this . id , this . anchor ) ;
3142 }
3243
44+ /**
45+ *
46+ * @param other The other point to compare to.
47+ * @returns Returns 0 if the two points are equal, -1 if this point is less
48+ * than the other point, and 1 if this point is greater than the other
49+ * point.
50+ */
3351 public compare ( other : Point ) : - 1 | 0 | 1 {
3452 const cmp = compare ( this . id , other . id ) ;
3553 if ( cmp !== 0 ) return cmp ;
@@ -98,13 +116,23 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
98116 return this . anchor === Anchor . Before ? pos : pos + 1 ;
99117 }
100118
119+ /**
120+ * Goes to the next visible character in the string. The `move` parameter
121+ * specifies how many characters to move the cursor by. If the cursor reaches
122+ * the end of the string, it will return `undefined`.
123+ *
124+ * @param move How many characters to move the cursor by.
125+ * @returns Next visible ID in string.
126+ */
101127 public nextId ( move : number = 1 ) : ITimestampStruct | undefined {
128+ // TODO: add tests for when cursor is at the end.
129+ if ( this . isEndOfStr ( ) ) return ;
102130 let remaining : number = move ;
103131 const { id, txt} = this ;
104132 const str = txt . str ;
105- const startFromStrRoot = equal ( id , str . id ) ;
106133 let chunk : StringChunk | undefined ;
107- if ( startFromStrRoot ) {
134+ // TODO: add tests for when cursor starts from start of string.
135+ if ( this . isStartOfStr ( ) ) {
108136 chunk = str . first ( ) ;
109137 while ( chunk && chunk . del ) chunk = str . next ( chunk ) ;
110138 if ( ! chunk ) return ;
@@ -145,10 +173,13 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
145173 * such character.
146174 */
147175 public prevId ( move : number = 1 ) : ITimestampStruct | undefined {
176+ // TODO: add tests for when cursor is at the start.
177+ if ( this . isStartOfStr ( ) ) return ;
148178 let remaining : number = move ;
149179 const { id, txt} = this ;
150180 const str = txt . str ;
151181 let chunk = this . chunk ( ) ;
182+ // TODO: handle case when cursor starts from end of string.
152183 if ( ! chunk ) return str . id ;
153184 if ( ! chunk . del ) {
154185 const offset = id . time - chunk . id . time ;
@@ -173,8 +204,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
173204
174205 public rightChar ( ) : ChunkSlice | undefined {
175206 const str = this . txt . str ;
176- const isBeginningOfDoc = equal ( this . id , str . id ) && this . anchor === Anchor . After ;
177- if ( isBeginningOfDoc ) {
207+ if ( this . isStartOfStr ( ) ) {
178208 let chunk = str . first ( ) ;
179209 while ( chunk && chunk . del ) chunk = str . next ( chunk ) ;
180210 return chunk ? new ChunkSlice ( chunk , 0 , 1 ) : undefined ;
@@ -201,6 +231,7 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
201231
202232 public leftChar ( ) : ChunkSlice | undefined {
203233 let chunk = this . chunk ( ) ;
234+ // TODO: Handle case when point references end of str.
204235 if ( ! chunk ) return ;
205236 if ( chunk . del ) {
206237 const prevId = this . prevId ( ) ;
@@ -221,6 +252,58 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
221252 return new ChunkSlice ( chunk , chunk . span - 1 , 1 ) ;
222253 }
223254
255+ public isStartOfStr ( ) : boolean {
256+ return equal ( this . id , this . txt . str . id ) && this . anchor === Anchor . After ;
257+ }
258+
259+ public isEndOfStr ( ) : boolean {
260+ return equal ( this . id , this . txt . str . id ) && this . anchor === Anchor . Before ;
261+ }
262+
263+ /**
264+ * Modifies the location of the point, such that the spatial location remains
265+ * and anchor remains the same, but ensures that the point references a
266+ * visible (non-deleted) character.
267+ */
268+ public refVisible ( ) : void {
269+ if ( this . anchor === Anchor . Before ) this . refBefore ( ) ;
270+ else this . refAfter ( ) ;
271+ }
272+
273+ public refStart ( ) : void {
274+ this . id = this . txt . str . id ;
275+ this . anchor = Anchor . After ;
276+ }
277+
278+ public refEnd ( ) : void {
279+ this . id = this . txt . str . id ;
280+ this . anchor = Anchor . Before ;
281+ }
282+
283+ /**
284+ * Modifies the location of the point, such that the spatial location remains
285+ * the same, but ensures that it is anchored before a character.
286+ */
287+ public refBefore ( ) : void {
288+ const chunk = this . chunk ( ) ;
289+ if ( ! chunk ) return this . refEnd ( ) ;
290+ if ( ! chunk . del || this . anchor === Anchor . Before ) return ;
291+ this . anchor = Anchor . Before ;
292+ this . id = this . nextId ( ) || this . txt . str . id ;
293+ }
294+
295+ /**
296+ * Modifies the location of the point, such that the spatial location remains
297+ * the same, but ensures that it is anchored after a character.
298+ */
299+ public refAfter ( ) : void {
300+ const chunk = this . chunk ( ) ;
301+ if ( ! chunk ) return this . refStart ( ) ;
302+ if ( ! chunk . del || this . anchor === Anchor . After ) return ;
303+ this . anchor = Anchor . After ;
304+ this . id = this . prevId ( ) || this . txt . str . id ;
305+ }
306+
224307 /**
225308 * Moves point past given number of visible characters. Accepts positive
226309 * and negative distances.
@@ -237,30 +320,6 @@ export class Point implements Pick<Stateful, 'refresh'>, Printable {
237320 }
238321 }
239322
240- /**
241- * Returns a point, which points at the same spatial location, but ensures
242- * that it is anchored after a character.
243- */
244- public anchorBefore ( ) : Point {
245- if ( this . anchor === Anchor . Before ) return this ;
246- const next = this . nextId ( ) ;
247- const txt = this . txt ;
248- if ( ! next ) return new Point ( txt , txt . str . id , Anchor . Before ) ;
249- return new Point ( txt , next , Anchor . Before ) ;
250- }
251-
252- /**
253- * Returns a point, which points at the same spatial location, but ensures
254- * that it is anchored after a character.
255- */
256- public anchorAfter ( ) : Point {
257- if ( this . anchor === Anchor . After ) return this ;
258- const prev = this . prevId ( ) ;
259- const txt = this . txt ;
260- if ( ! prev ) return new Point ( txt , txt . str . id , Anchor . After ) ;
261- return new Point ( txt , prev , Anchor . After ) ;
262- }
263-
264323 // ----------------------------------------------------------------- Stateful
265324
266325 public refresh ( ) : number {
0 commit comments