Skip to content

Commit 3ed7057

Browse files
committed
feat(json-crdt-extensions): 🎸 improve .fwd() and .bwd() string iterators, and implify
1 parent a617ebb commit 3ed7057

File tree

2 files changed

+46
-108
lines changed

2 files changed

+46
-108
lines changed

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

Lines changed: 20 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -103,32 +103,6 @@ export class Editor<T = string> {
103103
return true;
104104
}
105105

106-
/**
107-
* Returns a forward iterator through visible text, one character at a time,
108-
* starting from a given chunk and offset.
109-
*
110-
* @param chunk Chunk to start from.
111-
* @param offset Offset in the chunk to start from.
112-
* @returns The next visible character iterator.
113-
*/
114-
public fwd0(chunk: undefined | Chunk<T>, offset: number): CharIterator<T> {
115-
const str = this.txt.str;
116-
return () => {
117-
if (!chunk) return;
118-
const span = chunk.span;
119-
const offsetToReturn = offset;
120-
const chunkToReturn = chunk;
121-
if (offset >= span) return;
122-
offset++;
123-
if (offset >= span) {
124-
offset = 0;
125-
chunk = str.next(chunk);
126-
while (chunk && chunk.del) chunk = str.next(chunk);
127-
}
128-
return new ChunkSlice<T>(chunkToReturn, offsetToReturn, 1);
129-
};
130-
}
131-
132106
/**
133107
* Returns a forward iterator through visible text, one character at a time,
134108
* starting from a given ID.
@@ -137,59 +111,28 @@ export class Editor<T = string> {
137111
* @param chunk Chunk to start from.
138112
* @returns The next visible character iterator.
139113
*/
140-
public fwd1(id: ITimestampStruct, chunk?: Chunk<T>): CharIterator<T> {
141-
const str = this.txt.str;
142-
const startFromStrRoot = equal(id, str.id);
143-
if (startFromStrRoot) {
144-
chunk = str.first();
145-
while (chunk && chunk.del) chunk = str.next(chunk);
146-
return this.fwd0(chunk, 0);
147-
}
148-
let offset: number = 0;
149-
if (!chunk || !contains(chunk.id, chunk.span, id, 1)) {
150-
chunk = str.findById(id);
151-
if (!chunk) return () => undefined;
152-
offset = id.time - chunk.id.time;
153-
} else offset = id.time - chunk.id.time;
154-
if (!chunk.del) return this.fwd0(chunk, offset);
155-
while (chunk && chunk.del) chunk = str.next(chunk);
156-
return this.fwd0(chunk, 0);
157-
}
158-
159-
public bwd0(chunk: undefined | Chunk<T>, offset: number): CharIterator<T> {
160-
const txt = this.txt;
161-
const str = txt.str;
114+
public fwd(start: Point<T>): CharIterator<T> {
115+
let point: Point<T> | undefined = start.clone();
162116
return () => {
163-
if (!chunk || offset < 0) return;
164-
const offsetToReturn = offset;
165-
const chunkToReturn = chunk;
166-
offset--;
167-
if (offset < 0) {
168-
chunk = str.prev(chunk);
169-
while (chunk && chunk.del) chunk = str.prev(chunk);
170-
if (chunk) offset = chunk.span - 1;
171-
}
172-
return new ChunkSlice(chunkToReturn, offsetToReturn, 1);
117+
if (!point) return;
118+
const char = point.rightChar();
119+
if (!char) return point = undefined;
120+
const end = point.move(1);
121+
if (end) point = undefined;
122+
return char;
173123
};
174124
}
175125

176-
public bwd1(id: ITimestampStruct, chunk?: Chunk<T>): CharIterator<T> {
177-
const str = this.txt.str;
178-
const startFromStrRoot = equal(id, str.id);
179-
if (startFromStrRoot) {
180-
chunk = str.last();
181-
while (chunk && chunk.del) chunk = str.prev(chunk);
182-
return this.bwd0(chunk, chunk ? chunk.span - 1 : 0);
183-
}
184-
let offset: number = 0;
185-
if (!chunk || !contains(chunk.id, chunk.span, id, 1)) {
186-
chunk = str.findById(id);
187-
if (!chunk) return () => undefined;
188-
offset = id.time - chunk.id.time;
189-
} else offset = id.time - chunk.id.time;
190-
if (!chunk.del) return this.bwd0(chunk, offset);
191-
while (chunk && chunk.del) chunk = str.prev(chunk);
192-
return this.bwd0(chunk, chunk ? chunk.span - 1 : 0);
126+
public bwd(start: Point<T>): CharIterator<T> {
127+
let point: Point<T> | undefined = start.clone();
128+
return () => {
129+
if (!point) return;
130+
const char = point.leftChar();
131+
if (!char) return point = undefined;
132+
const end = point.move(-1);
133+
if (end) point = undefined;
134+
return char;
135+
};
193136
}
194137

195138
/**
@@ -238,10 +181,7 @@ export class Editor<T = string> {
238181
predicate: CharPredicate<string> = isLetter,
239182
firstLetterFound: boolean = false,
240183
): Point<T> {
241-
const firstChar = point.rightChar();
242-
if (!firstChar) return point;
243-
const fwd = this.fwd1(firstChar.id(), firstChar.chunk);
244-
return this.skipWord(fwd, predicate, firstLetterFound) || point;
184+
return this.skipWord(this.fwd(point), predicate, firstLetterFound) || point;
245185
}
246186

247187
/**
@@ -260,9 +200,7 @@ export class Editor<T = string> {
260200
predicate: CharPredicate<string> = isLetter,
261201
firstLetterFound: boolean = false,
262202
): Point<T> {
263-
const firstChar = point.leftChar();
264-
if (!firstChar) return point;
265-
const bwd = this.bwd1(firstChar.id(), firstChar.chunk);
203+
const bwd = this.bwd(point);
266204
const endPoint = this.skipWord(bwd, predicate, firstLetterFound);
267205
if (endPoint) endPoint.anchor = Anchor.Before;
268206
return endPoint || point;

‎src/json-crdt-extensions/peritext/editor/__tests__/Editor-iterators.spec.ts‎

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const setup = (
3131
describe('.fwd1()', () => {
3232
test('can use string root as initial point', () => {
3333
const {peritext, editor} = setup();
34-
const iterator = editor.fwd1(peritext.str.id);
34+
const iterator = editor.fwd(peritext.pointAbsStart());
3535
let str = '';
3636
while (1) {
3737
const res = iterator();
@@ -44,7 +44,7 @@ describe('.fwd1()', () => {
4444
test('can iterate through the entire string', () => {
4545
const {peritext, editor} = setup();
4646
const start = peritext.pointStart()!;
47-
const iterator = editor.fwd1(start.id);
47+
const iterator = editor.fwd(start);
4848
let str = '';
4949
while (1) {
5050
const res = iterator();
@@ -57,7 +57,7 @@ describe('.fwd1()', () => {
5757
test('can iterate through the entire string, starting from ABS start', () => {
5858
const {peritext, editor} = setup();
5959
const start = peritext.pointAbsStart()!;
60-
const iterator = editor.fwd1(start.id);
60+
const iterator = editor.fwd(start);
6161
let str = '';
6262
while (1) {
6363
const res = iterator();
@@ -70,7 +70,7 @@ describe('.fwd1()', () => {
7070
test('can iterate through the entire string, with initial chunk provided', () => {
7171
const {peritext, editor} = setup();
7272
const start = peritext.pointStart()!;
73-
const iterator = editor.fwd1(start.id, start.chunk());
73+
const iterator = editor.fwd(start);
7474
let str = '';
7575
while (1) {
7676
const res = iterator();
@@ -83,7 +83,7 @@ describe('.fwd1()', () => {
8383
test('can iterate starting in the middle of first chunk', () => {
8484
const {peritext, editor} = setup();
8585
const start = peritext.pointAt(2);
86-
const iterator = editor.fwd1(start.id);
86+
const iterator = editor.fwd(start);
8787
let str = '';
8888
while (1) {
8989
const res = iterator();
@@ -96,7 +96,7 @@ describe('.fwd1()', () => {
9696
test('can iterate starting in the middle of first chunk, with initial chunk provided', () => {
9797
const {peritext, editor} = setup();
9898
const start = peritext.pointAt(2);
99-
const iterator = editor.fwd1(start.id, start.chunk());
99+
const iterator = editor.fwd(start);
100100
let str = '';
101101
while (1) {
102102
const res = iterator();
@@ -109,7 +109,7 @@ describe('.fwd1()', () => {
109109
test('can iterate starting in the middle of second chunk', () => {
110110
const {peritext, editor} = setup();
111111
const start = peritext.pointAt(6);
112-
const iterator = editor.fwd1(start.id);
112+
const iterator = editor.fwd(start);
113113
let str = '';
114114
while (1) {
115115
const res = iterator();
@@ -122,7 +122,7 @@ describe('.fwd1()', () => {
122122
test('can iterate starting in the middle of second chunk, with initial chunk provided', () => {
123123
const {peritext, editor} = setup();
124124
const start = peritext.pointAt(6);
125-
const iterator = editor.fwd1(start.id, start.chunk());
125+
const iterator = editor.fwd(start);
126126
let str = '';
127127
while (1) {
128128
const res = iterator();
@@ -140,7 +140,7 @@ describe('.fwd1()', () => {
140140
});
141141
peritext.overlay.refresh();
142142
const start = peritext.pointAt(0);
143-
const iterator = editor.fwd1(start.id, start.chunk());
143+
const iterator = editor.fwd(start);
144144
let str = '';
145145
const bools: boolean[] = [];
146146
while (1) {
@@ -157,7 +157,7 @@ describe('.fwd1()', () => {
157157
describe('.bwd1()', () => {
158158
test('can use string root as initial point', () => {
159159
const {peritext, editor} = setup();
160-
const iterator = editor.bwd1(peritext.str.id);
160+
const iterator = editor.bwd(peritext.pointAbsEnd());
161161
let str = '';
162162
while (1) {
163163
const res = iterator();
@@ -170,7 +170,7 @@ describe('.bwd1()', () => {
170170
test('can iterate through the entire string', () => {
171171
const {peritext, editor} = setup();
172172
const end = peritext.pointEnd()!;
173-
const iterator = editor.bwd1(end.id);
173+
const iterator = editor.bwd(end);
174174
let str = '';
175175
while (1) {
176176
const res = iterator();
@@ -183,7 +183,7 @@ describe('.bwd1()', () => {
183183
test('can iterate through the entire string, starting from ABS end', () => {
184184
const {peritext, editor} = setup();
185185
const end = peritext.pointAbsEnd()!;
186-
const iterator = editor.bwd1(end.id);
186+
const iterator = editor.bwd(end);
187187
let str = '';
188188
while (1) {
189189
const res = iterator();
@@ -196,7 +196,7 @@ describe('.bwd1()', () => {
196196
test('can iterate through the entire string, with initial chunk provided', () => {
197197
const {peritext, editor} = setup();
198198
const end = peritext.pointEnd()!;
199-
const iterator = editor.bwd1(end.id, end.chunk());
199+
const iterator = editor.bwd(end);
200200
let str = '';
201201
while (1) {
202202
const res = iterator();
@@ -208,54 +208,54 @@ describe('.bwd1()', () => {
208208

209209
test('can iterate starting in the middle of first chunk', () => {
210210
const {peritext, editor} = setup();
211-
const end = peritext.pointAt(2);
212-
const iterator = editor.bwd1(end.id);
211+
const point = peritext.pointAt(2);
212+
const iterator = editor.bwd(point);
213213
let str = '';
214214
while (1) {
215215
const res = iterator();
216216
if (!res) break;
217217
str += res.view();
218218
}
219-
expect(str).toBe('210');
219+
expect(str).toBe('10');
220220
});
221221

222222
test('can iterate starting in the middle of first chunk, with initial chunk provided', () => {
223223
const {peritext, editor} = setup();
224-
const end = peritext.pointAt(2);
225-
const iterator = editor.bwd1(end.id, end.chunk());
224+
const point = peritext.pointAt(2);
225+
const iterator = editor.bwd(point);
226226
let str = '';
227227
while (1) {
228228
const res = iterator();
229229
if (!res) break;
230230
str += res.view();
231231
}
232-
expect(str).toBe('210');
232+
expect(str).toBe('10');
233233
});
234234

235235
test('can iterate starting in the middle of second chunk', () => {
236236
const {peritext, editor} = setup();
237-
const end = peritext.pointAt(6);
238-
const iterator = editor.bwd1(end.id);
237+
const point = peritext.pointAt(6);
238+
const iterator = editor.bwd(point);
239239
let str = '';
240240
while (1) {
241241
const res = iterator();
242242
if (!res) break;
243243
str += res.view();
244244
}
245-
expect(str).toBe('6543210');
245+
expect(str).toBe('543210');
246246
});
247247

248248
test('can iterate starting in the middle of second chunk, with initial chunk provided', () => {
249249
const {peritext, editor} = setup();
250-
const end = peritext.pointAt(6);
251-
const iterator = editor.bwd1(end.id, end.chunk());
250+
const point = peritext.pointAt(6);
251+
const iterator = editor.bwd(point);
252252
let str = '';
253253
while (1) {
254254
const res = iterator();
255255
if (!res) break;
256256
str += res.view();
257257
}
258-
expect(str).toBe('6543210');
258+
expect(str).toBe('543210');
259259
});
260260

261261
test('returns true for block split chars', () => {
@@ -266,7 +266,7 @@ describe('.bwd1()', () => {
266266
});
267267
peritext.overlay.refresh();
268268
const start = peritext.pointAt(3);
269-
const iterator = editor.bwd1(start.id, start.chunk());
269+
const iterator = editor.bwd(start);
270270
let str = '';
271271
const bools: boolean[] = [];
272272
while (1) {

0 commit comments

Comments
 (0)