refactoring: removed default connector and persistence, new code style, proper jsdocs, enabled typechecking

This commit is contained in:
Kevin Jahns
2018-10-29 21:58:21 +01:00
parent fe038822a3
commit e1ece6dc66
84 changed files with 3479 additions and 2580 deletions

View File

@@ -1,31 +1,33 @@
import { getStructReference } from '../Util/structReferences.js'
import ID from '../Util/ID/ID.js'
import { logID } from '../MessageHandler/messageToString.js'
import * as ID from '../Util/ID.js'
import { stringifyID } from '../message.js'
import { writeStructToTransaction } from '../Util/Transaction.js'
import * as decoding from '../../lib/decoding.js'
import * as encoding from '../../lib/encoding.js'
/**
* @private
* Delete all items in an ID-range
* TODO: implement getItemCleanStartNode for better performance (only one lookup)
* Delete all items in an ID-range.
* Does not create delete operations!
* TODO: implement getItemCleanStartNode for better performance (only one lookup).
*/
export function deleteItemRange (y, user, clock, range, gcChildren) {
const createDelete = y.connector !== null && y.connector._forwardAppliedStructs
let item = y.os.getItemCleanStart(new ID(user, clock))
let item = y.os.getItemCleanStart(ID.createID(user, clock))
if (item !== null) {
if (!item._deleted) {
item._splitAt(y, range)
item._delete(y, createDelete, true)
item._delete(y, false, true)
}
let itemLen = item._length
range -= itemLen
clock += itemLen
if (range > 0) {
let node = y.os.findNode(new ID(user, clock))
while (node !== null && node.val !== null && range > 0 && node.val._id.equals(new ID(user, clock))) {
let node = y.os.findNode(ID.createID(user, clock))
while (node !== null && node.val !== null && range > 0 && node.val._id.equals(ID.createID(user, clock))) {
const nodeVal = node.val
if (!nodeVal._deleted) {
nodeVal._splitAt(y, range)
nodeVal._delete(y, createDelete, gcChildren)
nodeVal._delete(y, false, gcChildren)
}
const nodeLen = nodeVal._length
range -= nodeLen
@@ -44,6 +46,13 @@ export function deleteItemRange (y, user, clock, range, gcChildren) {
*/
export default class Delete {
constructor () {
/**
* @type {ID.ID}
*/
this._targetID = null
/**
* @type {import('./Item.js').default}
*/
this._target = null
this._length = null
}
@@ -54,15 +63,18 @@ export default class Delete {
*
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
* @param {import('../Y.js').default} y The Yjs instance that this Item belongs to.
* @param {decoding.Decoder} decoder The decoder object to read data from.
*/
_fromBinary (y, decoder) {
// TODO: set target, and add it to missing if not found
// There is an edge case in p2p networks!
const targetID = decoder.readID()
/**
* @type {any}
*/
const targetID = ID.decode(decoder)
this._targetID = targetID
this._length = decoder.readVarUint()
this._length = decoding.readVarUint(decoder)
if (y.os.getItem(targetID) === null) {
return [targetID]
} else {
@@ -77,12 +89,12 @@ export default class Delete {
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
* @param {encoding.Encoder} encoder The encoder to write data to.
*/
_toBinary (encoder) {
encoder.writeUint8(getStructReference(this.constructor))
encoder.writeID(this._targetID)
encoder.writeVarUint(this._length)
encoding.writeUint8(encoder, getStructReference(this.constructor))
this._targetID.encode(encoder)
encoding.writeVarUint(encoder, this._length)
}
/**
@@ -102,12 +114,6 @@ export default class Delete {
// from remote
const id = this._targetID
deleteItemRange(y, id.user, id.clock, this._length, false)
} else if (y.connector !== null) {
// from local
y.connector.broadcastStruct(this)
}
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
writeStructToTransaction(y._transaction, this)
}
@@ -119,6 +125,6 @@ export default class Delete {
* @private
*/
_logString () {
return `Delete - target: ${logID(this._targetID)}, len: ${this._length}`
return `Delete - target: ${stringifyID(this._targetID)}, len: ${this._length}`
}
}

View File

@@ -1,11 +1,15 @@
import { getStructReference } from '../Util/structReferences.js'
import { RootFakeUserID } from '../Util/ID/RootID.js'
import ID from '../Util/ID/ID.js'
import * as ID from '../Util/ID.js'
import { writeStructToTransaction } from '../Util/Transaction.js'
import * as decoding from '../../lib/decoding.js'
import * as encoding from '../../lib/encoding.js'
// TODO should have the same base class as Item
export default class GC {
constructor () {
/**
* @type {ID.ID}
*/
this._id = null
this._length = 0
}
@@ -37,13 +41,7 @@ export default class GC {
n._length += next._length
y.os.delete(next._id)
}
if (id.user !== RootFakeUserID) {
if (y.connector !== null && (y.connector._forwardAppliedStructs || id.user === y.userID)) {
y.connector.broadcastStruct(this)
}
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
if (id.user !== ID.RootFakeUserID) {
writeStructToTransaction(y._transaction, this)
}
}
@@ -54,13 +52,13 @@ export default class GC {
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
* @param {encoding.Encoder} encoder The encoder to write data to.
* @private
*/
_toBinary (encoder) {
encoder.writeUint8(getStructReference(this.constructor))
encoder.writeID(this._id)
encoder.writeVarUint(this._length)
encoding.writeUint8(encoder, getStructReference(this.constructor))
this._id.encode(encoder)
encoding.writeVarUint(encoder, this._length)
}
/**
@@ -68,17 +66,20 @@ export default class GC {
*
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
* @param {import('../Y.js').default} y The Yjs instance that this Item belongs to.
* @param {decoding.Decoder} decoder The decoder object to read data from.
* @private
*/
_fromBinary (y, decoder) {
const id = decoder.readID()
/**
* @type {any}
*/
const id = ID.decode(decoder)
this._id = id
this._length = decoder.readVarUint()
this._length = decoding.readVarUint(decoder)
const missing = []
if (y.ss.getState(id.user) < id.clock) {
missing.push(new ID(id.user, id.clock - 1))
missing.push(ID.createID(id.user, id.clock - 1))
}
return missing
}
@@ -89,7 +90,7 @@ export default class GC {
_clonePartial (diff) {
const gc = new GC()
gc._id = new ID(this._id.user, this._id.clock + diff)
gc._id = ID.createID(this._id.user, this._id.clock + diff)
gc._length = this._length - diff
return gc
}

View File

@@ -1,9 +1,15 @@
import { getStructReference } from '../Util/structReferences.js'
import ID from '../Util/ID/ID.js'
import { default as RootID, RootFakeUserID } from '../Util/ID/RootID.js'
import * as ID from '../Util/ID.js'
import Delete from './Delete.js'
import { transactionTypeChanged, writeStructToTransaction } from '../Util/Transaction.js'
import GC from './GC.js'
import * as encoding from '../../lib/encoding.js'
import * as decoding from '../../lib/decoding.js'
import Y from '../Y.js'
/**
* @typedef {import('./Type.js').default} YType
*/
/**
* @private
@@ -15,7 +21,7 @@ import GC from './GC.js'
*/
export function splitHelper (y, a, b, diff) {
const aID = a._id
b._id = new ID(aID.user, aID.clock + diff)
b._id = ID.createID(aID.user, aID.clock + diff)
b._origin = a
b._left = a
b._right = a._right
@@ -55,7 +61,7 @@ export default class Item {
constructor () {
/**
* The uniqe identifier of this type.
* @type {ID}
* @type {ID.ID | ID.RootID}
*/
this._id = null
/**
@@ -99,7 +105,7 @@ export default class Item {
/**
* If this type's effect is reundone this type refers to the type that undid
* this operation.
* @type {Item}
* @type {YType}
*/
this._redone = null
}
@@ -110,7 +116,8 @@ export default class Item {
* @private
*/
_copy () {
return new this.constructor()
const C = this.constructor
return C()
}
/**
@@ -124,6 +131,9 @@ export default class Item {
if (this._redone !== null) {
return this._redone
}
if (this._parent instanceof Y) {
return
}
let struct = this._copy()
let left, right
if (this._parentSub === null) {
@@ -146,7 +156,7 @@ export default class Item {
}
if (parent._redone !== null) {
parent = parent._redone
// find next cloned items
// find next cloned_redo items
while (left !== null) {
if (left._redone !== null && left._redone._parent === parent) {
left = left._redone
@@ -178,7 +188,11 @@ export default class Item {
* @private
*/
get _lastId () {
return new ID(this._id.user, this._id.clock + this._length - 1)
/**
* @type {any}
*/
const id = this._id
return ID.createID(id.user, id.clock + this._length - 1)
}
/**
@@ -227,10 +241,11 @@ export default class Item {
* @param {Y} y The Yjs instance
* @param {boolean} createDelete Whether to propagate a message that this
* Type was deleted.
* @param {boolean} gcChildren
*
* @private
*/
_delete (y, createDelete = true) {
_delete (y, createDelete = true, gcChildren) {
if (!this._deleted) {
this._deleted = true
y.ds.mark(this._id, this._length, false)
@@ -240,9 +255,6 @@ export default class Item {
if (createDelete) {
// broadcast and persists Delete
del._integrate(y, true)
} else if (y.persistence !== null) {
// only persist Delete
y.persistence.saveStruct(y, del)
}
transactionTypeChanged(y, this._parent, this._parentSub)
y._transaction.deletedStructs.add(this)
@@ -280,21 +292,30 @@ export default class Item {
* * Add this struct to y.os
* * Check if this is struct deleted
*
* @param {Y} y
*
* @private
*/
_integrate (y) {
y._transaction.newTypes.add(this)
/**
* @type {any}
*/
const parent = this._parent
/**
* @type {any}
*/
const selfID = this._id
const user = selfID === null ? y.userID : selfID.user
const userState = y.ss.getState(user)
if (selfID === null) {
this._id = y.ss.getNextID(this._length)
} else if (selfID.user === RootFakeUserID) {
// nop
} else if (selfID.user === ID.RootFakeUserID) {
// is parent
return
} else if (selfID.clock < userState) {
// already applied..
return []
return
} else if (selfID.clock === userState) {
y.ss.setState(selfID.user, userState + this._length)
} else {
@@ -304,7 +325,7 @@ export default class Item {
if (!parent._deleted && !y._transaction.changedTypes.has(parent) && !y._transaction.newTypes.has(parent)) {
// this is the first time parent is updated
// or this types is new
this._parent._beforeChange()
parent._beforeChange()
}
/*
@@ -328,9 +349,9 @@ export default class Item {
if (this._left !== null) {
o = this._left._right
} else if (this._parentSub !== null) {
o = this._parent._map.get(this._parentSub) || null
o = parent._map.get(this._parentSub) || null
} else {
o = this._parent._start
o = parent._start
}
let conflictingItems = new Set()
let itemsBeforeOrigin = new Set()
@@ -386,17 +407,11 @@ export default class Item {
}
}
if (parent._deleted) {
this._delete(y, false)
this._delete(y, false, true)
}
y.os.put(this)
transactionTypeChanged(y, parent, parentSub)
if (this._id.user !== RootFakeUserID) {
if (y.connector !== null && (y.connector._forwardAppliedStructs || this._id.user === y.userID)) {
y.connector.broadcastStruct(this)
}
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
if (this._id.user !== ID.RootFakeUserID) {
writeStructToTransaction(y._transaction, this)
}
}
@@ -407,12 +422,12 @@ export default class Item {
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
* @param {encoding.Encoder} encoder The encoder to write data to.
*
* @private
*/
_toBinary (encoder) {
encoder.writeUint8(getStructReference(this.constructor))
encoding.writeUint8(encoder, getStructReference(this.constructor))
let info = 0
if (this._origin !== null) {
info += 0b1 // origin is defined
@@ -429,10 +444,10 @@ export default class Item {
if (this._parentSub !== null) {
info += 0b1000
}
encoder.writeUint8(info)
encoder.writeID(this._id)
encoding.writeUint8(encoder, info)
this._id.encode(encoder)
if (info & 0b1) {
encoder.writeID(this._origin._lastId)
this._origin._lastId.encode(encoder)
}
// TODO: remove
/* see above
@@ -441,14 +456,14 @@ export default class Item {
}
*/
if (info & 0b100) {
encoder.writeID(this._right_origin._id)
this._right_origin._id.encode(encoder)
}
if ((info & 0b101) === 0) {
// neither origin nor right is defined
encoder.writeID(this._parent._id)
this._parent._id.encode(encoder)
}
if (info & 0b1000) {
encoder.writeVarString(JSON.stringify(this._parentSub))
encoding.writeVarString(encoder, JSON.stringify(this._parentSub))
}
}
@@ -458,19 +473,19 @@ export default class Item {
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
* @param {decoding.Decoder} decoder The decoder object to read data from.
*
* @private
*/
_fromBinary (y, decoder) {
let missing = []
const info = decoder.readUint8()
const id = decoder.readID()
const info = decoding.readUint8(decoder)
const id = ID.decode(decoder)
this._id = id
// read origin
if (info & 0b1) {
// origin != null
const originID = decoder.readID()
const originID = ID.decode(decoder)
// we have to query for left again because it might have been split/merged..
const origin = y.os.getItemCleanEnd(originID)
if (origin === null) {
@@ -483,7 +498,7 @@ export default class Item {
// read right
if (info & 0b100) {
// right != null
const rightID = decoder.readID()
const rightID = ID.decode(decoder)
// we have to query for right again because it might have been split/merged..
const right = y.os.getItemCleanStart(rightID)
if (right === null) {
@@ -496,11 +511,11 @@ export default class Item {
// read parent
if ((info & 0b101) === 0) {
// neither origin nor right is defined
const parentID = decoder.readID()
const parentID = ID.decode(decoder)
// parent does not change, so we don't have to search for it again
if (this._parent === null) {
let parent
if (parentID.constructor === RootID) {
if (parentID.constructor === ID.RootID) {
parent = y.os.get(parentID)
} else {
parent = y.os.getItem(parentID)
@@ -513,27 +528,17 @@ export default class Item {
}
} else if (this._parent === null) {
if (this._origin !== null) {
if (this._origin.constructor === GC) {
// if origin is a gc, set parent also gc'd
this._parent = this._origin
} else {
this._parent = this._origin._parent
}
this._parent = this._origin._parent
} else if (this._right_origin !== null) {
// if origin is a gc, set parent also gc'd
if (this._right_origin.constructor === GC) {
this._parent = this._right_origin
} else {
this._parent = this._right_origin._parent
}
this._parent = this._right_origin._parent
}
}
if (info & 0b1000) {
// TODO: maybe put this in read parent condition (you can also read parentsub from left/right)
this._parentSub = JSON.parse(decoder.readVarString())
this._parentSub = JSON.parse(decoding.readVarString(decoder))
}
if (y.ss.getState(id.user) < id.clock) {
missing.push(new ID(id.user, id.clock - 1))
if (id instanceof ID.ID && y.ss.getState(id.user) < id.clock) {
missing.push(ID.createID(id.user, id.clock - 1))
}
return missing
}

View File

@@ -1,5 +1,11 @@
import Item from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
import { logItemHelper } from '../message.js'
import * as encoding from '../../lib/encoding.js'
import * as decoding from '../../lib/decoding.js'
/**
* @typedef {import('../index.js').Y} Y
*/
export default class ItemEmbed extends Item {
constructor () {
@@ -7,21 +13,28 @@ export default class ItemEmbed extends Item {
this.embed = null
}
_copy (undeleteChildren, copyPosition) {
let struct = super._copy(undeleteChildren, copyPosition)
let struct = super._copy()
struct.embed = this.embed
return struct
}
get _length () {
return 1
}
/**
* @param {Y} y
* @param {decoding.Decoder} decoder
*/
_fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder)
this.embed = JSON.parse(decoder.readVarString())
this.embed = JSON.parse(decoding.readVarString(decoder))
return missing
}
/**
* @param {encoding.Encoder} encoder
*/
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(JSON.stringify(this.embed))
encoding.writeVarString(encoder, JSON.stringify(this.embed))
}
/**
* Transform this YXml Type to a readable format.

View File

@@ -1,5 +1,11 @@
import Item from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
import { logItemHelper } from '../message.js'
import * as encoding from '../../lib/encoding.js'
import * as decoding from '../../lib/decoding.js'
/**
* @typedef {import('../index.js').Y} Y
*/
export default class ItemFormat extends Item {
constructor () {
@@ -8,7 +14,7 @@ export default class ItemFormat extends Item {
this.value = null
}
_copy (undeleteChildren, copyPosition) {
let struct = super._copy(undeleteChildren, copyPosition)
let struct = super._copy()
struct.key = this.key
struct.value = this.value
return struct
@@ -19,16 +25,23 @@ export default class ItemFormat extends Item {
get _countable () {
return false
}
/**
* @param {Y} y
* @param {decoding.Decoder} decoder
*/
_fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder)
this.key = decoder.readVarString()
this.value = JSON.parse(decoder.readVarString())
this.key = decoding.readVarString(decoder)
this.value = JSON.parse(decoding.readVarString(decoder))
return missing
}
/**
* @param {encoding.Encoder} encoder
*/
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(this.key)
encoder.writeVarString(JSON.stringify(this.value))
encoding.writeVarString(encoder, this.key)
encoding.writeVarString(encoder, JSON.stringify(this.value))
}
/**
* Transform this YXml Type to a readable format.

View File

@@ -1,5 +1,11 @@
import Item, { splitHelper } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
import { logItemHelper } from '../message.js'
import * as encoding from '../../lib/encoding.js'
import * as decoding from '../../lib/decoding.js'
/**
* @typedef {import('../index.js').Y} Y
*/
export default class ItemJSON extends Item {
constructor () {
@@ -14,12 +20,16 @@ export default class ItemJSON extends Item {
get _length () {
return this._content.length
}
/**
* @param {Y} y
* @param {decoding.Decoder} decoder
*/
_fromBinary (y, decoder) {
let missing = super._fromBinary(y, decoder)
let len = decoder.readVarUint()
let len = decoding.readVarUint(decoder)
this._content = new Array(len)
for (let i = 0; i < len; i++) {
const ctnt = decoder.readVarString()
const ctnt = decoding.readVarString(decoder)
let parsed
if (ctnt === 'undefined') {
parsed = undefined
@@ -30,10 +40,13 @@ export default class ItemJSON extends Item {
}
return missing
}
/**
* @param {encoding.Encoder} encoder
*/
_toBinary (encoder) {
super._toBinary(encoder)
let len = this._content.length
encoder.writeVarUint(len)
encoding.writeVarUint(encoder, len)
for (let i = 0; i < len; i++) {
let encoded
let content = this._content[i]
@@ -42,7 +55,7 @@ export default class ItemJSON extends Item {
} else {
encoded = JSON.stringify(content)
}
encoder.writeVarString(encoded)
encoding.writeVarString(encoder, encoded)
}
}
/**

View File

@@ -1,5 +1,11 @@
import Item, { splitHelper } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
import { logItemHelper } from '../message.js'
import * as encoding from '../../lib/encoding.js'
import * as decoding from '../../lib/decoding.js'
/**
* @typedef {import('../index.js').Y} Y
*/
export default class ItemString extends Item {
constructor () {
@@ -14,14 +20,21 @@ export default class ItemString extends Item {
get _length () {
return this._content.length
}
/**
* @param {Y} y
* @param {decoding.Decoder} decoder
*/
_fromBinary (y, decoder) {
let missing = super._fromBinary(y, decoder)
this._content = decoder.readVarString()
this._content = decoding.readVarString(decoder)
return missing
}
/**
* @param {encoding.Encoder} encoder
*/
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(this._content)
encoding.writeVarString(encoder, this._content)
}
/**
* Transform this YXml Type to a readable format.

View File

@@ -1,6 +1,11 @@
import Item from './Item.js'
import EventHandler from '../Util/EventHandler.js'
import ID from '../Util/ID/ID.js'
import { createID } from '../Util/ID.js'
import YEvent from '../Util/YEvent.js'
/**
* @typedef {import("../Y.js").default} Y
*/
// restructure children as if they were inserted one after another
function integrateChildren (y, start) {
@@ -22,7 +27,7 @@ export function getListItemIDByPosition (type, i) {
if (!n._deleted) {
if (pos <= i && i < pos + n._length) {
const id = n._id
return new ID(id.user, id.clock + i - pos)
return createID(id.user, id.clock + i - pos)
}
pos++
}
@@ -61,7 +66,7 @@ export default class Type extends Item {
* console.log(path) // might look like => [2, 'key1']
* child === type.get(path[0]).get(path[1])
*
* @param {YType} type Type target
* @param {Type | Y | any} type Type target
* @return {Array<string>} Path to the target
*/
getPathTo (type) {
@@ -91,6 +96,14 @@ export default class Type extends Item {
return path
}
/**
* @private
* Creates YArray Event and calls observers.
*/
_callObserver (transaction, parentSubs, remote) {
this._callEventHandler(transaction, new YEvent(this))
}
/**
* @private
* Call event listeners with an event. This will also add an event to all
@@ -99,6 +112,9 @@ export default class Type extends Item {
_callEventHandler (transaction, event) {
const changedParentTypes = transaction.changedParentTypes
this._eventHandler.callEventListeners(transaction, event)
/**
* @type {any}
*/
let type = this
while (type !== this._y) {
let events = changedParentTypes.get(type)
@@ -183,7 +199,7 @@ export default class Type extends Item {
this._start = null
integrateChildren(y, start)
}
// integrate map children
// integrate map children_integrate
const map = this._map
this._map = new Map()
for (let t of map.values()) {
@@ -206,6 +222,12 @@ export default class Type extends Item {
super._gc(y)
}
/**
* @abstract
* @return {Object | Array | number | string}
*/
toJSON () {}
/**
* @private
* Mark this Item as deleted.
@@ -213,7 +235,7 @@ export default class Type extends Item {
* @param {Y} y The Yjs instance
* @param {boolean} createDelete Whether to propagate a message that this
* Type was deleted.
* @param {boolean} [gcChildren=y._hasUndoManager===false] Whether to garbage
* @param {boolean} [gcChildren=(y._hasUndoManager===false)] Whether to garbage
* collect the children of this type.
*/
_delete (y, createDelete, gcChildren) {