Merge pull request #753 from nperez0111/attrs

Support attributing content in `DiffAttributionManager` & `SnapshotAttributionManager`
This commit is contained in:
Kevin Jahns
2025-12-17 16:44:17 +01:00
committed by GitHub
2 changed files with 19 additions and 22 deletions

View File

@@ -9,7 +9,6 @@ import {
insertIntoIdSet,
diffIdMap,
createIdMap,
createAttributionItem,
mergeIdMaps,
createID,
mergeIdSets,
@@ -349,15 +348,17 @@ export class DiffAttributionManager extends ObservableV2 {
/**
* @param {Doc} prevDoc
* @param {Doc} nextDoc
* @param {Object} [options] - options for the attribution manager
* @param {Array<import('./IdMap.js').AttributionItem<any>>} [options.attrs] - the attributes to apply to the diff
*/
constructor (prevDoc, nextDoc) {
constructor (prevDoc, nextDoc, { attrs = [] } = {}) {
super()
const _nextDocInserts = createInsertSetFromStructStore(nextDoc.store, false) // unmaintained
const _prevDocInserts = createInsertSetFromStructStore(prevDoc.store, false) // unmaintained
const nextDocDeletes = createDeleteSetFromStructStore(nextDoc.store) // maintained
const prevDocDeletes = createDeleteSetFromStructStore(prevDoc.store) // maintained
this.inserts = createIdMapFromIdSet(diffIdSet(_nextDocInserts, _prevDocInserts), [])
this.deletes = createIdMapFromIdSet(diffIdSet(nextDocDeletes, prevDocDeletes), [])
this.inserts = createIdMapFromIdSet(diffIdSet(_nextDocInserts, _prevDocInserts), attrs)
this.deletes = createIdMapFromIdSet(diffIdSet(nextDocDeletes, prevDocDeletes), attrs)
this._prevDoc = prevDoc
this._prevDocStore = prevDoc.store
this._nextDoc = nextDoc
@@ -365,10 +366,10 @@ export class DiffAttributionManager extends ObservableV2 {
this._nextBOH = nextDoc.on('beforeObserverCalls', tr => {
// update inserts
const diffInserts = diffIdSet(tr.insertSet, _prevDocInserts)
insertIntoIdMap(this.inserts, createIdMapFromIdSet(diffInserts, []))
insertIntoIdMap(this.inserts, createIdMapFromIdSet(diffInserts, attrs))
// update deletes
const diffDeletes = diffIdSet(diffIdSet(tr.deleteSet, prevDocDeletes), this.inserts)
insertIntoIdMap(this.deletes, createIdMapFromIdSet(diffDeletes, []))
insertIntoIdMap(this.deletes, createIdMapFromIdSet(diffDeletes, attrs))
// @todo fire update ranges on `diffInserts` and `diffDeletes`
})
this._prevBOH = prevDoc.on('beforeObserverCalls', tr => {
@@ -538,8 +539,10 @@ export class DiffAttributionManager extends ObservableV2 {
*
* @param {Doc} prevDoc
* @param {Doc} nextDoc
* @param {Object} [options] - options for the attribution manager
* @param {Array<import('./IdMap.js').AttributionItem<any>>} [options.attrs] - the attributes to apply to the diff
*/
export const createAttributionManagerFromDiff = (prevDoc, nextDoc) => new DiffAttributionManager(prevDoc, nextDoc)
export const createAttributionManagerFromDiff = (prevDoc, nextDoc, options) => new DiffAttributionManager(prevDoc, nextDoc, options)
/**
* Intended for projects that used the v13 snapshot feature. With this AttributionManager you can
@@ -553,17 +556,19 @@ export class SnapshotAttributionManager extends ObservableV2 {
/**
* @param {Snapshot} prevSnapshot
* @param {Snapshot} nextSnapshot
* @param {Object} [options] - options for the attribution manager
* @param {Array<import('./IdMap.js').AttributionItem<any>>} [options.attrs] - the attributes to apply to the diff
*/
constructor (prevSnapshot, nextSnapshot) {
constructor (prevSnapshot, nextSnapshot, { attrs = [] } = {}) {
super()
this.prevSnapshot = prevSnapshot
this.nextSnapshot = nextSnapshot
const inserts = createIdMap()
const deletes = createIdMapFromIdSet(diffIdSet(nextSnapshot.ds, prevSnapshot.ds), [createAttributionItem('change', '')])
const deletes = createIdMapFromIdSet(diffIdSet(nextSnapshot.ds, prevSnapshot.ds), attrs)
nextSnapshot.sv.forEach((clock, client) => {
const prevClock = prevSnapshot.sv.get(client) || 0
inserts.add(client, 0, prevClock, []) // content is included in prevSnapshot is rendered without attributes
inserts.add(client, prevClock, clock - prevClock, [createAttributionItem('change', '')]) // content is rendered as "inserted"
inserts.add(client, prevClock, clock - prevClock, attrs) // content is rendered as "inserted"
})
this.attrs = mergeIdMaps([diffIdMap(inserts, prevSnapshot.ds), deletes])
}
@@ -615,5 +620,7 @@ export class SnapshotAttributionManager extends ObservableV2 {
/**
* @param {Snapshot} prevSnapshot
* @param {Snapshot} nextSnapshot
* @param {Object} [options] - options for the attribution manager
* @param {Array<import('./IdMap.js').AttributionItem<any>>} [options.attrs] - the attributes to apply to the diff
*/
export const createAttributionManagerFromSnapshots = (prevSnapshot, nextSnapshot = prevSnapshot) => new SnapshotAttributionManager(prevSnapshot, nextSnapshot)
export const createAttributionManagerFromSnapshots = (prevSnapshot, nextSnapshot = prevSnapshot, options) => new SnapshotAttributionManager(prevSnapshot, nextSnapshot, options)

View File

@@ -37,16 +37,6 @@ export class AttributionItem {
}
}
/**
* @param {AttributionItem<any>} attr
*/
const _hashAttribution = attr => {
const encoder = encoding.createEncoder()
encoding.writeVarString(encoder, attr.name)
encoding.writeAny(encoder, attr.val)
return buf.toBase64(rabin.fingerprint(rabin.StandardIrreducible128, encoding.toUint8Array(encoder)))
}
/**
* @todo rename this to `createAttribute`
* @template V
@@ -596,7 +586,7 @@ export const decodeIdMap = data => readIdMap(new DSDecoderV2(decoding.createDeco
const _ensureAttrs = (idmap, attrs) => attrs.map(attr =>
idmap.attrs.has(attr)
? attr
: map.setIfUndefined(idmap.attrsH, _hashAttribution(attr), () => {
: map.setIfUndefined(idmap.attrsH, attr.hash(), () => {
idmap.attrs.add(attr)
return attr
}))