mirror of
https://github.com/yjs/yjs.git
synced 2026-02-24 04:01:14 +01:00
attributions and fixes to idmap
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user