@y/y 14.0.0-16 → 14.0.0-17
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/dist/{index-DyTeTfmj.js → index-BV-j5wdP.js} +2 -2
- package/dist/{index-R7GxO-36.js.map → index-BV-j5wdP.js.map} +1 -1
- package/dist/{internals.mjs → internals.js} +1 -1
- package/dist/internals.js.map +1 -0
- package/dist/{testHelper.mjs → testHelper.js} +2 -2
- package/dist/testHelper.js.map +1 -0
- package/dist/{yjs.mjs → yjs.js} +2 -2
- package/dist/yjs.js.map +1 -0
- package/package.json +8 -17
- package/dist/Skip-j0kX7pdq.js +0 -12173
- package/dist/Skip-j0kX7pdq.js.map +0 -1
- package/dist/index-DyTeTfmj.js.map +0 -1
- package/dist/index-R7GxO-36.js +0 -165
- package/dist/internals.cjs +0 -286
- package/dist/internals.cjs.map +0 -1
- package/dist/internals.mjs.map +0 -1
- package/dist/testHelper.cjs +0 -780
- package/dist/testHelper.cjs.map +0 -1
- package/dist/testHelper.mjs.map +0 -1
- package/dist/yjs.cjs +0 -151
- package/dist/yjs.cjs.map +0 -1
- package/dist/yjs.mjs.map +0 -1
- package/src/index.js +0 -153
- package/src/internals.js +0 -44
- package/src/structs/AbstractStruct.js +0 -59
- package/src/structs/ContentAny.js +0 -115
- package/src/structs/ContentBinary.js +0 -93
- package/src/structs/ContentDeleted.js +0 -101
- package/src/structs/ContentDoc.js +0 -141
- package/src/structs/ContentEmbed.js +0 -98
- package/src/structs/ContentFormat.js +0 -105
- package/src/structs/ContentJSON.js +0 -119
- package/src/structs/ContentString.js +0 -113
- package/src/structs/ContentType.js +0 -176
- package/src/structs/GC.js +0 -80
- package/src/structs/Item.js +0 -845
- package/src/structs/Skip.js +0 -75
- package/src/types/AbstractType.js +0 -1434
- package/src/types/YArray.js +0 -270
- package/src/types/YMap.js +0 -244
- package/src/types/YText.js +0 -934
- package/src/types/YXmlElement.js +0 -227
- package/src/types/YXmlFragment.js +0 -266
- package/src/types/YXmlHook.js +0 -68
- package/src/types/YXmlText.js +0 -66
- package/src/utils/AbstractConnector.js +0 -25
- package/src/utils/AttributionManager.js +0 -619
- package/src/utils/Doc.js +0 -372
- package/src/utils/EventHandler.js +0 -87
- package/src/utils/ID.js +0 -89
- package/src/utils/IdMap.js +0 -629
- package/src/utils/IdSet.js +0 -823
- package/src/utils/RelativePosition.js +0 -352
- package/src/utils/Snapshot.js +0 -220
- package/src/utils/StructSet.js +0 -137
- package/src/utils/StructStore.js +0 -289
- package/src/utils/Transaction.js +0 -489
- package/src/utils/UndoManager.js +0 -391
- package/src/utils/UpdateDecoder.js +0 -281
- package/src/utils/UpdateEncoder.js +0 -320
- package/src/utils/YEvent.js +0 -216
- package/src/utils/delta-helpers.js +0 -54
- package/src/utils/encoding.js +0 -623
- package/src/utils/isParentOf.js +0 -21
- package/src/utils/logging.js +0 -21
- package/src/utils/types.js +0 -28
- package/src/utils/updates.js +0 -715
- package/tests/testHelper.js +0 -600
package/src/structs/Item.js
DELETED
|
@@ -1,845 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GC,
|
|
3
|
-
getState,
|
|
4
|
-
AbstractStruct,
|
|
5
|
-
replaceStruct,
|
|
6
|
-
addStruct,
|
|
7
|
-
addToIdSet,
|
|
8
|
-
findRootTypeKey,
|
|
9
|
-
compareIDs,
|
|
10
|
-
getItem,
|
|
11
|
-
getItemCleanEnd,
|
|
12
|
-
getItemCleanStart,
|
|
13
|
-
readContentDeleted,
|
|
14
|
-
readContentBinary,
|
|
15
|
-
readContentJSON,
|
|
16
|
-
readContentAny,
|
|
17
|
-
readContentString,
|
|
18
|
-
readContentEmbed,
|
|
19
|
-
readContentDoc,
|
|
20
|
-
createID,
|
|
21
|
-
readContentFormat,
|
|
22
|
-
readContentType,
|
|
23
|
-
addChangedTypeToTransaction,
|
|
24
|
-
addStructToIdSet,
|
|
25
|
-
IdSet, StackItem, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line
|
|
26
|
-
} from '../internals.js'
|
|
27
|
-
|
|
28
|
-
import * as error from 'lib0/error'
|
|
29
|
-
import * as binary from 'lib0/binary'
|
|
30
|
-
import * as array from 'lib0/array'
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @typedef {import('../utils/types.js').YType} YType__
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @todo This should return several items
|
|
38
|
-
*
|
|
39
|
-
* @param {StructStore} store
|
|
40
|
-
* @param {ID} id
|
|
41
|
-
* @return {{item:Item, diff:number}}
|
|
42
|
-
*/
|
|
43
|
-
export const followRedone = (store, id) => {
|
|
44
|
-
/**
|
|
45
|
-
* @type {ID|null}
|
|
46
|
-
*/
|
|
47
|
-
let nextID = id
|
|
48
|
-
let diff = 0
|
|
49
|
-
let item
|
|
50
|
-
do {
|
|
51
|
-
if (diff > 0) {
|
|
52
|
-
nextID = createID(nextID.client, nextID.clock + diff)
|
|
53
|
-
}
|
|
54
|
-
item = getItem(store, nextID)
|
|
55
|
-
diff = nextID.clock - item.id.clock
|
|
56
|
-
nextID = item.redone
|
|
57
|
-
} while (nextID !== null && item instanceof Item)
|
|
58
|
-
return {
|
|
59
|
-
item, diff
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Make sure that neither item nor any of its parents is ever deleted.
|
|
65
|
-
*
|
|
66
|
-
* This property does not persist when storing it into a database or when
|
|
67
|
-
* sending it to other peers
|
|
68
|
-
*
|
|
69
|
-
* @param {Item|null} item
|
|
70
|
-
* @param {boolean} keep
|
|
71
|
-
*/
|
|
72
|
-
export const keepItem = (item, keep) => {
|
|
73
|
-
while (item !== null && item.keep !== keep) {
|
|
74
|
-
item.keep = keep
|
|
75
|
-
item = /** @type {YType__} */ (item.parent)._item
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Split leftItem into two items
|
|
81
|
-
* @param {Transaction?} transaction
|
|
82
|
-
* @param {Item} leftItem
|
|
83
|
-
* @param {number} diff
|
|
84
|
-
* @return {Item}
|
|
85
|
-
*
|
|
86
|
-
* @function
|
|
87
|
-
* @private
|
|
88
|
-
*/
|
|
89
|
-
export const splitItem = (transaction, leftItem, diff) => {
|
|
90
|
-
// create rightItem
|
|
91
|
-
const { client, clock } = leftItem.id
|
|
92
|
-
const rightItem = new Item(
|
|
93
|
-
createID(client, clock + diff),
|
|
94
|
-
leftItem,
|
|
95
|
-
createID(client, clock + diff - 1),
|
|
96
|
-
leftItem.right,
|
|
97
|
-
leftItem.rightOrigin,
|
|
98
|
-
leftItem.parent,
|
|
99
|
-
leftItem.parentSub,
|
|
100
|
-
leftItem.content.splice(diff)
|
|
101
|
-
)
|
|
102
|
-
if (leftItem.deleted) {
|
|
103
|
-
rightItem.markDeleted()
|
|
104
|
-
}
|
|
105
|
-
if (leftItem.keep) {
|
|
106
|
-
rightItem.keep = true
|
|
107
|
-
}
|
|
108
|
-
if (leftItem.redone !== null) {
|
|
109
|
-
rightItem.redone = createID(leftItem.redone.client, leftItem.redone.clock + diff)
|
|
110
|
-
}
|
|
111
|
-
if (transaction != null) {
|
|
112
|
-
// update left (do not set leftItem.rightOrigin as it will lead to problems when syncing)
|
|
113
|
-
leftItem.right = rightItem
|
|
114
|
-
// update right
|
|
115
|
-
if (rightItem.right !== null) {
|
|
116
|
-
rightItem.right.left = rightItem
|
|
117
|
-
}
|
|
118
|
-
// right is more specific.
|
|
119
|
-
transaction._mergeStructs.push(rightItem)
|
|
120
|
-
// update parent._map
|
|
121
|
-
if (rightItem.parentSub !== null && rightItem.right === null) {
|
|
122
|
-
/** @type {YType__} */ (rightItem.parent)._map.set(rightItem.parentSub, rightItem)
|
|
123
|
-
}
|
|
124
|
-
} else {
|
|
125
|
-
rightItem.left = null
|
|
126
|
-
rightItem.right = null
|
|
127
|
-
}
|
|
128
|
-
leftItem.length = diff
|
|
129
|
-
return rightItem
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* More generalized version of splitItem. Split leftStruct into two structs
|
|
134
|
-
* @param {Transaction?} transaction
|
|
135
|
-
* @param {AbstractStruct} leftStruct
|
|
136
|
-
* @param {number} diff
|
|
137
|
-
* @return {GC|Item}
|
|
138
|
-
*
|
|
139
|
-
* @function
|
|
140
|
-
* @private
|
|
141
|
-
*/
|
|
142
|
-
export const splitStruct = (transaction, leftStruct, diff) => {
|
|
143
|
-
if (leftStruct instanceof Item) {
|
|
144
|
-
return splitItem(transaction, leftStruct, diff)
|
|
145
|
-
} else {
|
|
146
|
-
const rightItem = leftStruct.splice(diff)
|
|
147
|
-
transaction?._mergeStructs.push(rightItem)
|
|
148
|
-
return rightItem
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* @param {Array<StackItem>} stack
|
|
154
|
-
* @param {ID} id
|
|
155
|
-
*/
|
|
156
|
-
const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackItem} s */ s => s.deletions.hasId(id))
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Redoes the effect of this operation.
|
|
160
|
-
*
|
|
161
|
-
* @param {Transaction} transaction The Yjs instance.
|
|
162
|
-
* @param {Item} item
|
|
163
|
-
* @param {Set<Item>} redoitems
|
|
164
|
-
* @param {IdSet} itemsToDelete
|
|
165
|
-
* @param {boolean} ignoreRemoteMapChanges
|
|
166
|
-
* @param {import('../utils/UndoManager.js').UndoManager} um
|
|
167
|
-
*
|
|
168
|
-
* @return {Item|null}
|
|
169
|
-
*
|
|
170
|
-
* @private
|
|
171
|
-
*/
|
|
172
|
-
export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) => {
|
|
173
|
-
const doc = transaction.doc
|
|
174
|
-
const store = doc.store
|
|
175
|
-
const ownClientID = doc.clientID
|
|
176
|
-
const redone = item.redone
|
|
177
|
-
if (redone !== null) {
|
|
178
|
-
return getItemCleanStart(transaction, redone)
|
|
179
|
-
}
|
|
180
|
-
let parentItem = /** @type {YType__} */ (item.parent)._item
|
|
181
|
-
/**
|
|
182
|
-
* @type {Item|null}
|
|
183
|
-
*/
|
|
184
|
-
let left = null
|
|
185
|
-
/**
|
|
186
|
-
* @type {Item|null}
|
|
187
|
-
*/
|
|
188
|
-
let right
|
|
189
|
-
// make sure that parent is redone
|
|
190
|
-
if (parentItem !== null && parentItem.deleted === true) {
|
|
191
|
-
// try to undo parent if it will be undone anyway
|
|
192
|
-
if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) === null)) {
|
|
193
|
-
return null
|
|
194
|
-
}
|
|
195
|
-
while (parentItem.redone !== null) {
|
|
196
|
-
parentItem = getItemCleanStart(transaction, parentItem.redone)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* @type {YType__}
|
|
201
|
-
*/
|
|
202
|
-
const parentType = /** @type {YType__} */ (parentItem === null ? item.parent : /** @type {ContentType} */ (parentItem.content).type)
|
|
203
|
-
|
|
204
|
-
if (item.parentSub === null) {
|
|
205
|
-
// Is an array item. Insert at the old position
|
|
206
|
-
left = item.left
|
|
207
|
-
right = item
|
|
208
|
-
// find next cloned_redo items
|
|
209
|
-
while (left !== null) {
|
|
210
|
-
/**
|
|
211
|
-
* @type {Item|null}
|
|
212
|
-
*/
|
|
213
|
-
let leftTrace = left
|
|
214
|
-
// trace redone until parent matches
|
|
215
|
-
while (leftTrace !== null && /** @type {YType__} */ (leftTrace.parent)._item !== parentItem) {
|
|
216
|
-
leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, leftTrace.redone)
|
|
217
|
-
}
|
|
218
|
-
if (leftTrace !== null && /** @type {YType__} */ (leftTrace.parent)._item === parentItem) {
|
|
219
|
-
left = leftTrace
|
|
220
|
-
break
|
|
221
|
-
}
|
|
222
|
-
left = left.left
|
|
223
|
-
}
|
|
224
|
-
while (right !== null) {
|
|
225
|
-
/**
|
|
226
|
-
* @type {Item|null}
|
|
227
|
-
*/
|
|
228
|
-
let rightTrace = right
|
|
229
|
-
// trace redone until parent matches
|
|
230
|
-
while (rightTrace !== null && /** @type {YType__} */ (rightTrace.parent)._item !== parentItem) {
|
|
231
|
-
rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, rightTrace.redone)
|
|
232
|
-
}
|
|
233
|
-
if (rightTrace !== null && /** @type {YType__} */ (rightTrace.parent)._item === parentItem) {
|
|
234
|
-
right = rightTrace
|
|
235
|
-
break
|
|
236
|
-
}
|
|
237
|
-
right = right.right
|
|
238
|
-
}
|
|
239
|
-
} else {
|
|
240
|
-
right = null
|
|
241
|
-
if (item.right && !ignoreRemoteMapChanges) {
|
|
242
|
-
left = item
|
|
243
|
-
// Iterate right while right is in itemsToDelete
|
|
244
|
-
// If it is intended to delete right while item is redone, we can expect that item should replace right.
|
|
245
|
-
while (left !== null && left.right !== null && (left.right.redone || itemsToDelete.hasId(left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) {
|
|
246
|
-
left = left.right
|
|
247
|
-
// follow redone
|
|
248
|
-
while (left.redone) left = getItemCleanStart(transaction, left.redone)
|
|
249
|
-
}
|
|
250
|
-
if (left && left.right !== null) {
|
|
251
|
-
// It is not possible to redo this item because it conflicts with a
|
|
252
|
-
// change from another client
|
|
253
|
-
return null
|
|
254
|
-
}
|
|
255
|
-
} else {
|
|
256
|
-
left = parentType._map.get(item.parentSub) || null
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
const nextClock = getState(store, ownClientID)
|
|
260
|
-
const nextId = createID(ownClientID, nextClock)
|
|
261
|
-
const redoneItem = new Item(
|
|
262
|
-
nextId,
|
|
263
|
-
left, left && left.lastId,
|
|
264
|
-
right, right && right.id,
|
|
265
|
-
parentType,
|
|
266
|
-
item.parentSub,
|
|
267
|
-
item.content.copy()
|
|
268
|
-
)
|
|
269
|
-
item.redone = nextId
|
|
270
|
-
keepItem(redoneItem, true)
|
|
271
|
-
redoneItem.integrate(transaction, 0)
|
|
272
|
-
return redoneItem
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Abstract class that represents any content.
|
|
277
|
-
*/
|
|
278
|
-
export class Item extends AbstractStruct {
|
|
279
|
-
/**
|
|
280
|
-
* @param {ID} id
|
|
281
|
-
* @param {Item | null} left
|
|
282
|
-
* @param {ID | null} origin
|
|
283
|
-
* @param {Item | null} right
|
|
284
|
-
* @param {ID | null} rightOrigin
|
|
285
|
-
* @param {AbstractType<any,any>|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it.
|
|
286
|
-
* @param {string | null} parentSub
|
|
287
|
-
* @param {AbstractContent} content
|
|
288
|
-
*/
|
|
289
|
-
constructor (id, left, origin, right, rightOrigin, parent, parentSub, content) {
|
|
290
|
-
super(id, content.getLength())
|
|
291
|
-
/**
|
|
292
|
-
* The item that was originally to the left of this item.
|
|
293
|
-
* @type {ID | null}
|
|
294
|
-
*/
|
|
295
|
-
this.origin = origin
|
|
296
|
-
/**
|
|
297
|
-
* The item that is currently to the left of this item.
|
|
298
|
-
* @type {Item | null}
|
|
299
|
-
*/
|
|
300
|
-
this.left = left
|
|
301
|
-
/**
|
|
302
|
-
* The item that is currently to the right of this item.
|
|
303
|
-
* @type {Item | null}
|
|
304
|
-
*/
|
|
305
|
-
this.right = right
|
|
306
|
-
/**
|
|
307
|
-
* The item that was originally to the right of this item.
|
|
308
|
-
* @type {ID | null}
|
|
309
|
-
*/
|
|
310
|
-
this.rightOrigin = rightOrigin
|
|
311
|
-
/**
|
|
312
|
-
* @type {AbstractType<any,any>|ID|null}
|
|
313
|
-
*/
|
|
314
|
-
this.parent = parent
|
|
315
|
-
/**
|
|
316
|
-
* If the parent refers to this item with some kind of key (e.g. YMap, the
|
|
317
|
-
* key is specified here. The key is then used to refer to the list in which
|
|
318
|
-
* to insert this item. If `parentSub = null` type._start is the list in
|
|
319
|
-
* which to insert to. Otherwise it is `parent._map`.
|
|
320
|
-
* @type {String | null}
|
|
321
|
-
*/
|
|
322
|
-
this.parentSub = parentSub
|
|
323
|
-
/**
|
|
324
|
-
* If this type's effect is redone this type refers to the type that undid
|
|
325
|
-
* this operation.
|
|
326
|
-
* @type {ID | null}
|
|
327
|
-
*/
|
|
328
|
-
this.redone = null
|
|
329
|
-
/**
|
|
330
|
-
* @type {AbstractContent}
|
|
331
|
-
*/
|
|
332
|
-
this.content = content
|
|
333
|
-
/**
|
|
334
|
-
* bit1: keep
|
|
335
|
-
* bit2: countable
|
|
336
|
-
* bit3: deleted
|
|
337
|
-
* bit4: mark - mark node as fast-search-marker
|
|
338
|
-
* @type {number} byte
|
|
339
|
-
*/
|
|
340
|
-
this.info = this.content.isCountable() ? binary.BIT2 : 0
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* This is used to mark the item as an indexed fast-search marker
|
|
345
|
-
*
|
|
346
|
-
* @type {boolean}
|
|
347
|
-
*/
|
|
348
|
-
set marker (isMarked) {
|
|
349
|
-
if (((this.info & binary.BIT4) > 0) !== isMarked) {
|
|
350
|
-
this.info ^= binary.BIT4
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
get marker () {
|
|
355
|
-
return (this.info & binary.BIT4) > 0
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* If true, do not garbage collect this Item.
|
|
360
|
-
*/
|
|
361
|
-
get keep () {
|
|
362
|
-
return (this.info & binary.BIT1) > 0
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
set keep (doKeep) {
|
|
366
|
-
if (this.keep !== doKeep) {
|
|
367
|
-
this.info ^= binary.BIT1
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
get countable () {
|
|
372
|
-
return (this.info & binary.BIT2) > 0
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Whether this item was deleted or not.
|
|
377
|
-
* @type {Boolean}
|
|
378
|
-
*/
|
|
379
|
-
get deleted () {
|
|
380
|
-
return (this.info & binary.BIT3) > 0
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
set deleted (doDelete) {
|
|
384
|
-
if (this.deleted !== doDelete) {
|
|
385
|
-
this.info ^= binary.BIT3
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
markDeleted () {
|
|
390
|
-
this.info |= binary.BIT3
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Return the creator clientID of the missing op or define missing items and return null.
|
|
395
|
-
*
|
|
396
|
-
* @param {Transaction} transaction
|
|
397
|
-
* @param {StructStore} store
|
|
398
|
-
* @return {null | number}
|
|
399
|
-
*/
|
|
400
|
-
getMissing (transaction, store) {
|
|
401
|
-
if (this.origin && (this.origin.clock >= getState(store, this.origin.client) || store.skips.hasId(this.origin))) {
|
|
402
|
-
return this.origin.client
|
|
403
|
-
}
|
|
404
|
-
if (this.rightOrigin && (this.rightOrigin.clock >= getState(store, this.rightOrigin.client) || store.skips.hasId(this.rightOrigin))) {
|
|
405
|
-
return this.rightOrigin.client
|
|
406
|
-
}
|
|
407
|
-
if (this.parent && this.parent.constructor === ID && (this.parent.clock >= getState(store, this.parent.client) || store.skips.hasId(this.parent))) {
|
|
408
|
-
return this.parent.client
|
|
409
|
-
}
|
|
410
|
-
// We have all missing ids, now find the items
|
|
411
|
-
if (this.origin) {
|
|
412
|
-
this.left = getItemCleanEnd(transaction, store, this.origin)
|
|
413
|
-
this.origin = this.left.lastId
|
|
414
|
-
}
|
|
415
|
-
if (this.rightOrigin) {
|
|
416
|
-
this.right = getItemCleanStart(transaction, this.rightOrigin)
|
|
417
|
-
this.rightOrigin = this.right.id
|
|
418
|
-
}
|
|
419
|
-
if ((this.left && this.left.constructor === GC) || (this.right && this.right.constructor === GC)) {
|
|
420
|
-
this.parent = null
|
|
421
|
-
} else if (!this.parent) {
|
|
422
|
-
// only set parent if this shouldn't be garbage collected
|
|
423
|
-
if (this.left && this.left.constructor === Item) {
|
|
424
|
-
this.parent = this.left.parent
|
|
425
|
-
this.parentSub = this.left.parentSub
|
|
426
|
-
} else if (this.right && this.right.constructor === Item) {
|
|
427
|
-
this.parent = this.right.parent
|
|
428
|
-
this.parentSub = this.right.parentSub
|
|
429
|
-
}
|
|
430
|
-
} else if (this.parent.constructor === ID) {
|
|
431
|
-
const parentItem = getItem(store, this.parent)
|
|
432
|
-
if (parentItem.constructor === GC) {
|
|
433
|
-
this.parent = null
|
|
434
|
-
} else {
|
|
435
|
-
this.parent = /** @type {ContentType} */ (parentItem.content).type
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
return null
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* @param {Transaction} transaction
|
|
443
|
-
* @param {number} offset
|
|
444
|
-
*/
|
|
445
|
-
integrate (transaction, offset) {
|
|
446
|
-
if (offset > 0) {
|
|
447
|
-
this.id.clock += offset
|
|
448
|
-
this.left = getItemCleanEnd(transaction, transaction.doc.store, createID(this.id.client, this.id.clock - 1))
|
|
449
|
-
this.origin = this.left.lastId
|
|
450
|
-
this.content = this.content.splice(offset)
|
|
451
|
-
this.length -= offset
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
if (this.parent) {
|
|
455
|
-
if ((!this.left && (!this.right || this.right.left !== null)) || (this.left && this.left.right !== this.right)) {
|
|
456
|
-
/**
|
|
457
|
-
* @type {Item|null}
|
|
458
|
-
*/
|
|
459
|
-
let left = this.left
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* @type {Item|null}
|
|
463
|
-
*/
|
|
464
|
-
let o
|
|
465
|
-
// set o to the first conflicting item
|
|
466
|
-
if (left !== null) {
|
|
467
|
-
o = left.right
|
|
468
|
-
} else if (this.parentSub !== null) {
|
|
469
|
-
o = /** @type {AbstractType<any>} */ (this.parent)._map.get(this.parentSub) || null
|
|
470
|
-
while (o !== null && o.left !== null) {
|
|
471
|
-
o = o.left
|
|
472
|
-
}
|
|
473
|
-
} else {
|
|
474
|
-
o = /** @type {AbstractType<any>} */ (this.parent)._start
|
|
475
|
-
}
|
|
476
|
-
// TODO: use something like DeleteSet here (a tree implementation would be best)
|
|
477
|
-
// @todo use global set definitions
|
|
478
|
-
/**
|
|
479
|
-
* @type {Set<Item>}
|
|
480
|
-
*/
|
|
481
|
-
const conflictingItems = new Set()
|
|
482
|
-
/**
|
|
483
|
-
* @type {Set<Item>}
|
|
484
|
-
*/
|
|
485
|
-
const itemsBeforeOrigin = new Set()
|
|
486
|
-
// Let c in conflictingItems, b in itemsBeforeOrigin
|
|
487
|
-
// ***{origin}bbbb{this}{c,b}{c,b}{o}***
|
|
488
|
-
// Note that conflictingItems is a subset of itemsBeforeOrigin
|
|
489
|
-
while (o !== null && o !== this.right) {
|
|
490
|
-
itemsBeforeOrigin.add(o)
|
|
491
|
-
conflictingItems.add(o)
|
|
492
|
-
if (compareIDs(this.origin, o.origin)) {
|
|
493
|
-
// case 1
|
|
494
|
-
if (o.id.client < this.id.client) {
|
|
495
|
-
left = o
|
|
496
|
-
conflictingItems.clear()
|
|
497
|
-
} else if (compareIDs(this.rightOrigin, o.rightOrigin)) {
|
|
498
|
-
// this and o are conflicting and point to the same integration points. The id decides which item comes first.
|
|
499
|
-
// Since this is to the left of o, we can break here
|
|
500
|
-
break
|
|
501
|
-
} // else, o might be integrated before an item that this conflicts with. If so, we will find it in the next iterations
|
|
502
|
-
} else if (o.origin !== null && itemsBeforeOrigin.has(getItem(transaction.doc.store, o.origin))) { // use getItem instead of getItemCleanEnd because we don't want / need to split items.
|
|
503
|
-
// case 2
|
|
504
|
-
if (!conflictingItems.has(getItem(transaction.doc.store, o.origin))) {
|
|
505
|
-
left = o
|
|
506
|
-
conflictingItems.clear()
|
|
507
|
-
}
|
|
508
|
-
} else {
|
|
509
|
-
break
|
|
510
|
-
}
|
|
511
|
-
o = o.right
|
|
512
|
-
}
|
|
513
|
-
this.left = left
|
|
514
|
-
}
|
|
515
|
-
// reconnect left/right + update parent map/start if necessary
|
|
516
|
-
if (this.left !== null) {
|
|
517
|
-
const right = this.left.right
|
|
518
|
-
this.right = right
|
|
519
|
-
this.left.right = this
|
|
520
|
-
} else {
|
|
521
|
-
let r
|
|
522
|
-
if (this.parentSub !== null) {
|
|
523
|
-
r = /** @type {AbstractType<any>} */ (this.parent)._map.get(this.parentSub) || null
|
|
524
|
-
while (r !== null && r.left !== null) {
|
|
525
|
-
r = r.left
|
|
526
|
-
}
|
|
527
|
-
} else {
|
|
528
|
-
r = /** @type {AbstractType<any>} */ (this.parent)._start
|
|
529
|
-
;/** @type {AbstractType<any>} */ (this.parent)._start = this
|
|
530
|
-
}
|
|
531
|
-
this.right = r
|
|
532
|
-
}
|
|
533
|
-
if (this.right !== null) {
|
|
534
|
-
this.right.left = this
|
|
535
|
-
} else if (this.parentSub !== null) {
|
|
536
|
-
// set as current parent value if right === null and this is parentSub
|
|
537
|
-
/** @type {AbstractType<any>} */ (this.parent)._map.set(this.parentSub, this)
|
|
538
|
-
if (this.left !== null) {
|
|
539
|
-
// this is the current attribute value of parent. delete right
|
|
540
|
-
this.left.delete(transaction)
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
// adjust length of parent
|
|
544
|
-
if (this.parentSub === null && this.countable && !this.deleted) {
|
|
545
|
-
/** @type {AbstractType<any>} */ (this.parent)._length += this.length
|
|
546
|
-
}
|
|
547
|
-
addStructToIdSet(transaction.insertSet, this)
|
|
548
|
-
addStruct(transaction.doc.store, this)
|
|
549
|
-
this.content.integrate(transaction, this)
|
|
550
|
-
// add parent to transaction.changed
|
|
551
|
-
addChangedTypeToTransaction(transaction, /** @type {import('../utils/types.js').YType} */ (this.parent), this.parentSub)
|
|
552
|
-
if ((/** @type {AbstractType<any>} */ (this.parent)._item !== null && /** @type {AbstractType<any>} */ (this.parent)._item.deleted) || (this.parentSub !== null && this.right !== null)) {
|
|
553
|
-
// delete if parent is deleted or if this is not the current attribute value of parent
|
|
554
|
-
this.delete(transaction)
|
|
555
|
-
}
|
|
556
|
-
} else {
|
|
557
|
-
// parent is not defined. Integrate GC struct instead
|
|
558
|
-
new GC(this.id, this.length).integrate(transaction, 0)
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Returns the next non-deleted item
|
|
564
|
-
*/
|
|
565
|
-
get next () {
|
|
566
|
-
let n = this.right
|
|
567
|
-
while (n !== null && n.deleted) {
|
|
568
|
-
n = n.right
|
|
569
|
-
}
|
|
570
|
-
return n
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* Returns the previous non-deleted item
|
|
575
|
-
*/
|
|
576
|
-
get prev () {
|
|
577
|
-
let n = this.left
|
|
578
|
-
while (n !== null && n.deleted) {
|
|
579
|
-
n = n.left
|
|
580
|
-
}
|
|
581
|
-
return n
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* Computes the last content address of this Item.
|
|
586
|
-
*/
|
|
587
|
-
get lastId () {
|
|
588
|
-
// allocating ids is pretty costly because of the amount of ids created, so we try to reuse whenever possible
|
|
589
|
-
return this.length === 1 ? this.id : createID(this.id.client, this.id.clock + this.length - 1)
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Try to merge two items
|
|
594
|
-
*
|
|
595
|
-
* @param {Item} right
|
|
596
|
-
* @return {boolean}
|
|
597
|
-
*/
|
|
598
|
-
mergeWith (right) {
|
|
599
|
-
if (
|
|
600
|
-
this.constructor === right.constructor &&
|
|
601
|
-
compareIDs(right.origin, this.lastId) &&
|
|
602
|
-
this.right === right &&
|
|
603
|
-
compareIDs(this.rightOrigin, right.rightOrigin) &&
|
|
604
|
-
this.id.client === right.id.client &&
|
|
605
|
-
this.id.clock + this.length === right.id.clock &&
|
|
606
|
-
this.deleted === right.deleted &&
|
|
607
|
-
this.redone === null &&
|
|
608
|
-
right.redone === null &&
|
|
609
|
-
this.content.constructor === right.content.constructor &&
|
|
610
|
-
this.content.mergeWith(right.content)
|
|
611
|
-
) {
|
|
612
|
-
const searchMarker = /** @type {AbstractType<any>} */ (this.parent)._searchMarker
|
|
613
|
-
if (searchMarker) {
|
|
614
|
-
searchMarker.forEach(marker => {
|
|
615
|
-
if (marker.p === right) {
|
|
616
|
-
// right is going to be "forgotten" so we need to update the marker
|
|
617
|
-
marker.p = this
|
|
618
|
-
// adjust marker index
|
|
619
|
-
if (!this.deleted && this.countable) {
|
|
620
|
-
marker.index -= this.length
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
})
|
|
624
|
-
}
|
|
625
|
-
if (right.keep) {
|
|
626
|
-
this.keep = true
|
|
627
|
-
}
|
|
628
|
-
this.right = right.right
|
|
629
|
-
if (this.right !== null) {
|
|
630
|
-
this.right.left = this
|
|
631
|
-
}
|
|
632
|
-
this.length += right.length
|
|
633
|
-
return true
|
|
634
|
-
}
|
|
635
|
-
return false
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* Mark this Item as deleted.
|
|
640
|
-
*
|
|
641
|
-
* @param {Transaction} transaction
|
|
642
|
-
*/
|
|
643
|
-
delete (transaction) {
|
|
644
|
-
if (!this.deleted) {
|
|
645
|
-
const parent = /** @type {import('../utils/types.js').YType} */ (this.parent)
|
|
646
|
-
// adjust the length of parent
|
|
647
|
-
if (this.countable && this.parentSub === null) {
|
|
648
|
-
parent._length -= this.length
|
|
649
|
-
}
|
|
650
|
-
this.markDeleted()
|
|
651
|
-
addToIdSet(transaction.deleteSet, this.id.client, this.id.clock, this.length)
|
|
652
|
-
addChangedTypeToTransaction(transaction, parent, this.parentSub)
|
|
653
|
-
this.content.delete(transaction)
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
* @param {Transaction} tr
|
|
659
|
-
* @param {boolean} parentGCd
|
|
660
|
-
*/
|
|
661
|
-
gc (tr, parentGCd) {
|
|
662
|
-
if (!this.deleted) {
|
|
663
|
-
throw error.unexpectedCase()
|
|
664
|
-
}
|
|
665
|
-
this.content.gc(tr)
|
|
666
|
-
if (parentGCd) {
|
|
667
|
-
replaceStruct(tr, this, new GC(this.id, this.length))
|
|
668
|
-
} else {
|
|
669
|
-
this.content = new ContentDeleted(this.length)
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
* Transform the properties of this type to binary and write it to an
|
|
675
|
-
* BinaryEncoder.
|
|
676
|
-
*
|
|
677
|
-
* This is called when this Item is sent to a remote peer.
|
|
678
|
-
*
|
|
679
|
-
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
|
|
680
|
-
* @param {number} offset
|
|
681
|
-
* @param {number} offsetEnd
|
|
682
|
-
*/
|
|
683
|
-
write (encoder, offset, offsetEnd) {
|
|
684
|
-
const origin = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin
|
|
685
|
-
const rightOrigin = this.rightOrigin
|
|
686
|
-
const parentSub = this.parentSub
|
|
687
|
-
const info = (this.content.getRef() & binary.BITS5) |
|
|
688
|
-
(origin === null ? 0 : binary.BIT8) | // origin is defined
|
|
689
|
-
(rightOrigin === null ? 0 : binary.BIT7) | // right origin is defined
|
|
690
|
-
(parentSub === null ? 0 : binary.BIT6) // parentSub is non-null
|
|
691
|
-
encoder.writeInfo(info)
|
|
692
|
-
if (origin !== null) {
|
|
693
|
-
encoder.writeLeftID(origin)
|
|
694
|
-
}
|
|
695
|
-
if (rightOrigin !== null) {
|
|
696
|
-
encoder.writeRightID(rightOrigin)
|
|
697
|
-
}
|
|
698
|
-
if (origin === null && rightOrigin === null) {
|
|
699
|
-
const parent = /** @type {AbstractType<any>} */ (this.parent)
|
|
700
|
-
if (parent._item !== undefined) {
|
|
701
|
-
const parentItem = parent._item
|
|
702
|
-
if (parentItem === null) {
|
|
703
|
-
// parent type on y._map
|
|
704
|
-
// find the correct key
|
|
705
|
-
const ykey = findRootTypeKey(parent)
|
|
706
|
-
encoder.writeParentInfo(true) // write parentYKey
|
|
707
|
-
encoder.writeString(ykey)
|
|
708
|
-
} else {
|
|
709
|
-
encoder.writeParentInfo(false) // write parent id
|
|
710
|
-
encoder.writeLeftID(parentItem.id)
|
|
711
|
-
}
|
|
712
|
-
} else if (parent.constructor === String) { // this edge case was added by differential updates
|
|
713
|
-
encoder.writeParentInfo(true) // write parentYKey
|
|
714
|
-
encoder.writeString(parent)
|
|
715
|
-
} else if (parent.constructor === ID) {
|
|
716
|
-
encoder.writeParentInfo(false) // write parent id
|
|
717
|
-
encoder.writeLeftID(parent)
|
|
718
|
-
} else {
|
|
719
|
-
error.unexpectedCase()
|
|
720
|
-
}
|
|
721
|
-
if (parentSub !== null) {
|
|
722
|
-
encoder.writeString(parentSub)
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
this.content.write(encoder, offset, offsetEnd)
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
|
731
|
-
* @param {number} info
|
|
732
|
-
*/
|
|
733
|
-
export const readItemContent = (decoder, info) => contentRefs[info & binary.BITS5](decoder)
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* A lookup map for reading Item content.
|
|
737
|
-
*
|
|
738
|
-
* @type {Array<function(UpdateDecoderV1 | UpdateDecoderV2):AbstractContent>}
|
|
739
|
-
*/
|
|
740
|
-
export const contentRefs = [
|
|
741
|
-
() => { error.unexpectedCase() }, // GC is not ItemContent
|
|
742
|
-
readContentDeleted, // 1
|
|
743
|
-
readContentJSON, // 2
|
|
744
|
-
readContentBinary, // 3
|
|
745
|
-
readContentString, // 4
|
|
746
|
-
readContentEmbed, // 5
|
|
747
|
-
readContentFormat, // 6
|
|
748
|
-
readContentType, // 7
|
|
749
|
-
readContentAny, // 8
|
|
750
|
-
readContentDoc, // 9
|
|
751
|
-
() => { error.unexpectedCase() } // 10 - Skip is not ItemContent
|
|
752
|
-
]
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
* Do not implement this class!
|
|
756
|
-
*/
|
|
757
|
-
export class AbstractContent {
|
|
758
|
-
/**
|
|
759
|
-
* @return {number}
|
|
760
|
-
*/
|
|
761
|
-
getLength () {
|
|
762
|
-
throw error.methodUnimplemented()
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
/**
|
|
766
|
-
* @return {Array<any>}
|
|
767
|
-
*/
|
|
768
|
-
getContent () {
|
|
769
|
-
throw error.methodUnimplemented()
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/**
|
|
773
|
-
* Should return false if this Item is some kind of meta information
|
|
774
|
-
* (e.g. format information).
|
|
775
|
-
*
|
|
776
|
-
* * Whether this Item should be addressable via `yarray.get(i)`
|
|
777
|
-
* * Whether this Item should be counted when computing yarray.length
|
|
778
|
-
*
|
|
779
|
-
* @return {boolean}
|
|
780
|
-
*/
|
|
781
|
-
isCountable () {
|
|
782
|
-
throw error.methodUnimplemented()
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
/**
|
|
786
|
-
* @return {AbstractContent}
|
|
787
|
-
*/
|
|
788
|
-
copy () {
|
|
789
|
-
throw error.methodUnimplemented()
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
/**
|
|
793
|
-
* @param {number} _offset
|
|
794
|
-
* @return {AbstractContent}
|
|
795
|
-
*/
|
|
796
|
-
splice (_offset) {
|
|
797
|
-
throw error.methodUnimplemented()
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* @param {AbstractContent} _right
|
|
802
|
-
* @return {boolean}
|
|
803
|
-
*/
|
|
804
|
-
mergeWith (_right) {
|
|
805
|
-
throw error.methodUnimplemented()
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
/**
|
|
809
|
-
* @param {Transaction} _transaction
|
|
810
|
-
* @param {Item} _item
|
|
811
|
-
*/
|
|
812
|
-
integrate (_transaction, _item) {
|
|
813
|
-
throw error.methodUnimplemented()
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
/**
|
|
817
|
-
* @param {Transaction} _transaction
|
|
818
|
-
*/
|
|
819
|
-
delete (_transaction) {
|
|
820
|
-
throw error.methodUnimplemented()
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
/**
|
|
824
|
-
* @param {Transaction} _transaction
|
|
825
|
-
*/
|
|
826
|
-
gc (_transaction) {
|
|
827
|
-
throw error.methodUnimplemented()
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
/**
|
|
831
|
-
* @param {UpdateEncoderV1 | UpdateEncoderV2} _encoder
|
|
832
|
-
* @param {number} _offset
|
|
833
|
-
* @param {number} _offsetEnd
|
|
834
|
-
*/
|
|
835
|
-
write (_encoder, _offset, _offsetEnd) {
|
|
836
|
-
throw error.methodUnimplemented()
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
/**
|
|
840
|
-
* @return {number}
|
|
841
|
-
*/
|
|
842
|
-
getRef () {
|
|
843
|
-
throw error.methodUnimplemented()
|
|
844
|
-
}
|
|
845
|
-
}
|