start rewriting y-richtext

This commit is contained in:
Kevin Jahns 2018-02-15 01:25:08 +01:00
parent fc500a8247
commit da748a78f4
4 changed files with 223 additions and 39 deletions

View File

@ -73,6 +73,13 @@ export default class Item {
get _length () {
return 1
}
/**
* Some elements are not supposed to be addressable. For example, an
* ItemFormat should not be retrievable via yarray.get(pos)
*/
get _countable () {
return true
}
/**
* Splits this struct so that another struct can be inserted in-between.
* This must be overwritten if _length > 1

45
src/Struct/ItemFormat.js Normal file
View File

@ -0,0 +1,45 @@
import { default as Item } from './Item.js'
import { logID } from '../MessageHandler/messageToString.js'
export default class ItemString extends Item {
constructor () {
super()
this.key = null
this.value = null
}
_copy (undeleteChildren, copyPosition) {
let struct = super._copy(undeleteChildren, copyPosition)
struct.key = this.key
struct.value = this.value
return struct
}
get _length () {
return 1
}
get _countable () {
return false
}
_fromBinary (y, decoder) {
let missing = super._fromBinary(y, decoder)
this.key = decoder.readVarString()
this.value = decoder.readVarString()
return missing
}
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(this.key)
encoder.writeVarString(this.value)
}
_logString () {
const left = this._left !== null ? this._left._lastId : null
const origin = this._origin !== null ? this._origin._lastId : null
return `ItemFormat(id:${logID(this._id)},key:${JSON.stringify(this.key)},value:${JSON.stringify(this.value)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
}
_splitAt (y, diff) {
if (diff === 0) {
return this
} else {
return this._right
}
}
}

View File

@ -45,7 +45,7 @@ export default class YArray extends Type {
get (pos) {
let n = this._start
while (n !== null) {
if (!n._deleted) {
if (!n._deleted && n._countable) {
if (pos < n._length) {
if (n.constructor === ItemJSON || n.constructor === ItemString) {
return n._content[pos]
@ -84,7 +84,7 @@ export default class YArray extends Type {
let pos = 0
let n = this._start
while (n !== null) {
if (!n._deleted) {
if (!n._deleted && n._countable) {
if (n instanceof Type) {
f(n, pos++, this)
} else {
@ -103,7 +103,7 @@ export default class YArray extends Type {
let length = 0
let n = this._start
while (n !== null) {
if (!n._deleted) {
if (!n._deleted && n._countable) {
length += n._length
}
n = n._right
@ -144,7 +144,7 @@ export default class YArray extends Type {
let item = this._start
let count = 0
while (item !== null && length > 0) {
if (!item._deleted) {
if (!item._deleted && item._countable) {
if (count <= pos && pos < count + item._length) {
const diffDel = pos - count
item = item._splitAt(this._y, diffDel)

View File

@ -1,7 +1,66 @@
import ItemString from '../Struct/ItemString.js'
import ItemFormat from '../Struct/ItemFormat.js'
import YArray from './YArray.js'
import { logID } from '../MessageHandler/messageToString.js'
function integrateItem (item, parent, y, left, right) {
item._origin = left
item._left = left
item._right = right
item._right_origin = right
item._parent = parent
if (y !== null) {
item._integrate(this._y)
} else if (left === null) {
parent._start = item
} else {
left._right = item
}
}
function findPosition (parent, pos, attributes) {
let currentAttributes = new Map()
let left = null
let right = parent._start
let count = 0
while (right !== null) {
switch (right.constructor) {
// case ItemBlockFormat: do not break..
case ItemString:
const rightLen = right._deleted ? 0 : (right._length - 1)
if (count <= pos && pos <= count + rightLen) {
const splitDiff = pos - count
right = right._splitAt(parent._y, splitDiff)
left = right._left
count += splitDiff
break
}
if (!right._deleted) {
count += right._length
}
break
case ItemFormat:
if (right._deleted === false) {
const key = right.key
const value = right.value
if (value === null) {
currentAttributes.delete(key)
} else if (attributes.hasOwnProperty(key)) {
// only set if relevant
currentAttributes.set(key, value)
}
}
break
}
left = right
right = right._right
}
if (pos > count) {
throw new Error('Position exceeds array range!')
}
return [left, right, currentAttributes]
}
export default class YText extends YArray {
constructor (string) {
super()
@ -13,55 +72,128 @@ export default class YText extends YArray {
}
}
toString () {
const strBuilder = []
let str = ''
let n = this._start
while (n !== null) {
if (!n._deleted) {
strBuilder.push(n._content)
if (!n._deleted && n._countable) {
str += n._content
}
n = n._right
}
return strBuilder.join('')
return str
}
insert (pos, text) {
/**
* As defined by Quilljs - https://quilljs.com/docs/delta/
*/
toRichtextDelta () {
let ops = []
let currentAttributes = new Map()
let str = ''
let n = this._start
function packStr () {
if (str.length > 0) {
// pack str with attributes to ops
let attributes = {}
for (let [key, value] of currentAttributes) {
attributes[key] = value
}
ops.push({ insert: str, attributes })
str = ''
}
}
while (n !== null) {
if (!n._deleted) {
switch (n.constructor) {
case ItemString:
str += n._content
break
case ItemFormat:
packStr()
const value = n.value
const key = n.key
if (value === null) {
currentAttributes.delete(key)
} else {
currentAttributes.set(key, value)
}
break
}
}
n = n._right
}
packStr()
return ops
}
insert (pos, text, attributes = {}) {
if (text.length <= 0) {
return
}
this._transact(y => {
let left = null
let right = this._start
let count = 0
while (right !== null) {
const rightLen = right._deleted ? 0 : (right._length - 1)
if (count <= pos && pos <= count + rightLen) {
const splitDiff = pos - count
right = right._splitAt(this._y, splitDiff)
left = right._left
count += splitDiff
break
let [left, right, currentAttributes] = findPosition(this, pos, attributes)
let negatedAttributes = new Map()
// insert format-start items
for (let key in attributes) {
const val = attributes[key]
const currentVal = currentAttributes.get(key)
if (currentVal !== val) {
// save negated attribute (set null if currentVal undefined)
negatedAttributes.set(key, currentVal || null)
let format = new ItemFormat()
format.key = key
format.value = val
integrateItem(format, this, y, left, right)
left = format
}
if (!right._deleted) {
count += right._length
}
left = right
right = right._right
}
if (pos > count) {
throw new Error('Position exceeds array range!')
}
// insert text content
let item = new ItemString()
item._origin = left
item._left = left
item._right = right
item._right_origin = right
item._parent = this
item._content = text
if (y !== null) {
item._integrate(this._y)
} else if (left === null) {
this._start = item
} else {
left._right = item
integrateItem(item, this, y, left, right)
left = item
// negate applied formats
for (let [key, value] of negatedAttributes) {
let format = new ItemFormat()
format.key = key
format.value = value
integrateItem(format, this, y, left, right)
left = format
}
})
}
format (pos, length, attributes) {
this._transact(y => {
let [left, _right, currentAttributes] = findPosition(this, pos, attributes)
if (_right === null) {
return
}
let negatedAttributes = new Map()
// insert format-start items
for (let key in attributes) {
const val = attributes[key]
const currentVal = currentAttributes.get(key)
if (currentVal !== val) {
// save negated attribute (set null if currentVal undefined)
negatedAttributes.set(key, currentVal || null)
let format = new ItemFormat()
format.key = key
format.value = val
integrateItem(format, this, y, left, _right)
left = format
}
}
// iterate until first non-format or null is found
// delete all formats with attributes[format.key] != null
while (length > 0 && left !== null) {
if (left._deleted === false) {
if (left.constructor === ItemFormat) {
if (attributes[left.key] != null) {
left.delete(y)
}
} else if (length < left._length) {
}
}
left = left._right
}
})
}