attributions and fixes to idmap

This commit is contained in:
Kevin Jahns
2025-04-21 01:13:41 +02:00
parent c7ab7a4ee5
commit 782fde5065
4 changed files with 41 additions and 30 deletions

View File

@@ -1016,11 +1016,12 @@ export class YText extends AbstractType {
let attributions = null
if (attrs != null) {
attributions = {}
attributions.changeType = deleted ? 'delete' : 'insert'
attrs.forEach(attr => {
switch (attr.name) {
case '_insertedBy':
case '_deletedBy':
case '_suggestedBy': {
case 'insertedBy':
case 'deletedBy':
case 'suggestedBy': {
const as = /** @type {any} */ (attributions)
const ls = as[attr.name] = as[attr.name] ?? []
ls.push(attr.val)
@@ -1046,10 +1047,15 @@ export class YText extends AbstractType {
}
case ContentFormat:
if (attributions != null) {
attributions.formattedBy = (deleted ? attributions.deletedBy : attributions.insertedBy) ?? []
delete attributions.deletedBy
delete attributions.insertedBy
d.useAttribution(attributions)
if (deleted) {
d.useAttribution(null)
} else {
attributions.formattedBy = (deleted ? attributions.deletedBy : attributions.insertedBy) ?? []
attributions.changeType = 'format'
delete attributions.deletedBy
delete attributions.insertedBy
d.useAttribution(attributions)
}
}
break
}

View File

@@ -106,22 +106,22 @@ export class DeltaBuilder extends Delta {
}
/**
* @param {FormattingAttributes} attributes
* @param {FormattingAttributes?} attributes
* @return {this}
*/
useAttributes (attributes) {
if (this._useAttributes === attributes) return this
this._useAttributes = object.assign({}, attributes)
this._useAttributes = attributes && object.assign({}, attributes)
if (this._lastOp?.constructor !== DeleteOp) this._lastOp = null
return this
}
/**
* @param {Attribution} attribution
* @param {Attribution?} attribution
*/
useAttribution (attribution) {
if (this._useAttribution === attribution) return this
this._useAttribution = object.assign({}, attribution)
this._useAttribution = attribution && object.assign({}, attribution)
if (this._lastOp?.constructor !== DeleteOp) this._lastOp = null
return this
}

View File

@@ -110,6 +110,16 @@ export class AttrRange {
* @typedef {{ clock: number, len: number, attrs: Array<Attribution<Attrs>>? }} MaybeAttrRange
*/
/**
* @template Attrs
*
* @param {number} clock
* @param {number} len
* @param {Array<Attribution<Attrs>>?} attrs
* @return {MaybeAttrRange<Attrs>}
*/
export const createMaybeAttrRange = (clock, len, attrs) => new AttrRange(clock, len, /** @type {any} */ (attrs))
/**
* @template Attrs
*/
@@ -238,21 +248,18 @@ export const mergeIdMaps = ams => {
if (!merged.clients.has(client)) {
// Write all missing keys from current set and all following.
// If merged already contains `client` current ds has already been added.
const ids = rangesLeft.getIds().slice()
let ids = rangesLeft.getIds().slice()
for (let i = amsI + 1; i < ams.length; i++) {
const nextIds = ams[i].clients.get(client)
if (nextIds) {
array.appendTo(ids, nextIds.getIds())
}
}
ids.forEach(id => {
// @ts-ignore
id.attrs = id.attrs.map(attr =>
map.setIfUndefined(attrMapper, attr, () =>
_ensureAttrs(merged, [attr])[0]
)
ids = ids.map(id => new AttrRange(id.clock, id.len, id.attrs.map(attr =>
map.setIfUndefined(attrMapper, attr, () =>
_ensureAttrs(merged, [attr])[0]
)
})
)))
merged.clients.set(client, new AttrRanges(ids))
}
})
@@ -323,7 +330,7 @@ export class IdMap {
if (r.len <= 0) break
const prevEnd = prev != null ? prev.clock + prev.len : index
if (prevEnd < index) {
res.push(/** @type {MaybeAttrRange<Attrs>} */ (new AttrRange(prevEnd, index - prevEnd, /** @type {any} */ (null))))
res.push(createMaybeAttrRange(prevEnd, index - prevEnd, null))
}
prev = r
res.push(r)
@@ -335,10 +342,10 @@ export class IdMap {
const last = res[res.length - 1]
const end = last.clock + last.len
if (end < id.clock + len) {
res.push(new AttrRange(end, id.clock + len - end, []))
res.push(createMaybeAttrRange(end, id.clock + len - end, null))
}
} else {
res.push(new AttrRange(id.clock, len, []))
res.push(createMaybeAttrRange(id.clock, len, null))
}
return res
}

View File

@@ -1,6 +1,6 @@
import * as t from 'lib0/testing'
import * as idmap from '../src/utils/IdMap.js'
import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution } from './testHelper.js'
import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution, validateIdMap } from './testHelper.js'
import * as YY from '../src/internals.js'
/**
@@ -93,13 +93,11 @@ export const testRepeatMergingMultipleIdMaps = tc => {
const oneHas = sets.some(ids => ids.has(new ID(iclient, iclock)))
t.assert(mergedHas === oneHas)
const mergedAttrs = merged.slice(new ID(iclient, iclock), 1)
if (mergedAttrs) {
mergedAttrs.forEach(a => {
if (a.attrs != null) {
composed.add(iclient, a.clock, a.len, a.attrs)
}
})
}
mergedAttrs.forEach(a => {
if (a.attrs != null) {
composed.add(iclient, a.clock, a.len, a.attrs)
}
})
}
}
compareIdmaps(merged, composed)