Skip to content

Commit 9c8927f

Browse files
authored
Merge pull request #915 from streamich/peritext-block-nested-attrs
Peritext block nested tag data
2 parents 989ac1c + 28b77ab commit 9c8927f

File tree

39 files changed

+1681
-338
lines changed

39 files changed

+1681
-338
lines changed

src/json-crdt-diff/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import type {Patch} from '../json-crdt-patch';
22
import type {NodeApi} from '../json-crdt/model';
33
import {JsonCrdtDiff} from './JsonCrdtDiff';
44

5-
export const diff = (src: NodeApi<any>, dst: unknown): Patch => {
5+
export const diff = (src: NodeApi<any>, dst: unknown): Patch | undefined => {
66
const diff = new JsonCrdtDiff(src.api.model);
77
const patch = diff.diff(src.node, dst);
8-
return patch;
8+
return patch.ops.length ? patch : void 0;
99
};
1010

11-
export const merge = (src: NodeApi<any>, dst: unknown): void => {
11+
export const merge = (src: NodeApi<any>, dst: unknown): Patch | undefined => {
1212
const patch = diff(src, dst);
13-
if (patch.ops.length) src.api.model.applyPatch(patch);
13+
if (patch) src.api.model.applyPatch(patch);
14+
return patch;
1415
};

src/json-crdt-extensions/peritext/__tests__/Peritext.render-block.spec.ts

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Model} from '../../../json-crdt/model';
22
import {Peritext} from '../Peritext';
3-
import type {Editor} from '../editor/Editor';
43
import {render} from './render';
4+
import type {Editor} from '../editor/Editor';
55

66
const runInlineSlicesTests = (
77
desc: string,
@@ -30,7 +30,7 @@ const runInlineSlicesTests = (
3030
test('can insert marker in the middle of text', () => {
3131
const {view, editor} = setup();
3232
editor.cursor.setAt(10);
33-
editor.saved.insMarker(['p'], {foo: 'bar'});
33+
editor.saved.insMarker([['p', 0, {foo: 'bar'}]]);
3434
expect(view()).toMatchInlineSnapshot(`
3535
"<>
3636
<0>
@@ -44,7 +44,7 @@ const runInlineSlicesTests = (
4444
test('can insert at the beginning of text', () => {
4545
const {view, editor} = setup();
4646
editor.cursor.setAt(0);
47-
editor.saved.insMarker(['p'], {foo: 'bar'});
47+
editor.saved.insMarker([['p', 0, {foo: 'bar'}]]);
4848
expect(view()).toMatchInlineSnapshot(`
4949
"<>
5050
<p> { foo = "bar" }
@@ -53,10 +53,61 @@ const runInlineSlicesTests = (
5353
`);
5454
});
5555

56+
test('nested block data', () => {
57+
const {view, editor} = setup();
58+
editor.cursor.setAt(5);
59+
editor.saved.insMarker([
60+
['ul', 0, {type: 'tasks'}],
61+
['li', 0, {completed: false}],
62+
]);
63+
editor.cursor.setAt(10);
64+
editor.saved.insMarker([
65+
['ul', 0, {type: 'tasks'}],
66+
['li', 1, {completed: true}],
67+
]);
68+
expect(view()).toMatchInlineSnapshot(`
69+
"<>
70+
<0>
71+
"abcde" { }
72+
<ul> { type = "tasks" }
73+
<li> { completed = !f }
74+
"fghi" { }
75+
<li> { completed = !t }
76+
"jklmnopqrstuvwxyz" { }
77+
"
78+
`);
79+
});
80+
81+
test('nested block data - 2', () => {
82+
const {view, editor} = setup();
83+
editor.cursor.setAt(5);
84+
editor.saved.insMarker([
85+
['ul', 0, {type: 'tasks'}],
86+
['li', 0, {completed: false}],
87+
]);
88+
editor.cursor.setAt(10);
89+
editor.saved.insMarker([
90+
['ul', 1, {type: 'tasks'}],
91+
['li', 0, {completed: true}],
92+
]);
93+
expect(view()).toMatchInlineSnapshot(`
94+
"<>
95+
<0>
96+
"abcde" { }
97+
<ul> { type = "tasks" }
98+
<li> { completed = !f }
99+
"fghi" { }
100+
<ul> { type = "tasks" }
101+
<li> { completed = !t }
102+
"jklmnopqrstuvwxyz" { }
103+
"
104+
`);
105+
});
106+
56107
test('can insert at the end of text', () => {
57108
const {view, editor} = setup();
58109
editor.cursor.setAt(26);
59-
editor.saved.insMarker(['unfurl'], {link: 'foobar'});
110+
editor.saved.insMarker([['unfurl', 0, {link: 'foobar'}]]);
60111
expect(view()).toMatchInlineSnapshot(`
61112
"<>
62113
<0>
@@ -71,14 +122,14 @@ const runInlineSlicesTests = (
71122
editor.cursor.setAt(5, 5);
72123
editor.saved.insOne('BOLD');
73124
editor.cursor.setAt(15);
74-
editor.saved.insMarker(['paragraph'], []);
125+
editor.saved.insMarker(['paragraph']);
75126
expect(view()).toMatchInlineSnapshot(`
76127
"<>
77128
<0>
78129
"abcde" { }
79130
"fghij" { BOLD = [ !u ] }
80131
"klmno" { }
81-
<paragraph> [ ]
132+
<paragraph>
82133
"pqrstuvwxyz" { }
83134
"
84135
`);
@@ -89,14 +140,14 @@ const runInlineSlicesTests = (
89140
editor.cursor.setAt(5, 5);
90141
editor.saved.insOne('BOLD');
91142
editor.cursor.setAt(10);
92-
editor.saved.insMarker(['paragraph'], []);
143+
editor.saved.insMarker(['paragraph']);
93144
expect(view()).toMatchInlineSnapshot(`
94145
"<>
95146
<0>
96147
"abcde" { }
97148
"fghij" { BOLD = [ !u ] }
98149
"" { }
99-
<paragraph> [ ]
150+
<paragraph>
100151
"klmnopqrstuvwxyz" { }
101152
"
102153
`);
@@ -107,12 +158,12 @@ const runInlineSlicesTests = (
107158
editor.cursor.setAt(15, 5);
108159
editor.saved.insOne('BOLD');
109160
editor.cursor.setAt(10);
110-
editor.saved.insMarker(['paragraph'], []);
161+
editor.saved.insMarker(['paragraph']);
111162
expect(view()).toMatchInlineSnapshot(`
112163
"<>
113164
<0>
114165
"abcdefghij" { }
115-
<paragraph> [ ]
166+
<paragraph>
116167
"klmno" { }
117168
"pqrst" { BOLD = [ !u ] }
118169
"uvwxyz" { }
@@ -125,12 +176,12 @@ const runInlineSlicesTests = (
125176
editor.cursor.setAt(15, 5);
126177
editor.saved.insOne('BOLD');
127178
editor.cursor.setAt(15);
128-
editor.saved.insMarker(['paragraph'], []);
179+
editor.saved.insMarker(['paragraph']);
129180
expect(view()).toMatchInlineSnapshot(`
130181
"<>
131182
<0>
132183
"abcdefghijklmno" { }
133-
<paragraph> [ ]
184+
<paragraph>
134185
"pqrst" { BOLD = [ !u ] }
135186
"uvwxyz" { }
136187
"
@@ -142,13 +193,13 @@ const runInlineSlicesTests = (
142193
editor.cursor.setAt(5, 10);
143194
editor.saved.insOne('BOLD');
144195
editor.cursor.setAt(10);
145-
editor.saved.insMarker(['paragraph'], []);
196+
editor.saved.insMarker(['paragraph']);
146197
expect(view()).toMatchInlineSnapshot(`
147198
"<>
148199
<0>
149200
"abcde" { }
150201
"fghij" { BOLD = [ !u ] }
151-
<paragraph> [ ]
202+
<paragraph>
152203
"klmno" { BOLD = [ !u ] }
153204
"pqrstuvwxyz" { }
154205
"
@@ -158,19 +209,19 @@ const runInlineSlicesTests = (
158209
test('can annotate with slice over two block splits', () => {
159210
const {view, editor} = setup();
160211
editor.cursor.setAt(10);
161-
editor.saved.insMarker(['p'], []);
212+
editor.saved.insMarker(['p']);
162213
editor.cursor.setAt(15);
163-
editor.saved.insMarker(['p'], []);
214+
editor.saved.insMarker(['p']);
164215
editor.cursor.setAt(8, 15);
165216
editor.saved.insOne('BOLD');
166217
expect(view()).toMatchInlineSnapshot(`
167218
"<>
168219
<0>
169220
"abcdefgh" { }
170221
"ij" { BOLD = [ !u ] }
171-
<p> [ ]
222+
<p>
172223
"klmn" { BOLD = [ !u ] }
173-
<p> [ ]
224+
<p>
174225
"opqrstu" { BOLD = [ !u ] }
175226
"vwxyz" { }
176227
"
@@ -180,16 +231,16 @@ const runInlineSlicesTests = (
180231
test('can insert two blocks', () => {
181232
const {view, editor} = setup();
182233
editor.cursor.setAt(10);
183-
editor.saved.insMarker('p', {});
234+
editor.saved.insMarker('p');
184235
editor.cursor.setAt(10 + 10 + 1);
185-
editor.saved.insMarker('p', {});
236+
editor.saved.insMarker('p');
186237
expect(view()).toMatchInlineSnapshot(`
187238
"<>
188239
<0>
189240
"abcdefghij" { }
190-
<p> { }
241+
<p>
191242
"klmnopqrst" { }
192-
<p> { }
243+
<p>
193244
"uvwxyz" { }
194245
"
195246
`);

src/json-crdt-extensions/peritext/block/Block.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {UndEndIterator, type UndEndNext} from '../../../util/iterator';
55
import {Inline} from './Inline';
66
import {formatType, getTag} from '../slice/util';
77
import {Range} from '../rga/Range';
8+
import {stringify} from '../../../json-text/stringify';
89
import type {Point} from '../rga/Point';
910
import type {OverlayPoint} from '../overlay/OverlayPoint';
1011
import type {Printable} from 'tree-dump';
@@ -49,7 +50,12 @@ export class Block<T = string, Attr = unknown> extends Range<T> implements IBloc
4950
}
5051

5152
public attr(): Attr | undefined {
52-
return this.marker?.data() as Attr | undefined;
53+
const path = this.path;
54+
const length = path.length;
55+
if (length === 0) return;
56+
const tagStep = path[length - 1];
57+
if (!Array.isArray(tagStep)) return;
58+
return tagStep[2] as Attr | undefined;
5359
}
5460

5561
public isLeaf(): boolean {
@@ -197,7 +203,8 @@ export class Block<T = string, Attr = unknown> extends Range<T> implements IBloc
197203
protected toStringHeader(): string {
198204
const hash = `#${this.hash.toString(36).slice(-4)}`;
199205
const tag = this.path.map((step) => formatType(step)).join('.');
200-
const header = `${super.toString('', true)} ${hash} ${tag} `;
206+
const data = stringify(this.attr());
207+
const header = `${super.toString('', true)} ${hash} ${tag} ${data}`;
201208
return header;
202209
}
203210

src/json-crdt-extensions/peritext/block/Inline.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ export class Inline<T = string> extends Range<T> implements Printable {
173173
for (let i = 0; i < length123; i++) {
174174
const slice = i >= length12 ? slices3[i - length12] : i >= length1 ? slices2[i - length1] : slices1[i];
175175
if (slice instanceof Range) {
176-
const type = slice.type as PathStep;
176+
const type = slice.type() as PathStep;
177177
switch (slice.stacking) {
178178
case SliceStacking.Cursor: {
179179
const stack: InlineAttrStack<T> = attr[SliceTypeName.Cursor] ?? (attr[SliceTypeName.Cursor] = []);
@@ -322,9 +322,10 @@ export class Inline<T = string> extends Range<T> implements Printable {
322322
: formatType(key) +
323323
' = ' +
324324
stringify(
325-
attr[key].map((attr) =>
326-
attr.slice instanceof Cursor ? [attr.slice.type, attr.slice.data()] : attr.slice.data(),
327-
),
325+
attr[key].map((attr) => {
326+
const slice = attr.slice;
327+
return slice instanceof Cursor ? [slice.type(), slice.data()] : slice.data();
328+
}),
328329
);
329330
}),
330331
),

src/json-crdt-extensions/peritext/block/__tests__/Block.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const setup = () => {
77
const data = {
88
source: 'http://example.com',
99
};
10-
kit.peritext.editor.saved.insMarker(['li', 'blockquote'], data);
10+
kit.peritext.editor.saved.insMarker(['li', ['blockquote', 0, data]]);
1111
kit.peritext.refresh();
1212
const marker = kit.peritext.overlay.markers().next().value!;
1313
const type = marker.type();

src/json-crdt-extensions/peritext/editor/Cursor.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ export class Cursor<T = string> extends PersistedSlice<T> {
2626
* @todo Remove getter `get` here.
2727
*/
2828
public get anchorSide(): CursorAnchor {
29-
return this.type as CursorAnchor;
29+
return this.type() as CursorAnchor;
3030
}
3131

3232
public isStartFocused(): boolean {
33-
return this.type === CursorAnchor.End || this.start.cmp(this.end) === 0;
33+
return this.type() === CursorAnchor.End || this.start.cmp(this.end) === 0;
3434
}
3535

3636
public isEndFocused(): boolean {
37-
return this.type === CursorAnchor.Start || this.start.cmp(this.end) === 0;
37+
return this.type() === CursorAnchor.Start || this.start.cmp(this.end) === 0;
3838
}
3939

4040
// ---------------------------------------------------------------- mutations

0 commit comments

Comments
 (0)