edytor 3.0.0 → 5.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/block/block.svelte.js +6 -21
- package/dist/block/inlineBlock.svelte.js +4 -6
- package/dist/clipboard/moveText.js +6 -8
- package/dist/cloudflare/DocumentRoom.d.ts +23 -12
- package/dist/cloudflare/DocumentRoom.js +10 -6
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/room/access.d.ts +8 -3
- package/dist/cloudflare/room/access.js +44 -20
- package/dist/cloudflare/room/admission.js +28 -5
- package/dist/cloudflare/room/comments.js +28 -1
- package/dist/cloudflare/room/context.d.ts +139 -129
- package/dist/cloudflare/room/context.js +11 -4
- package/dist/cloudflare/room/forged.d.ts +14 -0
- package/dist/cloudflare/room/forged.js +30 -2
- package/dist/cloudflare/room/frames.d.ts +4 -0
- package/dist/cloudflare/room/frames.js +5 -1
- package/dist/cloudflare/room/purge.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +45 -19
- package/dist/cloudflare/room/replicas.d.ts +4 -2
- package/dist/cloudflare/room/replicas.js +8 -3
- package/dist/cloudflare/room/suggest.d.ts +7 -0
- package/dist/cloudflare/room/suggest.js +143 -0
- package/dist/cloudflare/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- package/dist/cloudflare/routeDocumentSocket.d.ts +10 -1
- package/dist/cloudflare/routeDocumentSocket.js +5 -3
- package/dist/components/Content.svelte +9 -1
- package/dist/crdt/anchors.js +3 -2
- package/dist/crdt/attribution/attribution.d.ts +34 -12
- package/dist/crdt/attribution/attribution.js +59 -21
- 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/capability.js +9 -13
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.d.ts +1 -1
- package/dist/crdt/doc/funnel.js +34 -26
- package/dist/crdt/doc/gate.d.ts +3 -3
- package/dist/crdt/doc/history.d.ts +31 -9
- package/dist/crdt/doc/history.js +204 -73
- package/dist/crdt/doc/layout.js +16 -27
- package/dist/crdt/doc/meta.js +17 -23
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.d.ts +2 -2
- package/dist/crdt/doc/reads.js +20 -30
- 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 +55 -125
- package/dist/crdt/doc/suggest.d.ts +167 -0
- package/dist/crdt/doc/suggest.js +579 -0
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +26 -6
- package/dist/crdt/document.js +40 -15
- package/dist/crdt/edytor-doc.d.ts +410 -390
- package/dist/crdt/edytor-doc.js +143 -95
- package/dist/crdt/engine-api.d.ts +39 -1
- package/dist/crdt/engine.d.ts +2 -2
- package/dist/crdt/engine.js +4 -5
- package/dist/crdt/flow.d.ts +2 -2
- package/dist/crdt/flow.js +29 -37
- package/dist/crdt/index.d.ts +134 -127
- package/dist/crdt/index.js +4 -1
- package/dist/crdt/operations.d.ts +12 -3
- package/dist/crdt/parts.d.ts +43 -0
- package/dist/crdt/parts.js +44 -0
- package/dist/crdt/placement/display.d.ts +6 -31
- package/dist/crdt/placement/display.js +41 -146
- package/dist/crdt/placement/keys.d.ts +63 -0
- package/dist/crdt/placement/keys.js +95 -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 +188 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -0
- package/dist/crdt/protocol.d.ts +1 -1
- package/dist/crdt/protocol.js +1 -1
- package/dist/crdt/protocols/auth.d.ts +11 -1
- package/dist/crdt/protocols/auth.js +17 -1
- package/dist/crdt/protocols/comments.d.ts +16 -1
- package/dist/crdt/protocols/comments.js +9 -1
- package/dist/crdt/providers/index.d.ts +6 -4
- package/dist/crdt/providers/index.js +3 -1
- package/dist/crdt/providers/room.d.ts +16 -2
- package/dist/crdt/providers/room.js +64 -17
- package/dist/crdt/providers/websocket.d.ts +6 -4
- package/dist/crdt/providers/websocket.js +14 -7
- package/dist/crdt/purge.d.ts +24 -9
- package/dist/crdt/purge.js +266 -126
- package/dist/crdt/restore.d.ts +4 -3
- package/dist/crdt/restore.js +27 -34
- package/dist/crdt/schema.d.ts +30 -21
- package/dist/crdt/schema.js +39 -23
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +17 -15
- package/dist/crdt/structs.js +10 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +13 -23
- 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.d.ts +2 -4
- package/dist/crdt/text/index/layout.js +14 -30
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +420 -157
- 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 +29 -17
- 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 +87 -62
- package/dist/crdt/text/model.js +91 -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 +51 -42
- 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 +651 -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 +197 -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 +54 -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 +649 -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 +199 -12
- package/dist/edytor.svelte.d.ts +1 -1
- package/dist/edytor.svelte.js +103 -75
- package/dist/events/beforeInputCommands.d.ts +2 -2
- package/dist/events/beforeInputCommands.js +2 -2
- package/dist/events/nativeInteractiveControl.d.ts +11 -1
- package/dist/events/nativeInteractiveControl.js +41 -1
- package/dist/events/onBeforeInput.js +3 -3
- package/dist/events/onFocus.js +9 -11
- package/dist/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- package/dist/export/forms.d.ts +50 -50
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/labels.d.ts +8 -0
- package/dist/labels.js +6 -1
- package/dist/plugins/comments/CommentsController.svelte.d.ts +5 -1
- package/dist/plugins/comments/CommentsController.svelte.js +14 -4
- package/dist/plugins/find/search.js +2 -2
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/suggestions/SuggestionBars.svelte +40 -23
- package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +3 -1
- package/dist/plugins/suggestions/SuggestionRuns.svelte +77 -0
- package/dist/plugins/suggestions/SuggestionRuns.svelte.d.ts +9 -0
- package/dist/plugins/suggestions/shared.svelte.d.ts +58 -0
- package/dist/plugins/suggestions/shared.svelte.js +421 -0
- package/dist/plugins/suggestions/suggestionsPlugin.d.ts +15 -4
- package/dist/selection/domSelection.d.ts +2 -0
- package/dist/selection/domSelection.js +6 -2
- package/dist/selection/pointer.svelte.js +75 -48
- package/dist/selection/replaceSelection.js +2 -6
- package/dist/selection/selection.svelte.d.ts +3 -3
- package/dist/selection/selection.svelte.js +39 -32
- package/dist/selection/selection.utils.d.ts +14 -0
- package/dist/selection/selection.utils.js +28 -3
- package/dist/session/commands.js +21 -11
- package/dist/session/composition.svelte.d.ts +5 -3
- package/dist/session/composition.svelte.js +21 -17
- package/dist/session/handles.d.ts +4 -17
- package/dist/session/handles.js +11 -37
- package/dist/session/navigation.js +6 -9
- package/dist/session/selection.d.ts +11 -5
- package/dist/session/selection.js +14 -48
- package/dist/session/suggestions.svelte.d.ts +38 -9
- package/dist/session/suggestions.svelte.js +37 -18
- package/dist/session/virtual.d.ts +1 -1
- package/dist/surface/attributes.d.ts +5 -0
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/cells.d.ts +2 -26
- package/dist/surface/cells.js +2 -33
- package/dist/surface/observer.svelte.d.ts +6 -1
- package/dist/surface/observer.svelte.js +130 -83
- package/dist/surface/pin.svelte.d.ts +9 -9
- package/dist/surface/pin.svelte.js +15 -22
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +67 -22
- package/dist/text/text.svelte.js +14 -19
- package/dist/themes/notion.css +29 -0
- package/package.json +4 -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
|
@@ -481,11 +481,11 @@ export const createDeleteSetFromStructStore = ss => {
|
|
|
481
481
|
const dsitems = []
|
|
482
482
|
for (let i = 0; i < structs.length; i++) {
|
|
483
483
|
const struct = structs[i]
|
|
484
|
-
if (struct.
|
|
484
|
+
if (struct.removed) { // YP20: a held item is not deleted
|
|
485
485
|
const clock = struct.id.clock
|
|
486
486
|
let len = struct.length
|
|
487
487
|
if (i + 1 < structs.length) {
|
|
488
|
-
for (let next = structs[i + 1]; i + 1 < structs.length && next.
|
|
488
|
+
for (let next = structs[i + 1]; i + 1 < structs.length && next.removed; next = structs[++i + 1]) {
|
|
489
489
|
len += next.length
|
|
490
490
|
}
|
|
491
491
|
}
|
|
@@ -514,7 +514,7 @@ export const writeIdSet = (encoder, idSet) => {
|
|
|
514
514
|
.forEach(([client, _idRanges]) => {
|
|
515
515
|
const idRanges = _idRanges.getIds()
|
|
516
516
|
encoder.resetIdSetCurVal()
|
|
517
|
-
|
|
517
|
+
encoder.writeDsClient(client) // YP26
|
|
518
518
|
const len = idRanges.length
|
|
519
519
|
encoding.writeVarUint(encoder.restEncoder, len)
|
|
520
520
|
for (let i = 0; i < len; i++) {
|
|
@@ -537,7 +537,7 @@ export const readIdSet = decoder => {
|
|
|
537
537
|
const numClients = decoding.readVarUint(decoder.restDecoder)
|
|
538
538
|
for (let i = 0; i < numClients; i++) {
|
|
539
539
|
decoder.resetDsCurVal()
|
|
540
|
-
const client =
|
|
540
|
+
const client = decoder.readDsClient() // YP26
|
|
541
541
|
const numberOfDeletes = decoding.readVarUint(decoder.restDecoder)
|
|
542
542
|
if (numberOfDeletes > 0) {
|
|
543
543
|
/**
|
|
@@ -571,7 +571,7 @@ export const readAndApplyDeleteSet = (decoder, transaction, store) => {
|
|
|
571
571
|
const numClients = decoding.readVarUint(decoder.restDecoder)
|
|
572
572
|
for (let i = 0; i < numClients; i++) {
|
|
573
573
|
decoder.resetDsCurVal()
|
|
574
|
-
const client =
|
|
574
|
+
const client = decoder.readDsClient() // YP26
|
|
575
575
|
const numberOfDeletes = decoding.readVarUint(decoder.restDecoder)
|
|
576
576
|
const structs = store.clients.get(client) || []
|
|
577
577
|
const state = store.getClock(client)
|
|
@@ -589,7 +589,7 @@ export const readAndApplyDeleteSet = (decoder, transaction, store) => {
|
|
|
589
589
|
*/
|
|
590
590
|
let struct = structs[index]
|
|
591
591
|
// split the first item if necessary
|
|
592
|
-
if (!struct.
|
|
592
|
+
if (!struct.removed && struct.id.clock < clock && struct.isItem) { // YP20: removed, not held
|
|
593
593
|
// increment index, we now want to use the next struct
|
|
594
594
|
structs.splice(++index, 0, /** @type {Item} */ (struct).split(transaction, clock - struct.id.clock))
|
|
595
595
|
}
|
|
@@ -597,7 +597,7 @@ export const readAndApplyDeleteSet = (decoder, transaction, store) => {
|
|
|
597
597
|
// @ts-ignore
|
|
598
598
|
struct = structs[index++]
|
|
599
599
|
if (struct.id.clock < clockEnd) {
|
|
600
|
-
if (!struct.
|
|
600
|
+
if (!struct.removed) { // YP20
|
|
601
601
|
if (struct.isItem) {
|
|
602
602
|
if (clockEnd < struct.id.clock + struct.length) {
|
|
603
603
|
structs.splice(index, 0, /** @type {Item} */ (struct).split(transaction, clockEnd - struct.id.clock))
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./Transaction.js').Transaction} Transaction
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Paired marks — an edytor fork addition (UPSTREAM.md YP13), not upstream source.
|
|
3
7
|
*
|
|
@@ -117,33 +121,214 @@ export const formatValue = value => value instanceof MarkState ? value.value : v
|
|
|
117
121
|
/**
|
|
118
122
|
* The integration class of an item among the items of one origin: a
|
|
119
123
|
* left-side mark item `0`, content and plain formats `1`, a right-side
|
|
120
|
-
* mark item `2
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
+
* mark item `2`, a claim item `3` (YP21: a sequence unit `{ m }`, after
|
|
125
|
+
* everything else, so content and boundaries a concurrent writer puts at
|
|
126
|
+
* its origin land before it), a move record `4` or `5` by its side (YP22:
|
|
127
|
+
* after every slot of its origin — a split's pieces first — and a record
|
|
128
|
+
* standing after what precedes it before one standing before what follows
|
|
129
|
+
* it: the order every serial run of the gestures gives). Deleted or collected items keep the class
|
|
130
|
+
* their content gave them while it is there; a collected item (its content
|
|
131
|
+
* gone) would read `1`, which is why the fork never collects the content of
|
|
132
|
+
* an item of another class (`Item#gc` keeps it).
|
|
124
133
|
*
|
|
125
134
|
* @param {any} item
|
|
126
135
|
* @return {number}
|
|
127
136
|
*/
|
|
128
137
|
export const markClass = item => {
|
|
129
138
|
const content = item.content
|
|
130
|
-
if (content === undefined
|
|
139
|
+
if (content === undefined) return 1
|
|
140
|
+
if (content.cls !== undefined) return content.cls // YP29: a collected mark or claim (`ContentDeleted`)
|
|
141
|
+
if (typeof content.key !== 'string') {
|
|
142
|
+
// YP22: a move record (its side: 0 after, 1 before)
|
|
143
|
+
if (content.priority !== undefined && content.ids !== undefined) return 4 + content.side
|
|
144
|
+
// YP21: a claim item
|
|
145
|
+
return item.parentSub === null && typeof content.arr?.[0]?.m === 'string' ? 3 : 1
|
|
146
|
+
}
|
|
131
147
|
const role = pairedRole(content)
|
|
132
148
|
if (role < 0) return 1
|
|
133
149
|
const side = role === 0 ? content.value[4] : content.value[2]
|
|
134
150
|
return side === SIDE_LEFT ? 0 : 2
|
|
135
151
|
}
|
|
136
152
|
|
|
153
|
+
/**
|
|
154
|
+
* YP22: the unit id `client:clock` names, as its item and clock, `null` where
|
|
155
|
+
* a GC struct replaced it.
|
|
156
|
+
*
|
|
157
|
+
* @param {import('./StructStore.js').StructStore} store
|
|
158
|
+
* @param {number} client
|
|
159
|
+
* @param {number} clock
|
|
160
|
+
* @return {{ item: any, clock: number } | null}
|
|
161
|
+
*/
|
|
162
|
+
const unitAt = (store, client, clock) => {
|
|
163
|
+
const item = store.getItem({ client, clock })
|
|
164
|
+
return item.isItem ? { item, clock } : null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* YP22: the order of two units (`unitAt`): by clock in one item, else by
|
|
169
|
+
* `sequenceOrder` (`NaN`: not in one sequence); `0` for one unit.
|
|
170
|
+
*
|
|
171
|
+
* @param {{ item: any, clock: number }} p
|
|
172
|
+
* @param {{ item: any, clock: number }} q
|
|
173
|
+
* @return {number}
|
|
174
|
+
*/
|
|
175
|
+
const unitOrder = (p, q) => (p.item === q.item ? p.clock - q.clock : sequenceOrder(p.item, q.item))
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* YP22: a block slot's split key (`{ b, s: [client, clock] }`: the id of the
|
|
179
|
+
* boundary item of the split that made the block, kept when the slot is
|
|
180
|
+
* collected, `Item#gc`) as a unit (`unitAt`: `null` where a GC struct
|
|
181
|
+
* replaced the boundary), `undefined` for any other item (a slot no split
|
|
182
|
+
* made, a boundary, whose own `s` names a block).
|
|
183
|
+
*
|
|
184
|
+
* @param {any} item
|
|
185
|
+
* @param {import('./StructStore.js').StructStore} store
|
|
186
|
+
* @return {{ item: any, clock: number } | null | undefined}
|
|
187
|
+
*/
|
|
188
|
+
const splitKey = (item, store) => {
|
|
189
|
+
const s = item.content.arr?.[0]?.s
|
|
190
|
+
return Array.isArray(s) ? unitAt(store, s[0], s[1]) : undefined
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* YP22: whether `item` is a list item outside a text (a block's or an
|
|
195
|
+
* array's slot: its parent is no `content` node) written by the writer of
|
|
196
|
+
* its origin: it extends that writer's run there, and sorts before every
|
|
197
|
+
* other writer's slot of the same origin (`order.insert.run`: the lines one
|
|
198
|
+
* writer types after its own line stay together around a peer's line
|
|
199
|
+
* inserted there meanwhile). Read from the item's ids and parent, which
|
|
200
|
+
* collection never changes, so every replica integrates alike.
|
|
201
|
+
*
|
|
202
|
+
* @param {any} item
|
|
203
|
+
* @return {boolean}
|
|
204
|
+
*/
|
|
205
|
+
const extendsRun = (/** @type {Item} */ item) =>
|
|
206
|
+
item.parentSub === null && /** @type {YNode} */ (item.parent).name !== 'content' && item.origin !== null && item.origin.client === item.id.client
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* YP22: the order of two items of one sequence (one parent, one key):
|
|
210
|
+
* negative when `x` stands before `y`, positive after, `0` for one item,
|
|
211
|
+
* `NaN` when they are not in one sequence. The walk goes left from both at
|
|
212
|
+
* once, so it costs the distance between them.
|
|
213
|
+
*
|
|
214
|
+
* @param {any} x
|
|
215
|
+
* @param {any} y
|
|
216
|
+
* @return {number}
|
|
217
|
+
*/
|
|
218
|
+
export const sequenceOrder = (x, y) => {
|
|
219
|
+
if (x === y) return 0
|
|
220
|
+
if (x.parent !== y.parent || x.parentSub !== y.parentSub) return NaN
|
|
221
|
+
for (let l = x, r = y; ;) {
|
|
222
|
+
l = l.left
|
|
223
|
+
r = r.left
|
|
224
|
+
if (l === y || r === null) return 1
|
|
225
|
+
if (r === x || l === null) return -1
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* YP22: the chain record `r` stands at, outermost first — the units its
|
|
231
|
+
* writer named (`at`: the home slots from the root sequence down to the unit
|
|
232
|
+
* it stands at), else the first unit it moves — each as its item and clock,
|
|
233
|
+
* `null` where a GC struct replaced it.
|
|
234
|
+
*
|
|
235
|
+
* @param {any} r
|
|
236
|
+
* @param {import('./StructStore.js').StructStore} store
|
|
237
|
+
* @return {Array<{ item: any, clock: number } | null>}
|
|
238
|
+
*/
|
|
239
|
+
const chainOf = (r, store) => {
|
|
240
|
+
const c = r.content
|
|
241
|
+
return c.at !== null
|
|
242
|
+
? c.at.map((/** @type {ID} */ id) => unitAt(store, id.client, id.clock))
|
|
243
|
+
: c.ids.length > 0 ? [unitAt(store, c.ids[0], c.ids[1])] : []
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* YP22: the order of two move records at one origin: by the chains they
|
|
248
|
+
* stand at (`chainOf`), level by level from the root — two units of one
|
|
249
|
+
* sequence by `sequenceOrder` (one item: by clock), a unit before the units
|
|
250
|
+
* inside its block (the shorter chain first); chains that meet no common
|
|
251
|
+
* sequence order nothing — then by their `part` (the writer's order among
|
|
252
|
+
* the records standing at one unit), then the stronger record first (the
|
|
253
|
+
* greater priority, then client: a unit both move shows at the first of the
|
|
254
|
+
* two). Two writers that move blocks out of one list into one gap at once (an
|
|
255
|
+
* outdent, a lift, a divider after an item) keep the list's order there and
|
|
256
|
+
* every serial order's placement of what stands at one item, whatever their
|
|
257
|
+
* client ids (the delete contract's CW-01 and DR-crdt-6 rules). It reads only
|
|
258
|
+
* the records and the units they name, all present when a record integrates
|
|
259
|
+
* (`getMissing`), so every replica sorts alike in any delivery order.
|
|
260
|
+
*
|
|
261
|
+
* @param {any} o
|
|
262
|
+
* @param {any} item
|
|
263
|
+
* @param {Transaction} tr
|
|
264
|
+
* @return {number}
|
|
265
|
+
*/
|
|
266
|
+
const sourceOrder = (o, item, tr) => {
|
|
267
|
+
const store = tr.doc.store
|
|
268
|
+
const x = chainOf(o, store)
|
|
269
|
+
const y = chainOf(item, store)
|
|
270
|
+
let d = 0
|
|
271
|
+
for (let i = 0; ; i++) {
|
|
272
|
+
const p = x[i]
|
|
273
|
+
const q = y[i]
|
|
274
|
+
if (p === undefined || q === undefined) {
|
|
275
|
+
d = (p === undefined ? 0 : 1) - (q === undefined ? 0 : 1)
|
|
276
|
+
break
|
|
277
|
+
}
|
|
278
|
+
if (p === null || q === null) break
|
|
279
|
+
d = unitOrder(p, q)
|
|
280
|
+
if (d === 0) continue
|
|
281
|
+
if (Number.isNaN(d)) d = 0
|
|
282
|
+
break
|
|
283
|
+
}
|
|
284
|
+
if (d === 0) d = o.content.part - item.content.part
|
|
285
|
+
if (d === 0) d = item.content.priority - o.content.priority || item.id.client - o.id.client
|
|
286
|
+
return d
|
|
287
|
+
}
|
|
288
|
+
|
|
137
289
|
/**
|
|
138
290
|
* Whether `o` sorts before `item` among the items of one origin (the
|
|
139
|
-
* integration's conflict rule): by class, then
|
|
291
|
+
* integration's conflict rule): by class, then (YP22) block slots by their
|
|
292
|
+
* split key and the origin writer's own first, records by `sourceOrder`,
|
|
293
|
+
* then by client id as upstream.
|
|
140
294
|
*
|
|
141
295
|
* @param {any} o
|
|
142
296
|
* @param {any} item
|
|
297
|
+
* @param {Transaction} tr
|
|
143
298
|
* @return {boolean}
|
|
144
299
|
*/
|
|
145
|
-
export const sortsBefore = (o, item) => {
|
|
300
|
+
export const sortsBefore = (o, item, tr) => {
|
|
146
301
|
const a = markClass(o)
|
|
147
302
|
const b = markClass(item)
|
|
148
|
-
|
|
303
|
+
if (a !== b) return a < b
|
|
304
|
+
if (a === 1) {
|
|
305
|
+
// YP22: block slots a split made sort by their boundaries' order in the
|
|
306
|
+
// text (two writers' splits of one block keep its text's order), before
|
|
307
|
+
// the slots no split made; then the origin writer's own slots first.
|
|
308
|
+
const store = tr.doc.store
|
|
309
|
+
const x = splitKey(o, store)
|
|
310
|
+
const y = splitKey(item, store)
|
|
311
|
+
if (x !== undefined || y !== undefined) {
|
|
312
|
+
if (x === undefined || y === undefined) return y === undefined
|
|
313
|
+
if (x !== null && y !== null) {
|
|
314
|
+
// One total order over the keyed slots of an origin: the boundaries' text
|
|
315
|
+
// first (by the text node's item id, a root text first), then their place in
|
|
316
|
+
// it (two boundaries one item holds: their clocks).
|
|
317
|
+
const px = /** @type {YNode} */ (x.item.parent)._item
|
|
318
|
+
const py = /** @type {YNode} */ (y.item.parent)._item
|
|
319
|
+
if (x.item.parent !== y.item.parent) {
|
|
320
|
+
if (px === null || py === null) return px === null
|
|
321
|
+
return (px.id.client - py.id.client || px.id.clock - py.id.clock) < 0
|
|
322
|
+
}
|
|
323
|
+
const d = unitOrder(x, y)
|
|
324
|
+
if (d !== 0) return d < 0
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const run = extendsRun(o)
|
|
328
|
+
if (run !== extendsRun(item)) return run
|
|
329
|
+
} else if (a >= 4) {
|
|
330
|
+
const d = sourceOrder(o, item, tr)
|
|
331
|
+
if (d !== 0) return d < 0
|
|
332
|
+
}
|
|
333
|
+
return o.id.client < item.id.client
|
|
149
334
|
}
|
|
@@ -38,7 +38,7 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, deletedByH
|
|
|
38
38
|
*/
|
|
39
39
|
let right
|
|
40
40
|
// make sure that parent is redone
|
|
41
|
-
if (parentItem !== null && parentItem.
|
|
41
|
+
if (parentItem !== null && parentItem.removed) { // YP20: a held parent is alive
|
|
42
42
|
// try to undo parent if it will be undone anyway
|
|
43
43
|
if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, deletedByHistory) === null)) {
|
|
44
44
|
return null
|
|
@@ -245,7 +245,7 @@ export const tryGcDeleteSet = (tr, ds, gcFilter) => {
|
|
|
245
245
|
if (deleteItem.clock + deleteItem.len <= struct.id.clock) {
|
|
246
246
|
break
|
|
247
247
|
}
|
|
248
|
-
if (struct.isItem && struct.
|
|
248
|
+
if (struct.isItem && struct.removed && !(struct).keep && !isKeptReplaced(/** @type {Item} */ (struct)) && gcFilter(/** @type {Item} */ (struct))) { // YP14, YP20: removed
|
|
249
249
|
/** @type {Item} */ (struct).gc(tr, false)
|
|
250
250
|
}
|
|
251
251
|
}
|
|
@@ -42,9 +42,9 @@ function * lazyStructReaderGenerator (decoder) {
|
|
|
42
42
|
null, // right
|
|
43
43
|
(info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null, // right origin
|
|
44
44
|
// @ts-ignore Force writing a string here.
|
|
45
|
-
cantCopyParentInfo ? (
|
|
46
|
-
cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.
|
|
47
|
-
readItemContent(decoder, info) // item content
|
|
45
|
+
cantCopyParentInfo ? ((info & binary.BIT5) === binary.BIT5 ? decoder.readKey() : decoder.readLeftID()) : null, // parent (YP27: bit 5 a root's name, YP24: a key)
|
|
46
|
+
cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readKey() : null, // parentSub (YP24: a key)
|
|
47
|
+
readItemContent(decoder, info, createID(client, clock)) // item content (YP25: with its id)
|
|
48
48
|
)
|
|
49
49
|
yield struct
|
|
50
50
|
clock += struct.length
|
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
ContentFormat,
|
|
11
11
|
ContentString,
|
|
12
12
|
ContentType,
|
|
13
|
+
ContentHold, // YP20
|
|
14
|
+
ContentMove, // YP22
|
|
15
|
+
hideIds, // YP32
|
|
16
|
+
readFormatValue, // YP25
|
|
13
17
|
YXmlFragmentRefID,
|
|
14
18
|
YXmlElementRefID,
|
|
15
19
|
YXmlHookRefID
|
|
@@ -248,7 +252,7 @@ const insertMarkItem = (transaction, parent, index, side, content) => {
|
|
|
248
252
|
const markHelper = (transaction, parent, index, length, mark, value, startSide, endSide) => {
|
|
249
253
|
const doc = transaction.doc
|
|
250
254
|
const client = doc.clientID
|
|
251
|
-
const l = /** @type {any} */ (doc).
|
|
255
|
+
const l = /** @type {any} */ (doc)._clock + 1 // YP22: one clock with the move records
|
|
252
256
|
const k = doc.store.getClock(client)
|
|
253
257
|
insertMarkItem(transaction, parent, index, startSide, new ContentFormat(MARK_START + mark, [value, l, client, k, startSide]))
|
|
254
258
|
insertMarkItem(transaction, parent, index + length, endSide, new ContentFormat(MARK_END + mark, [client, k, endSide]))
|
|
@@ -292,28 +296,83 @@ export const insertAtGapEndHelper = (transaction, parent, index, content) => {
|
|
|
292
296
|
}
|
|
293
297
|
// YP7 end
|
|
294
298
|
|
|
299
|
+
// YP20 begin (edytor fork: hold records — see UPSTREAM.md YP20)
|
|
295
300
|
/**
|
|
301
|
+
* The first clock in `(clock, end)` where membership in `set` changes, else `end`.
|
|
302
|
+
*
|
|
303
|
+
* @param {import('./utils/ids.js').IdSet} set
|
|
304
|
+
* @param {number} client
|
|
305
|
+
* @param {number} clock
|
|
306
|
+
* @param {number} end
|
|
307
|
+
* @return {number}
|
|
308
|
+
*/
|
|
309
|
+
const runEnd = (set, client, clock, end) => {
|
|
310
|
+
for (const r of set.clients.get(client)?.getIds() ?? []) {
|
|
311
|
+
if (r.clock > clock) return math.min(r.clock, end)
|
|
312
|
+
if (r.clock + r.len > clock) return math.min(r.clock + r.len, end)
|
|
313
|
+
}
|
|
314
|
+
return end
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Append a hold record of `ids` to `list` (the document's records list).
|
|
319
|
+
*
|
|
320
|
+
* @param {Transaction} transaction
|
|
321
|
+
* @param {YNode} list
|
|
322
|
+
* @param {Array<number>} ids
|
|
323
|
+
*/
|
|
324
|
+
const holdHelper = (transaction, list, ids) => {
|
|
325
|
+
insertInGapHelper(transaction, list, list._length, new ContentHold(ids))
|
|
326
|
+
}
|
|
327
|
+
// YP20 end
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* With `held`, the live units are not deleted: their ids go to `held`
|
|
331
|
+
* (`client, clock, len`), except those `fresh` names (YP20). YP32: with
|
|
332
|
+
* `sort` (a suggestion's delete), `sort`'s `1` goes to `held`, `2` to
|
|
333
|
+
* `proposed` and `hidden`, `3` to `hidden`.
|
|
334
|
+
*
|
|
296
335
|
* @param {Transaction} transaction
|
|
297
336
|
* @param {ItemTextListPosition} currPos
|
|
298
337
|
* @param {number} length
|
|
338
|
+
* @param {Array<number>|null} [held]
|
|
339
|
+
* @param {import('./utils/ids.js').IdSet|null} [fresh]
|
|
340
|
+
* @param {((client: number, clock: number) => number)|null} [sort] YP32: a suggestion's delete sorts each unit
|
|
341
|
+
* @param {Array<number>|null} [proposed] YP32: the units it proposes
|
|
342
|
+
* @param {Array<number>|null} [hidden] YP32: the units it hides until `unhide`
|
|
299
343
|
* @return {ItemTextListPosition}
|
|
300
344
|
*
|
|
301
345
|
* @private
|
|
302
346
|
* @function
|
|
303
347
|
*/
|
|
304
|
-
export const deleteText = (transaction, currPos, length) => {
|
|
348
|
+
export const deleteText = (transaction, currPos, length, held = null, fresh = null, sort = null, proposed = null, hidden = null) => {
|
|
305
349
|
const startLength = length
|
|
350
|
+
let kept = 0 // YP20
|
|
306
351
|
while (length > 0 && currPos.right !== null) {
|
|
307
352
|
const item = currPos.right
|
|
308
353
|
if (!item.deleted && item.countable) {
|
|
309
|
-
|
|
310
|
-
|
|
354
|
+
const { client, clock } = item.id
|
|
355
|
+
// YP20: a held run ends where the open step's inserts start or end
|
|
356
|
+
let n = fresh === null ? math.min(length, item.length) : runEnd(fresh, client, clock, clock + math.min(length, item.length)) - clock
|
|
357
|
+
// YP32: a suggestion's delete sorts each unit — held (`1`), proposed (`2`) or stepped over (`3`)
|
|
358
|
+
const kind = held === null || (fresh !== null && fresh.has(client, clock)) ? 0 : sort === null ? 1 : sort(client, clock)
|
|
359
|
+
if (kind !== 0 && sort !== null) for (let k = 1; k < n; k++) if (sort(client, clock + k) !== kind) n = k
|
|
360
|
+
if (n < item.length) {
|
|
361
|
+
getItemCleanStart(transaction, createID(client, clock + n))
|
|
311
362
|
}
|
|
312
363
|
length -= item.length
|
|
313
|
-
|
|
364
|
+
if (kind === 0) {
|
|
365
|
+
item.delete(transaction)
|
|
366
|
+
} else {
|
|
367
|
+
if (kind === 2) /** @type {Array<number>} */ (proposed).push(client, clock, item.length)
|
|
368
|
+
;/** @type {Array<number>} */ (kind === 1 ? held : hidden).push(client, clock, item.length)
|
|
369
|
+
kept += item.length
|
|
370
|
+
}
|
|
314
371
|
}
|
|
315
372
|
currPos.forward()
|
|
316
373
|
}
|
|
374
|
+
// YP20: the held units hide when their record integrates
|
|
375
|
+
currPos.index -= kept
|
|
317
376
|
const parent = /** @type {YNode} */ (/** @type {Item} */ (currPos.left || currPos.right).parent)
|
|
318
377
|
if (parent._searchMarker) {
|
|
319
378
|
updateMarkerChanges(parent._searchMarker, currPos.index, -startLength + length)
|
|
@@ -939,11 +998,25 @@ export class YNode {
|
|
|
939
998
|
|
|
940
999
|
/**
|
|
941
1000
|
* Deletes elements starting from an index. A deleted node is left as it is.
|
|
1001
|
+
* YP20: with `into` (the document's records list), the live units are not
|
|
1002
|
+
* removed but held by one record appended to `into` (their writer's delete,
|
|
1003
|
+
* which an undo of the record takes back), except those `fresh` names (the
|
|
1004
|
+
* open undo step's inserts: removed, so that step's undo changes nothing).
|
|
1005
|
+
*
|
|
1006
|
+
* YP32: with `propose` (a suggestion's delete) each unit is sorted: held
|
|
1007
|
+
* by the record in `into` (`1`: its author's own), proposed (`2`: an id
|
|
1008
|
+
* list appended to `propose.into`, a suggestion's record, holding nothing)
|
|
1009
|
+
* or stepped over (`3`: already proposed); proposed and stepped-over units
|
|
1010
|
+
* read gone until `unhide`, so the rest of the plan reads the offsets it
|
|
1011
|
+
* was prepared with.
|
|
942
1012
|
*
|
|
943
1013
|
* @param {number} index Index at which to start deleting elements
|
|
944
1014
|
* @param {number} length The number of elements to remove. Defaults to 1.
|
|
1015
|
+
* @param {YNode|null} [into]
|
|
1016
|
+
* @param {import('./utils/ids.js').IdSet|null} [fresh]
|
|
1017
|
+
* @param {{ into: YNode, sort: (client: number, clock: number) => number }|null} [propose]
|
|
945
1018
|
*/
|
|
946
|
-
delete (index, length = 1) {
|
|
1019
|
+
delete (index, length = 1, into = null, fresh = null, propose = null) {
|
|
947
1020
|
if (!(length > 0)) return
|
|
948
1021
|
if (this.doc == null) {
|
|
949
1022
|
const runs = prelimOf(this).runs
|
|
@@ -959,11 +1032,63 @@ export class YNode {
|
|
|
959
1032
|
if (this._item !== null && this._item.deleted) return
|
|
960
1033
|
transact(this.doc, transaction => {
|
|
961
1034
|
const currPos = cursorAt(transaction, this, index)
|
|
962
|
-
|
|
1035
|
+
const held = into === null ? null : [] // YP20
|
|
1036
|
+
const [proposed, hidden] = [[], []] // YP32
|
|
1037
|
+
deleteText(transaction, currPos, length, held, fresh, propose && propose.sort, proposed, hidden)
|
|
1038
|
+
if (held !== null && held.length > 0) {
|
|
1039
|
+
// YP20: `deleteText` moved the markers as for a delete; the hold keeps them
|
|
1040
|
+
const ms = this._searchMarker === null ? null : this._searchMarker.slice()
|
|
1041
|
+
holdHelper(transaction, /** @type {YNode} */ (into), held)
|
|
1042
|
+
if (ms !== null && this._searchMarker !== null) this._searchMarker.splice(0, this._searchMarker.length, ...ms)
|
|
1043
|
+
}
|
|
1044
|
+
// YP32: the proposal, then what it hides until `unhide`
|
|
1045
|
+
if (proposed.length > 0) holdHelper(transaction, /** @type {any} */ (propose).into, proposed)
|
|
1046
|
+
if (hidden.length > 0) hideIds(transaction, hidden)
|
|
963
1047
|
plantMarker(this, currPos)
|
|
964
1048
|
})
|
|
965
1049
|
}
|
|
966
1050
|
|
|
1051
|
+
/**
|
|
1052
|
+
* YP20: append to this node (the document's records list) one hold record of
|
|
1053
|
+
* the items `ids` names (`client, clock, len` triples): they read as deleted
|
|
1054
|
+
* while the record is live.
|
|
1055
|
+
*
|
|
1056
|
+
* @param {Array<number>} ids
|
|
1057
|
+
*/
|
|
1058
|
+
hold (ids) {
|
|
1059
|
+
if (this.doc == null) throw new Error('hold: node is not integrated')
|
|
1060
|
+
if (ids.length > 0) transact(this.doc, transaction => holdHelper(transaction, this, ids))
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// YP22 begin (edytor fork: move records — see UPSTREAM.md YP22)
|
|
1064
|
+
/**
|
|
1065
|
+
* YP22: move the units `ids` (`client, clock, len` ranges, in the order
|
|
1066
|
+
* they will show) to live index `index` of this sequence: one move record
|
|
1067
|
+
* there (`ContentMove`); `side` is its class among the items of its origin
|
|
1068
|
+
* (`0` after, `1` before), `at` the chain it stands at for the sort of two
|
|
1069
|
+
* records at one origin — the units from the root sequence down to the
|
|
1070
|
+
* unit it stands at, outermost first (`null`: the first unit it names) —
|
|
1071
|
+
* and `part` the writer's order among the records standing at that unit.
|
|
1072
|
+
*
|
|
1073
|
+
* @param {number} index
|
|
1074
|
+
* @param {Array<number>} ids
|
|
1075
|
+
* @param {number} [side]
|
|
1076
|
+
* @param {number} [part]
|
|
1077
|
+
* @param {Array<ID>|null} [at]
|
|
1078
|
+
*/
|
|
1079
|
+
move (index, ids, side = 0, part = 0, at = null) {
|
|
1080
|
+
if (this.doc == null) throw new Error('move: node is not integrated')
|
|
1081
|
+
if (ids.length === 0) return
|
|
1082
|
+
transact(this.doc, transaction => {
|
|
1083
|
+
// Above every record seen from a peer (`_clock`, raised by remote records
|
|
1084
|
+
// only): this writer's records keep one priority until it sees another's,
|
|
1085
|
+
// so its gestures win or lose together against a concurrent peer's.
|
|
1086
|
+
const priority = /** @type {any} */ (transaction.doc)._clock + 1
|
|
1087
|
+
insertInGapHelper(transaction, this, index, new ContentMove(priority, side, ids, part, at))
|
|
1088
|
+
})
|
|
1089
|
+
}
|
|
1090
|
+
// YP22 end
|
|
1091
|
+
|
|
967
1092
|
/**
|
|
968
1093
|
* Executes a provided function on once on every key-value pair.
|
|
969
1094
|
*
|
|
@@ -1081,9 +1206,13 @@ export const readContentString = decoder => new ContentString(decoder.readString
|
|
|
1081
1206
|
|
|
1082
1207
|
/**
|
|
1083
1208
|
* @param {UpdateDecoder} decoder
|
|
1209
|
+
* @param {import('./utils/ID.js').ID} id YP25: the item's id (a paired mark's operation id is usually it)
|
|
1084
1210
|
* @return {ContentFormat}
|
|
1085
1211
|
*/
|
|
1086
|
-
export const readContentFormat =
|
|
1212
|
+
export const readContentFormat = (decoder, id) => {
|
|
1213
|
+
const key = decoder.readKey()
|
|
1214
|
+
return new ContentFormat(key, readFormatValue(decoder, key, id)) // YP25
|
|
1215
|
+
}
|
|
1087
1216
|
|
|
1088
1217
|
/**
|
|
1089
1218
|
* @param {UpdateDecoder} decoder
|
|
@@ -1112,14 +1241,68 @@ export const readContentBinary = decoder => new ContentBinary(decoder.readBuf())
|
|
|
1112
1241
|
*/
|
|
1113
1242
|
export const readContentDeleted = decoder => new ContentDeleted(decoder.readLen())
|
|
1114
1243
|
|
|
1244
|
+
/**
|
|
1245
|
+
* YP20, YP22: a record's id ranges, a count then one `client, clock, len` each.
|
|
1246
|
+
*
|
|
1247
|
+
* @param {UpdateDecoder} decoder
|
|
1248
|
+
* @return {Array<number>}
|
|
1249
|
+
*/
|
|
1250
|
+
const readIds = decoder => {
|
|
1251
|
+
const ids = []
|
|
1252
|
+
for (let n = decoder.readLen(); n > 0; n--) {
|
|
1253
|
+
const id = decoder.readLeftID()
|
|
1254
|
+
ids.push(id.client, id.clock, decoder.readLen())
|
|
1255
|
+
}
|
|
1256
|
+
return ids
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
* YP20: a hold record, one `client, clock, len` per unit.
|
|
1261
|
+
*
|
|
1262
|
+
* @param {UpdateDecoder} decoder
|
|
1263
|
+
* @return {ContentHold}
|
|
1264
|
+
*/
|
|
1265
|
+
export const readContentHold = decoder => new ContentHold(readIds(decoder))
|
|
1266
|
+
|
|
1267
|
+
/**
|
|
1268
|
+
* YP22: a move record: its priority, side, part and whether an anchor chain
|
|
1269
|
+
* follows in one word, the chain (a count, then its units), then one
|
|
1270
|
+
* `client, clock, len` per range.
|
|
1271
|
+
*
|
|
1272
|
+
* @param {UpdateDecoder} decoder
|
|
1273
|
+
* @return {ContentMove}
|
|
1274
|
+
*/
|
|
1275
|
+
export const readContentMove = decoder => {
|
|
1276
|
+
const word = decoder.readLen()
|
|
1277
|
+
/** @type {Array<ID>|null} */
|
|
1278
|
+
let at = null
|
|
1279
|
+
if (word & 16) {
|
|
1280
|
+
at = []
|
|
1281
|
+
for (let n = decoder.readLen(); n > 0; n--) at.push(decoder.readLeftID())
|
|
1282
|
+
}
|
|
1283
|
+
return new ContentMove(word >> 5, word & 1, readIds(decoder), (word >> 1) & 7, at)
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* YP29: a collected mark or claim, its length and class.
|
|
1288
|
+
*
|
|
1289
|
+
* @param {UpdateDecoder} decoder
|
|
1290
|
+
* @return {ContentDeleted}
|
|
1291
|
+
*/
|
|
1292
|
+
export const readContentClass = decoder => {
|
|
1293
|
+
const word = decoder.readLen()
|
|
1294
|
+
return new ContentDeleted(Math.floor(word / 4), word % 4)
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1115
1297
|
const refused = () => { error.unexpectedCase() }
|
|
1116
1298
|
|
|
1117
1299
|
/**
|
|
1118
1300
|
* A lookup map for reading Item content. YP19: JSON (2), embed (5) and
|
|
1119
1301
|
* subdocument (9) contents are refused like GC (0) and Skip (10): edytor
|
|
1120
|
-
* writes none, so an update holding one is malformed.
|
|
1302
|
+
* writes none, so an update holding one is malformed. YP20: 11 is a hold
|
|
1303
|
+
* record. YP22: 12 is a move record.
|
|
1121
1304
|
*
|
|
1122
|
-
* @type {Array<function(UpdateDecoder):AbstractContent>}
|
|
1305
|
+
* @type {Array<function(UpdateDecoder, import('./utils/ID.js').ID):AbstractContent>}
|
|
1123
1306
|
*/
|
|
1124
1307
|
export const contentRefs = [
|
|
1125
1308
|
refused, // 0 - GC is not ItemContent
|
|
@@ -1132,14 +1315,18 @@ export const contentRefs = [
|
|
|
1132
1315
|
readContentType, // 7
|
|
1133
1316
|
readContentAny, // 8
|
|
1134
1317
|
refused, // 9 - subdocument
|
|
1135
|
-
refused // 10 - Skip is not ItemContent
|
|
1318
|
+
refused, // 10 - Skip is not ItemContent
|
|
1319
|
+
readContentHold, // 11 - a hold record (YP20)
|
|
1320
|
+
readContentMove, // 12 - a move record (YP22)
|
|
1321
|
+
readContentClass // 13 - a collected mark or claim, its class (YP29)
|
|
1136
1322
|
]
|
|
1137
1323
|
|
|
1138
1324
|
/**
|
|
1139
1325
|
* @param {UpdateDecoder} decoder
|
|
1140
1326
|
* @param {number} info
|
|
1327
|
+
* @param {import('./utils/ID.js').ID} id YP25: the item's id (a format item reads it)
|
|
1141
1328
|
*/
|
|
1142
|
-
export const readItemContent = (decoder, info) => contentRefs[info & binary.
|
|
1329
|
+
export const readItemContent = (decoder, info, id) => contentRefs[info & binary.BITS4](decoder, id) // YP27: four bits
|
|
1143
1330
|
|
|
1144
1331
|
/**
|
|
1145
1332
|
* @param {UpdateDecoder} decoder
|
package/dist/edytor.svelte.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { Handles } from './session/handles.js';
|
|
|
11
11
|
import { type Awareness, type DocChange, type DocumentActor, type EdytorDocument, type OrderPolicy, type YDoc } from './crdt/index.js';
|
|
12
12
|
import { PresenceWriter, type PresenceOptions } from './collaboration/awarenessSelection.js';
|
|
13
13
|
import { type BlockMoveRequest } from './session/moves.js';
|
|
14
|
-
import {
|
|
14
|
+
import type { DocumentWrites } from './crdt/operations.js';
|
|
15
15
|
import type { Plugin, BlockDefinition, MarkDefinition, InlineBlockDefinition, EditorCommand, Placeholder } from './plugins.js';
|
|
16
16
|
import { type HotKey, type HotKeyCombination } from './session/keymap.js';
|
|
17
17
|
import { Dispatcher } from './session/commands.js';
|