mirror of
https://github.com/yjs/yjs.git
synced 2026-02-23 19:49:59 +01:00
getContent on Y.Map
This commit is contained in:
@@ -108,7 +108,7 @@ export {
|
||||
createInsertionSetFromStructStore,
|
||||
diffIdMap,
|
||||
diffIdSet,
|
||||
Attribution,
|
||||
AttributionItem as Attribution,
|
||||
encodeIdMap
|
||||
} from './internals.js'
|
||||
|
||||
|
||||
@@ -14,11 +14,18 @@ import {
|
||||
callTypeObservers,
|
||||
transact,
|
||||
warnPrematureAccess,
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
||||
createAttributionFromAttrs
|
||||
} from '../internals.js'
|
||||
|
||||
import * as array from 'lib0/array'
|
||||
import * as iterator from 'lib0/iterator'
|
||||
|
||||
/**
|
||||
* @template MapType
|
||||
* @typedef {{ [key: string]: { prevValue: MapType | undefined, value: MapType | undefined, attribution: any } }} MapAttributedContent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends YEvent<YMap<T>>
|
||||
@@ -186,6 +193,61 @@ export class YMap extends AbstractType {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
||||
* attributions.
|
||||
*
|
||||
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||
* @return {MapAttributedContent<MapType>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am) {
|
||||
/**
|
||||
* @type {MapAttributedContent<MapType>}
|
||||
*/
|
||||
const mapcontent = {}
|
||||
this._map.forEach((item, key) => {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const cs = []
|
||||
am.readContent(cs, item)
|
||||
const { deleted, attrs, content } = cs[cs.length - 1]
|
||||
const c = array.last(content.getContent())
|
||||
const attribution = createAttributionFromAttrs(attrs, deleted)
|
||||
if (deleted) {
|
||||
mapcontent[key] = { prevValue: c, value: undefined, attribution }
|
||||
} else {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
let cs = []
|
||||
for (let prevItem = item.left; prevItem != null; prevItem = prevItem.left) {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const tmpcs = []
|
||||
am.readContent(tmpcs, prevItem)
|
||||
cs = tmpcs.concat(cs)
|
||||
if (cs[0].attrs == null) {
|
||||
cs.splice(0, cs.findIndex(c => c.attrs != null))
|
||||
break
|
||||
}
|
||||
if (cs.length > 0) {
|
||||
cs.length = 1
|
||||
}
|
||||
}
|
||||
const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined
|
||||
mapcontent[key] = { prevValue, value: c, attribution }
|
||||
}
|
||||
})
|
||||
return mapcontent
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator of [key, value] pairs
|
||||
*
|
||||
|
||||
@@ -26,7 +26,8 @@ import {
|
||||
updateMarkerChanges,
|
||||
ContentType,
|
||||
warnPrematureAccess,
|
||||
noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction // eslint-disable-line
|
||||
noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction, // eslint-disable-line
|
||||
createAttributionFromAttrs
|
||||
} from '../internals.js'
|
||||
|
||||
import * as delta from '../utils/Delta.js'
|
||||
@@ -1017,67 +1018,39 @@ export class YText extends AbstractType {
|
||||
}
|
||||
for (let i = 0; i < cs.length; i++) {
|
||||
const { content, deleted, attrs } = cs[i]
|
||||
/**
|
||||
* @type {import('../utils/Delta.js').Attribution?}
|
||||
*/
|
||||
let attributions = null
|
||||
if (attrs != null) {
|
||||
attributions = {}
|
||||
if (deleted) {
|
||||
attributions.delete = []
|
||||
} else {
|
||||
attributions.insert = []
|
||||
}
|
||||
attrs.forEach(attr => {
|
||||
switch (attr.name) {
|
||||
case 'insert':
|
||||
case 'delete':
|
||||
case 'suggest': {
|
||||
const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions)
|
||||
const ls = as[attr.name] = as[attr.name] ?? []
|
||||
ls.push(attr.val)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
if (attr.name[0] !== '_') {
|
||||
/** @type {any} */ (attributions)[attr.name] = attr.val
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const attribution = createAttributionFromAttrs(attrs, deleted)
|
||||
switch (content.constructor) {
|
||||
case ContentString: {
|
||||
d.insert(/** @type {ContentString} */ (content).str, null, attributions)
|
||||
d.insert(/** @type {ContentString} */ (content).str, null, attribution)
|
||||
break
|
||||
}
|
||||
case ContentType:
|
||||
case ContentEmbed: {
|
||||
d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions)
|
||||
d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attribution)
|
||||
break
|
||||
}
|
||||
case ContentFormat: {
|
||||
const contentFormat = /** @type {ContentFormat} */ (content)
|
||||
if (attributions != null) {
|
||||
if (attribution != null) {
|
||||
/**
|
||||
* @type {import('../utils/Delta.js').Attribution}
|
||||
*/
|
||||
const formattingAttributions = object.assign({}, d.usedAttribution)
|
||||
const attributesChanged = /** @type {{ [key: string]: Array<any> }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {}))
|
||||
const formattingAttribution = object.assign({}, d.usedAttribution)
|
||||
const attributesChanged = /** @type {{ [key: string]: Array<any> }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {}))
|
||||
if (contentFormat.value === null) {
|
||||
delete attributesChanged[contentFormat.key]
|
||||
} else {
|
||||
const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? []
|
||||
by.push(...((deleted ? attributions.delete : attributions.insert) ?? []))
|
||||
const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt)
|
||||
if (attributedAt) formattingAttributions.attributedAt = attributedAt
|
||||
by.push(...((deleted ? attribution.delete : attribution.insert) ?? []))
|
||||
const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt)
|
||||
if (attributedAt) formattingAttribution.attributedAt = attributedAt
|
||||
}
|
||||
if (object.isEmpty(attributesChanged)) {
|
||||
d.useAttribution(null)
|
||||
} else {
|
||||
const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt)
|
||||
if (attributedAt != null) formattingAttributions.attributedAt = attributedAt
|
||||
d.useAttribution(formattingAttributions)
|
||||
const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt)
|
||||
if (attributedAt != null) formattingAttribution.attributedAt = attributedAt
|
||||
d.useAttribution(formattingAttribution)
|
||||
}
|
||||
}
|
||||
if (!deleted) {
|
||||
|
||||
@@ -4,6 +4,56 @@ import {
|
||||
|
||||
import * as error from 'lib0/error'
|
||||
|
||||
/**
|
||||
* @typedef {Object} Attribution
|
||||
* @property {Array<any>} [Attribution.insert]
|
||||
* @property {number} [Attribution.insertedAt]
|
||||
* @property {Array<any>} [Attribution.suggest]
|
||||
* @property {number} [Attribution.suggestedAt]
|
||||
* @property {Array<any>} [Attribution.delete]
|
||||
* @property {number} [Attribution.deletedAt]
|
||||
* @property {{ [key: string]: Array<any> }} [Attribution.attributes]
|
||||
* @property {number} [Attribution.attributedAt]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Array<import('./IdMap.js').AttributionItem<any>>?} attrs
|
||||
* @param {boolean} deleted - whether the attributed item is deleted
|
||||
* @return {Attribution?}
|
||||
*/
|
||||
export const createAttributionFromAttrs = (attrs, deleted) => {
|
||||
/**
|
||||
* @type {Attribution?}
|
||||
*/
|
||||
let attribution = null
|
||||
if (attrs != null) {
|
||||
attribution = {}
|
||||
if (deleted) {
|
||||
attribution.delete = []
|
||||
} else {
|
||||
attribution.insert = []
|
||||
}
|
||||
attrs.forEach(attr => {
|
||||
switch (attr.name) {
|
||||
case 'insert':
|
||||
case 'delete':
|
||||
case 'suggest': {
|
||||
const as = /** @type {import('../utils/Delta.js').Attribution} */ (attribution)
|
||||
const ls = as[attr.name] = as[attr.name] ?? []
|
||||
ls.push(attr.val)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
if (attr.name[0] !== '_') {
|
||||
/** @type {any} */ (attribution)[attr.name] = attr.val
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return attribution
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
@@ -11,7 +61,7 @@ export class AttributedContent {
|
||||
/**
|
||||
* @param {AbstractContent} content
|
||||
* @param {boolean} deleted
|
||||
* @param {Array<import('./IdMap.js').Attribution<T>> | null} attrs
|
||||
* @param {Array<import('./IdMap.js').AttributionItem<T>> | null} attrs
|
||||
*/
|
||||
constructor (content, deleted, attrs) {
|
||||
this.content = content
|
||||
|
||||
@@ -6,19 +6,11 @@ import * as fun from 'lib0/function'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{ [key: string]: any }} FormattingAttributes
|
||||
* @typedef {import('./AttributionManager.js').Attribution} Attribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Attribution
|
||||
* @property {Array<any>} [Attribution.insert]
|
||||
* @property {number} [Attribution.insertedAt]
|
||||
* @property {Array<any>} [Attribution.suggest]
|
||||
* @property {number} [Attribution.suggestedAt]
|
||||
* @property {Array<any>} [Attribution.delete]
|
||||
* @property {number} [Attribution.deletedAt]
|
||||
* @property {{ [key: string]: Array<any> }} [Attribution.attributes]
|
||||
* @property {number} [Attribution.attributedAt]
|
||||
* @typedef {{ [key: string]: any }} FormattingAttributes
|
||||
*/
|
||||
|
||||
export class InsertOp {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
_diffSet,
|
||||
findIndexInIdRanges,
|
||||
findRangeStartInIdRanges,
|
||||
DSDecoderV1, DSDecoderV2, IdSetEncoderV1, IdSetEncoderV2, IdSet, ID // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@@ -14,7 +15,7 @@ import * as rabin from 'lib0/hash/rabin'
|
||||
/**
|
||||
* @template V
|
||||
*/
|
||||
export class Attribution {
|
||||
export class AttributionItem {
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {V} val
|
||||
@@ -33,7 +34,7 @@ export class Attribution {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Attribution<any>} attr
|
||||
* @param {AttributionItem<any>} attr
|
||||
*/
|
||||
const _hashAttribution = attr => {
|
||||
const encoder = encoding.createEncoder()
|
||||
@@ -46,9 +47,9 @@ const _hashAttribution = attr => {
|
||||
* @template V
|
||||
* @param {string} name
|
||||
* @param {V} val
|
||||
* @return {Attribution<V>}
|
||||
* @return {AttributionItem<V>}
|
||||
*/
|
||||
export const createAttribution = (name, val) => new Attribution(name, val)
|
||||
export const createAttribution = (name, val) => new AttributionItem(name, val)
|
||||
|
||||
/**
|
||||
* @template T
|
||||
@@ -79,7 +80,7 @@ export class AttrRange {
|
||||
/**
|
||||
* @param {number} clock
|
||||
* @param {number} len
|
||||
* @param {Array<Attribution<Attrs>>} attrs
|
||||
* @param {Array<AttributionItem<Attrs>>} attrs
|
||||
*/
|
||||
constructor (clock, len, attrs) {
|
||||
/**
|
||||
@@ -107,7 +108,7 @@ export class AttrRange {
|
||||
|
||||
/**
|
||||
* @template Attrs
|
||||
* @typedef {{ clock: number, len: number, attrs: Array<Attribution<Attrs>>? }} MaybeAttrRange
|
||||
* @typedef {{ clock: number, len: number, attrs: Array<AttributionItem<Attrs>>? }} MaybeAttrRange
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -115,7 +116,7 @@ export class AttrRange {
|
||||
*
|
||||
* @param {number} clock
|
||||
* @param {number} len
|
||||
* @param {Array<Attribution<Attrs>>?} attrs
|
||||
* @param {Array<AttributionItem<Attrs>>?} attrs
|
||||
* @return {MaybeAttrRange<Attrs>}
|
||||
*/
|
||||
export const createMaybeAttrRange = (clock, len, attrs) => new AttrRange(clock, len, /** @type {any} */ (attrs))
|
||||
@@ -140,7 +141,7 @@ export class AttrRanges {
|
||||
/**
|
||||
* @param {number} clock
|
||||
* @param {number} length
|
||||
* @param {Array<Attribution<Attrs>>} attrs
|
||||
* @param {Array<AttributionItem<Attrs>>} attrs
|
||||
*/
|
||||
add (clock, length, attrs) {
|
||||
if (length === 0) return
|
||||
@@ -241,7 +242,7 @@ export const mergeIdMaps = ams => {
|
||||
/**
|
||||
* Maps attribution to the attribution of the merged idmap.
|
||||
*
|
||||
* @type {Map<Attribution<any>,Attribution<any>>}
|
||||
* @type {Map<AttributionItem<any>,AttributionItem<any>>}
|
||||
*/
|
||||
const attrMapper = new Map()
|
||||
const merged = createIdMap()
|
||||
@@ -271,7 +272,7 @@ export const mergeIdMaps = ams => {
|
||||
|
||||
/**
|
||||
* @param {IdSet} idset
|
||||
* @param {Array<Attribution<any>>} attrs
|
||||
* @param {Array<AttributionItem<any>>} attrs
|
||||
*/
|
||||
export const createIdMapFromIdSet = (idset, attrs) => {
|
||||
const idmap = createIdMap()
|
||||
@@ -279,7 +280,7 @@ export const createIdMapFromIdSet = (idset, attrs) => {
|
||||
attrs = _ensureAttrs(idmap, attrs)
|
||||
// filter out duplicates
|
||||
/**
|
||||
* @type {Array<Attribution<any>>}
|
||||
* @type {Array<AttributionItem<any>>}
|
||||
*/
|
||||
const checkedAttrs = []
|
||||
attrs.forEach(attr => {
|
||||
@@ -305,11 +306,11 @@ export class IdMap {
|
||||
*/
|
||||
this.clients = new Map()
|
||||
/**
|
||||
* @type {Map<string, Attribution<Attrs>>}
|
||||
* @type {Map<string, AttributionItem<Attrs>>}
|
||||
*/
|
||||
this.attrsH = new Map()
|
||||
/**
|
||||
* @type {Set<Attribution<Attrs>>}
|
||||
* @type {Set<AttributionItem<Attrs>>}
|
||||
*/
|
||||
this.attrs = new Set()
|
||||
}
|
||||
@@ -344,7 +345,7 @@ export class IdMap {
|
||||
* @type {Array<AttrRange<Attrs>>}
|
||||
*/
|
||||
const ranges = dr.getIds()
|
||||
let index = findIndexInIdRanges(ranges, id.clock)
|
||||
let index = findRangeStartInIdRanges(ranges, id.clock)
|
||||
if (index !== null) {
|
||||
let prev = null
|
||||
while (index < ranges.length) {
|
||||
@@ -356,9 +357,9 @@ export class IdMap {
|
||||
r = new AttrRange(r.clock, id.clock + len - r.clock, r.attrs)
|
||||
}
|
||||
if (r.len <= 0) break
|
||||
const prevEnd = prev != null ? prev.clock + prev.len : index
|
||||
if (prevEnd < index) {
|
||||
res.push(createMaybeAttrRange(prevEnd, index - prevEnd, null))
|
||||
const prevEnd = prev != null ? prev.clock + prev.len : id.clock
|
||||
if (prevEnd < r.clock) {
|
||||
res.push(createMaybeAttrRange(prevEnd, r.clock - prevEnd, null))
|
||||
}
|
||||
prev = r
|
||||
res.push(r)
|
||||
@@ -382,7 +383,7 @@ export class IdMap {
|
||||
* @param {number} client
|
||||
* @param {number} clock
|
||||
* @param {number} len
|
||||
* @param {Array<Attribution<Attrs>>} attrs
|
||||
* @param {Array<AttributionItem<Attrs>>} attrs
|
||||
*/
|
||||
add (client, clock, len, attrs) {
|
||||
if (len === 0) return
|
||||
@@ -411,7 +412,7 @@ export const writeIdMap = (encoder, idmap) => {
|
||||
encoding.writeVarUint(encoder.restEncoder, idmap.clients.size)
|
||||
let lastWrittenClientId = 0
|
||||
/**
|
||||
* @type {Map<Attribution<Attr>, number>}
|
||||
* @type {Map<AttributionItem<Attr>, number>}
|
||||
*/
|
||||
const visitedAttributions = map.create()
|
||||
/**
|
||||
@@ -482,7 +483,7 @@ export const readIdMap = decoder => {
|
||||
const idmap = new IdMap()
|
||||
const numClients = decoding.readVarUint(decoder.restDecoder)
|
||||
/**
|
||||
* @type {Array<Attribution<any>>}
|
||||
* @type {Array<AttributionItem<any>>}
|
||||
*/
|
||||
const visitedAttributions = []
|
||||
/**
|
||||
@@ -503,7 +504,7 @@ export const readIdMap = decoder => {
|
||||
const rangeClock = decoder.readDsClock()
|
||||
const rangeLen = decoder.readDsLen()
|
||||
/**
|
||||
* @type {Array<Attribution<any>>}
|
||||
* @type {Array<AttributionItem<any>>}
|
||||
*/
|
||||
const attrs = []
|
||||
const attrsLen = decoding.readVarUint(decoder.restDecoder)
|
||||
@@ -515,7 +516,7 @@ export const readIdMap = decoder => {
|
||||
if (attrNameId >= visitedAttrNames.length) {
|
||||
visitedAttrNames.push(decoding.readVarString(decoder.restDecoder))
|
||||
}
|
||||
visitedAttributions.push(new Attribution(visitedAttrNames[attrNameId], decoding.readAny(decoder.restDecoder)))
|
||||
visitedAttributions.push(new AttributionItem(visitedAttrNames[attrNameId], decoding.readAny(decoder.restDecoder)))
|
||||
}
|
||||
attrs.push(visitedAttributions[attrId])
|
||||
}
|
||||
@@ -539,8 +540,8 @@ export const decodeIdMap = data => readIdMap(new DSDecoderV2(decoding.createDeco
|
||||
/**
|
||||
* @template Attrs
|
||||
* @param {IdMap<Attrs>} idmap
|
||||
* @param {Array<Attribution<Attrs>>} attrs
|
||||
* @return {Array<Attribution<Attrs>>}
|
||||
* @param {Array<AttributionItem<Attrs>>} attrs
|
||||
* @return {Array<AttributionItem<Attrs>>}
|
||||
*/
|
||||
const _ensureAttrs = (idmap, attrs) => attrs.map(attr =>
|
||||
idmap.attrs.has(attr)
|
||||
|
||||
@@ -168,6 +168,35 @@ export const findIndexInIdRanges = (dis, clock) => {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first range that contains clock or comes after clock.
|
||||
*
|
||||
* @param {Array<IdRange>} dis
|
||||
* @param {number} clock
|
||||
* @return {number|null}
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const findRangeStartInIdRanges = (dis, clock) => {
|
||||
let left = 0
|
||||
let right = dis.length - 1
|
||||
while (left <= right) {
|
||||
const midindex = math.floor((left + right) / 2)
|
||||
const mid = dis[midindex]
|
||||
const midclock = mid.clock
|
||||
if (midclock <= clock) {
|
||||
if (clock < midclock + mid.len) {
|
||||
return midindex
|
||||
}
|
||||
left = midindex + 1
|
||||
} else {
|
||||
right = midindex - 1
|
||||
}
|
||||
}
|
||||
return left < dis.length ? left : null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<IdSet>} idSets
|
||||
* @return {IdSet} A fresh IdSet
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import * as Y from '../src/index.js'
|
||||
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
|
||||
import * as delta from '../src/utils/Delta.js'
|
||||
import {
|
||||
compareIDs
|
||||
compareIDs,
|
||||
noAttributionsManager,
|
||||
TwosetAttributionManager,
|
||||
createIdMapFromIdSet
|
||||
} from '../src/internals.js'
|
||||
import * as t from 'lib0/testing'
|
||||
import * as prng from 'lib0/prng'
|
||||
@@ -613,6 +617,41 @@ export const testYmapEventHasCorrectValueWhenSettingAPrimitiveFromOtherUser = tc
|
||||
compare(users)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testAttributedContent = _tc => {
|
||||
const ydoc = new Y.Doc({ gc: false })
|
||||
const ymap = ydoc.getMap()
|
||||
let attributionManager = noAttributionsManager
|
||||
|
||||
ydoc.on('afterTransaction', tr => {
|
||||
// attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')]))
|
||||
attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, []), createIdMapFromIdSet(tr.deleteSet, []))
|
||||
})
|
||||
t.group('initial value', () => {
|
||||
ymap.set('test', 42)
|
||||
let expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } }
|
||||
let attributedContent = ymap.getContent(attributionManager)
|
||||
console.log(attributedContent)
|
||||
t.compare(expectedContent, attributedContent)
|
||||
})
|
||||
t.group('overwrite value', () => {
|
||||
ymap.set('test', 'fourtytwo')
|
||||
let expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } }
|
||||
let attributedContent = ymap.getContent(attributionManager)
|
||||
console.log(attributedContent)
|
||||
t.compare(expectedContent, attributedContent)
|
||||
})
|
||||
t.group('delete value', () => {
|
||||
ymap.delete('test')
|
||||
let expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } }
|
||||
let attributedContent = ymap.getContent(attributionManager)
|
||||
console.log(attributedContent)
|
||||
t.compare(expectedContent, attributedContent)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Array<function(Doc,prng.PRNG):void>}
|
||||
*/
|
||||
|
||||
@@ -2632,7 +2632,6 @@ export const testAttributionManagerDefaultPerformance = tc => {
|
||||
t.info(`number of changes: ${N/1000}k`)
|
||||
t.info(`length of text: ${ytext.length}`)
|
||||
const M = 100
|
||||
|
||||
t.measureTime(`original toString perf <executed ${M} times>`, () => {
|
||||
for (let i = 0; i < M; i++) {
|
||||
ytext.toDelta()
|
||||
|
||||
Reference in New Issue
Block a user