-
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #611 from streamich/overlay-4
Add support for ephemeral Peritext overlays
- Loading branch information
Showing
29 changed files
with
780 additions
and
418 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
src/json-crdt-extensions/peritext/__tests__/Peritext.localSlices.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import {Model} from '../../../json-crdt/model'; | ||
import {Peritext} from '../Peritext'; | ||
|
||
const setup = () => { | ||
const model = Model.withLogicalClock(); | ||
model.api.root({ | ||
text: '', | ||
slices: [], | ||
}); | ||
model.api.str(['text']).ins(0, 'wworld'); | ||
model.api.str(['text']).ins(0, 'helo '); | ||
model.api.str(['text']).ins(2, 'l'); | ||
model.api.str(['text']).del(7, 1); | ||
const peritext = new Peritext(model, model.api.str(['text']).node, model.api.arr(['slices']).node); | ||
return {model, peritext}; | ||
}; | ||
|
||
test('clears change history', () => { | ||
const {peritext} = setup(); | ||
const {editor} = peritext; | ||
editor.cursor.setAt(0); | ||
editor.cursor.setAt(1); | ||
editor.cursor.setAt(2); | ||
editor.cursor.setAt(3); | ||
expect(peritext.localSlices.model.api.flush().ops.length).toBe(0); | ||
}); | ||
|
||
test('clears slice set tombstones', () => { | ||
const _random = Math.random; | ||
// It is probabilistic, if we set `Math.random` to 0 it will always remove tombstones. | ||
Math.random = () => 0; | ||
const {peritext} = setup(); | ||
const slicesRga = peritext.localSlices.model.root.node()!.get(0)!; | ||
const count = slicesRga.size(); | ||
const slice1 = peritext.localSlices.insOverwrite(peritext.rangeAt(1, 2), 1); | ||
const slice2 = peritext.localSlices.insOverwrite(peritext.rangeAt(1, 2), 3); | ||
const slice3 = peritext.localSlices.insOverwrite(peritext.rangeAt(1, 2), 2); | ||
expect(slicesRga.size()).toBe(count + 3); | ||
peritext.localSlices.del(slice2.id); | ||
expect(slicesRga.size()).toBe(count + 2); | ||
peritext.localSlices.del(slice1.id); | ||
expect(slicesRga.size()).toBe(count + 1); | ||
peritext.localSlices.del(slice3.id); | ||
expect(slicesRga.size()).toBe(count); | ||
Math.random = _random; | ||
}); |
49 changes: 49 additions & 0 deletions
49
src/json-crdt-extensions/peritext/__tests__/Peritext.overlay.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import {Model} from '../../../json-crdt/model'; | ||
import {size} from 'sonic-forest/lib/util'; | ||
import {Peritext} from '../Peritext'; | ||
|
||
const setup = () => { | ||
const model = Model.withLogicalClock(); | ||
model.api.root({ | ||
text: '', | ||
slices: [], | ||
}); | ||
model.api.str(['text']).ins(0, 'wworld'); | ||
model.api.str(['text']).ins(0, 'helo '); | ||
model.api.str(['text']).ins(2, 'l'); | ||
model.api.str(['text']).del(7, 1); | ||
const peritext = new Peritext(model, model.api.str(['text']).node, model.api.arr(['slices']).node); | ||
return {model, peritext}; | ||
}; | ||
|
||
test('can insert markers', () => { | ||
const {peritext} = setup(); | ||
const {editor} = peritext; | ||
expect(size(peritext.overlay.root)).toBe(0); | ||
editor.cursor.setAt(0); | ||
editor.insMarker(['p'], '<p>'); | ||
peritext.refresh(); | ||
expect(size(peritext.overlay.root)).toBe(1); | ||
editor.cursor.setAt(9); | ||
editor.insMarker(['p'], '<p>'); | ||
peritext.refresh(); | ||
expect(size(peritext.overlay.root)).toBe(3); | ||
}); | ||
|
||
test('can insert slices', () => { | ||
const {peritext} = setup(); | ||
const {editor} = peritext; | ||
expect(size(peritext.overlay.root)).toBe(0); | ||
editor.cursor.setAt(2, 2); | ||
editor.insStackSlice('bold'); | ||
peritext.refresh(); | ||
expect(size(peritext.overlay.root)).toBe(2); | ||
editor.cursor.setAt(6, 5); | ||
editor.insStackSlice('italic'); | ||
peritext.refresh(); | ||
expect(size(peritext.overlay.root)).toBe(4); | ||
editor.cursor.setAt(0, 5); | ||
editor.insStackSlice('underline'); | ||
peritext.refresh(); | ||
expect(size(peritext.overlay.root)).toBe(6); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import {Point} from '../rga/Point'; | ||
import {Range} from '../rga/Range'; | ||
import {CursorAnchor} from '../slice/constants'; | ||
import {PersistedSlice} from '../slice/PersistedSlice'; | ||
|
||
export class Cursor<T = string> extends PersistedSlice<T> { | ||
public get anchorSide(): CursorAnchor { | ||
return this.type as CursorAnchor; | ||
} | ||
|
||
public set anchorSide(value: CursorAnchor) { | ||
this.update({type: value}); | ||
} | ||
|
||
public anchor(): Point<T> { | ||
return this.anchorSide === CursorAnchor.Start ? this.start : this.end; | ||
} | ||
|
||
public focus(): Point<T> { | ||
return this.anchorSide === CursorAnchor.Start ? this.end : this.start; | ||
} | ||
|
||
public set(start: Point<T>, end?: Point<T>, anchorSide: CursorAnchor = this.anchorSide): void { | ||
if (!end || end === start) end = start.clone(); | ||
super.set(start, end); | ||
this.update({ | ||
range: this, | ||
type: anchorSide, | ||
}); | ||
} | ||
|
||
/** TODO: Move to {@link PersistedSlice}. */ | ||
public setAt(start: number, length: number = 0): void { | ||
let at = start; | ||
let len = length; | ||
if (len < 0) { | ||
at += len; | ||
len = -len; | ||
} | ||
const range = Range.at<T>(this.rga, start, length); | ||
const anchorSide = this.anchorSide; | ||
this.update({ | ||
range, | ||
type: anchorSide !== this.anchorSide ? anchorSide : undefined, | ||
}); | ||
} | ||
|
||
/** | ||
* Move one of the edges of the cursor to a new point. | ||
* | ||
* @param point Point to set the edge to. | ||
* @param edge 0 for "focus", 1 for "anchor." | ||
*/ | ||
public setEdge(point: Point<T>, edge: 0 | 1 = 0): void { | ||
if (this.start === this.end) this.end = this.end.clone(); | ||
let anchor = this.anchor(); | ||
let focus = this.focus(); | ||
if (edge === 0) focus = point; | ||
else anchor = point; | ||
if (focus.cmpSpatial(anchor) < 0) this.set(focus, anchor, CursorAnchor.End); | ||
else this.set(anchor, focus, CursorAnchor.Start); | ||
} | ||
|
||
public move(move: number): void { | ||
const {start, end} = this; | ||
start.move(move); | ||
if (start !== end) { | ||
end.move(move); | ||
} | ||
this.set(start, end); | ||
} | ||
|
||
// ---------------------------------------------------------------- Printable | ||
|
||
public toStringName(): string { | ||
const focusIcon = this.anchorSide === CursorAnchor.Start ? '.→|' : '|←.'; | ||
return `${super.toStringName()}, ${focusIcon}`; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.