implement diffDocsToDelta

This commit is contained in:
Kevin Jahns
2025-11-27 19:59:26 +01:00
parent d7b072e095
commit 804011160a
5 changed files with 80 additions and 2 deletions

View File

@@ -120,7 +120,8 @@ export {
mergeIdMaps, mergeIdMaps,
readIdMap, readIdMap,
readIdSet, readIdSet,
decodeIdMap decodeIdMap,
diffDocsToDelta
} from './internals.js' } from './internals.js'
const glo = /** @type {any} */ (typeof globalThis !== 'undefined' const glo = /** @type {any} */ (typeof globalThis !== 'undefined'

View File

@@ -18,6 +18,7 @@ export * from './utils/YEvent.js'
export * from './utils/StructSet.js' export * from './utils/StructSet.js'
export * from './utils/IdMap.js' export * from './utils/IdMap.js'
export * from './utils/AttributionManager.js' export * from './utils/AttributionManager.js'
export * from './utils/delta-helpers.js'
export * from './types/AbstractType.js' export * from './types/AbstractType.js'
export * from './types/YArray.js' export * from './types/YArray.js'

View File

@@ -1351,7 +1351,7 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am, deep, modified, de
if (attrsToRender == null) { if (attrsToRender == null) {
parent._map.forEach(renderAttrs) parent._map.forEach(renderAttrs)
} else { } else {
attrsToRender.forEach(key => renderAttrs(/** @type {Item} */ (parent._map.get(key)), key)) attrsToRender.forEach(key => key != null && renderAttrs(/** @type {Item} */ (parent._map.get(key)), key))
} }
} }

View File

@@ -0,0 +1,54 @@
import {
createInsertSetFromStructStore,
createDeleteSetFromStructStore,
createAttributionManagerFromDiff,
diffIdSet,
mergeIdSets,
Item,
AbstractType, Doc, // eslint-disable-line
iterateStructsByIdSet
} from '../internals.js'
import * as delta from 'lib0/delta'
import * as map from 'lib0/map'
import * as set from 'lib0/set'
/**
* @param {Doc} v1
* @param {Doc} v2
* @return {delta.DeltaBuilderAny}
*/
export const diffDocsToDelta = (v1, v2, { am = createAttributionManagerFromDiff(v1, v2) } = {}) => {
const d = delta.create()
v2.transact(tr => {
v2.share.forEach((type, typename) => {
const insertDiff = diffIdSet(createInsertSetFromStructStore(v2.store, false), createInsertSetFromStructStore(v1.store, false))
const deleteDiff = diffIdSet(createDeleteSetFromStructStore(v2.store), createDeleteSetFromStructStore(v1.store))
// don't render items that have been inserted and then deleted
const insertsOnly = diffIdSet(insertDiff, deleteDiff)
const deletesOnly = diffIdSet(deleteDiff, insertDiff)
const itemsToRender = mergeIdSets([insertsOnly, deleteDiff])
/**
* @type {Map<AbstractType, Set<string|null>>}
*/
const changedTypes = new Map()
iterateStructsByIdSet(tr, itemsToRender, /** @param {any} item */ item => {
while (item instanceof Item) {
const parent = /** @type {AbstractType} */ (item.parent)
const conf = map.setIfUndefined(changedTypes, parent, set.create)
if (conf.has(item.parentSub)) break // has already been marked as modified
conf.add(item.parentSub)
item = parent._item
}
})
const typeConf = changedTypes.get(type)
if (typeConf) {
// @ts-ignore
const shareDelta = type.getContent(am, {
itemsToRender, retainDeletes: true, renderAttrs: /** @type {Set<string>} */ (changedTypes.get(type)), renderChildren: typeConf.has(null), deletedItems: deletesOnly, modified: changedTypes, deep: true
})
d.update(typename, shareDelta)
}
})
})
return d
}

View File

@@ -86,3 +86,25 @@ export const testInsertionsIntoAttributedContent = _tc => {
ytext.applyDelta(delta.text().retain(9).insert('l'), am) ytext.applyDelta(delta.text().retain(9).insert('l'), am)
t.assert(ytext.toString() === 'hello world') t.assert(ytext.toString() === 'hello world')
} }
export const testYdocDiff = () => {
const ydocStart = new Y.Doc()
ydocStart.getText('text').insert(0, 'hello')
ydocStart.getArray('array').insert(0, [1, 2, 3])
ydocStart.getMap('map').set('k', 42)
ydocStart.getMap('map').set('nested', new Y.Array())
const ydocUpdated = Y.cloneDoc(ydocStart)
ydocUpdated.getText('text').insert(5, ' world')
ydocUpdated.getArray('array').insert(1, ['x'])
ydocUpdated.getMap('map').set('newk', 42)
ydocUpdated.getMap('map').get('nested').insert(0, [1])
// @todo add custom attribution
const d = Y.diffDocsToDelta(ydocStart, ydocUpdated)
t.compare(d, delta.create()
.update('text', delta.create().retain(5).insert('world'))
.update('array', delta.create().retain(1).insert(['x']))
.update('map', delta.create().set('newk', 42).update('nested', delta.create().insert([1])))
)
console.log(d.toJSON())
debugger
}