Use generic Item with typed content to reduce cache misses
This commit is contained in:
@@ -4,14 +4,14 @@ import {
|
||||
callEventHandlerListeners,
|
||||
addEventHandlerListener,
|
||||
createEventHandler,
|
||||
ItemType,
|
||||
nextID,
|
||||
isVisible,
|
||||
ItemJSON,
|
||||
ItemBinary,
|
||||
ContentType,
|
||||
ContentJSON,
|
||||
ContentBinary,
|
||||
createID,
|
||||
getItemCleanStart,
|
||||
Doc, Snapshot, Transaction, EventHandler, YEvent, AbstractItem, // eslint-disable-line
|
||||
Doc, Snapshot, Transaction, EventHandler, YEvent, Item, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as map from 'lib0/map.js'
|
||||
@@ -49,17 +49,17 @@ export const callTypeObservers = (type, transaction, event) => {
|
||||
export class AbstractType {
|
||||
constructor () {
|
||||
/**
|
||||
* @type {ItemType|null}
|
||||
* @type {Item|null}
|
||||
*/
|
||||
this._item = null
|
||||
/**
|
||||
* @private
|
||||
* @type {Map<string,AbstractItem>}
|
||||
* @type {Map<string,Item>}
|
||||
*/
|
||||
this._map = new Map()
|
||||
/**
|
||||
* @private
|
||||
* @type {AbstractItem|null}
|
||||
* @type {Item|null}
|
||||
*/
|
||||
this._start = null
|
||||
/**
|
||||
@@ -88,7 +88,7 @@ export class AbstractType {
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Doc} y The Yjs instance
|
||||
* @param {ItemType|null} item
|
||||
* @param {Item|null} item
|
||||
* @private
|
||||
*/
|
||||
_integrate (y, item) {
|
||||
@@ -187,7 +187,7 @@ export const typeListToArray = type => {
|
||||
let n = type._start
|
||||
while (n !== null) {
|
||||
if (n.countable && !n.deleted) {
|
||||
const c = n.getContent()
|
||||
const c = n.content.getContent()
|
||||
for (let i = 0; i < c.length; i++) {
|
||||
cs.push(c[i])
|
||||
}
|
||||
@@ -210,7 +210,7 @@ export const typeListToArraySnapshot = (type, snapshot) => {
|
||||
let n = type._start
|
||||
while (n !== null) {
|
||||
if (n.countable && isVisible(n, snapshot)) {
|
||||
const c = n.getContent()
|
||||
const c = n.content.getContent()
|
||||
for (let i = 0; i < c.length; i++) {
|
||||
cs.push(c[i])
|
||||
}
|
||||
@@ -234,7 +234,7 @@ export const typeListForEach = (type, f) => {
|
||||
let n = type._start
|
||||
while (n !== null) {
|
||||
if (n.countable && !n.deleted) {
|
||||
const c = n.getContent()
|
||||
const c = n.content.getContent()
|
||||
for (let i = 0; i < c.length; i++) {
|
||||
f(c[i], index++, type)
|
||||
}
|
||||
@@ -295,7 +295,7 @@ export const typeListCreateIterator = type => {
|
||||
}
|
||||
}
|
||||
// we found n, so we can set currentContent
|
||||
currentContent = n.getContent()
|
||||
currentContent = n.content.getContent()
|
||||
currentContentIndex = 0
|
||||
n = n.right // we used the content of n, now iterate to next
|
||||
}
|
||||
@@ -328,7 +328,7 @@ export const typeListForEachSnapshot = (type, f, snapshot) => {
|
||||
let n = type._start
|
||||
while (n !== null) {
|
||||
if (n.countable && isVisible(n, snapshot)) {
|
||||
const c = n.getContent()
|
||||
const c = n.content.getContent()
|
||||
for (let i = 0; i < c.length; i++) {
|
||||
f(c[i], index++, type)
|
||||
}
|
||||
@@ -349,7 +349,7 @@ export const typeListGet = (type, index) => {
|
||||
for (let n = type._start; n !== null; n = n.right) {
|
||||
if (!n.deleted && n.countable) {
|
||||
if (index < n.length) {
|
||||
return n.getContent()[index]
|
||||
return n.content.getContent()[index]
|
||||
}
|
||||
index -= n.length
|
||||
}
|
||||
@@ -359,7 +359,7 @@ export const typeListGet = (type, index) => {
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {AbstractItem?} referenceItem
|
||||
* @param {Item?} referenceItem
|
||||
* @param {Array<Object<string,any>|Array<any>|boolean|number|string|Uint8Array>} content
|
||||
*
|
||||
* @private
|
||||
@@ -374,7 +374,7 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem,
|
||||
let jsonContent = []
|
||||
const packJsonContent = () => {
|
||||
if (jsonContent.length > 0) {
|
||||
left = new ItemJSON(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, jsonContent)
|
||||
left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, new ContentJSON(jsonContent))
|
||||
left.integrate(transaction)
|
||||
jsonContent = []
|
||||
}
|
||||
@@ -393,12 +393,12 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem,
|
||||
switch (c.constructor) {
|
||||
case Uint8Array:
|
||||
case ArrayBuffer:
|
||||
left = new ItemBinary(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, new Uint8Array(/** @type {Uint8Array} */ (c)))
|
||||
left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, new ContentBinary(new Uint8Array(/** @type {Uint8Array} */ (c))))
|
||||
left.integrate(transaction)
|
||||
break
|
||||
default:
|
||||
if (c instanceof AbstractType) {
|
||||
left = new ItemType(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, c)
|
||||
left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, new ContentType(c))
|
||||
left.integrate(transaction)
|
||||
} else {
|
||||
throw new Error('Unexpected content type in insert operation')
|
||||
@@ -501,28 +501,30 @@ export const typeMapDelete = (transaction, parent, key) => {
|
||||
*/
|
||||
export const typeMapSet = (transaction, parent, key, value) => {
|
||||
const left = parent._map.get(key) || null
|
||||
let content
|
||||
if (value == null) {
|
||||
new ItemJSON(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, [value]).integrate(transaction)
|
||||
return
|
||||
}
|
||||
switch (value.constructor) {
|
||||
case Number:
|
||||
case Object:
|
||||
case Boolean:
|
||||
case Array:
|
||||
case String:
|
||||
new ItemJSON(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, [value]).integrate(transaction)
|
||||
break
|
||||
case Uint8Array:
|
||||
new ItemBinary(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, value).integrate(transaction)
|
||||
break
|
||||
default:
|
||||
if (value instanceof AbstractType) {
|
||||
new ItemType(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, value).integrate(transaction)
|
||||
} else {
|
||||
throw new Error('Unexpected content type')
|
||||
}
|
||||
content = new ContentJSON([value])
|
||||
} else {
|
||||
switch (value.constructor) {
|
||||
case Number:
|
||||
case Object:
|
||||
case Boolean:
|
||||
case Array:
|
||||
case String:
|
||||
content = new ContentJSON([value])
|
||||
break
|
||||
case Uint8Array:
|
||||
content = new ContentBinary(value)
|
||||
break
|
||||
default:
|
||||
if (value instanceof AbstractType) {
|
||||
content = new ContentType(value)
|
||||
} else {
|
||||
throw new Error('Unexpected content type')
|
||||
}
|
||||
}
|
||||
}
|
||||
new Item(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, content).integrate(transaction)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -535,7 +537,7 @@ export const typeMapSet = (transaction, parent, key, value) => {
|
||||
*/
|
||||
export const typeMapGet = (parent, key) => {
|
||||
const val = parent._map.get(key)
|
||||
return val !== undefined && !val.deleted ? val.getContent()[0] : undefined
|
||||
return val !== undefined && !val.deleted ? val.content.getContent()[val.length - 1] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -552,7 +554,7 @@ export const typeMapGetAll = (parent) => {
|
||||
let res = {}
|
||||
for (const [key, value] of parent._map) {
|
||||
if (!value.deleted) {
|
||||
res[key] = value.getContent()[value.length - 1]
|
||||
res[key] = value.content.getContent()[value.length - 1]
|
||||
}
|
||||
}
|
||||
return res
|
||||
@@ -585,11 +587,11 @@ export const typeMapGetSnapshot = (parent, key, snapshot) => {
|
||||
while (v !== null && (!snapshot.sm.has(v.id.client) || v.id.clock >= (snapshot.sm.get(v.id.client) || 0))) {
|
||||
v = v.left
|
||||
}
|
||||
return v !== null && isVisible(v, snapshot) ? v.getContent()[v.length - 1] : undefined
|
||||
return v !== null && isVisible(v, snapshot) ? v.content.getContent()[v.length - 1] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Map<string,AbstractItem>} map
|
||||
* @param {Map<string,Item>} map
|
||||
* @return {IterableIterator<Array<any>>}
|
||||
*
|
||||
* @private
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
YArrayRefID,
|
||||
callTypeObservers,
|
||||
transact,
|
||||
Doc, Transaction, ItemType, // eslint-disable-line
|
||||
Doc, Transaction, Item // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||
@@ -59,14 +59,13 @@ export class YArray extends AbstractType {
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Doc} y The Yjs instance
|
||||
* @param {ItemType} item
|
||||
* @param {Item} item
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
// @ts-ignore
|
||||
this.insert(0, this._prelimContent)
|
||||
this.insert(0, /** @type {Array} */ (this._prelimContent))
|
||||
this._prelimContent = null
|
||||
}
|
||||
get length () {
|
||||
@@ -106,8 +105,7 @@ export class YArray extends AbstractType {
|
||||
typeListInsertGenerics(transaction, this, index, content)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore _prelimContent is defined because this is not yet integrated
|
||||
this._prelimContent.splice(index, 0, ...content)
|
||||
/** @type {Array} */ (this._prelimContent).splice(index, 0, ...content)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +130,7 @@ export class YArray extends AbstractType {
|
||||
typeListDelete(transaction, this, index, length)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore _prelimContent is defined because this is not yet integrated
|
||||
this._prelimContent.splice(index, length)
|
||||
/** @type {Array} */ (this._prelimContent).splice(index, length)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,8 +172,7 @@ export class YArray extends AbstractType {
|
||||
* callback function
|
||||
*/
|
||||
map (f) {
|
||||
// @ts-ignore
|
||||
return typeListMap(this, f)
|
||||
return typeListMap(this, /** @type {any} */ (f))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
YMapRefID,
|
||||
callTypeObservers,
|
||||
transact,
|
||||
Doc, Transaction, ItemType, // eslint-disable-line
|
||||
Doc, Transaction, Item // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
@@ -61,14 +61,13 @@ export class YMap extends AbstractType {
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Doc} y The Yjs instance
|
||||
* @param {ItemType} item
|
||||
* @param {Item} item
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
// @ts-ignore
|
||||
for (let [key, value] of this._prelimContent) {
|
||||
for (let [key, value] of /** @type {Map<string, any>} */ (this._prelimContent)) {
|
||||
this.set(key, value)
|
||||
}
|
||||
this._prelimContent = null
|
||||
@@ -97,7 +96,7 @@ export class YMap extends AbstractType {
|
||||
const map = {}
|
||||
for (let [key, item] of this._map) {
|
||||
if (!item.deleted) {
|
||||
const v = item.getContent()[0]
|
||||
const v = item.content.getContent()[item.length - 1]
|
||||
map[key] = v instanceof AbstractType ? v.toJSON() : v
|
||||
}
|
||||
}
|
||||
@@ -119,7 +118,7 @@ export class YMap extends AbstractType {
|
||||
* @return {Iterator<string>}
|
||||
*/
|
||||
values () {
|
||||
return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => v[1].getContent()[v[1].length - 1])
|
||||
return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +127,7 @@ export class YMap extends AbstractType {
|
||||
* @return {IterableIterator<any>}
|
||||
*/
|
||||
entries () {
|
||||
return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => [v[0], v[1].getContent()[v[1].length - 1]])
|
||||
return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => [v[0], v[1].content.getContent()[v[1].length - 1]])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,8 +148,7 @@ export class YMap extends AbstractType {
|
||||
typeMapDelete(transaction, this, key)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._prelimContent.delete(key)
|
||||
/** @type {Map<string, any>} */ (this._prelimContent).delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +164,7 @@ export class YMap extends AbstractType {
|
||||
typeMapSet(transaction, this, key, value)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._prelimContent.set(key, value)
|
||||
/** @type {Map<string, any>} */ (this._prelimContent).set(key, value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -179,8 +176,7 @@ export class YMap extends AbstractType {
|
||||
* @return {T|undefined}
|
||||
*/
|
||||
get (key) {
|
||||
// @ts-ignore
|
||||
return typeMapGet(this, key)
|
||||
return /** @type {any} */ (typeMapGet(this, key))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
|
||||
import {
|
||||
YEvent,
|
||||
ItemEmbed,
|
||||
ItemString,
|
||||
ItemFormat,
|
||||
AbstractType,
|
||||
nextID,
|
||||
createID,
|
||||
@@ -16,7 +13,10 @@ import {
|
||||
YTextRefID,
|
||||
callTypeObservers,
|
||||
transact,
|
||||
Doc, ItemType, AbstractItem, Snapshot, StructStore, Transaction // eslint-disable-line
|
||||
ContentEmbed,
|
||||
ContentFormat,
|
||||
ContentString,
|
||||
Doc, Item, Snapshot, StructStore, Transaction // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||
@@ -24,8 +24,8 @@ import * as encoding from 'lib0/encoding.js'
|
||||
|
||||
export class ItemListPosition {
|
||||
/**
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
*/
|
||||
constructor (left, right) {
|
||||
this.left = left
|
||||
@@ -35,8 +35,8 @@ export class ItemListPosition {
|
||||
|
||||
export class ItemTextListPosition extends ItemListPosition {
|
||||
/**
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
*/
|
||||
constructor (left, right, currentAttributes) {
|
||||
@@ -47,8 +47,8 @@ export class ItemTextListPosition extends ItemListPosition {
|
||||
|
||||
export class ItemInsertionResult extends ItemListPosition {
|
||||
/**
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {Map<string,any>} negatedAttributes
|
||||
*/
|
||||
constructor (left, right, negatedAttributes) {
|
||||
@@ -61,8 +61,8 @@ export class ItemInsertionResult extends ItemListPosition {
|
||||
* @param {Transaction} transaction
|
||||
* @param {StructStore} store
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {number} count
|
||||
* @return {ItemTextListPosition}
|
||||
*
|
||||
@@ -71,9 +71,9 @@ export class ItemInsertionResult extends ItemListPosition {
|
||||
*/
|
||||
const findNextPosition = (transaction, store, currentAttributes, left, right, count) => {
|
||||
while (right !== null && count > 0) {
|
||||
switch (right.constructor) {
|
||||
case ItemEmbed:
|
||||
case ItemString:
|
||||
switch (right.content.constructor) {
|
||||
case ContentEmbed:
|
||||
case ContentString:
|
||||
if (!right.deleted) {
|
||||
if (count < right.length) {
|
||||
// split right
|
||||
@@ -82,10 +82,9 @@ const findNextPosition = (transaction, store, currentAttributes, left, right, co
|
||||
count -= right.length
|
||||
}
|
||||
break
|
||||
case ItemFormat:
|
||||
case ContentFormat:
|
||||
if (!right.deleted) {
|
||||
// @ts-ignore right is ItemFormat
|
||||
updateCurrentAttributes(currentAttributes, right)
|
||||
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -117,8 +116,8 @@ const findPosition = (transaction, store, parent, index) => {
|
||||
*
|
||||
* @param {Transaction} transaction
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {Map<string,any>} negatedAttributes
|
||||
* @return {ItemListPosition}
|
||||
*
|
||||
@@ -130,21 +129,19 @@ const insertNegatedAttributes = (transaction, parent, left, right, negatedAttrib
|
||||
while (
|
||||
right !== null && (
|
||||
right.deleted === true || (
|
||||
right.constructor === ItemFormat &&
|
||||
// @ts-ignore right is ItemFormat
|
||||
(negatedAttributes.get(right.key) === right.value)
|
||||
right.content.constructor === ContentFormat &&
|
||||
(negatedAttributes.get(/** @type {ContentFormat} */ (right.content).key) === /** @type {ContentFormat} */ (right.content).value)
|
||||
)
|
||||
)
|
||||
) {
|
||||
if (!right.deleted) {
|
||||
// @ts-ignore right is ItemFormat
|
||||
negatedAttributes.delete(right.key)
|
||||
negatedAttributes.delete(/** @type {ContentFormat} */ (right.content).key)
|
||||
}
|
||||
left = right
|
||||
right = right.right
|
||||
}
|
||||
for (let [key, val] of negatedAttributes) {
|
||||
left = new ItemFormat(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, key, val)
|
||||
left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, new ContentFormat(key, val))
|
||||
left.integrate(transaction)
|
||||
}
|
||||
return { left, right }
|
||||
@@ -152,14 +149,13 @@ const insertNegatedAttributes = (transaction, parent, left, right, negatedAttrib
|
||||
|
||||
/**
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
* @param {ItemFormat} item
|
||||
* @param {ContentFormat} format
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
const updateCurrentAttributes = (currentAttributes, item) => {
|
||||
const value = item.value
|
||||
const key = item.key
|
||||
const updateCurrentAttributes = (currentAttributes, format) => {
|
||||
const { key, value } = format
|
||||
if (value === null) {
|
||||
currentAttributes.delete(key)
|
||||
} else {
|
||||
@@ -168,8 +164,8 @@ const updateCurrentAttributes = (currentAttributes, item) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
* @param {Object<string,any>} attributes
|
||||
* @return {ItemListPosition}
|
||||
@@ -184,11 +180,9 @@ const minimizeAttributeChanges = (left, right, currentAttributes, attributes) =>
|
||||
break
|
||||
} else if (right.deleted) {
|
||||
// continue
|
||||
// @ts-ignore right is ItemFormat
|
||||
} else if (right.constructor === ItemFormat && (attributes[right.key] || null) === right.value) {
|
||||
} else if (right.content.constructor === ContentFormat && (attributes[(/** @type {ContentFormat} */ (right.content)).key] || null) === /** @type {ContentFormat} */ (right.content).value) {
|
||||
// found a format, update currentAttributes and continue
|
||||
// @ts-ignore right is ItemFormat
|
||||
updateCurrentAttributes(currentAttributes, right)
|
||||
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
@@ -201,8 +195,8 @@ const minimizeAttributeChanges = (left, right, currentAttributes, attributes) =>
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
* @param {Object<string,any>} attributes
|
||||
* @return {ItemInsertionResult}
|
||||
@@ -219,7 +213,7 @@ const insertAttributes = (transaction, parent, left, right, currentAttributes, a
|
||||
if (currentVal !== val) {
|
||||
// save negated attribute (set null if currentVal undefined)
|
||||
negatedAttributes.set(key, currentVal)
|
||||
left = new ItemFormat(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, key, val)
|
||||
left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, new ContentFormat(key, val))
|
||||
left.integrate(transaction)
|
||||
}
|
||||
}
|
||||
@@ -229,8 +223,8 @@ const insertAttributes = (transaction, parent, left, right, currentAttributes, a
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
* @param {string} text
|
||||
* @param {Object<string,any>} attributes
|
||||
@@ -250,11 +244,8 @@ const insertText = (transaction, parent, left, right, currentAttributes, text, a
|
||||
left = insertPos.left
|
||||
right = insertPos.right
|
||||
// insert content
|
||||
if (text.constructor === String) {
|
||||
left = new ItemString(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, text)
|
||||
} else {
|
||||
left = new ItemEmbed(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, text)
|
||||
}
|
||||
const content = text.constructor === String ? new ContentString(text) : new ContentEmbed(text)
|
||||
left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, content)
|
||||
left.integrate(transaction)
|
||||
return insertNegatedAttributes(transaction, parent, left, insertPos.right, insertPos.negatedAttributes)
|
||||
}
|
||||
@@ -262,8 +253,8 @@ const insertText = (transaction, parent, left, right, currentAttributes, text, a
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
* @param {number} length
|
||||
* @param {Object<string,any>} attributes
|
||||
@@ -282,26 +273,22 @@ const formatText = (transaction, parent, left, right, currentAttributes, length,
|
||||
// delete all formats with attributes[format.key] != null
|
||||
while (length > 0 && right !== null) {
|
||||
if (right.deleted === false) {
|
||||
switch (right.constructor) {
|
||||
case ItemFormat:
|
||||
// @ts-ignore right is ItemFormat
|
||||
const attr = attributes[right.key]
|
||||
switch (right.content.constructor) {
|
||||
case ContentFormat:
|
||||
const { key, value } = /** @type {ContentFormat} */ (right.content)
|
||||
const attr = attributes[key]
|
||||
if (attr !== undefined) {
|
||||
// @ts-ignore right is ItemFormat
|
||||
if (attr === right.value) {
|
||||
// @ts-ignore right is ItemFormat
|
||||
negatedAttributes.delete(right.key)
|
||||
if (attr === value) {
|
||||
negatedAttributes.delete(key)
|
||||
} else {
|
||||
// @ts-ignore right is ItemFormat
|
||||
negatedAttributes.set(right.key, right.value)
|
||||
negatedAttributes.set(key, value)
|
||||
}
|
||||
right.delete(transaction)
|
||||
}
|
||||
// @ts-ignore right is ItemFormat
|
||||
updateCurrentAttributes(currentAttributes, right)
|
||||
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
|
||||
break
|
||||
case ItemEmbed:
|
||||
case ItemString:
|
||||
case ContentEmbed:
|
||||
case ContentString:
|
||||
if (length < right.length) {
|
||||
getItemCleanStart(transaction, transaction.doc.store, createID(right.id.client, right.id.clock + length))
|
||||
}
|
||||
@@ -317,8 +304,8 @@ const formatText = (transaction, parent, left, right, currentAttributes, length,
|
||||
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {AbstractItem|null} left
|
||||
* @param {AbstractItem|null} right
|
||||
* @param {Item|null} left
|
||||
* @param {Item|null} right
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
* @param {number} length
|
||||
* @return {ItemListPosition}
|
||||
@@ -329,13 +316,12 @@ const formatText = (transaction, parent, left, right, currentAttributes, length,
|
||||
const deleteText = (transaction, left, right, currentAttributes, length) => {
|
||||
while (length > 0 && right !== null) {
|
||||
if (right.deleted === false) {
|
||||
switch (right.constructor) {
|
||||
case ItemFormat:
|
||||
// @ts-ignore right is ItemFormat
|
||||
updateCurrentAttributes(currentAttributes, right)
|
||||
switch (right.content.constructor) {
|
||||
case ContentFormat:
|
||||
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
|
||||
break
|
||||
case ItemEmbed:
|
||||
case ItemString:
|
||||
case ContentEmbed:
|
||||
case ContentString:
|
||||
if (length < right.length) {
|
||||
getItemCleanStart(transaction, transaction.doc.store, createID(right.id.client, right.id.clock + length))
|
||||
}
|
||||
@@ -411,13 +397,10 @@ export class YTextEvent extends YEvent {
|
||||
*/
|
||||
get delta () {
|
||||
if (this._delta === null) {
|
||||
const y = this.target.doc
|
||||
// @ts-ignore
|
||||
const y = /** @type {Doc} */ (this.target.doc)
|
||||
this._delta = []
|
||||
transact(y, transaction => {
|
||||
/**
|
||||
* @type {Array<DeltaItem>}
|
||||
*/
|
||||
const delta = []
|
||||
const delta = /** @type {Array<DeltaItem>} */ (this._delta)
|
||||
const currentAttributes = new Map() // saves all current attributes for insert
|
||||
const oldAttributes = new Map()
|
||||
let item = this.target._start
|
||||
@@ -432,7 +415,6 @@ export class YTextEvent extends YEvent {
|
||||
let insert = ''
|
||||
let retain = 0
|
||||
let deleteLen = 0
|
||||
this._delta = delta
|
||||
const addOp = () => {
|
||||
if (action !== null) {
|
||||
/**
|
||||
@@ -472,14 +454,13 @@ export class YTextEvent extends YEvent {
|
||||
}
|
||||
}
|
||||
while (item !== null) {
|
||||
switch (item.constructor) {
|
||||
case ItemEmbed:
|
||||
switch (item.content.constructor) {
|
||||
case ContentEmbed:
|
||||
if (this.adds(item)) {
|
||||
if (!this.deletes(item)) {
|
||||
addOp()
|
||||
action = 'insert'
|
||||
// @ts-ignore item is ItemFormat
|
||||
insert = item.embed
|
||||
insert = /** @type {ContentEmbed} */ (item.content).embed
|
||||
addOp()
|
||||
}
|
||||
} else if (this.deletes(item)) {
|
||||
@@ -496,15 +477,14 @@ export class YTextEvent extends YEvent {
|
||||
retain += 1
|
||||
}
|
||||
break
|
||||
case ItemString:
|
||||
case ContentString:
|
||||
if (this.adds(item)) {
|
||||
if (!this.deletes(item)) {
|
||||
if (action !== 'insert') {
|
||||
addOp()
|
||||
action = 'insert'
|
||||
}
|
||||
// @ts-ignore
|
||||
insert += item.string
|
||||
insert += /** @type {ContentString} */ (item.content).str
|
||||
}
|
||||
} else if (this.deletes(item)) {
|
||||
if (action !== 'delete') {
|
||||
@@ -520,59 +500,45 @@ export class YTextEvent extends YEvent {
|
||||
retain += item.length
|
||||
}
|
||||
break
|
||||
case ItemFormat:
|
||||
case ContentFormat:
|
||||
const { key, value } = /** @type {ContentFormat} */ (item.content)
|
||||
if (this.adds(item)) {
|
||||
if (!this.deletes(item)) {
|
||||
// @ts-ignore item is ItemFormat
|
||||
const curVal = currentAttributes.get(item.key) || null
|
||||
// @ts-ignore item is ItemFormat
|
||||
if (curVal !== item.value) {
|
||||
const curVal = currentAttributes.get(key) || null
|
||||
if (curVal !== value) {
|
||||
if (action === 'retain') {
|
||||
addOp()
|
||||
}
|
||||
// @ts-ignore item is ItemFormat
|
||||
if (item.value === (oldAttributes.get(item.key) || null)) {
|
||||
// @ts-ignore item is ItemFormat
|
||||
delete attributes[item.key]
|
||||
if (value === (oldAttributes.get(key) || null)) {
|
||||
delete attributes[key]
|
||||
} else {
|
||||
// @ts-ignore item is ItemFormat
|
||||
attributes[item.key] = item.value
|
||||
attributes[key] = value
|
||||
}
|
||||
} else {
|
||||
item.delete(transaction)
|
||||
}
|
||||
}
|
||||
} else if (this.deletes(item)) {
|
||||
// @ts-ignore item is ItemFormat
|
||||
oldAttributes.set(item.key, item.value)
|
||||
// @ts-ignore item is ItemFormat
|
||||
const curVal = currentAttributes.get(item.key) || null
|
||||
// @ts-ignore item is ItemFormat
|
||||
if (curVal !== item.value) {
|
||||
oldAttributes.set(key, value)
|
||||
const curVal = currentAttributes.get(key) || null
|
||||
if (curVal !== value) {
|
||||
if (action === 'retain') {
|
||||
addOp()
|
||||
}
|
||||
// @ts-ignore item is ItemFormat
|
||||
attributes[item.key] = curVal
|
||||
attributes[key] = curVal
|
||||
}
|
||||
} else if (!item.deleted) {
|
||||
// @ts-ignore item is ItemFormat
|
||||
oldAttributes.set(item.key, item.value)
|
||||
// @ts-ignore item is ItemFormat
|
||||
const attr = attributes[item.key]
|
||||
oldAttributes.set(key, value)
|
||||
const attr = attributes[key]
|
||||
if (attr !== undefined) {
|
||||
// @ts-ignore item is ItemFormat
|
||||
if (attr !== item.value) {
|
||||
if (attr !== value) {
|
||||
if (action === 'retain') {
|
||||
addOp()
|
||||
}
|
||||
// @ts-ignore item is ItemFormat
|
||||
if (item.value === null) {
|
||||
// @ts-ignore item is ItemFormat
|
||||
attributes[item.key] = item.value
|
||||
if (value === null) {
|
||||
attributes[key] = value
|
||||
} else {
|
||||
// @ts-ignore item is ItemFormat
|
||||
delete attributes[item.key]
|
||||
delete attributes[key]
|
||||
}
|
||||
} else {
|
||||
item.delete(transaction)
|
||||
@@ -583,26 +549,24 @@ export class YTextEvent extends YEvent {
|
||||
if (action === 'insert') {
|
||||
addOp()
|
||||
}
|
||||
// @ts-ignore item is ItemFormat
|
||||
updateCurrentAttributes(currentAttributes, item)
|
||||
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (item.content))
|
||||
}
|
||||
break
|
||||
}
|
||||
item = item.right
|
||||
}
|
||||
addOp()
|
||||
while (this._delta.length > 0) {
|
||||
let lastOp = this._delta[this._delta.length - 1]
|
||||
while (delta.length > 0) {
|
||||
let lastOp = delta[delta.length - 1]
|
||||
if (lastOp.retain !== undefined && lastOp.attributes === undefined) {
|
||||
// retain delta's if they don't assign attributes
|
||||
this._delta.pop()
|
||||
delta.pop()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// @ts-ignore _delta is defined above
|
||||
return this._delta
|
||||
}
|
||||
}
|
||||
@@ -636,15 +600,14 @@ export class YText extends AbstractType {
|
||||
|
||||
/**
|
||||
* @param {Doc} y
|
||||
* @param {ItemType} item
|
||||
* @param {Item} item
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
try {
|
||||
// @ts-ignore this._prelimContent is still defined
|
||||
this._pending.forEach(f => f())
|
||||
/** @type {Array<function>} */ (this._pending).forEach(f => f())
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
@@ -671,13 +634,12 @@ export class YText extends AbstractType {
|
||||
toString () {
|
||||
let str = ''
|
||||
/**
|
||||
* @type {AbstractItem|null}
|
||||
* @type {Item|null}
|
||||
*/
|
||||
let n = this._start
|
||||
while (n !== null) {
|
||||
if (!n.deleted && n.countable && n.constructor === ItemString) {
|
||||
// @ts-ignore
|
||||
str += n.string
|
||||
if (!n.deleted && n.countable && n.content.constructor === ContentString) {
|
||||
str += /** @type {ContentString} */ (n.content).str
|
||||
}
|
||||
n = n.right
|
||||
}
|
||||
@@ -711,8 +673,7 @@ export class YText extends AbstractType {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.applyDelta(delta))
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.applyDelta(delta))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,10 +693,6 @@ export class YText extends AbstractType {
|
||||
const ops = []
|
||||
const currentAttributes = new Map()
|
||||
let str = ''
|
||||
/**
|
||||
* @type {AbstractItem|null}
|
||||
*/
|
||||
// @ts-ignore
|
||||
let n = this._start
|
||||
function packStr () {
|
||||
if (str.length > 0) {
|
||||
@@ -762,8 +719,8 @@ export class YText extends AbstractType {
|
||||
}
|
||||
while (n !== null) {
|
||||
if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) {
|
||||
switch (n.constructor) {
|
||||
case ItemString:
|
||||
switch (n.content.constructor) {
|
||||
case ContentString:
|
||||
const cur = currentAttributes.get('ychange')
|
||||
if (snapshot !== undefined && !isVisible(n, snapshot)) {
|
||||
if (cur === undefined || cur.user !== n.id.client || cur.state !== 'removed') {
|
||||
@@ -779,20 +736,17 @@ export class YText extends AbstractType {
|
||||
packStr()
|
||||
currentAttributes.delete('ychange')
|
||||
}
|
||||
// @ts-ignore
|
||||
str += n.string
|
||||
str += /** @type {ContentString} */ (n.content).str
|
||||
break
|
||||
case ItemEmbed:
|
||||
case ContentEmbed:
|
||||
packStr()
|
||||
ops.push({
|
||||
// @ts-ignore item is ItemFormat
|
||||
insert: n.embed
|
||||
insert: /** @type {ContentEmbed} */ (n.content).embed
|
||||
})
|
||||
break
|
||||
case ItemFormat:
|
||||
break
|
||||
case ContentFormat:
|
||||
packStr()
|
||||
// @ts-ignore
|
||||
updateCurrentAttributes(currentAttributes, n)
|
||||
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (n.content))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -823,8 +777,7 @@ export class YText extends AbstractType {
|
||||
insertText(transaction, this, left, right, currentAttributes, text, attributes)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.insert(index, text, attributes))
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.insert(index, text, attributes))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,8 +802,7 @@ export class YText extends AbstractType {
|
||||
insertText(transaction, this, left, right, currentAttributes, embed, attributes)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.insertEmbed(index, embed, attributes))
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,8 +825,7 @@ export class YText extends AbstractType {
|
||||
deleteText(transaction, left, right, currentAttributes, length)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.delete(index, length))
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.delete(index, length))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,8 +850,7 @@ export class YText extends AbstractType {
|
||||
formatText(transaction, this, left, right, currentAttributes, length, attributes)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.format(index, length, attributes))
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.format(index, length, attributes))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
typeMapGetAll,
|
||||
typeListForEach,
|
||||
YXmlElementRefID,
|
||||
Snapshot, Doc, ItemType // eslint-disable-line
|
||||
Snapshot, Doc, Item // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
@@ -40,16 +40,14 @@ export class YXmlElement extends YXmlFragment {
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Doc} y The Yjs instance
|
||||
* @param {ItemType} item
|
||||
* @param {Item} item
|
||||
* @private
|
||||
*/
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
// @ts-ignore
|
||||
this.insert(0, this._prelimContent)
|
||||
this.insert(0, /** @type {Array} */ (this._prelimContent))
|
||||
this._prelimContent = null
|
||||
// @ts-ignore
|
||||
this._prelimAttrs.forEach((value, key) => {
|
||||
;(/** @type {Map<string, any>} */ (this._prelimAttrs)).forEach((value, key) => {
|
||||
this.setAttribute(key, value)
|
||||
})
|
||||
this._prelimContent = null
|
||||
@@ -105,8 +103,7 @@ export class YXmlElement extends YXmlFragment {
|
||||
typeMapDelete(transaction, this, attributeName)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._prelimAttrs.delete(attributeName)
|
||||
/** @type {Map<string,any>} */ (this._prelimAttrs).delete(attributeName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +121,7 @@ export class YXmlElement extends YXmlFragment {
|
||||
typeMapSet(transaction, this, attributeName, attributeValue)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._prelimAttrs.set(attributeName, attributeValue)
|
||||
/** @type {Map<string, any>} */ (this._prelimAttrs).set(attributeName, attributeValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,8 +135,7 @@ export class YXmlElement extends YXmlFragment {
|
||||
* @public
|
||||
*/
|
||||
getAttribute (attributeName) {
|
||||
// @ts-ignore
|
||||
return typeMapGet(this, attributeName)
|
||||
return /** @type {any} */ (typeMapGet(this, attributeName))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
YXmlFragmentRefID,
|
||||
callTypeObservers,
|
||||
transact,
|
||||
Transaction, ItemType, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
||||
ContentType, Transaction, Item, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
@@ -59,10 +59,9 @@ export class YXmlTreeWalker {
|
||||
this._filter = f
|
||||
this._root = root
|
||||
/**
|
||||
* @type {ItemType | null}
|
||||
* @type {Item}
|
||||
*/
|
||||
// @ts-ignore
|
||||
this._currentNode = root._start
|
||||
this._currentNode = /** @type {Item} */ (root._start)
|
||||
this._firstCall = true
|
||||
}
|
||||
|
||||
@@ -77,18 +76,21 @@ export class YXmlTreeWalker {
|
||||
* @public
|
||||
*/
|
||||
next () {
|
||||
/**
|
||||
* @type {Item|null}
|
||||
*/
|
||||
let n = this._currentNode
|
||||
if (n !== null && (!this._firstCall || n.deleted || !this._filter(n.type))) { // if first call, we check if we can use the first item
|
||||
let type = /** @type {ContentType} */ (n.content).type
|
||||
if (n !== null && (!this._firstCall || n.deleted || !this._filter(type))) { // if first call, we check if we can use the first item
|
||||
do {
|
||||
if (!n.deleted && (n.type.constructor === YXmlElement || n.type.constructor === YXmlFragment) && n.type._start !== null) {
|
||||
type = /** @type {ContentType} */ (n.content).type
|
||||
if (!n.deleted && (type.constructor === YXmlElement || type.constructor === YXmlFragment) && type._start !== null) {
|
||||
// walk down in the tree
|
||||
// @ts-ignore
|
||||
n = n.type._start
|
||||
n = type._start
|
||||
} else {
|
||||
// walk right or up in the tree
|
||||
while (n !== null) {
|
||||
if (n.right !== null) {
|
||||
// @ts-ignore
|
||||
n = n.right
|
||||
break
|
||||
} else if (n.parent === this._root) {
|
||||
@@ -98,16 +100,15 @@ export class YXmlTreeWalker {
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (n !== null && (n.deleted || !this._filter(n.type)))
|
||||
} while (n !== null && (n.deleted || !this._filter(/** @type {ContentType} */ (n.content).type)))
|
||||
}
|
||||
this._firstCall = false
|
||||
this._currentNode = n
|
||||
if (n === null) {
|
||||
// @ts-ignore return undefined if done=true (the expected result)
|
||||
// @ts-ignore
|
||||
return { value: undefined, done: true }
|
||||
}
|
||||
// @ts-ignore
|
||||
return { value: n.type, done: false }
|
||||
this._currentNode = n
|
||||
return { value: /** @type {any} */ (n.content).type, done: false }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user