after refactor - some tests are working again
This commit is contained in:
parent
30bf3742c9
commit
e56899a02c
@ -85,7 +85,8 @@ export default [{
|
||||
}),
|
||||
commonjs()
|
||||
]
|
||||
}, {
|
||||
}
|
||||
/* {
|
||||
input: ['./examples/codemirror.js', './examples/textarea.js', './examples/quill.js', './examples/dom.js', './examples/prosemirror.js'], // fs.readdirSync('./examples').filter(file => /(?<!\.(test|config))\.js$/.test(file)).map(file => './examples/' + file),
|
||||
output: {
|
||||
dir: 'examples/build',
|
||||
@ -102,4 +103,4 @@ export default [{
|
||||
commonjs(),
|
||||
...minificationPlugins
|
||||
]
|
||||
}]
|
||||
} */]
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
addStruct,
|
||||
addToDeleteSet,
|
||||
ItemDeleted,
|
||||
findRootTypeKey,
|
||||
ID, AbstractType, Y, Transaction // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@ -229,7 +230,7 @@ export class AbstractItem extends AbstractStruct {
|
||||
maplib.setIfUndefined(transaction.changed, parent, set.create).add(parentSub)
|
||||
}
|
||||
// @ts-ignore
|
||||
if (parent._item.deleted || (left !== null && parentSub !== null)) {
|
||||
if ((parent._item !== null && parent._item.deleted) || (left !== null && parentSub !== null)) {
|
||||
// delete if parent is deleted or if this is not the current attribute value of parent
|
||||
this.delete(transaction)
|
||||
} else if (parentSub !== null && left === null && right !== null) {
|
||||
@ -443,16 +444,12 @@ export class AbstractItem extends AbstractStruct {
|
||||
const info = (encodingRef & binary.BITS5) |
|
||||
((this.origin === null) ? 0 : binary.BIT8) | // origin is defined
|
||||
((this.rightOrigin === null) ? 0 : binary.BIT7) | // right origin is defined
|
||||
((this.parentSub !== null) ? 0 : binary.BIT6) // parentSub is non-null
|
||||
((this.parentSub === null) ? 0 : binary.BIT6) // parentSub is non-null
|
||||
encoding.writeUint8(encoder, info)
|
||||
if (offset === 0) {
|
||||
writeID(encoder, this.id)
|
||||
if (this.origin !== null) {
|
||||
if (this.origin !== null) {
|
||||
if (offset === 0) {
|
||||
writeID(encoder, this.origin.lastId)
|
||||
}
|
||||
} else {
|
||||
writeID(encoder, createID(this.id.client, this.id.clock + offset))
|
||||
if (this.origin !== null) {
|
||||
} else {
|
||||
writeID(encoder, createID(this.id.client, this.id.clock + offset - 1))
|
||||
}
|
||||
}
|
||||
@ -465,17 +462,7 @@ export class AbstractItem extends AbstractStruct {
|
||||
// parent type on y._map
|
||||
// find the correct key
|
||||
// @ts-ignore we know that y exists
|
||||
const map = parent._y.share
|
||||
let ykey = null
|
||||
for (const [key, type] of map) {
|
||||
if (type === parent) {
|
||||
ykey = key
|
||||
break
|
||||
}
|
||||
}
|
||||
if (ykey === null) {
|
||||
throw error.unexpectedCase()
|
||||
}
|
||||
const ykey = findRootTypeKey(this.parent)
|
||||
encoding.writeVarUint(encoder, 1) // write parentYKey
|
||||
encoding.writeVarString(encoder, ykey)
|
||||
} else {
|
||||
@ -509,7 +496,7 @@ export class AbstractItemRef extends AbstractRef {
|
||||
*/
|
||||
this.right = (info & binary.BIT7) === binary.BIT7 ? readID(decoder) : null
|
||||
const canCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0
|
||||
const hasParentYKey = decoding.readVarUint(decoder) === 1
|
||||
const hasParentYKey = canCopyParentInfo ? decoding.readVarUint(decoder) === 1 : false
|
||||
/**
|
||||
* If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
|
||||
* and we read the next string as parentYKey.
|
||||
@ -541,16 +528,4 @@ export class AbstractItemRef extends AbstractRef {
|
||||
missing.push(this.parent)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @return {Array<ID|null>}
|
||||
*/
|
||||
getMissing (transaction) {
|
||||
return [
|
||||
createID(this.id.client, this.id.clock - 1),
|
||||
this.left,
|
||||
this.right,
|
||||
this.parent
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ export class AbstractStruct {
|
||||
* @readonly
|
||||
*/
|
||||
this.id = id
|
||||
this.deleted = false
|
||||
}
|
||||
/**
|
||||
* Merge this struct with the item to the right.
|
||||
@ -35,12 +36,6 @@ export class AbstractStruct {
|
||||
get length () {
|
||||
throw error.methodUnimplemented()
|
||||
}
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get deleted () {
|
||||
throw error.methodUnimplemented()
|
||||
}
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
||||
* @param {number} offset
|
||||
@ -87,4 +82,10 @@ export class AbstractRef {
|
||||
toStruct (transaction) {
|
||||
throw error.methodUnimplemented()
|
||||
}
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
get length () {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,12 @@ export class GC extends AbstractStruct {
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.length = length
|
||||
this._len = length
|
||||
this.deleted = true
|
||||
}
|
||||
|
||||
get deleted () {
|
||||
return true
|
||||
get length () {
|
||||
return this._len
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,7 +38,7 @@ export class GC extends AbstractStruct {
|
||||
* @return {boolean}
|
||||
*/
|
||||
mergeWith (right) {
|
||||
this.length += right.length
|
||||
this._len += right.length
|
||||
return true
|
||||
}
|
||||
|
||||
@ -52,7 +53,7 @@ export class GC extends AbstractStruct {
|
||||
} else {
|
||||
writeID(encoder, createID(this.id.client, this.id.clock + offset))
|
||||
}
|
||||
encoding.writeVarUint(encoder, this.length)
|
||||
encoding.writeVarUint(encoder, this._len)
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +72,10 @@ export class GCRef extends AbstractRef {
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.length = decoding.readVarUint(decoder)
|
||||
this._len = decoding.readVarUint(decoder)
|
||||
}
|
||||
get length () {
|
||||
return this._len
|
||||
}
|
||||
missing () {
|
||||
return [
|
||||
@ -84,7 +88,7 @@ export class GCRef extends AbstractRef {
|
||||
toStruct () {
|
||||
return new GC(
|
||||
this.id,
|
||||
this.length
|
||||
this._len
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,11 @@ export class ItemDeleted extends AbstractItem {
|
||||
*/
|
||||
constructor (id, left, right, parent, parentSub, length) {
|
||||
super(id, left, right, parent, parentSub)
|
||||
this.length = length
|
||||
this._len = length
|
||||
this.deleted = true
|
||||
}
|
||||
get length () {
|
||||
return this._len
|
||||
}
|
||||
/**
|
||||
* @param {ID} id
|
||||
@ -47,7 +51,7 @@ export class ItemDeleted extends AbstractItem {
|
||||
*/
|
||||
mergeWith (right) {
|
||||
if (right.origin === this && this.right === right) {
|
||||
this.length += right.length
|
||||
this._len += right.length
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -73,7 +77,10 @@ export class ItemDeletedRef extends AbstractItemRef {
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.length = decoding.readVarUint(decoder)
|
||||
this.len = decoding.readVarUint(decoder)
|
||||
}
|
||||
get length () {
|
||||
return this.len
|
||||
}
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
@ -89,7 +96,7 @@ export class ItemDeletedRef extends AbstractItemRef {
|
||||
// @ts-ignore
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
|
||||
this.parentSub,
|
||||
this.length
|
||||
this.len
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ export class ItemEmbedRef extends AbstractItemRef {
|
||||
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
|
||||
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
|
||||
// @ts-ignore
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
|
||||
this.parentSub,
|
||||
this.embed
|
||||
)
|
||||
|
@ -81,7 +81,7 @@ export class ItemFormatRef extends AbstractItemRef {
|
||||
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
|
||||
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
|
||||
// @ts-ignore
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
|
||||
this.parentSub,
|
||||
this.key,
|
||||
this.value
|
||||
|
@ -108,6 +108,9 @@ export class ItemJSONRef extends AbstractItemRef {
|
||||
}
|
||||
this.content = cs
|
||||
}
|
||||
get length () {
|
||||
return this.content.length
|
||||
}
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @return {ItemJSON}
|
||||
@ -120,7 +123,7 @@ export class ItemJSONRef extends AbstractItemRef {
|
||||
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
|
||||
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
|
||||
// @ts-ignore
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
|
||||
this.parentSub,
|
||||
this.content
|
||||
)
|
||||
|
@ -97,6 +97,9 @@ export class ItemStringRef extends AbstractItemRef {
|
||||
*/
|
||||
this.string = decoding.readVarString(decoder)
|
||||
}
|
||||
get length () {
|
||||
return this.string.length
|
||||
}
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @return {ItemString}
|
||||
@ -109,7 +112,7 @@ export class ItemStringRef extends AbstractItemRef {
|
||||
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
|
||||
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
|
||||
// @ts-ignore
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
|
||||
this.parentSub,
|
||||
this.string
|
||||
)
|
||||
|
@ -49,6 +49,14 @@ export const typeRefs = [
|
||||
readYXmlText
|
||||
]
|
||||
|
||||
export const YArrayRefID = 0
|
||||
export const YMapRefID = 1
|
||||
export const YTextRefID = 2
|
||||
export const YXmlElementRefID = 3
|
||||
export const YXmlFragmentRefID = 4
|
||||
export const YXmlHookRefID = 5
|
||||
export const YXmlTextRefID = 6
|
||||
|
||||
export class ItemType extends AbstractItem {
|
||||
/**
|
||||
* @param {ID} id
|
||||
@ -62,6 +70,7 @@ export class ItemType extends AbstractItem {
|
||||
super(id, left, right, parent, parentSub)
|
||||
this.type = type
|
||||
}
|
||||
|
||||
getContent () {
|
||||
return [this.type]
|
||||
}
|
||||
@ -80,7 +89,8 @@ export class ItemType extends AbstractItem {
|
||||
* @param {Transaction} transaction
|
||||
*/
|
||||
integrate (transaction) {
|
||||
this.type._integrate(transaction, this)
|
||||
this.type._integrate(transaction.y, this)
|
||||
super.integrate(transaction)
|
||||
}
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
@ -168,7 +178,7 @@ export class ItemTypeRef extends AbstractItemRef {
|
||||
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
|
||||
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
|
||||
// @ts-ignore
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
|
||||
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
|
||||
this.parentSub,
|
||||
this.type
|
||||
)
|
||||
|
@ -68,12 +68,12 @@ export class AbstractType {
|
||||
* * This type is sent to other client
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Transaction} transaction The Yjs instance
|
||||
* @param {ItemType} item
|
||||
* @param {Y} y The Yjs instance
|
||||
* @param {ItemType|null} item
|
||||
* @private
|
||||
*/
|
||||
_integrate (transaction, item) {
|
||||
this._y = transaction.y
|
||||
_integrate (y, item) {
|
||||
this._y = y
|
||||
this._item = item
|
||||
}
|
||||
|
||||
@ -87,9 +87,7 @@ export class AbstractType {
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
*/
|
||||
_write (encoder) {
|
||||
throw new Error('unimplemented')
|
||||
}
|
||||
_write (encoder) { }
|
||||
|
||||
/**
|
||||
* The first non-deleted item
|
||||
@ -329,12 +327,19 @@ export const typeArrayGet = (type, index) => {
|
||||
* @param {Array<Object<string,any>|Array<any>|number|string|ArrayBuffer>} content
|
||||
*/
|
||||
export const typeArrayInsertGenericsAfter = (transaction, parent, referenceItem, content) => {
|
||||
let left = referenceItem
|
||||
const left = referenceItem
|
||||
const right = referenceItem === null ? parent._start : referenceItem.right
|
||||
/**
|
||||
* @type {Array<Object|Array|number>}
|
||||
*/
|
||||
let jsonContent = []
|
||||
const packJsonContent = () => {
|
||||
if (jsonContent.length > 0) {
|
||||
const item = new ItemJSON(nextID(transaction), left, right, parent, null, jsonContent)
|
||||
item.integrate(transaction)
|
||||
jsonContent = []
|
||||
}
|
||||
}
|
||||
content.forEach(c => {
|
||||
switch (c.constructor) {
|
||||
case Number:
|
||||
@ -344,11 +349,7 @@ export const typeArrayInsertGenericsAfter = (transaction, parent, referenceItem,
|
||||
jsonContent.push(c)
|
||||
break
|
||||
default:
|
||||
if (jsonContent.length > 0) {
|
||||
const item = new ItemJSON(nextID(transaction), left, right, parent, null, jsonContent)
|
||||
item.integrate(transaction)
|
||||
jsonContent = []
|
||||
}
|
||||
packJsonContent()
|
||||
switch (c.constructor) {
|
||||
case ArrayBuffer:
|
||||
// @ts-ignore c is definitely an ArrayBuffer
|
||||
@ -363,6 +364,7 @@ export const typeArrayInsertGenericsAfter = (transaction, parent, referenceItem,
|
||||
}
|
||||
}
|
||||
})
|
||||
packJsonContent()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -373,20 +375,22 @@ export const typeArrayInsertGenericsAfter = (transaction, parent, referenceItem,
|
||||
*/
|
||||
export const typeArrayInsertGenerics = (transaction, parent, index, content) => {
|
||||
if (index === 0) {
|
||||
typeArrayInsertGenericsAfter(transaction, parent, null, content)
|
||||
return typeArrayInsertGenericsAfter(transaction, parent, null, content)
|
||||
}
|
||||
for (let n = parent._start; n !== null; n = n.right) {
|
||||
let n = parent._start
|
||||
for (; n !== null; n = n.right) {
|
||||
if (!n.deleted && n.countable) {
|
||||
if (index <= n.length) {
|
||||
if (index < n.length) {
|
||||
// insert in-between
|
||||
getItemCleanStart(transaction.y.store, transaction, createID(n.id.client, n.id.clock + index))
|
||||
}
|
||||
return typeArrayInsertGenericsAfter(transaction, parent, n, content)
|
||||
break
|
||||
}
|
||||
index -= n.length
|
||||
}
|
||||
}
|
||||
throw new Error('Index exceeds array range')
|
||||
return typeArrayInsertGenericsAfter(transaction, parent, n, content)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -440,6 +444,10 @@ export const typeMapDelete = (transaction, parent, key) => {
|
||||
*/
|
||||
export const typeMapSet = (transaction, parent, key, value) => {
|
||||
const right = parent._map.get(key) || null
|
||||
if (value == null) {
|
||||
new ItemJSON(nextID(transaction), null, right, parent, key, [value]).integrate(transaction)
|
||||
return
|
||||
}
|
||||
switch (value.constructor) {
|
||||
case Number:
|
||||
case Object:
|
||||
|
@ -12,10 +12,12 @@ import {
|
||||
typeArrayInsertGenerics,
|
||||
typeArrayDelete,
|
||||
typeArrayMap,
|
||||
Transaction, ItemType, // eslint-disable-line
|
||||
YArrayRefID,
|
||||
Y, Transaction, ItemType, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
|
||||
/**
|
||||
* Event that describes the changes on a YArray
|
||||
@ -52,12 +54,12 @@ export class YArray extends AbstractType {
|
||||
* * This type is sent to other client
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Transaction} transaction The Yjs instance
|
||||
* @param {Y} y The Yjs instance
|
||||
* @param {ItemType} item
|
||||
* @private
|
||||
*/
|
||||
_integrate (transaction, item) {
|
||||
super._integrate(transaction, item)
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
// @ts-ignore
|
||||
this.insert(0, this._prelimContent)
|
||||
this._prelimContent = null
|
||||
@ -183,6 +185,13 @@ export class YArray extends AbstractType {
|
||||
push (content) {
|
||||
this.insert(this.length, content)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoding.writeVarUint(encoder, YArrayRefID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,9 +10,11 @@ import {
|
||||
typeMapGet,
|
||||
typeMapHas,
|
||||
createMapIterator,
|
||||
Transaction, ItemType, // eslint-disable-line
|
||||
YMapRefID,
|
||||
Y, Transaction, ItemType, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||
import * as iterator from 'lib0/iterator.js'
|
||||
|
||||
@ -53,12 +55,12 @@ export class YMap extends AbstractType {
|
||||
* * This type is sent to other client
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Transaction} transaction The Yjs instance
|
||||
* @param {Y} y The Yjs instance
|
||||
* @param {ItemType} item
|
||||
* @private
|
||||
*/
|
||||
_integrate (transaction, item) {
|
||||
super._integrate(transaction, item)
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
// @ts-ignore
|
||||
for (let [key, value] of this._prelimContent) {
|
||||
this.set(key, value)
|
||||
@ -88,7 +90,8 @@ export class YMap extends AbstractType {
|
||||
const map = {}
|
||||
for (let [key, item] of this._map) {
|
||||
if (!item.deleted) {
|
||||
map[key] = item.getContent()[0]
|
||||
const v = item.getContent()[0]
|
||||
map[key] = v instanceof AbstractType ? v.toJSON() : v
|
||||
}
|
||||
}
|
||||
return map
|
||||
@ -169,6 +172,13 @@ export class YMap extends AbstractType {
|
||||
has (key) {
|
||||
return typeMapHas(this, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoding.writeVarUint(encoder, YMapRefID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,10 +12,12 @@ import {
|
||||
createID,
|
||||
getItemCleanStart,
|
||||
isVisible,
|
||||
ItemType, AbstractItem, Snapshot, StructStore, Transaction // eslint-disable-line
|
||||
YTextRefID,
|
||||
Y, ItemType, AbstractItem, Snapshot, StructStore, Transaction // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
|
||||
/**
|
||||
* @private
|
||||
@ -566,11 +568,11 @@ export class YText extends AbstractType {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {Y} y
|
||||
* @param {ItemType} item
|
||||
*/
|
||||
_integrate (transaction, item) {
|
||||
super._integrate(transaction, item)
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
// @ts-ignore this._prelimContent is still defined
|
||||
this.insert(0, this._prelimContent.join(''))
|
||||
this._prelimContent = null
|
||||
@ -839,6 +841,13 @@ export class YText extends AbstractType {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoding.writeVarUint(encoder, YTextRefID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
typeArrayDelete,
|
||||
typeMapSet,
|
||||
typeMapDelete,
|
||||
Transaction, ItemType, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
||||
YXmlElementRefID,
|
||||
Y, Transaction, ItemType, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
@ -259,12 +260,12 @@ export class YXmlElement extends YXmlFragment {
|
||||
* * This type is sent to other client
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Transaction} transaction The Yjs instance
|
||||
* @param {Y} y The Yjs instance
|
||||
* @param {ItemType} item
|
||||
* @private
|
||||
*/
|
||||
_integrate (transaction, item) {
|
||||
super._integrate(transaction, item)
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
// @ts-ignore
|
||||
this.insert(0, this._prelimContent)
|
||||
this._prelimContent = null
|
||||
@ -285,19 +286,6 @@ export class YXmlElement extends YXmlFragment {
|
||||
return new YXmlElement(this.nodeName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the properties of this type to binary and write it to an
|
||||
* BinaryEncoder.
|
||||
*
|
||||
* This is called when this Item is sent to a remote peer.
|
||||
*
|
||||
* @private
|
||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoding.writeVarString(encoder, this.nodeName)
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.toDomString()
|
||||
}
|
||||
@ -460,6 +448,20 @@ export class YXmlElement extends YXmlFragment {
|
||||
}
|
||||
return dom
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the properties of this type to binary and write it to an
|
||||
* BinaryEncoder.
|
||||
*
|
||||
* This is called when this Item is sent to a remote peer.
|
||||
*
|
||||
* @private
|
||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoding.writeVarUint(encoder, YXmlElementRefID)
|
||||
encoding.writeVarString(encoder, this.nodeName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,10 @@
|
||||
* @module types
|
||||
*/
|
||||
|
||||
import { YMap } from '../internals.js'
|
||||
import {
|
||||
YMap,
|
||||
YXmlHookRefID
|
||||
} from '../internals.js'
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
import * as decoding from 'lib0/decoding.js'
|
||||
|
||||
@ -71,6 +74,7 @@ export class YXmlHook extends YMap {
|
||||
*/
|
||||
_write (encoder) {
|
||||
super._write(encoder)
|
||||
encoding.writeVarUint(encoder, YXmlHookRefID)
|
||||
encoding.writeVarString(encoder, this.hookName)
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,9 @@
|
||||
* @module types
|
||||
*/
|
||||
|
||||
import { YText } from '../internals.js'
|
||||
import { YText, YXmlTextRefID } from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||
|
||||
/**
|
||||
@ -33,6 +34,12 @@ export class YXmlText extends YText {
|
||||
}
|
||||
return dom
|
||||
}
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoding.writeVarUint(encoder, YXmlTextRefID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,6 +88,9 @@ export const findIndexSS = (structs, clock) => {
|
||||
if (clock < midclock + mid.length) {
|
||||
return midindex
|
||||
}
|
||||
if (left === midindex) {
|
||||
throw error.unexpectedCase()
|
||||
}
|
||||
left = midindex
|
||||
} else {
|
||||
right = midindex
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
writeDeleteSet,
|
||||
DeleteSet,
|
||||
sortAndMergeDeleteSet,
|
||||
getStates,
|
||||
AbstractType, AbstractItem, YEvent, ItemType, Y // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@ -52,12 +53,12 @@ export class Transaction {
|
||||
*/
|
||||
this.deleteSet = new DeleteSet()
|
||||
/**
|
||||
* If a state was modified, the original value is saved here.
|
||||
* Use `stateUpdates` to compute the original state before the transaction,
|
||||
* or to compute the set of inserted operations.
|
||||
* Holds the state before the transaction started.
|
||||
* @type {Map<Number,Number>}
|
||||
*/
|
||||
this.stateUpdates = new Map()
|
||||
this.beforeState = new Map()
|
||||
getStates(y.store).forEach(({client, clock}) => { this.beforeState.set(client, clock) })
|
||||
this.afterState = new Map()
|
||||
/**
|
||||
* All types that were directly modified (property added or child
|
||||
* inserted/deleted). New types are not included in this Set.
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { } from './StructStore.js'
|
||||
import { getStates } from './StructStore.js'
|
||||
|
||||
import {
|
||||
callEventHandlerListeners,
|
||||
@ -75,26 +75,29 @@ export class Y extends Observable {
|
||||
if (initialCall) {
|
||||
const transaction = this._transaction
|
||||
this._transaction = null
|
||||
// only call event listeners / observers if anything changed
|
||||
this.emit('beforeObserverCalls', [this, this._transaction])
|
||||
// emit change events on changed types
|
||||
transaction.changed.forEach((subs, itemtype) => {
|
||||
itemtype._callObserver(transaction, subs)
|
||||
})
|
||||
transaction.changedParentTypes.forEach((events, type) => {
|
||||
events = events
|
||||
.filter(event =>
|
||||
event.target._item === null || !event.target._item.deleted
|
||||
)
|
||||
events
|
||||
.forEach(event => {
|
||||
event.currentTarget = type
|
||||
})
|
||||
// we don't need to check for events.length
|
||||
// because we know it has at least one element
|
||||
callEventHandlerListeners(type._dEH, [events, transaction])
|
||||
})
|
||||
// only call afterTransaction listeners if anything changed
|
||||
const transactionChangedContent = transaction.changedParentTypes.size !== 0
|
||||
if (transactionChangedContent) {
|
||||
this.emit('beforeObserverCalls', [this, this._transaction])
|
||||
// emit change events on changed types
|
||||
transaction.changed.forEach((subs, itemtype) => {
|
||||
itemtype._callObserver(transaction, subs)
|
||||
})
|
||||
transaction.changedParentTypes.forEach((events, type) => {
|
||||
events = events
|
||||
.filter(event =>
|
||||
event.target._item === null || !event.target._item.deleted
|
||||
)
|
||||
events
|
||||
.forEach(event => {
|
||||
event.currentTarget = type
|
||||
})
|
||||
// we don't need to check for events.length
|
||||
// because we know it has at least one element
|
||||
callEventHandlerListeners(type._dEH, [events, transaction])
|
||||
getStates(transaction.y.store).forEach(({client, clock}) => {
|
||||
transaction.afterState.set(client, clock)
|
||||
})
|
||||
// when all changes & events are processed, emit afterTransaction event
|
||||
this.emit('afterTransaction', [this, transaction])
|
||||
@ -141,15 +144,17 @@ export class Y extends Observable {
|
||||
}
|
||||
}
|
||||
// on all affected store.clients props, try to merge
|
||||
for (const [client, clock] of transaction.stateUpdates) {
|
||||
/**
|
||||
* @type {Array<AbstractStruct>}
|
||||
*/
|
||||
// @ts-ignore
|
||||
const structs = store.clients.get(client)
|
||||
// we iterate from right to left so we can safely remove entries
|
||||
for (let i = structs.length - 1; i >= math.max(findIndexSS(structs, clock), 1); i--) {
|
||||
tryToMergeWithLeft(structs, i)
|
||||
for (const [client, clock] of transaction.beforeState) {
|
||||
if (transaction.afterState.get(client) !== clock) {
|
||||
/**
|
||||
* @type {Array<AbstractStruct>}
|
||||
*/
|
||||
// @ts-ignore
|
||||
const structs = store.clients.get(client)
|
||||
// we iterate from right to left so we can safely remove entries
|
||||
for (let i = structs.length - 1; i >= math.max(findIndexSS(structs, clock), 1); i--) {
|
||||
tryToMergeWithLeft(structs, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
// try to merge replacedItems
|
||||
@ -200,16 +205,21 @@ export class Y extends Observable {
|
||||
* @return {AbstractType<any>} The created type. Constructed with TypeConstructor
|
||||
*/
|
||||
get (name, TypeConstructor = AbstractType) {
|
||||
// @ts-ignore
|
||||
const type = map.setIfUndefined(this.share, name, () => new TypeConstructor())
|
||||
const type = map.setIfUndefined(this.share, name, () => {
|
||||
// @ts-ignore
|
||||
const t = new TypeConstructor()
|
||||
t._integrate(this, null)
|
||||
return t
|
||||
})
|
||||
const Constr = type.constructor
|
||||
if (Constr !== TypeConstructor) {
|
||||
if (TypeConstructor !== AbstractType && Constr !== TypeConstructor) {
|
||||
if (Constr === AbstractType) {
|
||||
const t = new Constr()
|
||||
t._map = type._map
|
||||
t._start = type._start
|
||||
t._length = type._length
|
||||
this.share.set(name, t)
|
||||
t._integrate(this, null)
|
||||
return t
|
||||
} else {
|
||||
throw new Error(`Type with the name ${name} has already been defined with a different constructor`)
|
||||
|
@ -67,7 +67,7 @@ export class YEvent {
|
||||
* @return {boolean}
|
||||
*/
|
||||
adds (struct) {
|
||||
return struct.id.clock > (this.transaction.stateUpdates.get(struct.id.client) || 0)
|
||||
return struct.id.clock > (this.transaction.beforeState.get(struct.id.client) || 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@ import {
|
||||
writeID,
|
||||
createID,
|
||||
readID,
|
||||
getState,
|
||||
getStates,
|
||||
Transaction, AbstractStruct, AbstractRef, StructStore, ID // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@ -51,7 +53,8 @@ const createStructReaderIterator = (decoder, structsLen, nextID) => iterator.cre
|
||||
} else {
|
||||
const info = decoding.readUint8(decoder)
|
||||
value = new structRefs[binary.BITS5 & info](decoder, nextID, info)
|
||||
nextID = createID(nextID.client, nextID.clock)
|
||||
nextID = createID(nextID.client, nextID.clock + value.length)
|
||||
structsLen--
|
||||
}
|
||||
return { done, value }
|
||||
})
|
||||
@ -60,18 +63,30 @@ const createStructReaderIterator = (decoder, structsLen, nextID) => iterator.cre
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {Transaction} transaction
|
||||
*/
|
||||
export const writeStructsFromTransaction = (encoder, transaction) => writeStructs(encoder, transaction.y.store, transaction.stateUpdates)
|
||||
export const writeStructsFromTransaction = (encoder, transaction) => writeStructs(encoder, transaction.y.store, transaction.beforeState)
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {StructStore} store
|
||||
* @param {StateMap} sm
|
||||
* @param {StateMap} _sm
|
||||
*/
|
||||
export const writeStructs = (encoder, store, sm) => {
|
||||
export const writeStructs = (encoder, store, _sm) => {
|
||||
// we filter all valid _sm entries into sm
|
||||
const sm = new Map()
|
||||
_sm.forEach((clock, client) => {
|
||||
if (getState(store, client) > clock) {
|
||||
sm.set(client, clock)
|
||||
}
|
||||
})
|
||||
getStates(store).forEach(({client}) => {
|
||||
if (!_sm.has(client)) {
|
||||
sm.set(client, 0)
|
||||
}
|
||||
})
|
||||
const encoderUserPosMap = map.create()
|
||||
// write # states that were updated
|
||||
encoding.writeVarUint(encoder, sm.size)
|
||||
sm.forEach((client, clock) => {
|
||||
sm.forEach((clock, client) => {
|
||||
// write first id
|
||||
writeID(encoder, createID(client, clock))
|
||||
encoderUserPosMap.set(client, encoding.length(encoder))
|
||||
@ -79,7 +94,7 @@ export const writeStructs = (encoder, store, sm) => {
|
||||
// We will fill out this value later *)
|
||||
encoding.writeUint32(encoder, 0)
|
||||
})
|
||||
sm.forEach((client, clock) => {
|
||||
sm.forEach((clock, client) => {
|
||||
const decPos = encoderUserPosMap.get(client)
|
||||
encoding.setUint32(encoder, decPos, encoding.length(encoder) - decPos)
|
||||
/**
|
||||
@ -115,8 +130,8 @@ export const readStructs = (decoder, transaction, store) => {
|
||||
* @type {Map<number,Iterator<AbstractRef>>}
|
||||
*/
|
||||
const structReaders = new Map()
|
||||
const clientStateUpdates = decoding.readVarUint(decoder)
|
||||
for (let i = 0; i < clientStateUpdates; i++) {
|
||||
const clientbeforeState = decoding.readVarUint(decoder)
|
||||
for (let i = 0; i < clientbeforeState; i++) {
|
||||
const nextID = readID(decoder)
|
||||
const decoderPos = decoder.pos + decoding.readUint32(decoder)
|
||||
const structReaderDecoder = decoding.clone(decoder, decoderPos)
|
||||
|
@ -268,7 +268,7 @@ export const compare = users => {
|
||||
while (users[0].tc.flushAllMessages()) {}
|
||||
const userArrayValues = users.map(u => u.getArray('array').toJSON().map(val => JSON.stringify(val)))
|
||||
const userMapValues = users.map(u => u.getMap('map').toJSON())
|
||||
const userXmlValues = users.map(u => u.getXmlFragment('xml').toString())
|
||||
const userXmlValues = users.map(u => u.get('xml', Y.XmlElement).toString())
|
||||
const userTextValues = users.map(u => u.getText('text').toDelta())
|
||||
for (var i = 0; i < users.length - 1; i++) {
|
||||
t.describe(`Comparing user${i} with user${i + 1}`)
|
||||
|
Loading…
x
Reference in New Issue
Block a user