fix first y-array test
This commit is contained in:
@@ -1,26 +1,44 @@
|
||||
import StructManager from '../Util/StructManager.js'
|
||||
import { getReference } from '../Util/structReferences.js'
|
||||
|
||||
export function deleteItemRange (y, user, clock, range) {
|
||||
let items = y.os.getItems(this._target, this._length)
|
||||
for (let i = items.length - 1; i >= 0; i--) {
|
||||
items[i]._delete(y, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete is not a real struct. It will not be saved in OS
|
||||
*/
|
||||
export default class Delete {
|
||||
constructor () {
|
||||
this._target = null
|
||||
this._targetID = null
|
||||
this._length = null
|
||||
}
|
||||
_fromBinary (y, decoder) {
|
||||
this._targetID = decoder.readOpID()
|
||||
this._targetID = decoder.readID()
|
||||
this._length = decoder.readVarUint()
|
||||
}
|
||||
_toBinary (y, encoder) {
|
||||
encoder.writeUint8(StructManager.getReference(this.constructor))
|
||||
encoder.writeOpID(this._targetID)
|
||||
_toBinary (encoder) {
|
||||
encoder.writeUint8(getReference(this.constructor))
|
||||
encoder.writeID(this._targetID)
|
||||
encoder.writeVarUint(this._length)
|
||||
}
|
||||
_integrate (y) {
|
||||
let items = y.os.getItems(this._target, this._length)
|
||||
for (let i = items.length - 1; i >= 0; i--) {
|
||||
items[i]._delete()
|
||||
/**
|
||||
* - If created remotely (a remote user deleted something),
|
||||
* this Delete is applied to all structs in id-range.
|
||||
* - If created lokally (e.g. when y-array deletes a range of elements),
|
||||
* this struct is broadcasted only (it is already executed)
|
||||
*/
|
||||
_integrate (y, locallyCreated = false) {
|
||||
if (!locallyCreated) {
|
||||
// from remote
|
||||
const id = this._targetID
|
||||
deleteItemRange(y, id.user, id.clock, this._length)
|
||||
} else {
|
||||
// from local
|
||||
y.connector.broadcastStruct(this)
|
||||
}
|
||||
// TODO: only broadcast if created by local user or if y.connector._forwardAppliedStructs..
|
||||
y.connector.broadcastStruct(this)
|
||||
if (y.persistence !== null) {
|
||||
y.persistence.saveOperations(this)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
import { getReference } from '../Util/structReferences.js'
|
||||
import ID from '../Util/ID.js'
|
||||
import { RootFakeUserID } from '../Util/RootID.js'
|
||||
import Delete from './Delete.js'
|
||||
|
||||
/**
|
||||
* Helper utility to split an Item (see _splitAt)
|
||||
* - copy all properties from a to b
|
||||
* - connect a to b
|
||||
* - assigns the correct _id
|
||||
* - save b to os
|
||||
*/
|
||||
export function splitHelper (y, a, b, diff) {
|
||||
const aID = a._id
|
||||
b._id = new ID(aID.user, aID.clock + diff)
|
||||
b._origin = a
|
||||
b._left = a
|
||||
a._right = b
|
||||
a._right_origin = b
|
||||
b._parent = a._parent
|
||||
b._parentSub = a._parentSub
|
||||
b._deleted = a._deleted
|
||||
y.os.put(b)
|
||||
}
|
||||
|
||||
export default class Item {
|
||||
constructor () {
|
||||
@@ -13,22 +37,30 @@ export default class Item {
|
||||
get _length () {
|
||||
return 1
|
||||
}
|
||||
_getDistanceToOrigin () {
|
||||
if (this.left == null) {
|
||||
return 0
|
||||
} else {
|
||||
var d = 0
|
||||
var o = this.left
|
||||
while (o !== null && !this.origin.equals(o.id)) {
|
||||
d++
|
||||
o = o.left
|
||||
}
|
||||
return d
|
||||
/**
|
||||
* Splits this struct so that another struct can be inserted in-between.
|
||||
* This must be overwritten if _length > 1
|
||||
* Returns right part after split
|
||||
* - diff === 0 => this
|
||||
* - diff === length => this._right
|
||||
* - otherwise => split _content and return right part of split
|
||||
* (see ItemJSON/ItemString for implementation)
|
||||
*/
|
||||
_splitAt (y, diff) {
|
||||
if (diff === 0) {
|
||||
return this
|
||||
}
|
||||
return this._right
|
||||
}
|
||||
_delete (y) {
|
||||
_delete (y, createDelete = true) {
|
||||
this._deleted = true
|
||||
y.ds.markDeleted(this._id, this._length)
|
||||
if (createDelete) {
|
||||
let del = new Delete()
|
||||
del._targetID = this._id
|
||||
del._length = this._length
|
||||
del._integrate(y, true)
|
||||
}
|
||||
}
|
||||
/*
|
||||
* - Integrate the struct so that other types/structs can see it
|
||||
@@ -36,8 +68,12 @@ export default class Item {
|
||||
* - Check if this is struct deleted
|
||||
*/
|
||||
_integrate (y) {
|
||||
if (this._id === null) {
|
||||
const selfID = this._id
|
||||
if (selfID === null) {
|
||||
this._id = y.ss.getNextID(this._length)
|
||||
} else if (selfID.clock < y.ss.getState(selfID.user)) {
|
||||
// already applied..
|
||||
return []
|
||||
}
|
||||
/*
|
||||
# $this has to find a unique position between origin and the next known character
|
||||
@@ -71,86 +107,145 @@ export default class Item {
|
||||
// Note that conflictingItems is a subset of itemsBeforeOrigin
|
||||
while (o !== null && o !== this._right) {
|
||||
itemsBeforeOrigin.add(o)
|
||||
if (this.origin === o.origin) {
|
||||
if (this._origin === o._origin) {
|
||||
// case 1
|
||||
if (o._id.user < this._id.user) {
|
||||
this.left = o
|
||||
this._left = o
|
||||
conflictingItems = new Set()
|
||||
}
|
||||
} else if (itemsBeforeOrigin.has(o)) {
|
||||
// case 2
|
||||
if (conflictingItems.has(o)) {
|
||||
this.left = o
|
||||
this._left = o
|
||||
conflictingItems = new Set()
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
o = o.right
|
||||
o = o._right
|
||||
}
|
||||
y.os.set(this)
|
||||
y.ds.checkIfDeleted(this)
|
||||
if (y.connector._forwardAppliedStructs || this._id.user === y.userID) {
|
||||
y.connector.broadcastStruct(this)
|
||||
if (this._left === null) {
|
||||
if (this._parentSub !== null) {
|
||||
this._parent._map.set(this._parentSub, this)
|
||||
} else {
|
||||
this._parent._start = this
|
||||
}
|
||||
}
|
||||
if (y.persistence !== null) {
|
||||
y.persistence.saveOperations(this)
|
||||
y.os.put(this)
|
||||
if (this._id.user !== RootFakeUserID) {
|
||||
if (y.connector._forwardAppliedStructs || this._id.user === y.userID) {
|
||||
y.connector.broadcastStruct(this)
|
||||
}
|
||||
if (y.persistence !== null) {
|
||||
y.persistence.saveOperations(this)
|
||||
}
|
||||
y.ds.applyMissingDeletesOnStruct(this)
|
||||
}
|
||||
}
|
||||
_toBinary (y, encoder) {
|
||||
encoder.writeUint8(StructManager.getReference(this.constructor))
|
||||
encoder.writeOpID(this._id)
|
||||
encoder.writeOpID(this._parent._id)
|
||||
encoder.writeVarString(this.parentSub === null ? '' : JSON.stringify(this.parentSub))
|
||||
encoder.writeOpID(this._left === null ? null : this._left._id)
|
||||
encoder.writeOpID(this._right_origin === null ? null : this._right_origin._id)
|
||||
encoder.writeOpID(this._origin === null ? null : this._origin._id)
|
||||
_toBinary (encoder) {
|
||||
encoder.writeUint8(getReference(this.constructor))
|
||||
let info = 0
|
||||
if (this._origin !== null) {
|
||||
info += 0b1 // origin is defined
|
||||
}
|
||||
if (this._left !== this._origin) {
|
||||
info += 0b10 // do not copy origin to left
|
||||
}
|
||||
if (this._right_origin !== null) {
|
||||
info += 0b100
|
||||
}
|
||||
if (this._parentSub !== null) {
|
||||
info += 0b1000
|
||||
}
|
||||
encoder.writeUint8(info)
|
||||
encoder.writeID(this._id)
|
||||
if (info & 0b1) {
|
||||
encoder.writeID(this._origin._id)
|
||||
}
|
||||
if (info & 0b10) {
|
||||
encoder.writeID(this._left._id)
|
||||
}
|
||||
if (info & 0b100) {
|
||||
encoder.writeID(this._right_origin._id)
|
||||
}
|
||||
if (~info & 0b101) {
|
||||
// neither origin nor right is defined
|
||||
encoder.writeID(this._parent._id)
|
||||
}
|
||||
if (info & 0b1000) {
|
||||
encoder.writeVarString(JSON.stringify(this._parentSub))
|
||||
}
|
||||
}
|
||||
_fromBinary (y, decoder) {
|
||||
let missing = []
|
||||
this._id = decoder.readOpID()
|
||||
let parent = decoder.readOpID()
|
||||
let parentSub = decoder.readVarString()
|
||||
if (parentSub.length > 0) {
|
||||
this._parentSub = JSON.parse(parentSub)
|
||||
}
|
||||
let left = decoder.readOpID()
|
||||
let right = decoder.readOpId()
|
||||
let origin = decoder.readOpID()
|
||||
if (parent !== null && this._parent === null) {
|
||||
let _parent = y.os.get(parent)
|
||||
if (_parent === null) {
|
||||
missing.push(parent)
|
||||
} else {
|
||||
this._parent = _parent
|
||||
const info = decoder.readUint8()
|
||||
this._id = decoder.readID()
|
||||
// read origin
|
||||
if (info & 0b1) {
|
||||
// origin != null
|
||||
const originID = decoder.readID()
|
||||
if (this._origin === null) {
|
||||
const origin = y.os.getItemCleanEnd(originID)
|
||||
if (origin === null) {
|
||||
missing.push(originID)
|
||||
} else {
|
||||
this._origin = origin
|
||||
}
|
||||
}
|
||||
}
|
||||
if (origin !== null && this._origin === null) {
|
||||
let _origin = y.os.getCleanStart(origin)
|
||||
if (_origin === null) {
|
||||
missing.push(origin)
|
||||
} else {
|
||||
this._origin = _origin
|
||||
// read left
|
||||
if (info & 0b10) {
|
||||
// left !== origin
|
||||
const leftID = decoder.readID()
|
||||
if (this._left === null) {
|
||||
const left = y.os.getItemCleanEnd(leftID)
|
||||
if (left === null) {
|
||||
// use origin instead
|
||||
this._left = this._origin
|
||||
} else {
|
||||
this._left = left
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._left = this._origin
|
||||
}
|
||||
// read right
|
||||
if (info & 0b100) {
|
||||
// right != null
|
||||
const rightID = decoder.readID()
|
||||
if (this._right_origin === null) {
|
||||
const right = y.os.getCleanStart(rightID)
|
||||
if (right === null) {
|
||||
missing.push(right)
|
||||
} else {
|
||||
this._right = right
|
||||
this._right_origin = right
|
||||
}
|
||||
}
|
||||
}
|
||||
if (left !== null && this._left === null) {
|
||||
let _left = y.os.getCleanEnd(left)
|
||||
if (_left === null) {
|
||||
// use origin instead
|
||||
this._left = this._origin
|
||||
} else {
|
||||
this._left = _left
|
||||
// read parent
|
||||
if (~info & 0b101) {
|
||||
// neither origin nor right is defined
|
||||
const parentID = decoder.readID()
|
||||
if (this._parent === null) {
|
||||
const parent = y.os.get(parentID)
|
||||
if (parent === null) {
|
||||
missing.push(parent)
|
||||
} else {
|
||||
this._parent = parent
|
||||
}
|
||||
}
|
||||
} else if (this._parent === null) {
|
||||
if (this._origin !== null) {
|
||||
this._parent = this._origin._parent
|
||||
} else if (this._right_origin !== null) {
|
||||
this._parent = this._origin._parent
|
||||
}
|
||||
}
|
||||
if (right !== null && this._right_origin === null) {
|
||||
let _right = y.os.getCleanStart(right)
|
||||
if (_right === null) {
|
||||
missing.push(right)
|
||||
} else {
|
||||
this._right = _right
|
||||
this._right_origin = _right
|
||||
}
|
||||
if (info & 0b1000) {
|
||||
this._parentSub = decoder.readVarString()
|
||||
}
|
||||
return missing
|
||||
}
|
||||
_logString () {
|
||||
return `left: ${this._left}, origin: ${this._origin}, right: ${this._right}, parent: ${this._parent}, parentSub: ${this._parentSub}`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Item from './Item.js'
|
||||
import { splitHelper, default as Item } from './Item.js'
|
||||
|
||||
export default class ItemJSON extends Item {
|
||||
constructor () {
|
||||
@@ -17,8 +17,8 @@ export default class ItemJSON extends Item {
|
||||
}
|
||||
return missing
|
||||
}
|
||||
_toBinary (y, encoder) {
|
||||
super._toBinary(y, encoder)
|
||||
_toBinary (encoder) {
|
||||
super._toBinary(encoder)
|
||||
let len = this._content.length
|
||||
encoder.writeVarUint(len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
@@ -29,4 +29,15 @@ export default class ItemJSON extends Item {
|
||||
let s = super._logString()
|
||||
return 'ItemJSON: ' + s
|
||||
}
|
||||
_splitAt (y, diff) {
|
||||
if (diff === 0) {
|
||||
return this
|
||||
} else if (diff >= this._length) {
|
||||
return this._right
|
||||
}
|
||||
let item = new ItemJSON()
|
||||
item._content = this._content.splice(diff)
|
||||
splitHelper(y, this, item, diff)
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Item from './Item.js'
|
||||
import { splitHelper, default as Item } from './Item.js'
|
||||
|
||||
export default class ItemString extends Item {
|
||||
constructor () {
|
||||
@@ -13,12 +13,24 @@ export default class ItemString extends Item {
|
||||
this._content = decoder.readVarString()
|
||||
return missing
|
||||
}
|
||||
_toBinary (y, encoder) {
|
||||
super._toBinary(y, encoder)
|
||||
_toBinary (encoder) {
|
||||
super._toBinary(encoder)
|
||||
encoder.writeVarString(this._content)
|
||||
}
|
||||
_logString () {
|
||||
let s = super._logString()
|
||||
return 'ItemString: ' + s
|
||||
}
|
||||
_splitAt (y, diff) {
|
||||
if (diff === 0) {
|
||||
return this
|
||||
} else if (diff >= this._length) {
|
||||
return this._right
|
||||
}
|
||||
let item = new ItemString()
|
||||
item._content = this._content.slice(diff)
|
||||
this._content = this._content.slice(0, diff)
|
||||
splitHelper(y, this, item, diff)
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,11 @@ export default class Type extends Item {
|
||||
super()
|
||||
this._map = new Map()
|
||||
this._start = null
|
||||
this._y = null
|
||||
}
|
||||
_integrate (y) {
|
||||
super._integrate(y)
|
||||
this._y = y
|
||||
}
|
||||
_delete (y) {
|
||||
super._delete(y)
|
||||
|
||||
Reference in New Issue
Block a user