Skip to content

Commit d64b8ab

Browse files
committed
fix(json-crdt-extensions): 🐛 recompute different Overlay state hash when text changes
1 parent 6fa5d41 commit d64b8ab

File tree

2 files changed

+35
-6
lines changed

2 files changed

+35
-6
lines changed

src/json-crdt-extensions/peritext/overlay/Overlay.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,13 @@ export class Overlay<T = string> implements Printable, Stateful {
307307
return result;
308308
}
309309

310-
public isBlockSplit(id: ITimestampStruct): boolean {
310+
/**
311+
* Returns `true` if the current character is a marker sentinel.
312+
*
313+
* @param id ID of the point to check.
314+
* @returns Whether the point is a marker point.
315+
*/
316+
public isMarker(id: ITimestampStruct): boolean {
311317
const point = this.txt.point(id, Anchor.Before);
312318
const overlayPoint = this.getOrNextLower(point);
313319
return (
@@ -325,7 +331,7 @@ export class Overlay<T = string> implements Printable, Stateful {
325331
hash = this.refreshSlices(hash, txt.savedSlices);
326332
hash = this.refreshSlices(hash, txt.extraSlices);
327333
hash = this.refreshSlices(hash, txt.localSlices);
328-
if (!slicesOnly) this.computeSplitTextHashes();
334+
if (!slicesOnly) hash = this.computeSplitTextHashes(hash);
329335
return (this.hash = hash);
330336
}
331337

@@ -455,15 +461,15 @@ export class Overlay<T = string> implements Printable, Stateful {
455461

456462
public leadingTextHash: number = 0;
457463

458-
protected computeSplitTextHashes(): void {
464+
protected computeSplitTextHashes(stateTotal: number): number {
459465
const txt = this.txt;
460466
const str = txt.str;
461467
const firstChunk = str.first();
462-
if (!firstChunk) return;
468+
if (!firstChunk) return stateTotal;
463469
let chunk: Chunk<T> | undefined = firstChunk;
464470
let marker: MarkerOverlayPoint<T> | undefined = undefined;
465-
let state: number = CONST.START_STATE;
466471
const i = this.tuples0(undefined);
472+
let state: number = CONST.START_STATE;
467473
for (let pair = i(); pair; pair = i()) {
468474
const [p1, p2] = pair;
469475
// TODO: need to incorporate slice attribute hash here?
@@ -478,13 +484,15 @@ export class Overlay<T = string> implements Printable, Stateful {
478484
state = updateNum(state, overlayPointHash);
479485
if (p1) {
480486
p1.hash = overlayPointHash;
487+
stateTotal = updateNum(stateTotal, overlayPointHash);
481488
}
482489
if (p2 instanceof MarkerOverlayPoint) {
483490
if (marker) {
484491
marker.textHash = state;
485492
} else {
486493
this.leadingTextHash = state;
487494
}
495+
stateTotal = updateNum(stateTotal, state);
488496
state = CONST.START_STATE;
489497
marker = p2;
490498
}
@@ -494,6 +502,7 @@ export class Overlay<T = string> implements Printable, Stateful {
494502
} else {
495503
this.leadingTextHash = state;
496504
}
505+
return stateTotal;
497506
}
498507

499508
// ---------------------------------------------------------------- Printable

src/json-crdt-extensions/peritext/overlay/__tests__/Overlay.refresh.spec.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ describe('Overlay.refresh()', () => {
266266
});
267267
});
268268

269-
describe('cursor', () => {
269+
describe('local slices - cursor', () => {
270270
describe('updates hash', () => {
271271
testRefresh('when cursor char ID changes', (kit, refresh) => {
272272
kit.peritext.editor.cursor.setAt(1);
@@ -302,4 +302,24 @@ describe('Overlay.refresh()', () => {
302302
});
303303
});
304304
});
305+
306+
describe('text contents', () => {
307+
describe('updates hash', () => {
308+
testRefresh('when the first character is deleted and reinserted', (kit, refresh) => {
309+
const index = 0;
310+
const char = kit.peritext.strApi().view()[index];
311+
refresh();
312+
kit.peritext.strApi().del(index, 1);
313+
kit.peritext.strApi().ins(index, char);
314+
});
315+
316+
testRefresh('when the last character is deleted and reinserted', (kit, refresh) => {
317+
const index = kit.peritext.strApi().view().length - 1;
318+
const char = kit.peritext.strApi().view()[index];
319+
refresh();
320+
kit.peritext.strApi().del(index, 1);
321+
kit.peritext.strApi().ins(index, char);
322+
});
323+
});
324+
});
305325
});

0 commit comments

Comments
 (0)