start rewriting y-richtext
This commit is contained in:
parent
fc500a8247
commit
da748a78f4
@ -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
45
src/Struct/ItemFormat.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user