edytor 3.0.0 → 4.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 +2 -2
- package/dist/cloudflare/DocumentRoom.d.ts +13 -9
- package/dist/cloudflare/DocumentRoom.js +10 -6
- package/dist/cloudflare/room/context.d.ts +27 -26
- package/dist/cloudflare/room/context.js +2 -2
- package/dist/cloudflare/room/purge.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +44 -19
- package/dist/cloudflare/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- package/dist/crdt/attribution/attribution.d.ts +31 -12
- package/dist/crdt/attribution/attribution.js +56 -18
- package/dist/crdt/attribution/block.d.ts +3 -3
- package/dist/crdt/attribution/block.js +7 -8
- package/dist/crdt/attribution/index.d.ts +1 -1
- package/dist/crdt/attribution/index.js +1 -1
- package/dist/crdt/data.d.ts +62 -31
- package/dist/crdt/data.js +211 -103
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.js +29 -24
- package/dist/crdt/doc/gate.d.ts +3 -3
- package/dist/crdt/doc/history.d.ts +25 -9
- package/dist/crdt/doc/history.js +150 -40
- package/dist/crdt/doc/layout.js +11 -22
- package/dist/crdt/doc/meta.js +12 -17
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.js +2 -2
- package/dist/crdt/doc/seed.js +2 -2
- package/dist/crdt/doc/split-merge.js +12 -15
- package/dist/crdt/doc/steps.d.ts +25 -13
- package/dist/crdt/doc/steps.js +52 -122
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +16 -5
- package/dist/crdt/document.js +21 -4
- package/dist/crdt/edytor-doc.d.ts +62 -64
- package/dist/crdt/edytor-doc.js +18 -13
- package/dist/crdt/engine-api.d.ts +33 -1
- package/dist/crdt/engine.d.ts +0 -1
- package/dist/crdt/engine.js +1 -4
- package/dist/crdt/flow.d.ts +1 -1
- package/dist/crdt/flow.js +25 -27
- package/dist/crdt/index.d.ts +26 -25
- package/dist/crdt/index.js +1 -1
- package/dist/crdt/placement/display.d.ts +5 -30
- package/dist/crdt/placement/display.js +39 -144
- package/dist/crdt/placement/keys.d.ts +58 -0
- package/dist/crdt/placement/keys.js +90 -0
- package/dist/crdt/placement/model.d.ts +119 -118
- package/dist/crdt/placement/model.js +242 -142
- package/dist/crdt/placement/resolve.d.ts +123 -35
- package/dist/crdt/placement/resolve.js +187 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -0
- package/dist/crdt/providers/room.d.ts +16 -2
- package/dist/crdt/providers/room.js +64 -17
- package/dist/crdt/providers/websocket.js +5 -3
- package/dist/crdt/purge.d.ts +17 -8
- package/dist/crdt/purge.js +236 -126
- package/dist/crdt/restore.d.ts +2 -2
- package/dist/crdt/restore.js +18 -34
- package/dist/crdt/schema.d.ts +22 -21
- package/dist/crdt/schema.js +29 -23
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +15 -15
- package/dist/crdt/structs.js +8 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +12 -0
- package/dist/crdt/text/index/fold.d.ts +2 -3
- package/dist/crdt/text/index/fold.js +194 -44
- package/dist/crdt/text/index/layout.js +5 -8
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +404 -138
- package/dist/crdt/text/index/records.d.ts +3 -3
- package/dist/crdt/text/index/records.js +24 -31
- package/dist/crdt/text/index/shared.d.ts +11 -3
- package/dist/crdt/text/index/shared.js +12 -3
- package/dist/crdt/text/index/state.d.ts +21 -17
- package/dist/crdt/text/index/state.js +15 -17
- package/dist/crdt/text/index/streams.d.ts +10 -5
- package/dist/crdt/text/index/streams.js +78 -27
- package/dist/crdt/text/items.d.ts +10 -3
- package/dist/crdt/text/items.js +7 -0
- package/dist/crdt/text/model.d.ts +77 -62
- package/dist/crdt/text/model.js +84 -169
- package/dist/crdt/text/ranges.d.ts +1 -1
- package/dist/crdt/text/ranges.js +3 -3
- package/dist/crdt/text/rows.d.ts +13 -7
- package/dist/crdt/text/rows.js +24 -7
- package/dist/crdt/text/runs.d.ts +17 -12
- package/dist/crdt/text/runs.js +24 -7
- package/dist/crdt/text/streams.d.ts +26 -13
- package/dist/crdt/text/streams.js +41 -14
- package/dist/crdt/vendor/yjs/UPSTREAM.md +568 -21
- package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +194 -10
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
- package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
- package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
- package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
- package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +43 -7
- package/dist/crdt/vendor/yjs/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
- package/dist/crdt/vendor/yjs/src/structs/Item.js +610 -51
- package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
- package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
- package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
- package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
- package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
- package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
- package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
- package/dist/crdt/vendor/yjs/src/ynode.js +177 -12
- package/dist/edytor.svelte.js +4 -1
- package/dist/events/beforeInputCommands.js +2 -2
- package/dist/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- package/dist/export/forms.d.ts +25 -25
- package/dist/surface/observer.svelte.js +2 -2
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +59 -8
- package/package.json +2 -2
- package/dist/crdt/placement/rank.d.ts +0 -87
- package/dist/crdt/placement/rank.js +0 -332
- package/dist/crdt/placement/source.d.ts +0 -43
- package/dist/crdt/placement/source.js +0 -66
- package/dist/crdt/text/delete-index.d.ts +0 -129
- package/dist/crdt/text/delete-index.js +0 -365
- package/dist/crdt/text/deletes.d.ts +0 -76
- package/dist/crdt/text/deletes.js +0 -362
- package/dist/crdt/text/index/anchored.d.ts +0 -17
- package/dist/crdt/text/index/anchored.js +0 -197
|
@@ -13,15 +13,21 @@
|
|
|
13
13
|
* - It ends at the next delimiting boundary of the same text, or at the text
|
|
14
14
|
* end. A boundary whose nonce matches no record is inert: skipped by reads,
|
|
15
15
|
* never a delimiter.
|
|
16
|
-
* -
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* - A merge writes one CLAIM ITEM `{m}` (one countable unit, never
|
|
17
|
+
* displayed) at the end of the gap at the end of the holder's stream (fork
|
|
18
|
+
* patch YP21: among the items of one origin it sorts after content and
|
|
19
|
+
* boundaries, so YATA decides which piece of a concurrent split keeps it).
|
|
20
|
+
* A block's claims are the claim items of its stream, in text order.
|
|
21
|
+
* - `display(b)` = `stream(b)`, then `display(m)` for each claim `{m}` of `b`
|
|
22
|
+
* whose max-stamp live claimer is `b`. Owner resolution over claims
|
|
23
|
+
* (`claimGraph`: max-stamp claim, cycles, claims held by deleted blocks are
|
|
24
|
+
* inert) is unchanged.
|
|
20
25
|
*
|
|
21
26
|
* Every position therefore lies in exactly one stream: the block that displays
|
|
22
27
|
* it is a pure function of replicated items and is never repaired. Typing at a
|
|
23
28
|
* stream's start inserts after its boundary, at its end before the next one;
|
|
24
|
-
* the per-stream delete never removes a boundary; every read skips
|
|
29
|
+
* the per-stream delete never removes a boundary or a claim; every read skips
|
|
30
|
+
* them.
|
|
25
31
|
*
|
|
26
32
|
* Anchors are `{b, a}`: `b` the home block of the backing text, `a` an
|
|
27
33
|
* engine relative position (`i` the bound item, `a` the side: `< 0` left). A
|
|
@@ -36,9 +42,9 @@
|
|
|
36
42
|
*/
|
|
37
43
|
import type { EngineApi, EngineDoc, EngineNode } from '../engine-api.js';
|
|
38
44
|
import { type IdSetOf } from '../structs.js';
|
|
39
|
-
export { isBoundary, nodeStart } from './items.js';
|
|
45
|
+
export { isBoundary, isClaimItem, isRowMark, nodeStart } from './items.js';
|
|
40
46
|
export { canonKey, deepFreeze, inlineItemOf, protectItems, readRange } from './ranges.js';
|
|
41
|
-
export { byId, delimiters, displayOf, locate, ownedLength, pieces, placeText, scanText } from './streams.js';
|
|
47
|
+
export { byId, claimsIn, delimiters, displayOf, locate, ownedLength, pieces, placeText, removedKeys, scanText } from './streams.js';
|
|
42
48
|
export type BlockId = string;
|
|
43
49
|
/** A serialized relative position inside a backing text (`i: null`: a text end). */
|
|
44
50
|
export type Anchor = {
|
|
@@ -48,26 +54,15 @@ export type Anchor = {
|
|
|
48
54
|
} | null;
|
|
49
55
|
a: number;
|
|
50
56
|
};
|
|
51
|
-
/** An engine item id as a claim payload stores it. */
|
|
52
|
-
export type ItemRef = {
|
|
53
|
-
c: number;
|
|
54
|
-
k: number;
|
|
55
|
-
};
|
|
56
57
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* of an own text) and `r` the item right after it (the next stream's
|
|
62
|
-
* boundary; `null` at the text's end). A split the writer did not see moves
|
|
63
|
-
* the claim to the piece that ends the region between them.
|
|
58
|
+
* A claim item's payload: "the display of the block whose stream holds me
|
|
59
|
+
* continues with block `m`'s" (`merge.claim.anchor`: YATA places it, so a
|
|
60
|
+
* split its writer did not see leaves it in the piece that ends the
|
|
61
|
+
* holder's region).
|
|
64
62
|
*/
|
|
65
63
|
export type MergeClaim = {
|
|
66
64
|
m: BlockId;
|
|
67
|
-
a?: ItemRef;
|
|
68
|
-
r?: ItemRef | null;
|
|
69
65
|
};
|
|
70
|
-
export declare const isMergeClaim: (v: unknown) => v is MergeClaim;
|
|
71
66
|
/** Deterministic claim stamp = engine item id (client-major order). */
|
|
72
67
|
export type Stamp = {
|
|
73
68
|
c: number;
|
|
@@ -75,19 +70,13 @@ export type Stamp = {
|
|
|
75
70
|
};
|
|
76
71
|
export declare const cmpStamp: (a: Stamp, b: Stamp) => number;
|
|
77
72
|
/**
|
|
78
|
-
* One live merge claim:
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* no list stores (`id.same.concurrent`). `a`/`r`: its anchor (see
|
|
82
|
-
* {@link MergeClaim}).
|
|
73
|
+
* One live merge claim: its stamp is its claim item's id; an implicit
|
|
74
|
+
* claim, which no item stores (`id.same.concurrent`), stamps below every
|
|
75
|
+
* item (`stamp.c < 0`).
|
|
83
76
|
*/
|
|
84
77
|
export type Claim = {
|
|
85
78
|
m: BlockId;
|
|
86
79
|
stamp: Stamp;
|
|
87
|
-
seqIndex: number;
|
|
88
|
-
a?: ItemRef;
|
|
89
|
-
r?: ItemRef | null;
|
|
90
|
-
holder?: BlockId;
|
|
91
80
|
};
|
|
92
81
|
/** Boundary-item payload in a backing text. */
|
|
93
82
|
export type Boundary = {
|
|
@@ -111,8 +100,6 @@ export type SeqItem = {
|
|
|
111
100
|
type?: EngineNode;
|
|
112
101
|
};
|
|
113
102
|
};
|
|
114
|
-
/** The live merge claims of a `claims` list, with their stamps. */
|
|
115
|
-
export declare const readClaims: (node: EngineNode | undefined) => Claim[];
|
|
116
103
|
/** One emitted range-read element — the `ContentItem` shape. */
|
|
117
104
|
export type RangeItem = {
|
|
118
105
|
kind: 'text';
|
|
@@ -164,16 +151,10 @@ export type TextBlockRec = {
|
|
|
164
151
|
n: unknown;
|
|
165
152
|
/** The block's own backing text, when it has one. */
|
|
166
153
|
content: EngineNode | undefined;
|
|
167
|
-
/**
|
|
168
|
-
claimsNode: EngineNode | undefined;
|
|
169
|
-
/**
|
|
170
|
-
* Its effective merge claims (`merge.claim.anchor`): its list's claims
|
|
171
|
-
* that stay with it, in list order, then those other holders' anchors
|
|
172
|
-
* moved to it (each with its `holder`), by stamp.
|
|
173
|
-
*/
|
|
154
|
+
/** Its merge claims: its implicit ones, then the claim items of its stream, in text order. */
|
|
174
155
|
claims: Claim[];
|
|
175
|
-
/** Its
|
|
176
|
-
|
|
156
|
+
/** Its implicit claims (`id.same.concurrent`), which no item stores. */
|
|
157
|
+
implicit?: Claim[];
|
|
177
158
|
};
|
|
178
159
|
/** The internal "no display owner" verdict — a symbol, never a block id. */
|
|
179
160
|
export declare const DEAD: unique symbol;
|
|
@@ -191,12 +172,23 @@ export declare const claimGraph: (blocks: ReadonlyMap<BlockId, TextBlockRec>) =>
|
|
|
191
172
|
};
|
|
192
173
|
/** `owner(b)` for every block (see {@link claimGraph}). */
|
|
193
174
|
export declare const computeOwners: (blocks: ReadonlyMap<BlockId, TextBlockRec>) => Map<BlockId, Owner>;
|
|
194
|
-
/**
|
|
195
|
-
|
|
175
|
+
/**
|
|
176
|
+
* A unit a row tracks at live index `at` of its text (`key`: its item id): a
|
|
177
|
+
* live boundary, or a live claim item (`m`, with its stamp), which never
|
|
178
|
+
* delimits.
|
|
179
|
+
*/
|
|
180
|
+
export type Bound = {
|
|
196
181
|
at: number;
|
|
197
182
|
key: string;
|
|
198
|
-
}
|
|
199
|
-
|
|
183
|
+
} & ((Boundary & {
|
|
184
|
+
m?: undefined;
|
|
185
|
+
}) | {
|
|
186
|
+
m: BlockId;
|
|
187
|
+
stamp: Stamp;
|
|
188
|
+
s?: undefined;
|
|
189
|
+
n?: undefined;
|
|
190
|
+
});
|
|
191
|
+
/** One scanned backing text: its live boundaries and claim items in order, and its live length. */
|
|
200
192
|
export type TextRow = {
|
|
201
193
|
home: BlockId;
|
|
202
194
|
text: EngineNode;
|
|
@@ -204,7 +196,7 @@ export type TextRow = {
|
|
|
204
196
|
len: number;
|
|
205
197
|
key: string;
|
|
206
198
|
};
|
|
207
|
-
/** One stream: `[start, end)` of `home`'s text, minus the inert boundaries at `inert`. */
|
|
199
|
+
/** One stream: `[start, end)` of `home`'s text, minus the inert boundaries and claim items at `inert`. */
|
|
208
200
|
export type Stream = {
|
|
209
201
|
block: BlockId;
|
|
210
202
|
home: BlockId;
|
|
@@ -232,6 +224,8 @@ export type Ownership = {
|
|
|
232
224
|
hidden: (b: BlockId) => boolean;
|
|
233
225
|
/** The claimed block's max-stamp live claimer. */
|
|
234
226
|
top: (m: BlockId) => BlockId | undefined;
|
|
227
|
+
/** The position of `m` among `holder`'s claims (`-1`: none): the order the claimed blocks' children show in. */
|
|
228
|
+
claimIndex: (holder: BlockId, m: BlockId) => number;
|
|
235
229
|
streamOf: (b: BlockId) => Stream | undefined;
|
|
236
230
|
/** The streams of `home`'s text, in text order. */
|
|
237
231
|
streamsIn: (home: BlockId) => readonly Stream[];
|
|
@@ -239,8 +233,6 @@ export type Ownership = {
|
|
|
239
233
|
streamAt: (home: BlockId, i: number) => Stream | undefined;
|
|
240
234
|
/** `display(b)` as boundary-free pieces in reading order; `null` when hidden or dead. */
|
|
241
235
|
display: (b: BlockId) => Seg[] | null;
|
|
242
|
-
/** `b`'s effective claims (`merge.claim.anchor`), when the context computed them. */
|
|
243
|
-
claimsOf?: (b: BlockId) => readonly Claim[];
|
|
244
236
|
};
|
|
245
237
|
/** Construct a detached v14 node, viewed through the structural interface. */
|
|
246
238
|
export declare const newNode: (name: string) => EngineNode;
|
|
@@ -257,6 +249,11 @@ export declare const openRangeCursor: (text: EngineNode) => RangeCursor;
|
|
|
257
249
|
export declare const itemsOfRange: (text: EngineNode, i0: number, i1: number) => RangeItem[];
|
|
258
250
|
/** The items of `segs`, one cursor per backing text. */
|
|
259
251
|
export declare const readSegs: (segs: readonly Seg[], stats?: RangeReadStats) => RangeItem[];
|
|
252
|
+
/**
|
|
253
|
+
* A from-scratch ownership context over `given` (every text scanned; the
|
|
254
|
+
* index's oracle): each block's claims are its implicit ones, then the claim
|
|
255
|
+
* items of its stream.
|
|
256
|
+
*/
|
|
260
257
|
export declare const computeOwnership: (doc: EngineDoc, given: ReadonlyMap<BlockId, TextBlockRec>) => Ownership;
|
|
261
258
|
/** `b`'s display pieces (`[]` when hidden or streamless and claimless). */
|
|
262
259
|
export declare const flatten: (b: BlockId, blocks: unknown, own: Ownership) => Seg[];
|
|
@@ -271,7 +268,14 @@ export declare const blockTextOf: (b: BlockId, blocks: unknown, own: Ownership)
|
|
|
271
268
|
* `marks` (an operation for each mark its gap gave it otherwise).
|
|
272
269
|
*/
|
|
273
270
|
export declare const insertIntoText: (doc: EngineDoc, blocks: unknown, own: Ownership, b: BlockId, offset: number, payload: string | EngineNode, marks?: Record<string, unknown>) => void;
|
|
274
|
-
/**
|
|
271
|
+
/**
|
|
272
|
+
* The per-stream delete: each range lies inside one stream's pieces, so no
|
|
273
|
+
* boundary is ever removed. The text is held by this writer's record (fork
|
|
274
|
+
* patch YP20, root `textdel`, in the history's scope): an undo deletes the
|
|
275
|
+
* record and the text shows again where it was, unless another writer's
|
|
276
|
+
* record holds it. Text the open undo step inserted is removed instead, so
|
|
277
|
+
* that step's undo still changes nothing.
|
|
278
|
+
*/
|
|
275
279
|
export declare const deleteRange: (doc: EngineDoc, blocks: unknown, own: Ownership, b: BlockId, offset: number, length: number) => void;
|
|
276
280
|
export declare const formatRangeIn: (doc: EngineDoc, blocks: unknown, own: Ownership, b: BlockId, offset: number, length: number, formats: Record<string, unknown>) => void;
|
|
277
281
|
/**
|
|
@@ -290,19 +294,25 @@ export declare const findAtom: (own: Ownership, b: BlockId, inlineId: string) =>
|
|
|
290
294
|
} | null;
|
|
291
295
|
/**
|
|
292
296
|
* Split `b`'s display at `offset` for the new block `newId` (incarnation
|
|
293
|
-
* `n`): one boundary through fork patch YP7 at the end of the gap
|
|
294
|
-
* follow the split point — every claim of the stream's own
|
|
295
|
-
* holder's claims after the one that led down —
|
|
296
|
-
*
|
|
297
|
+
* `n`): one boundary through fork patch YP7 at the end of the gap. The
|
|
298
|
+
* claims that follow the split point — every claim of the stream's own
|
|
299
|
+
* block, then each holder's claims after the one that led down — are
|
|
300
|
+
* written again, in that order, at the end of the new block's stream, and
|
|
301
|
+
* their items deleted (an implicit claim is written on the new block, whose
|
|
302
|
+
* claim outranks it). No text is copied.
|
|
297
303
|
*/
|
|
298
|
-
export declare const splitAt: (blocks: ReadonlyMap<BlockId, TextBlockRec>, own: Ownership, b: BlockId, offset: number, newId: BlockId, n: number) =>
|
|
304
|
+
export declare const splitAt: (doc: EngineDoc, blocks: ReadonlyMap<BlockId, TextBlockRec>, own: Ownership, b: BlockId, offset: number, newId: BlockId, n: number) => void;
|
|
299
305
|
/**
|
|
300
|
-
*
|
|
301
|
-
* the end of `into`'s stream (
|
|
302
|
-
*
|
|
303
|
-
*
|
|
306
|
+
* Merge `from` into `into`: one claim item `{m: from}` at the end of the gap
|
|
307
|
+
* at the end of `into`'s stream (fork patch YP7; YP21 sorts it after the
|
|
308
|
+
* content and boundaries a concurrent writer puts at its origin, so a split
|
|
309
|
+
* the merge's writer did not see leaves it in the piece that ends `into`'s
|
|
310
|
+
* region: `merge.claim.anchor`). `into` has a stream: the caller gives a
|
|
311
|
+
* streamless block its own text first.
|
|
304
312
|
*/
|
|
305
|
-
export declare const claimInto: (
|
|
313
|
+
export declare const claimInto: (own: Pick<Ownership, "streamOf">, from: BlockId, into: BlockId) => void;
|
|
314
|
+
/** Delete the items of `claims` (an implicit claim has none). */
|
|
315
|
+
export declare const deleteClaims: (doc: EngineDoc, claims: readonly Claim[]) => void;
|
|
306
316
|
/**
|
|
307
317
|
* Give streamless block `rec` its own text and re-mint its nonce, so a late
|
|
308
318
|
* copy of its old boundary is inert. Both writes carry a writer id
|
|
@@ -340,8 +350,8 @@ export declare const bindText: (_Y: EngineApi) => {
|
|
|
340
350
|
at: number;
|
|
341
351
|
node: EngineNode;
|
|
342
352
|
} | null;
|
|
343
|
-
splitAt: (blocks: ReadonlyMap<BlockId, TextBlockRec>, own: Ownership, b: BlockId, offset: number, newId: BlockId, n: number) =>
|
|
344
|
-
claimInto: (
|
|
353
|
+
splitAt: (doc: EngineDoc, blocks: ReadonlyMap<BlockId, TextBlockRec>, own: Ownership, b: BlockId, offset: number, newId: BlockId, n: number) => void;
|
|
354
|
+
claimInto: (own: Pick<Ownership, "streamOf">, from: BlockId, into: BlockId) => void;
|
|
345
355
|
ownText: (doc: EngineDoc, rec: TextBlockRec) => {
|
|
346
356
|
from: unknown;
|
|
347
357
|
to: number;
|
|
@@ -353,4 +363,9 @@ export declare const bindText: (_Y: EngineApi) => {
|
|
|
353
363
|
* typing and keeps the text every replica shares.
|
|
354
364
|
*/
|
|
355
365
|
export declare const ownTextIds: WeakMap<EngineDoc, IdSetOf>;
|
|
366
|
+
/**
|
|
367
|
+
* Each history's open step on a doc (`doc/history.ts` registers them): what
|
|
368
|
+
* the step still capturing inserted, `null` when none is open.
|
|
369
|
+
*/
|
|
370
|
+
export declare const openSteps: WeakMap<EngineDoc, Set<() => IdSetOf | null>>;
|
|
356
371
|
export type TextEngine = ReturnType<typeof bindText>;
|
package/dist/crdt/text/model.js
CHANGED
|
@@ -1,45 +1,16 @@
|
|
|
1
1
|
import { Y } from '../engine.js';
|
|
2
2
|
import { asYDoc, asYNode, attrItems, engineOps, newNode as makeNode, rangeCursorOf, storeOf } from '../structs.js';
|
|
3
|
-
import { CONTENT, CONTENT_NODE, ID, NONCE, SCHEMA } from '../schema.js';
|
|
3
|
+
import { CONTENT, CONTENT_NODE, ID, NONCE, SCHEMA, TEXT_DELETES_ROOT } from '../schema.js';
|
|
4
4
|
import { DEV } from 'esm-env';
|
|
5
5
|
import { hash32, hash53 } from '../rand.js';
|
|
6
|
-
import { bindDeletes } from './deletes.js';
|
|
7
6
|
import { fitMarks, writeMarks } from './marks.js';
|
|
8
7
|
import { nodeStart } from './items.js';
|
|
9
8
|
import { readRange } from './ranges.js';
|
|
10
|
-
import { delimiters, displayOf, locate, ownedLength, pieces, placeText, rangesOf, scanText } from './streams.js';
|
|
11
|
-
export { isBoundary, nodeStart } from './items.js';
|
|
9
|
+
import { claimsIn, delimiters, displayOf, locate, ownedLength, pieces, placeText, rangesOf, removedKeys, scanText } from './streams.js';
|
|
10
|
+
export { isBoundary, isClaimItem, isRowMark, nodeStart } from './items.js';
|
|
12
11
|
export { canonKey, deepFreeze, inlineItemOf, protectItems, readRange } from './ranges.js';
|
|
13
|
-
export { byId, delimiters, displayOf, locate, ownedLength, pieces, placeText, scanText } from './streams.js';
|
|
14
|
-
const isItemRef = (v) => v != null &&
|
|
15
|
-
typeof v === 'object' &&
|
|
16
|
-
Number.isInteger(v.c) &&
|
|
17
|
-
Number.isInteger(v.k);
|
|
18
|
-
export const isMergeClaim = (v) => v != null && typeof v === 'object' && typeof v.m === 'string';
|
|
12
|
+
export { byId, claimsIn, delimiters, displayOf, locate, ownedLength, pieces, placeText, removedKeys, scanText } from './streams.js';
|
|
19
13
|
export const cmpStamp = (a, b) => a.c - b.c || a.k - b.k;
|
|
20
|
-
/** The live merge claims of a `claims` list, with their stamps. */
|
|
21
|
-
export const readClaims = (node) => {
|
|
22
|
-
const out = [];
|
|
23
|
-
let seqIndex = 0;
|
|
24
|
-
for (let it = node ? nodeStart(node) : null; it !== null; it = it.right) {
|
|
25
|
-
if (it.deleted || it.countable === false)
|
|
26
|
-
continue;
|
|
27
|
-
const arr = it.content.getContent();
|
|
28
|
-
for (let j = 0; j < it.length; j++) {
|
|
29
|
-
const p = arr[j];
|
|
30
|
-
if (isMergeClaim(p)) {
|
|
31
|
-
const claim = { m: p.m, stamp: { c: it.id.client, k: it.id.clock + j }, seqIndex };
|
|
32
|
-
if (isItemRef(p.a)) {
|
|
33
|
-
claim.a = p.a;
|
|
34
|
-
claim.r = isItemRef(p.r) ? p.r : null;
|
|
35
|
-
}
|
|
36
|
-
out.push(claim);
|
|
37
|
-
}
|
|
38
|
-
seqIndex++;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return out;
|
|
42
|
-
};
|
|
43
14
|
/** The internal "no display owner" verdict — a symbol, never a block id. */
|
|
44
15
|
export const DEAD = Symbol('edytor.crdt.dead');
|
|
45
16
|
/**
|
|
@@ -106,7 +77,6 @@ export const claimGraph = (blocks) => {
|
|
|
106
77
|
};
|
|
107
78
|
/** `owner(b)` for every block (see {@link claimGraph}). */
|
|
108
79
|
export const computeOwners = (blocks) => claimGraph(blocks).owners;
|
|
109
|
-
const D = bindDeletes(Y);
|
|
110
80
|
const ops = engineOps(Y);
|
|
111
81
|
/** Construct a detached v14 node, viewed through the structural interface. */
|
|
112
82
|
export const newNode = (name) => makeNode(Y, name);
|
|
@@ -144,116 +114,44 @@ export const readSegs = (segs, stats) => {
|
|
|
144
114
|
}
|
|
145
115
|
return out;
|
|
146
116
|
};
|
|
147
|
-
/** A from-scratch ownership context over `blocks` (every text scanned). */
|
|
148
117
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* segment when `r` is null) in the row of its holder's stream, when that
|
|
153
|
-
* segment is the holder's or a later one.
|
|
118
|
+
* A from-scratch ownership context over `given` (every text scanned; the
|
|
119
|
+
* index's oracle): each block's claims are its implicit ones, then the claim
|
|
120
|
+
* items of its stream.
|
|
154
121
|
*/
|
|
155
|
-
const effectiveClaims = (doc, blocks, rows, delim, streams) => {
|
|
156
|
-
/** Live index of unit `ref` (assoc 0) in `text`, or `null` when it is not `text`'s. */
|
|
157
|
-
const indexIn = (text, ref) => {
|
|
158
|
-
if (!text._item?.id)
|
|
159
|
-
return null;
|
|
160
|
-
const abs = Y.indexOfId(asYDoc(doc), Y.createID(ref.c, ref.k));
|
|
161
|
-
return abs === null || abs.type !== text ? null : abs.index;
|
|
162
|
-
};
|
|
163
|
-
const targetOf = (holder, c) => {
|
|
164
|
-
if (c.a === undefined)
|
|
165
|
-
return holder;
|
|
166
|
-
const s = streams.get(holder);
|
|
167
|
-
const row = s && rows.get(s.text);
|
|
168
|
-
if (row === undefined || indexIn(row.text, c.a) === null)
|
|
169
|
-
return holder;
|
|
170
|
-
const cuts = [];
|
|
171
|
-
row.bounds.forEach((b, j) => {
|
|
172
|
-
if (delim.get(b.s) === b.key)
|
|
173
|
-
cuts.push(j);
|
|
174
|
-
});
|
|
175
|
-
const head = delim.has(row.home) ? null : row.home;
|
|
176
|
-
// The gap before `r`: a live boundary's own index, else the boundaries before it.
|
|
177
|
-
let gap = row.bounds.length;
|
|
178
|
-
if (c.r != null) {
|
|
179
|
-
const key = `${c.r.c}:${c.r.k}`;
|
|
180
|
-
const at = row.bounds.findIndex((b) => b.key === key);
|
|
181
|
-
if (at >= 0)
|
|
182
|
-
gap = at;
|
|
183
|
-
else {
|
|
184
|
-
const p = indexIn(row.text, c.r);
|
|
185
|
-
if (p === null)
|
|
186
|
-
return holder;
|
|
187
|
-
gap = row.bounds.filter((b) => b.at < p).length;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
const segment = (g) => cuts.filter((j) => j < g).length;
|
|
191
|
-
const k = segment(gap);
|
|
192
|
-
// The holder's own segment: the one its stream starts in.
|
|
193
|
-
const k0 = segment(row.bounds.filter((b) => b.at < s.start).length);
|
|
194
|
-
if (k < k0)
|
|
195
|
-
return holder;
|
|
196
|
-
return (k === 0 ? head : row.bounds[cuts[k - 1]].s) ?? holder;
|
|
197
|
-
};
|
|
198
|
-
const own = new Map();
|
|
199
|
-
const moved = new Map();
|
|
200
|
-
for (const [h, rec] of blocks) {
|
|
201
|
-
const stays = [];
|
|
202
|
-
for (const c of rec.listClaims ?? rec.claims) {
|
|
203
|
-
const t = targetOf(h, c);
|
|
204
|
-
if (t === h)
|
|
205
|
-
stays.push(c);
|
|
206
|
-
else {
|
|
207
|
-
let into = moved.get(t);
|
|
208
|
-
if (into === undefined)
|
|
209
|
-
moved.set(t, (into = []));
|
|
210
|
-
into.push({ ...c, holder: h });
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
own.set(h, stays);
|
|
214
|
-
}
|
|
215
|
-
for (const [t, list] of moved) {
|
|
216
|
-
if (!own.has(t))
|
|
217
|
-
continue;
|
|
218
|
-
list.sort((x, y) => cmpStamp(x.stamp, y.stamp));
|
|
219
|
-
own.set(t, [...own.get(t), ...list]);
|
|
220
|
-
}
|
|
221
|
-
return own;
|
|
222
|
-
};
|
|
223
122
|
export const computeOwnership = (doc, given) => {
|
|
224
123
|
const rows = [];
|
|
225
124
|
for (const rec of given.values())
|
|
226
125
|
if (rec.content)
|
|
227
126
|
rows.push(scanText(rec.id, rec.content));
|
|
228
|
-
const delim = delimiters(given, rows);
|
|
127
|
+
const delim = delimiters(given, rows, removedKeys(doc.get(SCHEMA.roots.registry)));
|
|
229
128
|
const streams = new Map();
|
|
230
129
|
const inText = new Map();
|
|
130
|
+
const rowOf = new Map();
|
|
231
131
|
for (const row of rows) {
|
|
232
|
-
const list = placeText(row, delim);
|
|
132
|
+
const list = placeText(row, delim, (s) => !given.has(s));
|
|
233
133
|
inText.set(row.home, list);
|
|
134
|
+
rowOf.set(row.text, row);
|
|
234
135
|
for (const s of list)
|
|
235
136
|
streams.set(s.block, s);
|
|
236
137
|
}
|
|
237
|
-
// The records with their effective claims (`merge.claim.anchor`).
|
|
238
|
-
const effective = effectiveClaims(doc, given, new Map(rows.map((row) => [row.text, row])), delim, streams);
|
|
239
138
|
const blocks = new Map();
|
|
240
|
-
for (const [id, rec] of given)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
});
|
|
139
|
+
for (const [id, rec] of given) {
|
|
140
|
+
const s = streams.get(id);
|
|
141
|
+
const items = s === undefined ? [] : claimsIn(rowOf.get(s.text), s);
|
|
142
|
+
blocks.set(id, { ...rec, claims: [...(rec.implicit ?? []), ...items] });
|
|
143
|
+
}
|
|
246
144
|
const { owners, top } = claimGraph(blocks);
|
|
247
145
|
const ownerOf = (b) => owners.get(b) ?? DEAD;
|
|
248
146
|
const own = {
|
|
249
147
|
ownerOf,
|
|
250
148
|
hidden: (b) => ownerOf(b) !== b,
|
|
251
149
|
top: (m) => top.get(m),
|
|
150
|
+
claimIndex: (holder, m) => blocks.get(holder)?.claims.findIndex((c) => c.m === m) ?? -1,
|
|
252
151
|
streamOf: (b) => streams.get(b),
|
|
253
152
|
streamsIn: (home) => inText.get(home) ?? [],
|
|
254
153
|
streamAt: (home, i) => (inText.get(home) ?? []).find((x) => x.start <= i && i <= x.end),
|
|
255
|
-
display: (b) => displayOf(b, blocks, own)
|
|
256
|
-
claimsOf: (b) => blocks.get(b)?.claims ?? []
|
|
154
|
+
display: (b) => displayOf(b, blocks, own)
|
|
257
155
|
};
|
|
258
156
|
return own;
|
|
259
157
|
};
|
|
@@ -288,16 +186,19 @@ export const insertIntoText = (doc, blocks, own, b, offset, payload, marks) => {
|
|
|
288
186
|
const have = at.find((p) => !p.deleted && p.len > 0)?.formats;
|
|
289
187
|
fitMarks(doc, text, hit.idx, payload.length, have, marks);
|
|
290
188
|
};
|
|
291
|
-
/**
|
|
189
|
+
/**
|
|
190
|
+
* The per-stream delete: each range lies inside one stream's pieces, so no
|
|
191
|
+
* boundary is ever removed. The text is held by this writer's record (fork
|
|
192
|
+
* patch YP20, root `textdel`, in the history's scope): an undo deletes the
|
|
193
|
+
* record and the text shows again where it was, unless another writer's
|
|
194
|
+
* record holds it. Text the open undo step inserted is removed instead, so
|
|
195
|
+
* that step's undo still changes nothing.
|
|
196
|
+
*/
|
|
292
197
|
export const deleteRange = (doc, blocks, own, b, offset, length) => {
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
spans.push({ c: p.id.client, k: p.id.clock, n: p.len });
|
|
298
|
-
r.text.delete(r.a, r.b - r.a);
|
|
299
|
-
}
|
|
300
|
-
D.markDeleted(doc, spans);
|
|
198
|
+
const records = doc.get(TEXT_DELETES_ROOT);
|
|
199
|
+
const fresh = openInserts(doc);
|
|
200
|
+
for (const r of rangesOf(flatten(b, blocks, own), offset, offset + length))
|
|
201
|
+
r.text.delete(r.a, r.b - r.a, records, fresh);
|
|
301
202
|
};
|
|
302
203
|
export const formatRangeIn = (doc, blocks, own, b, offset, length, formats) => {
|
|
303
204
|
for (const r of rangesOf(flatten(b, blocks, own), offset, offset + length))
|
|
@@ -337,60 +238,52 @@ export const findAtom = (own, b, inlineId) => {
|
|
|
337
238
|
};
|
|
338
239
|
/**
|
|
339
240
|
* Split `b`'s display at `offset` for the new block `newId` (incarnation
|
|
340
|
-
* `n`): one boundary through fork patch YP7 at the end of the gap
|
|
341
|
-
* follow the split point — every claim of the stream's own
|
|
342
|
-
* holder's claims after the one that led down —
|
|
343
|
-
*
|
|
241
|
+
* `n`): one boundary through fork patch YP7 at the end of the gap. The
|
|
242
|
+
* claims that follow the split point — every claim of the stream's own
|
|
243
|
+
* block, then each holder's claims after the one that led down — are
|
|
244
|
+
* written again, in that order, at the end of the new block's stream, and
|
|
245
|
+
* their items deleted (an implicit claim is written on the new block, whose
|
|
246
|
+
* claim outranks it). No text is copied.
|
|
344
247
|
*/
|
|
345
|
-
export const splitAt = (blocks, own, b, offset, newId, n) => {
|
|
248
|
+
export const splitAt = (doc, blocks, own, b, offset, newId, n) => {
|
|
346
249
|
const segs = flatten(b, blocks, own);
|
|
347
250
|
const hit = locate(segs, Math.max(0, Math.min(offset, ownedLength(segs))));
|
|
348
251
|
if (hit === null)
|
|
349
252
|
throw new Error(`splitAt: "${b}" has no stream`);
|
|
350
253
|
const { seg, idx } = hit;
|
|
351
|
-
const moved = [];
|
|
352
|
-
// A claim another block stores (its anchor moved it here) is deleted from that list.
|
|
353
|
-
const after = (holder, from) => blocks
|
|
354
|
-
.get(holder)
|
|
355
|
-
.claims.slice(from)
|
|
356
|
-
.forEach((c) => moved.push({ holder: c.holder ?? holder, claim: c }));
|
|
357
|
-
after(seg.block, 0);
|
|
254
|
+
const moved = [...blocks.get(seg.block).claims];
|
|
358
255
|
for (let i = seg.path.length - 1; i >= 0; i--)
|
|
359
|
-
|
|
256
|
+
moved.push(...blocks.get(seg.path[i].holder).claims.slice(seg.path[i].entry + 1));
|
|
360
257
|
const text = seg.text;
|
|
258
|
+
// The new block's stream ends where the source's did, one boundary later.
|
|
259
|
+
const end = own.streamOf(seg.block).end + 1;
|
|
361
260
|
text.insertAtGapEnd(idx, [{ s: newId, n }]);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (claim.seqIndex >= 0)
|
|
367
|
-
byHolder.set(holder, [...(byHolder.get(holder) ?? []), claim.seqIndex]);
|
|
368
|
-
for (const [holder, idxs] of byHolder) {
|
|
369
|
-
const list = blocks.get(holder).claimsNode;
|
|
370
|
-
for (const i of idxs.sort((x, y) => y - x))
|
|
371
|
-
list.delete(i, 1);
|
|
372
|
-
}
|
|
373
|
-
// Re-inserted with their anchors: the region they end is the new block's now.
|
|
374
|
-
return moved.map(({ claim: c }) => c.a === undefined ? { m: c.m } : { m: c.m, a: c.a, r: c.r ?? null });
|
|
261
|
+
if (moved.length === 0)
|
|
262
|
+
return;
|
|
263
|
+
text.insertAtGapEnd(end, moved.map((c) => ({ m: c.m })));
|
|
264
|
+
deleteClaims(doc, moved);
|
|
375
265
|
};
|
|
376
266
|
/**
|
|
377
|
-
*
|
|
378
|
-
* the end of `into`'s stream (
|
|
379
|
-
*
|
|
380
|
-
*
|
|
267
|
+
* Merge `from` into `into`: one claim item `{m: from}` at the end of the gap
|
|
268
|
+
* at the end of `into`'s stream (fork patch YP7; YP21 sorts it after the
|
|
269
|
+
* content and boundaries a concurrent writer puts at its origin, so a split
|
|
270
|
+
* the merge's writer did not see leaves it in the piece that ends `into`'s
|
|
271
|
+
* region: `merge.claim.anchor`). `into` has a stream: the caller gives a
|
|
272
|
+
* streamless block its own text first.
|
|
381
273
|
*/
|
|
382
|
-
export const claimInto = (
|
|
383
|
-
const list = blocks.get(into).claimsNode;
|
|
384
|
-
const claim = { m: from };
|
|
274
|
+
export const claimInto = (own, from, into) => {
|
|
385
275
|
const s = own.streamOf(into);
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
276
|
+
s.text.insertAtGapEnd(s.end, [{ m: from }]);
|
|
277
|
+
};
|
|
278
|
+
/** Delete the items of `claims` (an implicit claim has none). */
|
|
279
|
+
export const deleteClaims = (doc, claims) => {
|
|
280
|
+
const store = storeOf(doc);
|
|
281
|
+
for (const { stamp } of claims) {
|
|
282
|
+
if (stamp.c < 0 || store.getItem({ client: stamp.c, clock: stamp.k }).deleted)
|
|
283
|
+
continue;
|
|
284
|
+
const at = Y.indexOfId(asYDoc(doc), Y.createID(stamp.c, stamp.k));
|
|
285
|
+
at.type.delete(at.index, 1);
|
|
392
286
|
}
|
|
393
|
-
list.insert(list.length, [claim]);
|
|
394
287
|
};
|
|
395
288
|
/**
|
|
396
289
|
* Give streamless block `rec` its own text and re-mint its nonce, so a late
|
|
@@ -467,3 +360,25 @@ export const bindText = (_Y) => ({
|
|
|
467
360
|
* typing and keeps the text every replica shares.
|
|
468
361
|
*/
|
|
469
362
|
export const ownTextIds = new WeakMap();
|
|
363
|
+
/**
|
|
364
|
+
* Each history's open step on a doc (`doc/history.ts` registers them): what
|
|
365
|
+
* the step still capturing inserted, `null` when none is open.
|
|
366
|
+
*/
|
|
367
|
+
export const openSteps = new WeakMap();
|
|
368
|
+
/**
|
|
369
|
+
* What this writer inserted in the open undo steps of `doc` (`null`: none):
|
|
370
|
+
* a history capturing a peer's frames (the room's validation) opens steps of
|
|
371
|
+
* that peer's writes, never fresh here.
|
|
372
|
+
*/
|
|
373
|
+
const openInserts = (doc) => {
|
|
374
|
+
let fresh = null;
|
|
375
|
+
for (const open of openSteps.get(doc) ?? []) {
|
|
376
|
+
const ranges = open()?.clients.get(doc.clientID);
|
|
377
|
+
if (ranges === undefined)
|
|
378
|
+
continue;
|
|
379
|
+
fresh ??= ops.idSet();
|
|
380
|
+
for (const r of ranges.getIds())
|
|
381
|
+
fresh.add(doc.clientID, r.clock, r.len);
|
|
382
|
+
}
|
|
383
|
+
return fresh;
|
|
384
|
+
};
|
|
@@ -15,6 +15,6 @@ export declare const inlineItemOf: (entry: unknown) => RangeItem;
|
|
|
15
15
|
/**
|
|
16
16
|
* Read `[i0, i1)` through the cursor: text pieces as UTF-16 slices under their
|
|
17
17
|
* folded marks (adjacent equal marks merge), inline atoms one item each.
|
|
18
|
-
* Boundary items are skipped — the one read predicate stream ownership needs.
|
|
18
|
+
* Boundary and claim items are skipped — the one read predicate stream ownership needs.
|
|
19
19
|
*/
|
|
20
20
|
export declare const readRange: (cur: RangeCursor, i0: number, i1: number, stats?: RangeReadStats, items?: RangeItem[]) => RangeItem[];
|
package/dist/crdt/text/ranges.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ID, TYPE } from '../schema.js';
|
|
2
2
|
import { readData } from '../data.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isRowMark } from './items.js';
|
|
4
4
|
/** Canonical JSON key (sorted keys, recursive) — mark-set equality/interning. */
|
|
5
5
|
export const canonKey = (v) => {
|
|
6
6
|
if (v === null || typeof v !== 'object')
|
|
@@ -52,7 +52,7 @@ export const inlineItemOf = (entry) => {
|
|
|
52
52
|
/**
|
|
53
53
|
* Read `[i0, i1)` through the cursor: text pieces as UTF-16 slices under their
|
|
54
54
|
* folded marks (adjacent equal marks merge), inline atoms one item each.
|
|
55
|
-
* Boundary items are skipped — the one read predicate stream ownership needs.
|
|
55
|
+
* Boundary and claim items are skipped — the one read predicate stream ownership needs.
|
|
56
56
|
*/
|
|
57
57
|
export const readRange = (cur, i0, i1, stats, items = []) => {
|
|
58
58
|
for (const piece of cur.read(i0, i1, stats)) {
|
|
@@ -73,7 +73,7 @@ export const readRange = (cur, i0, i1, stats, items = []) => {
|
|
|
73
73
|
else if (typeof c.getContent === 'function') {
|
|
74
74
|
const arr = c.getContent();
|
|
75
75
|
for (let k = piece.offset; k < piece.offset + piece.len; k++) {
|
|
76
|
-
if (!
|
|
76
|
+
if (!isRowMark(arr[k]))
|
|
77
77
|
items.push(inlineItemOf(arr[k]));
|
|
78
78
|
}
|
|
79
79
|
}
|