edytor 5.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cloudflare/DocumentRoom.d.ts +77 -2
- package/dist/cloudflare/DocumentRoom.js +37 -1
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/index.js +1 -1
- package/dist/cloudflare/room/access.d.ts +6 -1
- package/dist/cloudflare/room/access.js +30 -4
- package/dist/cloudflare/room/admission.d.ts +11 -0
- package/dist/cloudflare/room/admission.js +43 -10
- package/dist/cloudflare/room/context.d.ts +73 -76
- package/dist/cloudflare/room/context.js +17 -6
- package/dist/cloudflare/room/forged.d.ts +1 -1
- package/dist/cloudflare/room/forged.js +11 -9
- package/dist/cloudflare/room/frames.d.ts +2 -0
- package/dist/cloudflare/room/frames.js +2 -0
- package/dist/cloudflare/room/presence.js +1 -4
- package/dist/cloudflare/room/reseed.d.ts +93 -0
- package/dist/cloudflare/room/reseed.js +500 -0
- package/dist/cloudflare/room/scheduler.d.ts +1 -1
- package/dist/cloudflare/room/scheduler.js +9 -1
- package/dist/cloudflare/room/shared.d.ts +10 -0
- package/dist/cloudflare/room/shared.js +10 -0
- package/dist/cloudflare/room/storage.d.ts +7 -0
- package/dist/cloudflare/room/storage.js +33 -1
- package/dist/components/Edytor.svelte +18 -3
- package/dist/components/Edytor.svelte.d.ts +10 -1
- package/dist/components/EdytorRoot.svelte +17 -0
- package/dist/components/EdytorRoot.svelte.d.ts +4 -0
- package/dist/components/Suggestion.svelte +1 -1
- package/dist/crdt/attribution/block.d.ts +3 -0
- package/dist/crdt/attribution/block.js +3 -2
- package/dist/crdt/doc/history.js +2 -3
- package/dist/crdt/doc/ops.d.ts +81 -0
- package/dist/crdt/doc/ops.js +1898 -0
- package/dist/crdt/doc/suggest.d.ts +6 -2
- package/dist/crdt/doc/suggest.js +6 -5
- package/dist/crdt/edytor-doc.d.ts +157 -157
- package/dist/crdt/edytor-doc.js +15 -43
- package/dist/crdt/engine-room.d.ts +4 -0
- package/dist/crdt/engine-room.js +6 -0
- package/dist/crdt/flow.d.ts +4 -46
- package/dist/crdt/flow.js +2 -262
- package/dist/crdt/index.d.ts +64 -75
- package/dist/crdt/operations.d.ts +1 -1
- package/dist/crdt/placement/resolve.d.ts +1 -1
- package/dist/crdt/protocol.d.ts +2 -2
- package/dist/crdt/protocol.js +2 -2
- package/dist/crdt/protocols/auth.d.ts +8 -1
- package/dist/crdt/protocols/auth.js +14 -1
- package/dist/crdt/protocols/awareness.js +46 -81
- package/dist/crdt/providers/index.d.ts +11 -22
- package/dist/crdt/providers/index.js +22 -3
- package/dist/crdt/providers/indexeddb.d.ts +6 -10
- package/dist/crdt/providers/indexeddb.js +10 -16
- package/dist/crdt/providers/room.d.ts +26 -0
- package/dist/crdt/providers/room.js +41 -12
- package/dist/crdt/providers/websocket.d.ts +10 -14
- package/dist/crdt/providers/websocket.js +43 -23
- package/dist/crdt/rangeDelete.d.ts +2 -47
- package/dist/crdt/rangeDelete.js +1 -169
- package/dist/crdt/reseed.d.ts +127 -0
- package/dist/crdt/reseed.js +533 -0
- package/dist/crdt/restore.d.ts +13 -1
- package/dist/crdt/restore.js +8 -3
- package/dist/crdt/schema.d.ts +6 -0
- package/dist/crdt/schema.js +6 -0
- package/dist/crdt/structs.d.ts +8 -11
- package/dist/crdt/structs.js +25 -44
- package/dist/crdt/text/index/build.d.ts +119 -0
- package/dist/crdt/text/index/build.js +2801 -0
- package/dist/crdt/text/index/shared.d.ts +2 -6
- package/dist/crdt/text/index/shared.js +2 -5
- package/dist/crdt/text/model.js +2 -3
- package/dist/crdt/text/runs.d.ts +9 -12
- package/dist/crdt/text/runs.js +2 -165
- package/dist/crdt/vendor/yjs/UPSTREAM.md +101 -5
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +10 -10
- package/dist/crdt/vendor/yjs/dts/utils/encoding.d.ts +1 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/Item.js +24 -13
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +12 -10
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +42 -1
- package/dist/events/domTextOffset.d.ts +2 -0
- package/dist/events/domTextOffset.js +8 -0
- package/dist/export/forms.d.ts +50 -50
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
- package/dist/plugins/richtext/richTextOperations.js +26 -32
- package/dist/selection/selection.svelte.d.ts +1 -1
- package/dist/selection/selection.svelte.js +87 -106
- package/dist/selection/selection.utils.js +5 -8
- package/dist/session/commands.js +49 -51
- package/dist/session/handles.js +3 -7
- package/dist/surface/attributes.d.ts +3 -3
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/observer.svelte.d.ts +7 -7
- package/dist/surface/observer.svelte.js +225 -336
- package/dist/surface/projector.svelte.js +2 -2
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +7 -0
- package/package.json +1 -1
- package/dist/crdt/doc/capability.d.ts +0 -56
- package/dist/crdt/doc/capability.js +0 -207
- package/dist/crdt/doc/content.d.ts +0 -16
- package/dist/crdt/doc/content.js +0 -77
- package/dist/crdt/doc/delete.d.ts +0 -15
- package/dist/crdt/doc/delete.js +0 -84
- package/dist/crdt/doc/layout.d.ts +0 -9
- package/dist/crdt/doc/layout.js +0 -178
- package/dist/crdt/doc/meta.d.ts +0 -19
- package/dist/crdt/doc/meta.js +0 -198
- package/dist/crdt/doc/moves.d.ts +0 -21
- package/dist/crdt/doc/moves.js +0 -274
- package/dist/crdt/doc/split-merge.d.ts +0 -10
- package/dist/crdt/doc/split-merge.js +0 -179
- package/dist/crdt/doc/steps.d.ts +0 -65
- package/dist/crdt/doc/steps.js +0 -311
- package/dist/crdt/text/index/cache.d.ts +0 -16
- package/dist/crdt/text/index/cache.js +0 -175
- package/dist/crdt/text/index/claims.d.ts +0 -18
- package/dist/crdt/text/index/claims.js +0 -203
- package/dist/crdt/text/index/fold.d.ts +0 -21
- package/dist/crdt/text/index/fold.js +0 -656
- package/dist/crdt/text/index/layout.d.ts +0 -22
- package/dist/crdt/text/index/layout.js +0 -258
- package/dist/crdt/text/index/placement.d.ts +0 -47
- package/dist/crdt/text/index/placement.js +0 -738
- package/dist/crdt/text/index/records.d.ts +0 -13
- package/dist/crdt/text/index/records.js +0 -139
- package/dist/crdt/text/index/report.d.ts +0 -18
- package/dist/crdt/text/index/report.js +0 -337
- package/dist/crdt/text/index/state.d.ts +0 -197
- package/dist/crdt/text/index/state.js +0 -116
- package/dist/crdt/text/index/streams.d.ts +0 -25
- package/dist/crdt/text/index/streams.js +0 -248
|
@@ -1,738 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Placements and the children index (maintained), the display
|
|
3
|
-
* ownership the display reads (`ownShim`), and THE shown-kind rule
|
|
4
|
-
* (`typeOf`).
|
|
5
|
-
*
|
|
6
|
-
* A structural change re-decides only what it can change: the display
|
|
7
|
-
* edge of the blocks whose slot, record or mover changed (fork patch
|
|
8
|
-
* YP22), and the display slot of every block whose slot walk
|
|
9
|
-
* (`displaySlotOf`) reads one that changed — its stored subtree, through
|
|
10
|
-
* the blocks it displays — patched into the parents' child lists. A
|
|
11
|
-
* block's candidate edges are its live move records, strongest first, then
|
|
12
|
-
* its home slot; it shows at the first that closes no cycle through the
|
|
13
|
-
* accepted edges (`settle`): the later move wins, and the block whose edge
|
|
14
|
-
* is the oldest of a cycle falls back to its next candidate, back where it
|
|
15
|
-
* stood (its previous record, else home; the root's tail only when every
|
|
16
|
-
* candidate closes a cycle), so a cycle resolves locally: no global pass.
|
|
17
|
-
* The first build places every block the same way (their unique acyclic
|
|
18
|
-
* acceptance, whatever the order; the tests' greedy rebuild,
|
|
19
|
-
* `src/tests/oracles/placements.ts`, is what the self-checks compare). A
|
|
20
|
-
* placed block's order key is found from its sequence neighbours
|
|
21
|
-
* (`placement/keys.ts`), never by walking the parent's sequence; a rebuild
|
|
22
|
-
* keys every list once, in its sequence's order. The layout rules re-run
|
|
23
|
-
* only on the layouts a change reaches.
|
|
24
|
-
*/
|
|
25
|
-
import { bySlot, childrenIndex, displaySlotOf, documentOrder } from '../../placement/model.js';
|
|
26
|
-
import { denseKey, keyBetween } from '../../placement/keys.js';
|
|
27
|
-
import { NO_SLOT, byTail, candidatesOf, cycleThrough, edgeParentOf, collectSlots, homeOf, registryOwners, ownerOfNode, positionsOfItem, slotOwner, slotsBefore } from '../../placement/resolve.js';
|
|
28
|
-
import { cmpStamp, sameUnit } from '../../placement/slots.js';
|
|
29
|
-
import { Y } from '../../engine.js';
|
|
30
|
-
import { engineOps, firstItem } from '../../structs.js';
|
|
31
|
-
import { DEAD, displayOf } from '../model.js';
|
|
32
|
-
/** Placement maintenance of one doc's index, over its state, claims, streams and layout rules. */
|
|
33
|
-
export const indexPlacement = (ix) => {
|
|
34
|
-
const { doc, registry, blocks, displaysMap, ownerChanged, placementsMap, following, dirtyLists, leftLists, placementSeeds, stateSeeds, kindsOf, slotsByBlock, rawOrder, edges, fallen, rejected, ensureOwners, ownerOf, top, streamOf, streamsIn, streamAt, kindOf, lineKind, itemKind, rowKind, rowOfTable, cellKind, cellRank, dissolve, redissolve } = ix;
|
|
35
|
-
const kept = engineOps(Y).isKeptReplaced;
|
|
36
|
-
// ── placements and the children index (maintained) ────────────
|
|
37
|
-
/** The document holds a layout or an item kind: `kids0` and `kidsMap` differ. */
|
|
38
|
-
let layoutMode = false;
|
|
39
|
-
/** The line kinds the roles declare, and those of the lines islands the document holds. */
|
|
40
|
-
let lineKinds = new Set();
|
|
41
|
-
let orderCache = null;
|
|
42
|
-
let layoutBlocks = 0;
|
|
43
|
-
let itemBlocks = 0;
|
|
44
|
-
/** Tables, rows and cells (`table.*`): their rules run in the layout pass. */
|
|
45
|
-
let tableBlocks = 0;
|
|
46
|
-
const lineCounts = new Map();
|
|
47
|
-
const ownShim = {
|
|
48
|
-
ownerOf,
|
|
49
|
-
hidden: (b) => ownerOf(b) !== b || ix.dissolved.has(b),
|
|
50
|
-
childless: (b) => kindOf(b)?.childless === true,
|
|
51
|
-
island: (b) => kindOf(b)?.island === true,
|
|
52
|
-
container: (b) => kindOf(b)?.container === true,
|
|
53
|
-
lined: (b) => lineKind(b) !== undefined,
|
|
54
|
-
passes: (b) => ix.dissolved.has(b),
|
|
55
|
-
sheds: (owner, child) => {
|
|
56
|
-
// A layout shows only its items, a table its rows, a row its cells.
|
|
57
|
-
const only = itemKind(owner) ?? rowKind(owner) ?? cellKind(owner);
|
|
58
|
-
return only !== undefined && blocks.get(child)?.type !== only;
|
|
59
|
-
},
|
|
60
|
-
slotRank: (owner, id) => cellRank(owner, id),
|
|
61
|
-
top,
|
|
62
|
-
claimIndex: (holder, m) => blocks.get(holder)?.claims.findIndex((c) => c.m === m) ?? -1,
|
|
63
|
-
streamOf,
|
|
64
|
-
streamsIn,
|
|
65
|
-
streamAt,
|
|
66
|
-
display: (b) => displayOf(b, blocks, ownShim)
|
|
67
|
-
};
|
|
68
|
-
/** Re-count `id`'s kind facts; a fact the roles never declared forces a full pass. */
|
|
69
|
-
const noteKind = (id) => {
|
|
70
|
-
const old = kindsOf.get(id);
|
|
71
|
-
const type = blocks.get(id)?.type;
|
|
72
|
-
const now = type === undefined
|
|
73
|
-
? undefined
|
|
74
|
-
: {
|
|
75
|
-
layout: itemKind(id) !== undefined,
|
|
76
|
-
item: ix.itemKinds.has(type),
|
|
77
|
-
line: lineKind(id),
|
|
78
|
-
table: rowKind(id) !== undefined,
|
|
79
|
-
row: ix.rowKinds.has(type),
|
|
80
|
-
cell: ix.cellKinds.has(type)
|
|
81
|
-
};
|
|
82
|
-
const tableish = (k) => k?.table || k?.row || k?.cell ? 1 : 0;
|
|
83
|
-
tableBlocks += tableish(now) - tableish(old);
|
|
84
|
-
if (old?.layout)
|
|
85
|
-
layoutBlocks--;
|
|
86
|
-
if (old?.item)
|
|
87
|
-
itemBlocks--;
|
|
88
|
-
if (old?.line !== undefined)
|
|
89
|
-
lineCounts.set(old.line, lineCounts.get(old.line) - 1);
|
|
90
|
-
if (now === undefined)
|
|
91
|
-
kindsOf.delete(id);
|
|
92
|
-
else
|
|
93
|
-
kindsOf.set(id, now);
|
|
94
|
-
if (now?.layout)
|
|
95
|
-
layoutBlocks++;
|
|
96
|
-
if (now?.item)
|
|
97
|
-
itemBlocks++;
|
|
98
|
-
if (now?.line !== undefined)
|
|
99
|
-
lineCounts.set(now.line, (lineCounts.get(now.line) ?? 0) + 1);
|
|
100
|
-
const item = itemKind(id);
|
|
101
|
-
if (item !== undefined && !ix.itemKinds.has(item))
|
|
102
|
-
ix.placementFull = true;
|
|
103
|
-
if (now?.line !== undefined && !lineKinds.has(now.line))
|
|
104
|
-
ix.placementFull = true;
|
|
105
|
-
// A table of a kind whose row the index does not know yet: its rows and cells change kind facts.
|
|
106
|
-
const rows = type === undefined ? undefined : rowOfTable(type);
|
|
107
|
-
if (rows !== undefined && ix.rowKinds.get(rows[0]) !== rows[1])
|
|
108
|
-
ix.placementFull = true;
|
|
109
|
-
if (layoutMode !== layoutBlocks + itemBlocks + tableBlocks > 0)
|
|
110
|
-
ix.placementFull = true;
|
|
111
|
-
};
|
|
112
|
-
/** The ownership the layout rules read: none of their decisions applied. */
|
|
113
|
-
const own0 = {
|
|
114
|
-
...ownShim,
|
|
115
|
-
hidden: (b) => ownerOf(b) !== b,
|
|
116
|
-
passes: () => false
|
|
117
|
-
};
|
|
118
|
-
/** `id`'s slot in the index `own` reads (none: hidden, or under no live parent). */
|
|
119
|
-
const slotIn = (own, id) => {
|
|
120
|
-
const pl = placementsMap.get(id);
|
|
121
|
-
if (pl === undefined || !blocks.has(id) || own.hidden(id))
|
|
122
|
-
return undefined;
|
|
123
|
-
const { parent, rank, reset } = displaySlotOf(own, placementsMap, pl, id);
|
|
124
|
-
if (parent === DEAD)
|
|
125
|
-
return undefined;
|
|
126
|
-
return reset === null ? { parent, rank } : { parent, rank, reset };
|
|
127
|
-
};
|
|
128
|
-
/** The index of the first slot of `list` not before `x`. */
|
|
129
|
-
const seek = (list, x) => {
|
|
130
|
-
let lo = 0;
|
|
131
|
-
let hi = list.length;
|
|
132
|
-
while (lo < hi) {
|
|
133
|
-
const mid = (lo + hi) >> 1;
|
|
134
|
-
if (bySlot(list[mid], x) < 0)
|
|
135
|
-
lo = mid + 1;
|
|
136
|
-
else
|
|
137
|
-
hi = mid;
|
|
138
|
-
}
|
|
139
|
-
return lo;
|
|
140
|
-
};
|
|
141
|
-
/**
|
|
142
|
-
* Re-decide the slots of `affected` in `kids`/`slotMap` (read through
|
|
143
|
-
* `own`), patching the child lists they leave or join (each list
|
|
144
|
-
* copied once: a list a reader holds never changes under it). Returns
|
|
145
|
-
* the blocks whose slot changed, with their parents before and after.
|
|
146
|
-
*/
|
|
147
|
-
const patch = (kids, slotMap, own, affected) => {
|
|
148
|
-
const changed = new Map();
|
|
149
|
-
const copies = new Map();
|
|
150
|
-
const list = (p) => {
|
|
151
|
-
let l = copies.get(p);
|
|
152
|
-
if (l === undefined)
|
|
153
|
-
copies.set(p, (l = [...(kids.get(p) ?? [])]));
|
|
154
|
-
return l;
|
|
155
|
-
};
|
|
156
|
-
for (const id of affected) {
|
|
157
|
-
const old = slotMap.get(id);
|
|
158
|
-
const next = slotIn(own, id);
|
|
159
|
-
if (old?.parent === next?.parent &&
|
|
160
|
-
old?.rank === next?.rank &&
|
|
161
|
-
old?.reset === next?.reset &&
|
|
162
|
-
(old === undefined) === (next === undefined))
|
|
163
|
-
continue;
|
|
164
|
-
if (old !== undefined) {
|
|
165
|
-
const l = list(old.parent);
|
|
166
|
-
const at = seek(l, { id, rank: old.rank });
|
|
167
|
-
if (l[at]?.id === id)
|
|
168
|
-
l.splice(at, 1);
|
|
169
|
-
}
|
|
170
|
-
if (next !== undefined) {
|
|
171
|
-
const l = list(next.parent);
|
|
172
|
-
const slot = next.reset === undefined
|
|
173
|
-
? { id, rank: next.rank }
|
|
174
|
-
: { id, rank: next.rank, reset: next.reset };
|
|
175
|
-
l.splice(seek(l, slot), 0, slot);
|
|
176
|
-
slotMap.set(id, next);
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
slotMap.delete(id);
|
|
180
|
-
if (kids === ix.kidsMap)
|
|
181
|
-
leftLists.add(id);
|
|
182
|
-
}
|
|
183
|
-
changed.set(id, [old?.parent, next?.parent]);
|
|
184
|
-
}
|
|
185
|
-
for (const [p, l] of copies) {
|
|
186
|
-
if (l.length === 0)
|
|
187
|
-
kids.delete(p);
|
|
188
|
-
else
|
|
189
|
-
kids.set(p, l);
|
|
190
|
-
if (kids === ix.kidsMap)
|
|
191
|
-
dirtyLists.add(p);
|
|
192
|
-
}
|
|
193
|
-
return changed;
|
|
194
|
-
};
|
|
195
|
-
/** `seeds` and every block whose slot walk reads one of them: stored subtrees, through what each displays. */
|
|
196
|
-
const reach = (seeds) => {
|
|
197
|
-
const out = new Set();
|
|
198
|
-
const stack = [...seeds];
|
|
199
|
-
for (let x = stack.pop(); x !== undefined; x = stack.pop()) {
|
|
200
|
-
if (out.has(x))
|
|
201
|
-
continue;
|
|
202
|
-
out.add(x);
|
|
203
|
-
for (const c of rawOrder.get(x) ?? [])
|
|
204
|
-
stack.push(c.id);
|
|
205
|
-
for (const d of displaysMap.get(x) ?? [])
|
|
206
|
-
if (d !== x)
|
|
207
|
-
for (const c of rawOrder.get(d) ?? [])
|
|
208
|
-
stack.push(c.id);
|
|
209
|
-
}
|
|
210
|
-
return out;
|
|
211
|
-
};
|
|
212
|
-
/** Whether `id` shows as its slot's kind (`following`): read from its slot and kinds. */
|
|
213
|
-
const noteFollowing = (id) => {
|
|
214
|
-
const slot = ix.slots.get(id);
|
|
215
|
-
const follows = slot !== undefined &&
|
|
216
|
-
(slot.reset !== undefined ||
|
|
217
|
-
(slot.parent !== null && lineKind(slot.parent) !== undefined) ||
|
|
218
|
-
lineKinds.has(blocks.get(id)?.type ?? ''));
|
|
219
|
-
if (follows)
|
|
220
|
-
following.add(id);
|
|
221
|
-
else
|
|
222
|
-
following.delete(id);
|
|
223
|
-
};
|
|
224
|
-
// ── edges, keys and acceptance ─────────────────────────────────
|
|
225
|
-
/**
|
|
226
|
-
* Whether the slot map has been read from the sequences: once, before the
|
|
227
|
-
* first rebuild (a shell settled on the first fold asks for a home), then
|
|
228
|
-
* the fold keeps it and every rebuild fills it again.
|
|
229
|
-
*/
|
|
230
|
-
let slotsRead = false;
|
|
231
|
-
/** What the first read collected, for a rebuild while no sequence changed since (one scan). */
|
|
232
|
-
let collected = null;
|
|
233
|
-
let collectedAt = -1;
|
|
234
|
-
const readSlots = () => {
|
|
235
|
-
slotsRead = true;
|
|
236
|
-
slotsByBlock.clear();
|
|
237
|
-
// Every registry node, its kept incarnations too (the records may not all be built yet).
|
|
238
|
-
collected = collectSlots(doc, registryOwners(registry, kept));
|
|
239
|
-
collectedAt = ix.seqEpoch;
|
|
240
|
-
for (const [b, refs] of collected.slots)
|
|
241
|
-
slotsByBlock.set(b, refs);
|
|
242
|
-
};
|
|
243
|
-
/** Put `ref` (`null`: none) where the slot map holds unit `u` of the slots naming id `b`. */
|
|
244
|
-
const putSlot = (b, u, ref) => {
|
|
245
|
-
const list = (slotsByBlock.get(b) ?? []).filter((x) => !sameUnit(x, u));
|
|
246
|
-
if (ref !== null)
|
|
247
|
-
list.push(ref);
|
|
248
|
-
if (list.length === 0)
|
|
249
|
-
slotsByBlock.delete(b);
|
|
250
|
-
else
|
|
251
|
-
slotsByBlock.set(b, list);
|
|
252
|
-
};
|
|
253
|
-
/** `id`'s home slot among the slots naming its registry id (`null`: none). */
|
|
254
|
-
const homeFor = (id) => {
|
|
255
|
-
if (!slotsRead)
|
|
256
|
-
readSlots();
|
|
257
|
-
return homeOf(registry, kept, slotsByBlock, id);
|
|
258
|
-
};
|
|
259
|
-
/**
|
|
260
|
-
* Re-own the home slots of `node`'s sequence after the registry value it
|
|
261
|
-
* stands for changed (a concurrent creation of one id won or lost, its
|
|
262
|
-
* losing incarnation shown or not): each slot's parent is the node's owner
|
|
263
|
-
* now, and its block re-placed; a node that is no block of the document
|
|
264
|
-
* any more holds no slot (as a rebuild reads it).
|
|
265
|
-
*/
|
|
266
|
-
const reslot = (node) => {
|
|
267
|
-
if (!slotsRead)
|
|
268
|
-
return; // the first read collects them as they are
|
|
269
|
-
const owner = ownerOfNode(registry, kept, node);
|
|
270
|
-
for (let it = firstItem(node); it !== null; it = it.right)
|
|
271
|
-
for (const { value, unit, via } of positionsOfItem(doc, it)) {
|
|
272
|
-
if (via !== null)
|
|
273
|
-
continue; // a record's unit is another sequence's slot
|
|
274
|
-
const b = value.b;
|
|
275
|
-
putSlot(b, unit, owner === undefined ? null : { parent: owner, ...unit });
|
|
276
|
-
for (const id of new Set([b, slotOwner(registry, kept, b, unit.c)])) {
|
|
277
|
-
const rec = blocks.get(id);
|
|
278
|
-
if (rec !== undefined)
|
|
279
|
-
rec.home = homeOf(registry, kept, slotsByBlock, id);
|
|
280
|
-
placementSeeds.add(id);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
/** The slot items a rebuild collected (its homes' items, read once), while it places every block. */
|
|
285
|
-
let known = null;
|
|
286
|
-
/** `id`'s candidate edges, read from the engine: its live records, strongest first, then home (`[]`: no slot, or gone). */
|
|
287
|
-
const candidatesFor = (id) => {
|
|
288
|
-
const home = blocks.get(id)?.home ?? homeFor(id);
|
|
289
|
-
return home === null ? [] : candidatesOf(doc, registry, kept, home, known?.get(home));
|
|
290
|
-
};
|
|
291
|
-
/** The owner of the sequence `id`'s slot shows in as the engine stands (its mover's, else home's; `null`: the root). */
|
|
292
|
-
const edgeParent = (id) => {
|
|
293
|
-
const home = blocks.get(id)?.home ?? homeFor(id);
|
|
294
|
-
return home === null ? undefined : edgeParentOf(doc, registry, kept, home);
|
|
295
|
-
};
|
|
296
|
-
/** The raw children of `parent` in order (every block whose edge parent it is), kept sorted by key. */
|
|
297
|
-
const rawList = (parent) => {
|
|
298
|
-
let l = rawOrder.get(parent);
|
|
299
|
-
if (l === undefined)
|
|
300
|
-
rawOrder.set(parent, (l = []));
|
|
301
|
-
return l;
|
|
302
|
-
};
|
|
303
|
-
/** Counts the placement writes: a key's walk that saw one happen under it walks again. */
|
|
304
|
-
let placedEpoch = 0;
|
|
305
|
-
/** Whether a placement is keyed as it is decided: not while a rebuild places every block (it keys them after, in one pass). */
|
|
306
|
-
let keyed = true;
|
|
307
|
-
const setPlacement = (id, pl) => {
|
|
308
|
-
ix.foldStats.placed++;
|
|
309
|
-
placedEpoch++;
|
|
310
|
-
const old = placementsMap.get(id);
|
|
311
|
-
if (keyed && old !== undefined) {
|
|
312
|
-
const l = rawList(old.parent);
|
|
313
|
-
const at = seek(l, { id, rank: old.rank });
|
|
314
|
-
if (l[at]?.id === id)
|
|
315
|
-
l.splice(at, 1);
|
|
316
|
-
if (l.length === 0)
|
|
317
|
-
rawOrder.delete(old.parent);
|
|
318
|
-
}
|
|
319
|
-
if (pl === undefined)
|
|
320
|
-
placementsMap.delete(id);
|
|
321
|
-
else {
|
|
322
|
-
placementsMap.set(id, pl);
|
|
323
|
-
if (!keyed)
|
|
324
|
-
return;
|
|
325
|
-
const l = rawList(pl.parent);
|
|
326
|
-
const slot = { id, rank: pl.rank };
|
|
327
|
-
l.splice(seek(l, slot), 0, slot);
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
/**
|
|
331
|
-
* The key `id` takes under `edge.parent`: between the key of the nearest
|
|
332
|
-
* block shown where it stands before `id`'s place in the sequence (placed
|
|
333
|
-
* first when it is a pending seed; walked past when it shows elsewhere)
|
|
334
|
-
* and the next key of that list. Placing a pending neighbour can drop a
|
|
335
|
-
* block the walk already passed back into that stretch (a cycle's
|
|
336
|
-
* fallback, back where it stood): a walk under which a placement changed
|
|
337
|
-
* walks again, so the key it returns reads one state.
|
|
338
|
-
*/
|
|
339
|
-
const keyFor = (id, edge, place) => {
|
|
340
|
-
for (;;) {
|
|
341
|
-
const before = placedEpoch;
|
|
342
|
-
const lrank = leftKey(id, edge, place);
|
|
343
|
-
if (placedEpoch !== before)
|
|
344
|
-
continue;
|
|
345
|
-
const list = rawOrder.get(edge.parent) ?? [];
|
|
346
|
-
const i = lrank === undefined ? 0 : seek(list, { id: '', rank: lrank }) + 1;
|
|
347
|
-
return keyBetween(lrank, list[i]?.rank);
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
/** The key of the nearest block shown before `id`'s place at `edge`, under its parent (`undefined`: none). */
|
|
351
|
-
const leftKey = (id, edge, place) => {
|
|
352
|
-
const home = blocks.get(id)?.home ?? homeFor(id);
|
|
353
|
-
for (const e of slotsBefore(doc, edge, home)) {
|
|
354
|
-
const b = slotOwner(registry, kept, e.value.b, e.unit.c);
|
|
355
|
-
// `id` itself (its place before this one) keys nothing.
|
|
356
|
-
if (b === id)
|
|
357
|
-
continue;
|
|
358
|
-
// A slot that is not its block's home (a second creation of one id) places nothing.
|
|
359
|
-
const h = blocks.get(b)?.home ?? homeFor(b);
|
|
360
|
-
if (h === null || !sameUnit(h, e.unit))
|
|
361
|
-
continue;
|
|
362
|
-
place(b);
|
|
363
|
-
const pl = placementsMap.get(b);
|
|
364
|
-
const shows = edges.get(b);
|
|
365
|
-
if (pl !== undefined &&
|
|
366
|
-
pl.parent === edge.parent &&
|
|
367
|
-
!rejected.has(b) &&
|
|
368
|
-
shows != null &&
|
|
369
|
-
shows.via === e.via)
|
|
370
|
-
return pl.rank;
|
|
371
|
-
}
|
|
372
|
-
return undefined;
|
|
373
|
-
};
|
|
374
|
-
/** The key of `id` at the root's tail, among the rejected by stamp then id, after the last top-level block. */
|
|
375
|
-
const tailKey = (id, stamp) => {
|
|
376
|
-
const tail = [...rejected]
|
|
377
|
-
.filter((x) => x !== id)
|
|
378
|
-
.map((x) => ({ id: x, stamp: edges.get(x)?.stamp ?? NO_SLOT }))
|
|
379
|
-
.sort(byTail);
|
|
380
|
-
const list = rawOrder.get(null) ?? [];
|
|
381
|
-
const me = { id, stamp };
|
|
382
|
-
let at = 0;
|
|
383
|
-
while (at < tail.length && byTail(tail[at], me) < 0)
|
|
384
|
-
at++;
|
|
385
|
-
const prev = at > 0
|
|
386
|
-
? placementsMap.get(tail[at - 1].id)?.rank
|
|
387
|
-
: list[list.length - tail.length - 1]?.rank;
|
|
388
|
-
const next = at < tail.length ? placementsMap.get(tail[at].id)?.rank : undefined;
|
|
389
|
-
return keyBetween(prev, next);
|
|
390
|
-
};
|
|
391
|
-
/** Show `id` at the root's tail: every edge closes a cycle, or none. */
|
|
392
|
-
const reject = (id, edge) => {
|
|
393
|
-
edges.set(id, edge);
|
|
394
|
-
if (placementsMap.has(id))
|
|
395
|
-
setPlacement(id, undefined);
|
|
396
|
-
fallen.delete(id);
|
|
397
|
-
rejected.add(id);
|
|
398
|
-
const rank = keyed ? tailKey(id, edge?.stamp ?? NO_SLOT) : '';
|
|
399
|
-
setPlacement(id, { parent: null, rank });
|
|
400
|
-
};
|
|
401
|
-
/** The same display edge: the same sequence, the same record, the same stamp. */
|
|
402
|
-
const sameEdge = (a, b) => a.parent === b.parent && a.via === b.via && cmpStamp(a.stamp, b.stamp) === 0;
|
|
403
|
-
/** Whether `edge` (of `id`) is a place `id` may take now: a live parent, and no cycle it would be the oldest edge of. */
|
|
404
|
-
const admits = (id, edge) => {
|
|
405
|
-
const parent = edge.parent;
|
|
406
|
-
if (parent === undefined || (parent !== null && !blocks.has(parent)))
|
|
407
|
-
return { ok: false, drop: null };
|
|
408
|
-
const cycle = cycleThrough(placementsMap, ownerOf, id, parent);
|
|
409
|
-
if (cycle === null)
|
|
410
|
-
return { ok: true, drop: null };
|
|
411
|
-
let [min, minStamp] = [id, edge.stamp];
|
|
412
|
-
for (const m of cycle) {
|
|
413
|
-
const e = m === id ? edge : edges.get(m);
|
|
414
|
-
if (m !== id && e && cmpStamp(e.stamp, minStamp) < 0)
|
|
415
|
-
[min, minStamp] = [m, e.stamp];
|
|
416
|
-
}
|
|
417
|
-
return min === id ? { ok: false, drop: null } : { ok: true, drop: min };
|
|
418
|
-
};
|
|
419
|
-
/**
|
|
420
|
-
* Place `id` at its strongest candidate edge from `from` on that closes no
|
|
421
|
-
* cycle it would be the oldest edge of (the later move wins). When the
|
|
422
|
-
* edge closes a cycle through an older edge, that edge's block is dropped
|
|
423
|
-
* to its next candidate (back where it stood before that move), and so on
|
|
424
|
-
* down its records to home; a block every candidate of which fails shows
|
|
425
|
-
* at the root's tail. `place` places a pending neighbour first. Returns
|
|
426
|
-
* the blocks whose placement changed.
|
|
427
|
-
*/
|
|
428
|
-
const settle = (id, place, from = 0) => {
|
|
429
|
-
const changed = [id];
|
|
430
|
-
const cands = candidatesFor(id);
|
|
431
|
-
for (let n = from; n < cands.length; n++) {
|
|
432
|
-
const edge = cands[n];
|
|
433
|
-
// The key first: its walk places the pending neighbours, so the cycle
|
|
434
|
-
// check reads them placed (a neighbour placed after it could close a
|
|
435
|
-
// cycle through `id` that no check saw).
|
|
436
|
-
const rank = keyed ? keyFor(id, edge, place) : '';
|
|
437
|
-
const { ok, drop } = admits(id, edge);
|
|
438
|
-
if (!ok)
|
|
439
|
-
continue;
|
|
440
|
-
edges.set(id, edge);
|
|
441
|
-
rejected.delete(id);
|
|
442
|
-
if (n > 0)
|
|
443
|
-
fallen.add(id);
|
|
444
|
-
else
|
|
445
|
-
fallen.delete(id);
|
|
446
|
-
if (placementsMap.has(id))
|
|
447
|
-
setPlacement(id, undefined);
|
|
448
|
-
setPlacement(id, { parent: edge.parent, rank });
|
|
449
|
-
if (drop !== null) {
|
|
450
|
-
const now = edges.get(drop);
|
|
451
|
-
const at = now ? candidatesFor(drop).findIndex((c) => sameEdge(c, now)) : -1;
|
|
452
|
-
changed.push(...settle(drop, () => { }, at + 1));
|
|
453
|
-
}
|
|
454
|
-
return changed;
|
|
455
|
-
}
|
|
456
|
-
reject(id, cands[0] ?? null);
|
|
457
|
-
return changed;
|
|
458
|
-
};
|
|
459
|
-
/** Whether a candidate stronger than `id`'s accepted edge would take it now. */
|
|
460
|
-
const canRise = (id) => {
|
|
461
|
-
const cands = candidatesFor(id);
|
|
462
|
-
const now = edges.get(id);
|
|
463
|
-
const at = rejected.has(id) || !now ? cands.length : cands.findIndex((c) => sameEdge(c, now));
|
|
464
|
-
for (let n = 0; n < (at < 0 ? cands.length : at); n++)
|
|
465
|
-
if (admits(id, cands[n]).ok)
|
|
466
|
-
return true;
|
|
467
|
-
return false;
|
|
468
|
-
};
|
|
469
|
-
/**
|
|
470
|
-
* Try every block below its strongest edge again (a changed edge may have
|
|
471
|
-
* broken its cycle, or made another the one to drop): those that rise join
|
|
472
|
-
* `moved`.
|
|
473
|
-
*/
|
|
474
|
-
const retryFallen = (moved) => {
|
|
475
|
-
for (let round = 0; round < fallen.size + rejected.size + 2; round++) {
|
|
476
|
-
let changed = false;
|
|
477
|
-
const below = [...rejected, ...fallen]
|
|
478
|
-
.map((x) => ({ id: x, stamp: candidatesFor(x)[0]?.stamp ?? NO_SLOT }))
|
|
479
|
-
.sort((a, b) => -byTail(a, b));
|
|
480
|
-
for (const { id } of below) {
|
|
481
|
-
if (!canRise(id))
|
|
482
|
-
continue;
|
|
483
|
-
for (const x of settle(id, () => { }))
|
|
484
|
-
moved.add(x);
|
|
485
|
-
changed = true;
|
|
486
|
-
}
|
|
487
|
-
if (!changed)
|
|
488
|
-
break;
|
|
489
|
-
}
|
|
490
|
-
};
|
|
491
|
-
/**
|
|
492
|
-
* Everything rebuilt from the sequences (the first build, roles): every
|
|
493
|
-
* block placed as an edit places one (`settle`, then `retryFallen`) —
|
|
494
|
-
* the one acyclic acceptance, whatever the order the blocks come in —
|
|
495
|
-
* then keyed in one pass over each sequence's places (dense keys in its
|
|
496
|
-
* order), the root's tail after the last top-level block.
|
|
497
|
-
*/
|
|
498
|
-
const rebuildPlacements = () => {
|
|
499
|
-
ix.placementFull = false;
|
|
500
|
-
placementSeeds.clear();
|
|
501
|
-
stateSeeds.clear();
|
|
502
|
-
ownerChanged.clear();
|
|
503
|
-
const declared = ix.roles?.declared();
|
|
504
|
-
ix.itemKinds = new Set(declared?.items);
|
|
505
|
-
for (const b of blocks.keys()) {
|
|
506
|
-
const item = itemKind(b);
|
|
507
|
-
if (item !== undefined)
|
|
508
|
-
ix.itemKinds.add(item);
|
|
509
|
-
}
|
|
510
|
-
lineKinds = new Set(declared?.lines);
|
|
511
|
-
ix.rowKinds = new Map(declared?.rows);
|
|
512
|
-
for (const b of blocks.keys()) {
|
|
513
|
-
const type = blocks.get(b)?.type;
|
|
514
|
-
const rows = type === undefined ? undefined : rowOfTable(type);
|
|
515
|
-
if (rows !== undefined && rowKind(b) !== undefined)
|
|
516
|
-
ix.rowKinds.set(rows[0], rows[1]);
|
|
517
|
-
}
|
|
518
|
-
ix.cellKinds = new Set(ix.rowKinds.values());
|
|
519
|
-
kindsOf.clear();
|
|
520
|
-
[layoutBlocks, itemBlocks, tableBlocks] = [0, 0, 0];
|
|
521
|
-
lineCounts.clear();
|
|
522
|
-
for (const b of blocks.keys())
|
|
523
|
-
noteKind(b);
|
|
524
|
-
for (const line of lineCounts.keys())
|
|
525
|
-
lineKinds.add(line);
|
|
526
|
-
ix.placementFull = false;
|
|
527
|
-
// The first read's slots while no sequence changed since (one scan).
|
|
528
|
-
const { slots, items, places } = collected !== null && collectedAt === ix.seqEpoch
|
|
529
|
-
? collected
|
|
530
|
-
: collectSlots(doc, blocks.values());
|
|
531
|
-
collected = null;
|
|
532
|
-
slotsByBlock.clear();
|
|
533
|
-
for (const [b, refs] of slots)
|
|
534
|
-
slotsByBlock.set(b, refs);
|
|
535
|
-
slotsRead = true;
|
|
536
|
-
placementsMap.clear();
|
|
537
|
-
edges.clear();
|
|
538
|
-
rawOrder.clear();
|
|
539
|
-
fallen.clear();
|
|
540
|
-
rejected.clear();
|
|
541
|
-
[keyed, known] = [false, items];
|
|
542
|
-
for (const id of blocks.keys())
|
|
543
|
-
settle(id, () => { });
|
|
544
|
-
if (fallen.size + rejected.size > 0)
|
|
545
|
-
retryFallen(new Set());
|
|
546
|
-
[keyed, known] = [true, null];
|
|
547
|
-
// The lists in key order: a block where its edge shows it, in its sequence's order.
|
|
548
|
-
for (const [owner, list] of places)
|
|
549
|
-
for (const e of list) {
|
|
550
|
-
const b = slotOwner(registry, kept, e.value.b, e.unit.c);
|
|
551
|
-
const pl = placementsMap.get(b);
|
|
552
|
-
const h = blocks.get(b)?.home;
|
|
553
|
-
if (pl?.parent !== owner || rejected.has(b) || !h || !sameUnit(h, e.unit))
|
|
554
|
-
continue;
|
|
555
|
-
if (edges.get(b)?.via !== e.via)
|
|
556
|
-
continue;
|
|
557
|
-
const l = rawList(owner);
|
|
558
|
-
pl.rank = denseKey(l.length);
|
|
559
|
-
l.push({ id: b, rank: pl.rank });
|
|
560
|
-
}
|
|
561
|
-
// The root's tail, keyed as an edit keys a rejected block, in its order.
|
|
562
|
-
const tail = [...rejected].map((x) => ({ id: x, stamp: edges.get(x)?.stamp ?? NO_SLOT }));
|
|
563
|
-
rejected.clear();
|
|
564
|
-
for (const { id } of tail.sort(byTail))
|
|
565
|
-
reject(id, edges.get(id) ?? null);
|
|
566
|
-
layoutMode = layoutBlocks + itemBlocks + tableBlocks > 0;
|
|
567
|
-
ix.dissolved = new Set();
|
|
568
|
-
const index = (map) => {
|
|
569
|
-
const at = new Map();
|
|
570
|
-
for (const [parent, list] of map)
|
|
571
|
-
for (const { id, rank, reset } of list)
|
|
572
|
-
at.set(id, reset === undefined ? { parent, rank } : { parent, rank, reset });
|
|
573
|
-
return at;
|
|
574
|
-
};
|
|
575
|
-
ix.kids0 = childrenIndex(placementsMap, own0);
|
|
576
|
-
ix.slots0 = index(ix.kids0);
|
|
577
|
-
if (layoutMode) {
|
|
578
|
-
ix.dissolved = dissolve(ix.kids0);
|
|
579
|
-
ix.kidsMap = childrenIndex(placementsMap, ownShim);
|
|
580
|
-
ix.slots = index(ix.kidsMap);
|
|
581
|
-
}
|
|
582
|
-
else
|
|
583
|
-
[ix.kidsMap, ix.slots] = [ix.kids0, ix.slots0];
|
|
584
|
-
following.clear();
|
|
585
|
-
for (const id of ix.slots.keys())
|
|
586
|
-
noteFollowing(id);
|
|
587
|
-
orderCache = null;
|
|
588
|
-
ix.kidsVersion++;
|
|
589
|
-
ix.allListsDirty = true;
|
|
590
|
-
};
|
|
591
|
-
const ensurePlacements = () => {
|
|
592
|
-
ensureOwners();
|
|
593
|
-
if (ix.placementFull)
|
|
594
|
-
return rebuildPlacements();
|
|
595
|
-
if (placementSeeds.size + stateSeeds.size + ownerChanged.size === 0)
|
|
596
|
-
return;
|
|
597
|
-
const moved = new Set();
|
|
598
|
-
const seeds = new Set(placementSeeds);
|
|
599
|
-
const done = new Set();
|
|
600
|
-
/** Place one seed (its left neighbour first, when it is a seed too). */
|
|
601
|
-
const place = (id) => {
|
|
602
|
-
if (done.has(id) || !seeds.has(id))
|
|
603
|
-
return;
|
|
604
|
-
done.add(id);
|
|
605
|
-
if (!blocks.has(id)) {
|
|
606
|
-
if (placementsMap.has(id)) {
|
|
607
|
-
setPlacement(id, undefined);
|
|
608
|
-
edges.delete(id);
|
|
609
|
-
fallen.delete(id);
|
|
610
|
-
rejected.delete(id);
|
|
611
|
-
moved.add(id);
|
|
612
|
-
}
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
const top = candidatesFor(id)[0] ?? null;
|
|
616
|
-
const old = edges.get(id);
|
|
617
|
-
// The same edge, placed at its strongest: its key still orders it (its neighbours' keys are theirs).
|
|
618
|
-
if (top !== null &&
|
|
619
|
-
old &&
|
|
620
|
-
!rejected.has(id) &&
|
|
621
|
-
!fallen.has(id) &&
|
|
622
|
-
placementsMap.has(id) &&
|
|
623
|
-
sameEdge(top, old))
|
|
624
|
-
return;
|
|
625
|
-
for (const x of settle(id, place))
|
|
626
|
-
moved.add(x);
|
|
627
|
-
};
|
|
628
|
-
for (const id of [...seeds])
|
|
629
|
-
place(id);
|
|
630
|
-
// A changed claim owner can close a cycle through a display edge that did not change.
|
|
631
|
-
for (const o of ownerChanged)
|
|
632
|
-
for (const c of [...(rawOrder.get(o) ?? [])]) {
|
|
633
|
-
const edge = edges.get(c.id);
|
|
634
|
-
if (!edge || rejected.has(c.id) || edge.parent === undefined)
|
|
635
|
-
continue;
|
|
636
|
-
if (cycleThrough(placementsMap, ownerOf, c.id, edge.parent) !== null)
|
|
637
|
-
for (const x of settle(c.id, () => { }))
|
|
638
|
-
moved.add(x);
|
|
639
|
-
}
|
|
640
|
-
if (rejected.size + fallen.size > 0 && (moved.size > 0 || ownerChanged.size > 0))
|
|
641
|
-
retryFallen(moved);
|
|
642
|
-
const seeds2 = new Set([...moved, ...stateSeeds, ...ownerChanged]);
|
|
643
|
-
const kinds = [...stateSeeds];
|
|
644
|
-
placementSeeds.clear();
|
|
645
|
-
stateSeeds.clear();
|
|
646
|
-
ownerChanged.clear();
|
|
647
|
-
const affected = reach(seeds2);
|
|
648
|
-
let touched = affected;
|
|
649
|
-
const changed = patch(ix.kids0, ix.slots0, own0, affected);
|
|
650
|
-
let any = changed.size > 0;
|
|
651
|
-
if (layoutMode) {
|
|
652
|
-
const flips = redissolve(changed, kinds);
|
|
653
|
-
const again = flips.size > 0 ? reach([...affected, ...flips]) : affected;
|
|
654
|
-
any = patch(ix.kidsMap, ix.slots, ownShim, again).size > 0 || any;
|
|
655
|
-
touched = again;
|
|
656
|
-
}
|
|
657
|
-
for (const id of touched)
|
|
658
|
-
noteFollowing(id);
|
|
659
|
-
if (any) {
|
|
660
|
-
orderCache = null;
|
|
661
|
-
ix.kidsVersion++;
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
/**
|
|
665
|
-
* A plain block (the document's default kind) directly in a container
|
|
666
|
-
* whose item is a kind of its own that renders content (a list) shows
|
|
667
|
-
* as that item — the read-time side of the facade's `fitted`: a race
|
|
668
|
-
* (a peer's lift of an item another peer's outdent moves, a concurrent
|
|
669
|
-
* undo of a retype) or an explicit write never shows a bare paragraph
|
|
670
|
-
* in a list. Anywhere else it shows `plain`.
|
|
671
|
-
*/
|
|
672
|
-
const itemOf = (under, plain) => {
|
|
673
|
-
const parent = typeOf(under);
|
|
674
|
-
const { container, child } = ix.roles.of(parent);
|
|
675
|
-
return container && ix.roles.of(child).rendersContent ? child : plain;
|
|
676
|
-
};
|
|
677
|
-
/**
|
|
678
|
-
* The kind `id` displays as — THE shown-kind rule, read from its slot
|
|
679
|
-
* and the role table. A block directly in an island declared `lines`
|
|
680
|
-
* shows its line kind. A block of a line kind anywhere else shows its
|
|
681
|
-
* display parent's default child — an undo can put a line a peer
|
|
682
|
-
* retyped back in its island, or leave one a peer moved away outside
|
|
683
|
-
* it — and so does a block displayed out of an island
|
|
684
|
-
* or a container (a list) that still has its default child
|
|
685
|
-
* kind, unless an outer container shows it as one of its
|
|
686
|
-
* items (a nested list's item). A block that renders
|
|
687
|
-
* content never shows as a kind that does not: a line kind
|
|
688
|
-
* then shows the document's default kind. A block stored as the
|
|
689
|
-
* document's default kind directly in a list shows as its item
|
|
690
|
-
* (`itemOf`). Any other shows its stored kind (a retype shows). Read from the stored kinds
|
|
691
|
-
* at call time: a retype rebuilds no placement.
|
|
692
|
-
*/
|
|
693
|
-
const typeOf = (id) => {
|
|
694
|
-
const stored = blocks.get(id)?.type ?? 'unknown';
|
|
695
|
-
const slot = ix.slots.get(id);
|
|
696
|
-
const roles = ix.roles;
|
|
697
|
-
if (roles === null || slot === undefined)
|
|
698
|
-
return stored;
|
|
699
|
-
const { parent: under, reset } = slot;
|
|
700
|
-
const line = under === null || lineKinds.size === 0 ? undefined : lineKind(under);
|
|
701
|
-
if (line !== undefined)
|
|
702
|
-
return line;
|
|
703
|
-
const lined = lineKinds.has(stored);
|
|
704
|
-
const from = reset === undefined ? undefined : (blocks.get(reset)?.type ?? null);
|
|
705
|
-
const plain = roles.of(null).child;
|
|
706
|
-
if (!lined && (from === undefined || stored !== roles.of(from).child))
|
|
707
|
-
return stored === plain && under !== null ? itemOf(under, stored) : stored;
|
|
708
|
-
// Out of a removed list, inside an outer list of its kind: still an item. A
|
|
709
|
-
// container whose item is the document's default kind (a column) holds no
|
|
710
|
-
// items of its own: a paragraph under one is no list's item.
|
|
711
|
-
if (!lined && typeof from === 'string' && roles.of(from).container)
|
|
712
|
-
for (let u = under; u !== null; u = ix.slots.get(u)?.parent ?? null) {
|
|
713
|
-
const t = roles.of(typeOf(u));
|
|
714
|
-
if (t.container && t.child === stored && stored !== plain)
|
|
715
|
-
return stored;
|
|
716
|
-
}
|
|
717
|
-
const kind = roles.of(under === null ? null : typeOf(under)).child;
|
|
718
|
-
if (roles.of(kind).rendersContent || !roles.of(stored).rendersContent)
|
|
719
|
-
return kind;
|
|
720
|
-
// Never a line kind outside its island, even where the slot's kind shows nothing.
|
|
721
|
-
return lined ? plain : stored;
|
|
722
|
-
};
|
|
723
|
-
/** The document order, cached until a child list changes. */
|
|
724
|
-
const order = () => (orderCache ??= documentOrder(ix.kidsMap));
|
|
725
|
-
return {
|
|
726
|
-
ownShim,
|
|
727
|
-
own0,
|
|
728
|
-
noteKind,
|
|
729
|
-
homeFor,
|
|
730
|
-
putSlot,
|
|
731
|
-
edgeParent,
|
|
732
|
-
noteFollowing,
|
|
733
|
-
ensurePlacements,
|
|
734
|
-
reslot,
|
|
735
|
-
typeOf,
|
|
736
|
-
order
|
|
737
|
-
};
|
|
738
|
-
};
|