working on snapshotting and version history

This commit is contained in:
Kevin Jahns
2019-01-09 23:54:36 +01:00
parent ec58a99748
commit 77e479c03b
17 changed files with 648 additions and 378 deletions

View File

@@ -9,6 +9,8 @@ import * as stringify from '../utils/structStringify.js'
import { YEvent } from '../utils/YEvent.js'
import { Transaction } from '../utils/Transaction.js' // eslint-disable-line
import { Item } from '../structs/Item.js' // eslint-disable-line
import { ItemBinary } from '../structs/ItemBinary.js'
import { isVisible } from '../utils/snapshot.js'
/**
* Event that describes the changes on a YArray
@@ -89,6 +91,7 @@ export class YArray extends Type {
* Returns the i-th element from a YArray.
*
* @param {number} index The index of the element to return from the YArray
* @return {any}
*/
get (index) {
let n = this._start
@@ -112,10 +115,11 @@ export class YArray extends Type {
/**
* Transforms this YArray to a JavaScript Array.
*
* @param {Object} [snapshot]
* @return {Array}
*/
toArray () {
return this.map(c => c)
toArray (snapshot) {
return this.map(c => c, snapshot)
}
/**
@@ -137,14 +141,15 @@ export class YArray extends Type {
* element of this YArray.
*
* @param {Function} f Function that produces an element of the new Array
* @param {import('../protocols/history.js').HistorySnapshot} [snapshot]
* @return {Array} A new array with each element being the result of the
* callback function
*/
map (f) {
map (f, snapshot) {
const res = []
this.forEach((c, i) => {
res.push(f(c, i, this))
})
}, snapshot)
return res
}
@@ -152,14 +157,17 @@ export class YArray extends Type {
* Executes a provided function on once on overy element of this YArray.
*
* @param {Function} f A function to execute on every element of this YArray.
* @param {import('../protocols/history.js').HistorySnapshot} [snapshot]
*/
forEach (f) {
forEach (f, snapshot) {
let index = 0
let n = this._start
while (n !== null) {
if (!n._deleted && n._countable) {
if (isVisible(n, snapshot) && n._countable) {
if (n instanceof Type) {
f(n, index++, this)
} else if (n.constructor === ItemBinary) {
f(n._content, index++, this)
} else {
const content = n._content
const contentLen = content.length
@@ -239,7 +247,7 @@ export class YArray extends Type {
*
* @private
* @param {Item} left The element container to use as a reference.
* @param {Array} content The Array of content to insert (see {@see insert})
* @param {Array<number|string|Object|ArrayBuffer>} content The Array of content to insert (see {@see insert})
*/
insertAfter (left, content) {
this._transact(y => {
@@ -276,6 +284,29 @@ export class YArray extends Type {
left._right = c
}
left = c
} else if (c.constructor === ArrayBuffer) {
if (prevJsonIns !== null) {
if (y !== null) {
prevJsonIns._integrate(y)
}
left = prevJsonIns
prevJsonIns = null
}
const itemBinary = new ItemBinary()
itemBinary._origin = left
itemBinary._left = left
itemBinary._right = right
itemBinary._right_origin = right
itemBinary._parent = this
itemBinary._content = c
if (y !== null) {
itemBinary._integrate(y)
} else if (left === null) {
this._start = itemBinary
} else {
left._right = itemBinary
}
left = itemBinary
} else {
if (prevJsonIns === null) {
prevJsonIns = new ItemJSON()
@@ -294,6 +325,8 @@ export class YArray extends Type {
prevJsonIns._integrate(y)
} else if (prevJsonIns._left === null) {
this._start = prevJsonIns
} else {
left._right = prevJsonIns
}
}
})
@@ -314,7 +347,7 @@ export class YArray extends Type {
* yarray.insert(2, [1, 2])
*
* @param {number} index The index to insert content at.
* @param {Array} content The array of content
* @param {Array<number|string|ArrayBuffer|Type>} content The array of content
*/
insert (index, content) {
this._transact(() => {
@@ -347,7 +380,7 @@ export class YArray extends Type {
/**
* Appends content to this YArray.
*
* @param {Array} content Array of content to append.
* @param {Array<number|string|ArrayBuffer|Type>} content Array of content to append.
*/
push (content) {
let n = this._start

View File

@@ -7,6 +7,8 @@ import { Type } from '../structs/Type.js'
import { ItemJSON } from '../structs/ItemJSON.js'
import * as stringify from '../utils/structStringify.js'
import { YEvent } from '../utils/YEvent.js'
import { ItemBinary } from '../structs/ItemBinary.js'
import { isVisible } from '../utils/snapshot.js';
/**
* Event that describes the changes on a YMap.
@@ -53,6 +55,8 @@ export class YMap extends Type {
} else {
res = item.toString()
}
} else if (item.constructor === ItemBinary) {
res = item._content
} else {
res = item._content[0]
}
@@ -65,14 +69,23 @@ export class YMap extends Type {
/**
* Returns the keys for each element in the YMap Type.
*
* @param {import('../protocols/history.js').HistorySnapshot} [snapshot]
* @return {Array}
*/
keys () {
keys (snapshot) {
// TODO: Should return either Iterator or Set!
let keys = []
for (let [key, value] of this._map) {
if (!value._deleted) {
keys.push(key)
if (snapshot === undefined) {
for (let [key, value] of this._map) {
if (value._deleted) {
keys.push(key)
}
}
} else {
for (let key in this._map) {
if (this.has(key, snapshot)) {
keys.push(key)
}
}
}
return keys
@@ -96,7 +109,7 @@ export class YMap extends Type {
* Adds or updates an element with a specified key and value.
*
* @param {string} key The key of the element to add to this YMap
* @param {Object | string | number | Type} value The value of the element to add
* @param {Object | string | number | Type | ArrayBuffer } value The value of the element to add
*/
set (key, value) {
this._transact(y => {
@@ -120,6 +133,9 @@ export class YMap extends Type {
value = v
} else if (value instanceof Item) {
v = value
} else if (value.constructor === ArrayBuffer) {
v = new ItemBinary()
v._content = value
} else {
v = new ItemJSON()
v._content = [value]
@@ -141,16 +157,27 @@ export class YMap extends Type {
* Returns a specified element from this YMap.
*
* @param {string} key The key of the element to return.
* @param {import('../protocols/history.js').HistorySnapshot} [snapshot]
*/
get (key) {
get (key, snapshot) {
let v = this._map.get(key)
if (v === undefined || v._deleted) {
if (v === undefined) {
return undefined
}
if (v instanceof Type) {
return v
} else {
return v._content[v._content.length - 1]
if (snapshot !== undefined) {
// iterate until found element that exists
while (!snapshot.sm.has(v._id.user) || v._id.clock >= snapshot.sm.get(v._id.user)) {
v = v._right
}
}
if (isVisible(v, snapshot)) {
if (v instanceof Type) {
return v
} else if (v.constructor === ItemBinary) {
return v._content
} else {
return v._content[v._content.length - 1]
}
}
}
@@ -158,14 +185,20 @@ export class YMap extends Type {
* Returns a boolean indicating whether the specified key exists or not.
*
* @param {string} key The key to test.
* @param {import('../protocols/history.js').HistorySnapshot} [snapshot]
*/
has (key) {
has (key, snapshot) {
let v = this._map.get(key)
if (v === undefined || v._deleted) {
if (v === undefined) {
return false
} else {
return true
}
if (snapshot !== undefined) {
// iterate until found element that exists
while (!snapshot.sm.has(v._id.user) || v._id.clock >= snapshot.sm.get(v._id.user)) {
v = v._right
}
}
return isVisible(v, snapshot)
}
/**

View File

@@ -7,6 +7,7 @@ import { ItemString } from '../structs/ItemString.js'
import { ItemFormat } from '../structs/ItemFormat.js'
import * as stringify from '../utils/structStringify.js'
import { YArrayEvent, YArray } from './YArray.js'
import { isVisible } from '../utils/snapshot.js'
/**
* @private
@@ -570,11 +571,12 @@ export class YText extends YArray {
/**
* Returns the Delta representation of this YText type.
*
* @param {import('../protocols/history.js').HistorySnapshot} [snapshot]
* @return {Delta} The Delta representation of this type.
*
* @public
*/
toDelta () {
toDelta (snapshot) {
let ops = []
let currentAttributes = new Map()
let str = ''
@@ -600,7 +602,7 @@ export class YText extends YArray {
}
}
while (n !== null) {
if (!n._deleted) {
if (isVisible(n, snapshot)) {
switch (n.constructor) {
case ItemString:
str += n._content

View File

@@ -300,26 +300,34 @@ export class YXmlElement extends YXmlFragment {
*
* @param {String} attributeName The attribute name that identifies the
* queried value.
* @param {import('../protocols/history.js').HistorySnapshot} [snapshot]
* @return {String} The queried attribute value.
*
* @public
*/
getAttribute (attributeName) {
return YMap.prototype.get.call(this, attributeName)
getAttribute (attributeName, snapshot) {
return YMap.prototype.get.call(this, attributeName, snapshot)
}
/**
* Returns all attribute name/value pairs in a JSON Object.
*
* @param {import('../protocols/history.js').HistorySnapshot} [snapshot]
* @return {Object} A JSON Object that describes the attributes.
*
* @public
*/
getAttributes () {
getAttributes (snapshot) {
const obj = {}
for (let [key, value] of this._map) {
if (!value._deleted) {
obj[key] = value._content[0]
if (snapshot === undefined) {
for (let [key, value] of this._map) {
if (!value._deleted) {
obj[key] = value._content[0]
}
}
} else {
for (let key in this._map) {
return YMap.prototype.get.call(this, key, snapshot)
}
}
return obj