1+ import { printTree } from 'sonic-forest/lib/print/printTree' ;
12import { Anchor } from './rga/constants' ;
23import { Point } from './rga/Point' ;
34import { Range } from './rga/Range' ;
45import { Editor } from './editor/Editor' ;
5- import { printTree } from '../../util/print/printTree' ;
66import { ArrNode , StrNode } from '../../json-crdt/nodes' ;
77import { Slices } from './slice/Slices' ;
8- import { type ITimestampStruct } from '../../json-crdt-patch/clock' ;
8+ import { Overlay } from './overlay/Overlay' ;
9+ import { Chars } from './constants' ;
10+ import { interval } from '../../json-crdt-patch/clock' ;
11+ import { CONST , updateNum } from '../../json-hash' ;
12+ import type { ITimestampStruct } from '../../json-crdt-patch/clock' ;
913import type { Model } from '../../json-crdt/model' ;
1014import type { Printable } from '../../util/print/types' ;
15+ import type { StringChunk } from './util/types' ;
16+ import type { SliceType } from './types' ;
17+ import type { MarkerSlice } from './slice/MarkerSlice' ;
1118
1219/**
1320 * Context for a Peritext instance. Contains all the data and methods needed to
@@ -16,6 +23,7 @@ import type {Printable} from '../../util/print/types';
1623export class Peritext implements Printable {
1724 public readonly slices : Slices ;
1825 public readonly editor : Editor ;
26+ public readonly overlay = new Overlay ( this ) ;
1927
2028 constructor (
2129 public readonly model : Model ,
@@ -30,7 +38,30 @@ export class Peritext implements Printable {
3038 return this . model . api . wrap ( this . str ) ;
3139 }
3240
33- // ------------------------------------------------------------------- Points
41+ /** @todo Find a better place for this function. */
42+ public firstVisChunk ( ) : StringChunk | undefined {
43+ const str = this . str ;
44+ let curr = str . first ( ) ;
45+ if ( ! curr ) return ;
46+ while ( curr . del ) {
47+ curr = str . next ( curr ) ;
48+ if ( ! curr ) return ;
49+ }
50+ return curr ;
51+ }
52+
53+ /** Select a single character before a point. */
54+ public findCharBefore ( point : Point ) : Range | undefined {
55+ if ( point . anchor === Anchor . After ) {
56+ const chunk = point . chunk ( ) ;
57+ if ( chunk && ! chunk . del ) return this . range ( this . point ( point . id , Anchor . Before ) , point ) ;
58+ }
59+ const id = point . prevId ( ) ;
60+ if ( ! id ) return ;
61+ return this . range ( this . point ( id , Anchor . Before ) , this . point ( id , Anchor . After ) ) ;
62+ }
63+
64+ // ------------------------------------------------------------------- points
3465
3566 /**
3667 * Creates a point at a character ID.
@@ -81,7 +112,7 @@ export class Peritext implements Printable {
81112 return this . point ( this . str . id , Anchor . Before ) ;
82113 }
83114
84- // ------------------------------------------------------------------- Ranges
115+ // ------------------------------------------------------------------- ranges
85116
86117 /**
87118 * Creates a range from two points. The points can be in any order.
@@ -117,7 +148,7 @@ export class Peritext implements Printable {
117148 return Range . at ( this . str , start , length ) ;
118149 }
119150
120- // --------------------------------------------------------------- Insertions
151+ // --------------------------------------------------------------------- text
121152
122153 /**
123154 * Insert plain text at a view position in the text.
@@ -146,15 +177,37 @@ export class Peritext implements Printable {
146177 return textId ;
147178 }
148179
149- /** Select a single character before a point. */
150- public findCharBefore ( point : Point ) : Range | undefined {
151- if ( point . anchor === Anchor . After ) {
152- const chunk = point . chunk ( ) ;
153- if ( chunk && ! chunk . del ) return this . range ( this . point ( point . id , Anchor . Before ) , point ) ;
154- }
155- const id = point . prevId ( ) ;
156- if ( ! id ) return ;
157- return this . range ( this . point ( id , Anchor . Before ) , this . point ( id , Anchor . After ) ) ;
180+ // ------------------------------------------------------------------ markers
181+
182+ public insMarker (
183+ after : ITimestampStruct ,
184+ type : SliceType ,
185+ data ?: unknown ,
186+ char : string = Chars . BlockSplitSentinel ,
187+ ) : MarkerSlice {
188+ const api = this . model . api ;
189+ const builder = api . builder ;
190+ const str = this . str ;
191+ /**
192+ * We skip one clock cycle to prevent Block-wise RGA from merging adjacent
193+ * characters. We want the marker chunk to always be its own distinct chunk.
194+ */
195+ builder . nop ( 1 ) ;
196+ const textId = builder . insStr ( str . id , after , char [ 0 ] ) ;
197+ const point = this . point ( textId , Anchor . Before ) ;
198+ const range = this . range ( point , point ) ;
199+ return this . slices . insMarker ( range , type , data ) ;
200+ }
201+
202+ /** @todo This can probably use .del() */
203+ public delMarker ( split : MarkerSlice ) : void {
204+ const str = this . str ;
205+ const api = this . model . api ;
206+ const builder = api . builder ;
207+ const strChunk = split . start . chunk ( ) ;
208+ if ( strChunk ) builder . del ( str . id , [ interval ( strChunk . id , 0 , 1 ) ] ) ;
209+ builder . del ( this . slices . set . id , [ interval ( split . id , 0 , 1 ) ] ) ;
210+ api . apply ( ) ;
158211 }
159212
160213 // ---------------------------------------------------------------- Printable
@@ -169,6 +222,8 @@ export class Peritext implements Printable {
169222 ( tab ) => this . str . toString ( tab ) ,
170223 nl ,
171224 ( tab ) => this . slices . toString ( tab ) ,
225+ nl ,
226+ ( tab ) => this . overlay . toString ( tab ) ,
172227 ] )
173228 ) ;
174229 }
@@ -178,6 +233,9 @@ export class Peritext implements Printable {
178233 public hash : number = 0 ;
179234
180235 public refresh ( ) : number {
181- return this . slices . refresh ( ) ;
236+ let state : number = CONST . START_STATE ;
237+ this . overlay . refresh ( ) ;
238+ state = updateNum ( state , this . overlay . hash ) ;
239+ return ( this . hash = state ) ;
182240 }
183241}
0 commit comments