@y/y 14.0.0-rc.2 → 14.0.0-rc.21
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 +36 -12
- package/dist/src/index.d.ts +24 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/structs/AbstractStruct.d.ts +14 -17
- package/dist/src/structs/AbstractStruct.d.ts.map +1 -1
- package/dist/src/structs/GC.d.ts +9 -14
- package/dist/src/structs/GC.d.ts.map +1 -1
- package/dist/src/structs/Item.d.ts +569 -31
- package/dist/src/structs/Item.d.ts.map +1 -1
- package/dist/src/structs/Skip.d.ts +9 -11
- package/dist/src/structs/Skip.d.ts.map +1 -1
- package/dist/src/utils/BlockSet.d.ts +8 -7
- package/dist/src/utils/BlockSet.d.ts.map +1 -1
- package/dist/src/utils/Doc.d.ts +4 -9
- package/dist/src/utils/Doc.d.ts.map +1 -1
- package/dist/src/utils/ID.d.ts +0 -1
- package/dist/src/utils/ID.d.ts.map +1 -1
- package/dist/src/utils/RelativePosition.d.ts +2 -5
- package/dist/src/utils/RelativePosition.d.ts.map +1 -1
- package/dist/src/utils/Renderer.d.ts +144 -0
- package/dist/src/utils/Renderer.d.ts.map +1 -0
- package/dist/src/utils/Snapshot.d.ts +7 -11
- package/dist/src/utils/Snapshot.d.ts.map +1 -1
- package/dist/src/utils/StructStore.d.ts +45 -23
- package/dist/src/utils/StructStore.d.ts.map +1 -1
- package/dist/src/utils/Transaction.d.ts +11 -21
- package/dist/src/utils/Transaction.d.ts.map +1 -1
- package/dist/src/utils/UndoManager.d.ts +13 -14
- package/dist/src/utils/UndoManager.d.ts.map +1 -1
- package/dist/src/utils/UpdateDecoder.d.ts +1 -1
- package/dist/src/utils/UpdateDecoder.d.ts.map +1 -1
- package/dist/src/utils/UpdateEncoder.d.ts +0 -1
- package/dist/src/utils/UpdateEncoder.d.ts.map +1 -1
- package/dist/src/utils/YEvent.d.ts +22 -26
- package/dist/src/utils/YEvent.d.ts.map +1 -1
- package/dist/src/utils/content-helper.d.ts +2 -0
- package/dist/src/utils/content-helper.d.ts.map +1 -0
- package/dist/src/utils/delta-helpers.d.ts +2 -3
- package/dist/src/utils/delta-helpers.d.ts.map +1 -1
- package/dist/src/utils/encoding-helpers.d.ts +6 -0
- package/dist/src/utils/encoding-helpers.d.ts.map +1 -0
- package/dist/src/utils/encoding.d.ts +16 -18
- package/dist/src/utils/encoding.d.ts.map +1 -1
- package/dist/src/utils/ids.d.ts +331 -0
- package/dist/src/utils/ids.d.ts.map +1 -0
- package/dist/src/utils/isParentOf.d.ts +1 -2
- package/dist/src/utils/isParentOf.d.ts.map +1 -1
- package/dist/src/utils/logging.d.ts +0 -1
- package/dist/src/utils/logging.d.ts.map +1 -1
- package/dist/src/utils/meta.d.ts +15 -64
- package/dist/src/utils/meta.d.ts.map +1 -1
- package/dist/src/utils/renderer-helpers.d.ts +99 -0
- package/dist/src/utils/renderer-helpers.d.ts.map +1 -0
- package/dist/src/utils/schemas.d.ts +3 -0
- package/dist/src/utils/schemas.d.ts.map +1 -0
- package/dist/src/utils/transaction-helpers.d.ts +18 -0
- package/dist/src/utils/transaction-helpers.d.ts.map +1 -0
- package/dist/src/utils/updates.d.ts +19 -25
- package/dist/src/utils/updates.d.ts.map +1 -1
- package/dist/src/ytype.d.ts +144 -41
- package/dist/src/ytype.d.ts.map +1 -1
- package/global.d.ts +53 -0
- package/package.json +12 -16
- package/src/index.js +32 -124
- package/src/structs/AbstractStruct.js +21 -16
- package/src/structs/GC.js +15 -20
- package/src/structs/Item.js +992 -318
- package/src/structs/Skip.js +16 -19
- package/src/utils/BlockSet.js +20 -20
- package/src/utils/Doc.js +18 -29
- package/src/utils/ID.js +0 -2
- package/src/utils/RelativePosition.js +15 -25
- package/src/utils/{AttributionManager.js → Renderer.js} +42 -197
- package/src/utils/Snapshot.js +15 -37
- package/src/utils/StructStore.js +89 -227
- package/src/utils/Transaction.js +89 -321
- package/src/utils/UndoManager.js +157 -19
- package/src/utils/UpdateDecoder.js +2 -3
- package/src/utils/UpdateEncoder.js +0 -4
- package/src/utils/YEvent.js +20 -26
- package/src/utils/content-helper.js +0 -0
- package/src/utils/delta-helpers.js +21 -42
- package/src/utils/encoding-helpers.js +110 -0
- package/src/utils/encoding.js +197 -122
- package/src/utils/ids.js +1527 -0
- package/src/utils/isParentOf.js +2 -4
- package/src/utils/logging.js +0 -4
- package/src/utils/meta.js +57 -46
- package/src/utils/renderer-helpers.js +110 -0
- package/src/utils/schemas.js +3 -0
- package/src/utils/transaction-helpers.js +413 -0
- package/src/utils/updates.js +24 -146
- package/src/ytype.js +626 -255
- package/tests/testHelper.js +10 -12
- package/dist/src/internals.d.ts +0 -36
- package/dist/src/internals.d.ts.map +0 -1
- package/dist/src/structs/ContentAny.d.ts +0 -67
- package/dist/src/structs/ContentAny.d.ts.map +0 -1
- package/dist/src/structs/ContentBinary.d.ts +0 -64
- package/dist/src/structs/ContentBinary.d.ts.map +0 -1
- package/dist/src/structs/ContentDeleted.d.ts +0 -64
- package/dist/src/structs/ContentDeleted.d.ts.map +0 -1
- package/dist/src/structs/ContentDoc.d.ts +0 -72
- package/dist/src/structs/ContentDoc.d.ts.map +0 -1
- package/dist/src/structs/ContentEmbed.d.ts +0 -67
- package/dist/src/structs/ContentEmbed.d.ts.map +0 -1
- package/dist/src/structs/ContentFormat.d.ts +0 -69
- package/dist/src/structs/ContentFormat.d.ts.map +0 -1
- package/dist/src/structs/ContentJSON.d.ts +0 -70
- package/dist/src/structs/ContentJSON.d.ts.map +0 -1
- package/dist/src/structs/ContentString.d.ts +0 -70
- package/dist/src/structs/ContentString.d.ts.map +0 -1
- package/dist/src/structs/ContentType.d.ts +0 -77
- package/dist/src/structs/ContentType.d.ts.map +0 -1
- package/dist/src/utils/AttributionManager.d.ts +0 -238
- package/dist/src/utils/AttributionManager.d.ts.map +0 -1
- package/dist/src/utils/IdMap.d.ts +0 -164
- package/dist/src/utils/IdMap.d.ts.map +0 -1
- package/dist/src/utils/IdSet.d.ts +0 -163
- package/dist/src/utils/IdSet.d.ts.map +0 -1
- package/dist/tests/IdMap.tests.d.ts +0 -9
- package/dist/tests/IdMap.tests.d.ts.map +0 -1
- package/dist/tests/IdSet.tests.d.ts +0 -9
- package/dist/tests/IdSet.tests.d.ts.map +0 -1
- package/dist/tests/attribution.tests.d.ts +0 -9
- package/dist/tests/attribution.tests.d.ts.map +0 -1
- package/dist/tests/compatibility.tests.d.ts +0 -5
- package/dist/tests/compatibility.tests.d.ts.map +0 -1
- package/dist/tests/delta.tests.d.ts +0 -7
- package/dist/tests/delta.tests.d.ts.map +0 -1
- package/dist/tests/doc.tests.d.ts +0 -13
- package/dist/tests/doc.tests.d.ts.map +0 -1
- package/dist/tests/encoding.tests.d.ts +0 -5
- package/dist/tests/encoding.tests.d.ts.map +0 -1
- package/dist/tests/index.d.ts +0 -2
- package/dist/tests/index.d.ts.map +0 -1
- package/dist/tests/relativePositions.tests.d.ts +0 -11
- package/dist/tests/relativePositions.tests.d.ts.map +0 -1
- package/dist/tests/snapshot.tests.d.ts +0 -14
- package/dist/tests/snapshot.tests.d.ts.map +0 -1
- package/dist/tests/testHelper.d.ts +0 -171
- package/dist/tests/testHelper.d.ts.map +0 -1
- package/dist/tests/undo-redo.tests.d.ts +0 -27
- package/dist/tests/undo-redo.tests.d.ts.map +0 -1
- package/dist/tests/updates.tests.d.ts +0 -26
- package/dist/tests/updates.tests.d.ts.map +0 -1
- package/dist/tests/y-array.tests.d.ts +0 -43
- package/dist/tests/y-array.tests.d.ts.map +0 -1
- package/dist/tests/y-map.tests.d.ts +0 -42
- package/dist/tests/y-map.tests.d.ts.map +0 -1
- package/dist/tests/y-text.tests.d.ts +0 -49
- package/dist/tests/y-text.tests.d.ts.map +0 -1
- package/dist/tests/y-xml.tests.d.ts +0 -14
- package/dist/tests/y-xml.tests.d.ts.map +0 -1
- package/src/internals.js +0 -35
- 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 -152
- package/src/utils/IdMap.js +0 -673
- package/src/utils/IdSet.js +0 -825
package/src/utils/UndoManager.js
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
mergeIdSets,
|
|
3
|
-
iterateStructsByIdSet,
|
|
4
|
-
keepItem,
|
|
5
|
-
transact,
|
|
6
|
-
createID,
|
|
7
|
-
redoItem,
|
|
8
|
-
isParentOf,
|
|
9
|
-
followRedone,
|
|
10
|
-
getItemCleanStart,
|
|
11
|
-
YEvent, Transaction, Doc, Item, GC, IdSet, YType, // eslint-disable-line
|
|
12
|
-
diffIdSet
|
|
13
|
-
} from '../internals.js'
|
|
14
|
-
|
|
15
1
|
import * as time from 'lib0/time'
|
|
16
2
|
import * as array from 'lib0/array'
|
|
17
3
|
import * as logging from 'lib0/logging'
|
|
18
4
|
import { ObservableV2 } from 'lib0/observable'
|
|
19
5
|
|
|
6
|
+
import { mergeIdSets, iterateStructsByIdSet, diffIdSet } from './ids.js'
|
|
7
|
+
import { Item, followRedone } from '../structs/Item.js'
|
|
8
|
+
|
|
9
|
+
import { transact } from './Transaction.js'
|
|
10
|
+
import { createID } from './ID.js'
|
|
11
|
+
import { isParentOf } from './isParentOf.js'
|
|
12
|
+
import { getItemCleanStart } from './transaction-helpers.js'
|
|
13
|
+
import { Doc } from './Doc.js'
|
|
14
|
+
import { YType } from '../ytype.js'
|
|
15
|
+
|
|
20
16
|
export class StackItem {
|
|
21
17
|
/**
|
|
22
18
|
* @param {IdSet} insertions
|
|
@@ -96,7 +92,7 @@ const popStackItem = (undoManager, stack, eventType) => {
|
|
|
96
92
|
}
|
|
97
93
|
})
|
|
98
94
|
itemsToRedo.forEach(struct => {
|
|
99
|
-
performedChange = redoItem(transaction, struct, itemsToRedo, stackItem.inserts, undoManager.
|
|
95
|
+
performedChange = redoItem(transaction, struct, itemsToRedo, stackItem.inserts, undoManager.ignoreRemoteAttributeChanges, undoManager) !== null || performedChange
|
|
100
96
|
})
|
|
101
97
|
// We want to delete in reverse order so that children are deleted before
|
|
102
98
|
// parents, so we have more information available when items are filtered.
|
|
@@ -135,7 +131,7 @@ const popStackItem = (undoManager, stack, eventType) => {
|
|
|
135
131
|
* filter returns false, the type/item won't be deleted even it is in the
|
|
136
132
|
* undo/redo scope.
|
|
137
133
|
* @property {Set<any>} [UndoManagerOptions.trackedOrigins=new Set([null])]
|
|
138
|
-
* @property {boolean} [
|
|
134
|
+
* @property {boolean} [ignoreRemoteAttributeChanges] By default, the UndoManager will never overwrite remote changes. In some cases this might be the expected behavior. This property enables overwriting remote changes on attribute changes. (previously named `ignoreRemoteMapChanges`)
|
|
139
135
|
* @property {Doc} [doc] The document that this UndoManager operates on. Only needed if typeScope is empty.
|
|
140
136
|
*/
|
|
141
137
|
|
|
@@ -166,7 +162,7 @@ export class UndoManager extends ObservableV2 {
|
|
|
166
162
|
captureTransaction = _tr => true,
|
|
167
163
|
deleteFilter = () => true,
|
|
168
164
|
trackedOrigins = new Set([null]),
|
|
169
|
-
|
|
165
|
+
ignoreRemoteAttributeChanges = false,
|
|
170
166
|
doc = /** @type {Doc} */ (array.isArray(typeScope) ? typeScope[0].doc : typeScope instanceof Doc ? typeScope : typeScope.doc)
|
|
171
167
|
} = {}) {
|
|
172
168
|
super()
|
|
@@ -202,7 +198,7 @@ export class UndoManager extends ObservableV2 {
|
|
|
202
198
|
*/
|
|
203
199
|
this.currStackItem = null
|
|
204
200
|
this.lastChange = 0
|
|
205
|
-
this.
|
|
201
|
+
this.ignoreRemoteAttributeChanges = ignoreRemoteAttributeChanges
|
|
206
202
|
this.captureTimeout = captureTimeout
|
|
207
203
|
/**
|
|
208
204
|
* @param {Transaction} transaction
|
|
@@ -397,7 +393,7 @@ export class UndoManager extends ObservableV2 {
|
|
|
397
393
|
* This is not guaranteed to work on documents with gc enabled!
|
|
398
394
|
*
|
|
399
395
|
* @param {Doc} ydoc
|
|
400
|
-
* @param {
|
|
396
|
+
* @param {ContentIds} contentIds
|
|
401
397
|
* @param {UndoManagerOptions} opts
|
|
402
398
|
*/
|
|
403
399
|
export const undoContentIds = (ydoc, contentIds, opts = {}) => {
|
|
@@ -405,3 +401,145 @@ export const undoContentIds = (ydoc, contentIds, opts = {}) => {
|
|
|
405
401
|
um.undoStack.push(new StackItem(diffIdSet(contentIds.inserts, contentIds.deletes), diffIdSet(contentIds.deletes, contentIds.inserts)))
|
|
406
402
|
um.undo()
|
|
407
403
|
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* @param {Array<StackItem>} stack
|
|
407
|
+
* @param {ID} id
|
|
408
|
+
*/
|
|
409
|
+
const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackItem} s */ s => s.deletes.hasId(id))
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Redoes the effect of this operation.
|
|
413
|
+
*
|
|
414
|
+
* @param {Transaction} transaction The Yjs instance.
|
|
415
|
+
* @param {Item} item
|
|
416
|
+
* @param {Set<Item>} redoitems
|
|
417
|
+
* @param {IdSet} itemsToDelete
|
|
418
|
+
* @param {boolean} ignoreRemoteAttributeChanges
|
|
419
|
+
* @param {import('../utils/UndoManager.js').UndoManager} um
|
|
420
|
+
*
|
|
421
|
+
* @return {Item|null}
|
|
422
|
+
*
|
|
423
|
+
* @private
|
|
424
|
+
*/
|
|
425
|
+
export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemoteAttributeChanges, um) => {
|
|
426
|
+
const doc = transaction.doc
|
|
427
|
+
const store = doc.store
|
|
428
|
+
const ownClientID = doc.clientID
|
|
429
|
+
const redone = item.redone
|
|
430
|
+
if (redone !== null) {
|
|
431
|
+
return getItemCleanStart(transaction, redone)
|
|
432
|
+
}
|
|
433
|
+
let parentItem = /** @type {YType} */ (item.parent)._item
|
|
434
|
+
/**
|
|
435
|
+
* @type {Item|null}
|
|
436
|
+
*/
|
|
437
|
+
let left = null
|
|
438
|
+
/**
|
|
439
|
+
* @type {Item|null}
|
|
440
|
+
*/
|
|
441
|
+
let right
|
|
442
|
+
// make sure that parent is redone
|
|
443
|
+
if (parentItem !== null && parentItem.deleted === true) {
|
|
444
|
+
// try to undo parent if it will be undone anyway
|
|
445
|
+
if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, ignoreRemoteAttributeChanges, um) === null)) {
|
|
446
|
+
return null
|
|
447
|
+
}
|
|
448
|
+
while (parentItem.redone !== null) {
|
|
449
|
+
parentItem = getItemCleanStart(transaction, parentItem.redone)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* @type {YType}
|
|
454
|
+
*/
|
|
455
|
+
const parentType = /** @type {YType} */ (parentItem === null ? item.parent : /** @type {ContentType} */ (parentItem.content).type)
|
|
456
|
+
|
|
457
|
+
if (item.parentSub === null) {
|
|
458
|
+
// Is an array item. Insert at the old position
|
|
459
|
+
left = item.left
|
|
460
|
+
right = item
|
|
461
|
+
// find next cloned_redo items
|
|
462
|
+
while (left !== null) {
|
|
463
|
+
/**
|
|
464
|
+
* @type {Item|null}
|
|
465
|
+
*/
|
|
466
|
+
let leftTrace = left
|
|
467
|
+
// trace redone until parent matches
|
|
468
|
+
while (leftTrace !== null && /** @type {YType} */ (leftTrace.parent)._item !== parentItem) {
|
|
469
|
+
leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, leftTrace.redone)
|
|
470
|
+
}
|
|
471
|
+
if (leftTrace !== null && /** @type {YType} */ (leftTrace.parent)._item === parentItem) {
|
|
472
|
+
left = leftTrace
|
|
473
|
+
break
|
|
474
|
+
}
|
|
475
|
+
left = left.left
|
|
476
|
+
}
|
|
477
|
+
while (right !== null) {
|
|
478
|
+
/**
|
|
479
|
+
* @type {Item|null}
|
|
480
|
+
*/
|
|
481
|
+
let rightTrace = right
|
|
482
|
+
// trace redone until parent matches
|
|
483
|
+
while (rightTrace !== null && /** @type {YType} */ (rightTrace.parent)._item !== parentItem) {
|
|
484
|
+
rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, rightTrace.redone)
|
|
485
|
+
}
|
|
486
|
+
if (rightTrace !== null && /** @type {YType} */ (rightTrace.parent)._item === parentItem) {
|
|
487
|
+
right = rightTrace
|
|
488
|
+
break
|
|
489
|
+
}
|
|
490
|
+
right = right.right
|
|
491
|
+
}
|
|
492
|
+
} else {
|
|
493
|
+
right = null
|
|
494
|
+
if (item.right && !ignoreRemoteAttributeChanges) {
|
|
495
|
+
left = item
|
|
496
|
+
// Iterate right while right is in itemsToDelete
|
|
497
|
+
// If it is intended to delete right while item is redone, we can expect that item should replace right.
|
|
498
|
+
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))) {
|
|
499
|
+
left = left.right
|
|
500
|
+
// follow redone
|
|
501
|
+
while (left.redone) left = getItemCleanStart(transaction, left.redone)
|
|
502
|
+
}
|
|
503
|
+
if (left && left.right !== null) {
|
|
504
|
+
// It is not possible to redo this item because it conflicts with a
|
|
505
|
+
// change from another client
|
|
506
|
+
return null
|
|
507
|
+
}
|
|
508
|
+
} else {
|
|
509
|
+
left = parentType._map.get(item.parentSub) || null
|
|
510
|
+
}
|
|
511
|
+
if (left !== null && /** @type {YType} */ (left.parent)._item !== parentItem) {
|
|
512
|
+
left = parentType._map.get(item.parentSub) || null
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const nextClock = store.getClock(ownClientID)
|
|
516
|
+
const nextId = createID(ownClientID, nextClock)
|
|
517
|
+
const redoneItem = new Item(
|
|
518
|
+
nextId,
|
|
519
|
+
left, left && left.lastId,
|
|
520
|
+
right, right && right.id,
|
|
521
|
+
parentType,
|
|
522
|
+
item.parentSub,
|
|
523
|
+
item.content.copy()
|
|
524
|
+
)
|
|
525
|
+
item.redone = nextId
|
|
526
|
+
keepItem(redoneItem, true)
|
|
527
|
+
redoneItem.integrate(transaction, 0)
|
|
528
|
+
return redoneItem
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Make sure that neither item nor any of its parents is ever deleted.
|
|
533
|
+
*
|
|
534
|
+
* This property does not persist when storing it into a database or when
|
|
535
|
+
* sending it to other peers
|
|
536
|
+
*
|
|
537
|
+
* @param {Item|null} item
|
|
538
|
+
* @param {boolean} keep
|
|
539
|
+
*/
|
|
540
|
+
export const keepItem = (item, keep) => {
|
|
541
|
+
while (item !== null && item.keep !== keep) {
|
|
542
|
+
item.keep = keep
|
|
543
|
+
item = /** @type {YType} */ (item.parent)._item
|
|
544
|
+
}
|
|
545
|
+
}
|
package/src/utils/YEvent.js
CHANGED
|
@@ -1,30 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
diffIdSet,
|
|
3
|
-
mergeIdSets,
|
|
4
|
-
noAttributionsManager,
|
|
5
|
-
YType, Doc, AbstractAttributionManager, Item, Transaction, AbstractStruct, // eslint-disable-line
|
|
6
|
-
createAbsolutePositionFromRelativePosition,
|
|
7
|
-
createRelativePosition
|
|
8
|
-
} from '../internals.js'
|
|
9
|
-
|
|
10
1
|
import * as map from 'lib0/map'
|
|
11
|
-
import * as delta from 'lib0/delta' // eslint-disable-line
|
|
12
2
|
import * as set from 'lib0/set'
|
|
13
3
|
|
|
4
|
+
import { diffIdSet, mergeIdSets } from './ids.js'
|
|
5
|
+
import { baseRenderer } from './renderer-helpers.js'
|
|
6
|
+
import { createAbsolutePositionFromRelativePosition, createRelativePosition } from './RelativePosition.js'
|
|
7
|
+
|
|
14
8
|
/**
|
|
15
|
-
* @template {
|
|
9
|
+
* @template {DeltaConf} DConf
|
|
16
10
|
* YEvent describes the changes on a YType.
|
|
17
11
|
*/
|
|
18
12
|
export class YEvent {
|
|
19
13
|
/**
|
|
20
14
|
* @param {YType<DConf>} target The changed type.
|
|
21
|
-
* @param {Transaction} transaction
|
|
15
|
+
* @param {import('./Transaction.js').Transaction} transaction
|
|
22
16
|
* @param {Set<any>?} subs The keys that changed
|
|
23
17
|
*/
|
|
24
18
|
constructor (target, transaction, subs) {
|
|
25
19
|
/**
|
|
26
20
|
* The type on which this event was created on.
|
|
27
|
-
* @type {YType<DConf>}
|
|
21
|
+
* @type {import('../ytype.js').YType<DConf>}
|
|
28
22
|
*/
|
|
29
23
|
this.target = target
|
|
30
24
|
/**
|
|
@@ -34,15 +28,15 @@ export class YEvent {
|
|
|
34
28
|
this.currentTarget = target
|
|
35
29
|
/**
|
|
36
30
|
* The transaction that triggered this event.
|
|
37
|
-
* @type {Transaction}
|
|
31
|
+
* @type {import('./Transaction.js').Transaction}
|
|
38
32
|
*/
|
|
39
33
|
this.transaction = transaction
|
|
40
34
|
/**
|
|
41
|
-
* @type {
|
|
35
|
+
* @type {Delta<import('../ytype.js').DeltaConfDeltaToYType<DConf>>|null}
|
|
42
36
|
*/
|
|
43
37
|
this._delta = null
|
|
44
38
|
/**
|
|
45
|
-
* @type {
|
|
39
|
+
* @type {Delta<DConf>|null}
|
|
46
40
|
*/
|
|
47
41
|
this._deltaDeep = null
|
|
48
42
|
/**
|
|
@@ -91,14 +85,14 @@ export class YEvent {
|
|
|
91
85
|
|
|
92
86
|
/**
|
|
93
87
|
* @template {boolean} [Deep=false]
|
|
94
|
-
* @param {AbstractAttributionManager} am
|
|
95
88
|
* @param {object} [opts]
|
|
89
|
+
* @param {AbstractRenderer} [opts.renderer] - renders the content (with attributions); defaults to the target type's active renderer (see {@link YType#useRenderer}), i.e. `baseRenderer` unless changed
|
|
96
90
|
* @param {Deep} [opts.deep]
|
|
97
|
-
* @return {Deep extends true ?
|
|
91
|
+
* @return {Deep extends true ? Delta<DConf> : Delta<import('../ytype.js').DeltaConfDeltaToYType<DConf>>} The Delta representation of this type.
|
|
98
92
|
*
|
|
99
93
|
* @public
|
|
100
94
|
*/
|
|
101
|
-
getDelta (
|
|
95
|
+
getDelta ({ renderer = this.target._renderer, deep } = {}) {
|
|
102
96
|
const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)])
|
|
103
97
|
/**
|
|
104
98
|
* @todo this should be done only one in the transaction step
|
|
@@ -126,14 +120,14 @@ export class YEvent {
|
|
|
126
120
|
}
|
|
127
121
|
modified = dchanged
|
|
128
122
|
}
|
|
129
|
-
return /** @type {any} */ (this.target.toDelta(
|
|
123
|
+
return /** @type {any} */ (this.target.toDelta({ renderer, itemsToRender, retainDeletes: true, deletedItems: this.transaction.deleteSet, deep: !!deep, modified }))
|
|
130
124
|
}
|
|
131
125
|
|
|
132
126
|
/**
|
|
133
127
|
* Compute the changes in the delta format.
|
|
134
128
|
* A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document.
|
|
135
129
|
*
|
|
136
|
-
* @type {
|
|
130
|
+
* @type {Delta<import('../ytype.js').DeltaConfDeltaToYType<DConf>>} The Delta representation of this type.
|
|
137
131
|
* @public
|
|
138
132
|
*/
|
|
139
133
|
get delta () {
|
|
@@ -144,11 +138,11 @@ export class YEvent {
|
|
|
144
138
|
* Compute the changes in the delta format.
|
|
145
139
|
* A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document.
|
|
146
140
|
*
|
|
147
|
-
* @type {
|
|
141
|
+
* @type {Delta<DConf>} The Delta representation of this type.
|
|
148
142
|
* @public
|
|
149
143
|
*/
|
|
150
144
|
get deltaDeep () {
|
|
151
|
-
return /** @type {any} */ (this._deltaDeep ?? (this._deltaDeep = /** @type {any} */ (this.getDelta(
|
|
145
|
+
return /** @type {any} */ (this._deltaDeep ?? (this._deltaDeep = /** @type {any} */ (this.getDelta({ deep: true }))))
|
|
152
146
|
}
|
|
153
147
|
}
|
|
154
148
|
|
|
@@ -164,13 +158,13 @@ export class YEvent {
|
|
|
164
158
|
*
|
|
165
159
|
* @param {YType} parent
|
|
166
160
|
* @param {YType} child target
|
|
167
|
-
* @param {
|
|
161
|
+
* @param {AbstractRenderer} renderer
|
|
168
162
|
* @return {Array<string|number>} Path to the target
|
|
169
163
|
*
|
|
170
164
|
* @private
|
|
171
165
|
* @function
|
|
172
166
|
*/
|
|
173
|
-
export const getPathTo = (parent, child,
|
|
167
|
+
export const getPathTo = (parent, child, renderer = baseRenderer) => {
|
|
174
168
|
const path = []
|
|
175
169
|
const doc = /** @type {Doc} */ (parent.doc)
|
|
176
170
|
while (child._item !== null && child !== parent) {
|
|
@@ -180,7 +174,7 @@ export const getPathTo = (parent, child, am = noAttributionsManager) => {
|
|
|
180
174
|
} else {
|
|
181
175
|
const parent = /** @type {import('../ytype.js').YType} */ (child._item.parent)
|
|
182
176
|
// parent is array-ish
|
|
183
|
-
const apos = /** @type {import('../utils/RelativePosition.js').AbsolutePosition} */ (createAbsolutePositionFromRelativePosition(createRelativePosition(parent, child._item.id), doc, false,
|
|
177
|
+
const apos = /** @type {import('../utils/RelativePosition.js').AbsolutePosition} */ (createAbsolutePositionFromRelativePosition(createRelativePosition(parent, child._item.id), doc, false, renderer))
|
|
184
178
|
path.unshift(apos.index)
|
|
185
179
|
}
|
|
186
180
|
child = /** @type {YType} */ (child._item.parent)
|
|
File without changes
|
|
@@ -1,54 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createInsertSetFromStructStore,
|
|
3
|
-
createDeleteSetFromStructStore,
|
|
4
|
-
createAttributionManagerFromDiff,
|
|
5
|
-
diffIdSet,
|
|
6
|
-
mergeIdSets,
|
|
7
|
-
Item,
|
|
8
|
-
YType, Doc, // eslint-disable-line
|
|
9
|
-
iterateStructsByIdSet
|
|
10
|
-
} from '../internals.js'
|
|
11
1
|
import * as delta from 'lib0/delta'
|
|
12
|
-
import
|
|
13
|
-
import
|
|
2
|
+
import { createInsertSetFromStructStore, createDeleteSetFromStructStore, diffIdSet, mergeIdSets } from './ids.js'
|
|
3
|
+
import { createDiffRenderer } from './Renderer.js'
|
|
4
|
+
import { computeModifiedFromItems } from '../ytype.js'
|
|
14
5
|
|
|
15
6
|
/**
|
|
16
7
|
* @param {Doc} v1
|
|
17
8
|
* @param {Doc} v2
|
|
18
9
|
* @return {delta.DeltaBuilderAny}
|
|
19
10
|
*/
|
|
20
|
-
export const diffDocsToDelta = (v1, v2, {
|
|
11
|
+
export const diffDocsToDelta = (v1, v2, { renderer = createDiffRenderer(v1, v2) } = {}) => {
|
|
12
|
+
const insertDiff = diffIdSet(createInsertSetFromStructStore(v2.store, false), createInsertSetFromStructStore(v1.store, false))
|
|
13
|
+
const deleteDiff = diffIdSet(createDeleteSetFromStructStore(v2.store), createDeleteSetFromStructStore(v1.store))
|
|
14
|
+
// don't render items that have been inserted and then deleted
|
|
15
|
+
const insertsOnly = diffIdSet(insertDiff, deleteDiff)
|
|
16
|
+
const deletesOnly = diffIdSet(deleteDiff, insertDiff)
|
|
17
|
+
const itemsToRender = mergeIdSets([insertsOnly, deleteDiff])
|
|
18
|
+
/**
|
|
19
|
+
* @type {Map<YType, Set<string|null>>}
|
|
20
|
+
*/
|
|
21
|
+
const changedTypes = computeModifiedFromItems(v2.store, itemsToRender)
|
|
21
22
|
const d = delta.create()
|
|
22
|
-
v2.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const insertsOnly = diffIdSet(insertDiff, deleteDiff)
|
|
28
|
-
const deletesOnly = diffIdSet(deleteDiff, insertDiff)
|
|
29
|
-
const itemsToRender = mergeIdSets([insertsOnly, deleteDiff])
|
|
30
|
-
/**
|
|
31
|
-
* @type {Map<YType, Set<string|null>>}
|
|
32
|
-
*/
|
|
33
|
-
const changedTypes = new Map()
|
|
34
|
-
iterateStructsByIdSet(tr, itemsToRender, /** @param {any} item */ item => {
|
|
35
|
-
while (item instanceof Item) {
|
|
36
|
-
const parent = /** @type {YType} */ (item.parent)
|
|
37
|
-
const conf = map.setIfUndefined(changedTypes, parent, set.create)
|
|
38
|
-
if (conf.has(item.parentSub)) break // has already been marked as modified
|
|
39
|
-
conf.add(item.parentSub)
|
|
40
|
-
item = parent._item
|
|
41
|
-
}
|
|
23
|
+
v2.share.forEach((type, typename) => {
|
|
24
|
+
const typeConf = changedTypes.get(type)
|
|
25
|
+
if (typeConf) {
|
|
26
|
+
const shareDelta = type.toDelta({
|
|
27
|
+
renderer, itemsToRender, retainDeletes: true, deletedItems: deletesOnly, modified: changedTypes, deep: true
|
|
42
28
|
})
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// @ts-ignore
|
|
46
|
-
const shareDelta = type.toDelta(am, {
|
|
47
|
-
itemsToRender, retainDeletes: true, deletedItems: deletesOnly, modified: changedTypes, deep: true
|
|
48
|
-
})
|
|
49
|
-
d.modifyAttr(typename, shareDelta)
|
|
50
|
-
}
|
|
51
|
-
})
|
|
29
|
+
d.modifyAttr(typename, shareDelta)
|
|
30
|
+
}
|
|
52
31
|
})
|
|
53
32
|
return d
|
|
54
33
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as encoding from 'lib0/encoding'
|
|
2
|
+
import * as math from 'lib0/math'
|
|
3
|
+
import * as array from 'lib0/array'
|
|
4
|
+
|
|
5
|
+
import { findIndexSS } from './transaction-helpers.js'
|
|
6
|
+
import { Skip } from '../structs/Skip.js'
|
|
7
|
+
import { createID } from './ID.js'
|
|
8
|
+
import { writeIdSet } from './ids.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
12
|
+
* @param {Array<GC|Item|Skip>} structs All structs by `client`
|
|
13
|
+
* @param {number} client
|
|
14
|
+
* @param {Array<IdRange>} idranges
|
|
15
|
+
*
|
|
16
|
+
* @function
|
|
17
|
+
*/
|
|
18
|
+
export const writeStructs = (encoder, structs, client, idranges) => {
|
|
19
|
+
let structsToWrite = 0 // this accounts for the skips
|
|
20
|
+
/**
|
|
21
|
+
* @type {Array<{ start: number, end: number, startClock: number, endClock: number }>}
|
|
22
|
+
*/
|
|
23
|
+
const indexRanges = []
|
|
24
|
+
const firstPossibleClock = structs[0].id.clock
|
|
25
|
+
const lastStruct = array.last(structs)
|
|
26
|
+
const lastPossibleClock = lastStruct.id.clock + lastStruct.length
|
|
27
|
+
idranges.forEach(idrange => {
|
|
28
|
+
const startClock = math.max(idrange.clock, firstPossibleClock)
|
|
29
|
+
const endClock = math.min(idrange.clock + idrange.len, lastPossibleClock)
|
|
30
|
+
if (startClock >= endClock) return // structs for this range do not exist
|
|
31
|
+
// inclusive start
|
|
32
|
+
const start = findIndexSS(structs, startClock)
|
|
33
|
+
// exclusive end
|
|
34
|
+
const end = findIndexSS(structs, endClock - 1) + 1
|
|
35
|
+
structsToWrite += end - start
|
|
36
|
+
indexRanges.push({
|
|
37
|
+
start,
|
|
38
|
+
end,
|
|
39
|
+
startClock,
|
|
40
|
+
endClock
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
structsToWrite += idranges.length - 1
|
|
44
|
+
// start writing with this clock. this is updated to the next clock that we expect to write
|
|
45
|
+
let clock = indexRanges[0].startClock
|
|
46
|
+
// write # encoded structs
|
|
47
|
+
encoding.writeVarUint(encoder.restEncoder, structsToWrite)
|
|
48
|
+
encoder.writeClient(client)
|
|
49
|
+
// write clock
|
|
50
|
+
encoding.writeVarUint(encoder.restEncoder, clock)
|
|
51
|
+
indexRanges.forEach(indexRange => {
|
|
52
|
+
const skipLen = indexRange.startClock - clock
|
|
53
|
+
if (skipLen > 0) {
|
|
54
|
+
new Skip(createID(client, clock), skipLen).write(encoder, 0)
|
|
55
|
+
clock += skipLen
|
|
56
|
+
}
|
|
57
|
+
for (let i = indexRange.start; i < indexRange.end; i++) {
|
|
58
|
+
const struct = structs[i]
|
|
59
|
+
const structEnd = struct.id.clock + struct.length
|
|
60
|
+
const offsetEnd = math.max(structEnd - indexRange.endClock, 0)
|
|
61
|
+
struct.write(encoder, clock - struct.id.clock, offsetEnd)
|
|
62
|
+
clock = structEnd - offsetEnd
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
69
|
+
* @param {StructStore} store
|
|
70
|
+
* @param {IdSet} idset
|
|
71
|
+
*
|
|
72
|
+
* @todo at the moment this writes the full deleteset range
|
|
73
|
+
*
|
|
74
|
+
* @private
|
|
75
|
+
* @function
|
|
76
|
+
*/
|
|
77
|
+
export const writeStructsFromIdSet = (encoder, store, idset) => {
|
|
78
|
+
// write # states that were updated
|
|
79
|
+
encoding.writeVarUint(encoder.restEncoder, idset.clients.size)
|
|
80
|
+
// Write items with higher client ids first
|
|
81
|
+
// This heavily improves the conflict algorithm.
|
|
82
|
+
array.from(idset.clients.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, ids]) => {
|
|
83
|
+
const idRanges = ids.getIds()
|
|
84
|
+
const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client))
|
|
85
|
+
writeStructs(encoder, structs, client, idRanges)
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
91
|
+
* @param {Transaction} transaction
|
|
92
|
+
*
|
|
93
|
+
* @private
|
|
94
|
+
* @function
|
|
95
|
+
*/
|
|
96
|
+
export const writeStructsFromTransaction = (encoder, transaction) => writeStructsFromIdSet(encoder, transaction.doc.store, transaction.insertSet)
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
100
|
+
* @param {Transaction} transaction
|
|
101
|
+
* @return {boolean} Whether data was written.
|
|
102
|
+
*/
|
|
103
|
+
export const writeUpdateMessageFromTransaction = (encoder, transaction) => {
|
|
104
|
+
if (transaction.deleteSet.clients.size === 0 && transaction.insertSet.clients.size === 0) {
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
writeStructsFromTransaction(encoder, transaction)
|
|
108
|
+
writeIdSet(encoder, transaction.deleteSet)
|
|
109
|
+
return true
|
|
110
|
+
}
|