Use generic Item with typed content to reduce cache misses
This commit is contained in:
parent
3fba4f25a5
commit
2192aa5821
2053
package-lock.json
generated
2053
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@
|
|||||||
"lint": "standard && tsc",
|
"lint": "standard && tsc",
|
||||||
"docs": "rm -rf docs; jsdoc --configure ./.jsdoc.json --verbose --readme ./README.v13.md --package ./package.json || true",
|
"docs": "rm -rf docs; jsdoc --configure ./.jsdoc.json --verbose --readme ./README.v13.md --package ./package.json || true",
|
||||||
"serve-docs": "npm run docs && serve ./docs/",
|
"serve-docs": "npm run docs && serve ./docs/",
|
||||||
"preversion": "PRODUCTION=1 npm run dist && npm run docs && node ./dist/tests.js --repitition-time 1000",
|
"preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && node ./dist/tests.js --repitition-time 1000",
|
||||||
"postversion": "git push && git push --tags",
|
"postversion": "git push && git push --tags",
|
||||||
"debug": "concurrently 'live-server --port=3443 --entry-file=test.html' 'npm run watch'",
|
"debug": "concurrently 'live-server --port=3443 --entry-file=test.html' 'npm run watch'",
|
||||||
"trace-deopt": "clear && rollup -c && node --trace-deopt dist/test.js",
|
"trace-deopt": "clear && rollup -c && node --trace-deopt dist/test.js",
|
||||||
@ -59,9 +59,7 @@
|
|||||||
"live-server": "^1.2.1",
|
"live-server": "^1.2.1",
|
||||||
"rollup": "^1.11.3",
|
"rollup": "^1.11.3",
|
||||||
"rollup-cli": "^1.0.9",
|
"rollup-cli": "^1.0.9",
|
||||||
"rollup-plugin-commonjs": "^9.3.4",
|
|
||||||
"rollup-plugin-node-resolve": "^4.2.4",
|
"rollup-plugin-node-resolve": "^4.2.4",
|
||||||
"rollup-plugin-terser": "^4.0.4",
|
|
||||||
"standard": "^11.0.1",
|
"standard": "^11.0.1",
|
||||||
"tui-jsdoc-template": "^1.2.2",
|
"tui-jsdoc-template": "^1.2.2",
|
||||||
"typescript": "^3.4.5",
|
"typescript": "^3.4.5",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import nodeResolve from 'rollup-plugin-node-resolve'
|
import nodeResolve from 'rollup-plugin-node-resolve'
|
||||||
import commonjs from 'rollup-plugin-commonjs'
|
|
||||||
import { terser } from 'rollup-plugin-terser'
|
const localImports = process.env.LOCALIMPORTS
|
||||||
|
|
||||||
const customModules = new Set([
|
const customModules = new Set([
|
||||||
'y-websocket',
|
'y-websocket',
|
||||||
@ -23,33 +23,18 @@ const debugResolve = {
|
|||||||
if (importee === 'yjs') {
|
if (importee === 'yjs') {
|
||||||
return `${process.cwd()}/src/index.js`
|
return `${process.cwd()}/src/index.js`
|
||||||
}
|
}
|
||||||
/*
|
if (localImports) {
|
||||||
if (customModules.has(importee.split('/')[0])) {
|
if (customModules.has(importee.split('/')[0])) {
|
||||||
return `${process.cwd()}/../${importee}/src/${importee}.js`
|
return `${process.cwd()}/../${importee}/src/${importee}.js`
|
||||||
|
}
|
||||||
|
if (customLibModules.has(importee.split('/')[0])) {
|
||||||
|
return `${process.cwd()}/../${importee}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (customLibModules.has(importee.split('/')[0])) {
|
|
||||||
return `${process.cwd()}/../${importee}`
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const minificationPlugins = process.env.PRODUCTION ? [terser({
|
|
||||||
module: true,
|
|
||||||
compress: {
|
|
||||||
hoist_vars: true,
|
|
||||||
module: true,
|
|
||||||
passes: 5,
|
|
||||||
pure_getters: true,
|
|
||||||
unsafe_comps: true,
|
|
||||||
unsafe_undefined: true
|
|
||||||
},
|
|
||||||
mangle: {
|
|
||||||
toplevel: true
|
|
||||||
}
|
|
||||||
})] : []
|
|
||||||
|
|
||||||
export default [{
|
export default [{
|
||||||
input: './src/index.js',
|
input: './src/index.js',
|
||||||
output: [{
|
output: [{
|
||||||
@ -84,6 +69,5 @@ export default [{
|
|||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
mainFields: ['module', 'browser', 'main']
|
mainFields: ['module', 'browser', 'main']
|
||||||
})
|
})
|
||||||
// commonjs()
|
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
|
16
src/index.js
16
src/index.js
@ -13,16 +13,16 @@ export {
|
|||||||
YMapEvent,
|
YMapEvent,
|
||||||
YArrayEvent,
|
YArrayEvent,
|
||||||
YEvent,
|
YEvent,
|
||||||
AbstractItem,
|
Item,
|
||||||
AbstractStruct,
|
AbstractStruct,
|
||||||
GC,
|
GC,
|
||||||
ItemBinary,
|
ContentBinary,
|
||||||
ItemDeleted,
|
ContentDeleted,
|
||||||
ItemEmbed,
|
ContentEmbed,
|
||||||
ItemFormat,
|
ContentFormat,
|
||||||
ItemJSON,
|
ContentJSON,
|
||||||
ItemString,
|
ContentString,
|
||||||
ItemType,
|
ContentType,
|
||||||
AbstractType,
|
AbstractType,
|
||||||
RelativePosition,
|
RelativePosition,
|
||||||
createRelativePositionFromTypeIndex,
|
createRelativePositionFromTypeIndex,
|
||||||
|
@ -21,14 +21,14 @@ export * from './types/YXmlHook.js'
|
|||||||
export * from './types/YXmlText.js'
|
export * from './types/YXmlText.js'
|
||||||
|
|
||||||
export * from './structs/AbstractStruct.js'
|
export * from './structs/AbstractStruct.js'
|
||||||
export * from './structs/AbstractItem.js'
|
|
||||||
export * from './structs/GC.js'
|
export * from './structs/GC.js'
|
||||||
export * from './structs/ItemBinary.js'
|
export * from './structs/ContentBinary.js'
|
||||||
export * from './structs/ItemDeleted.js'
|
export * from './structs/ContentDeleted.js'
|
||||||
export * from './structs/ItemEmbed.js'
|
export * from './structs/ContentEmbed.js'
|
||||||
export * from './structs/ItemFormat.js'
|
export * from './structs/ContentFormat.js'
|
||||||
export * from './structs/ItemJSON.js'
|
export * from './structs/ContentJSON.js'
|
||||||
export * from './structs/ItemString.js'
|
export * from './structs/ContentString.js'
|
||||||
export * from './structs/ItemType.js'
|
export * from './structs/ContentType.js'
|
||||||
|
export * from './structs/Item.js'
|
||||||
|
|
||||||
export * from './utils/encoding.js'
|
export * from './utils/encoding.js'
|
||||||
|
@ -12,14 +12,16 @@ import * as error from 'lib0/error.js'
|
|||||||
export class AbstractStruct {
|
export class AbstractStruct {
|
||||||
/**
|
/**
|
||||||
* @param {ID} id
|
* @param {ID} id
|
||||||
|
* @param {number} length
|
||||||
*/
|
*/
|
||||||
constructor (id) {
|
constructor (id, length) {
|
||||||
/**
|
/**
|
||||||
* The uniqe identifier of this struct.
|
* The uniqe identifier of this struct.
|
||||||
* @type {ID}
|
* @type {ID}
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
this.id = id
|
this.id = id
|
||||||
|
this.length = length
|
||||||
this.deleted = false
|
this.deleted = false
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -32,12 +34,6 @@ export class AbstractStruct {
|
|||||||
mergeWith (right) {
|
mergeWith (right) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
get length () {
|
|
||||||
throw error.methodUnimplemented()
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
* @param {encoding.Encoder} encoder The encoder to write data to.
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
@ -89,10 +85,4 @@ export class AbstractStructRef {
|
|||||||
toStruct (transaction, store, offset) {
|
toStruct (transaction, store, offset) {
|
||||||
throw error.methodUnimplemented()
|
throw error.methodUnimplemented()
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
get length () {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
92
src/structs/ContentBinary.js
Normal file
92
src/structs/ContentBinary.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import {
|
||||||
|
StructStore, Item, Transaction // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
import * as encoding from 'lib0/encoding.js'
|
||||||
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
import * as buffer from 'lib0/buffer.js'
|
||||||
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class ContentBinary {
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} content
|
||||||
|
*/
|
||||||
|
constructor (content) {
|
||||||
|
this.content = content
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getLength () {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {Array<any>}
|
||||||
|
*/
|
||||||
|
getContent () {
|
||||||
|
return [this.content]
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isCountable () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {ContentBinary}
|
||||||
|
*/
|
||||||
|
copy () {
|
||||||
|
return new ContentBinary(this.content)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {number} offset
|
||||||
|
* @return {ContentBinary}
|
||||||
|
*/
|
||||||
|
splice (offset) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {ContentBinary} right
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
mergeWith (right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Item} item
|
||||||
|
*/
|
||||||
|
integrate (transaction, item) {}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
delete (transaction) {}
|
||||||
|
/**
|
||||||
|
* @param {StructStore} store
|
||||||
|
*/
|
||||||
|
gc (store) {}
|
||||||
|
/**
|
||||||
|
* @param {encoding.Encoder} encoder
|
||||||
|
* @param {number} offset
|
||||||
|
*/
|
||||||
|
write (encoder, offset) {
|
||||||
|
encoding.writeVarUint8Array(encoder, this.content)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRef () {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @return {ContentBinary}
|
||||||
|
*/
|
||||||
|
export const readContentBinary = decoder => new ContentBinary(buffer.copyUint8Array(decoding.readVarUint8Array(decoder)))
|
98
src/structs/ContentDeleted.js
Normal file
98
src/structs/ContentDeleted.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
addToDeleteSet,
|
||||||
|
StructStore, Item, Transaction // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
import * as encoding from 'lib0/encoding.js'
|
||||||
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class ContentDeleted {
|
||||||
|
/**
|
||||||
|
* @param {number} len
|
||||||
|
*/
|
||||||
|
constructor (len) {
|
||||||
|
this.len = len
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getLength () {
|
||||||
|
return this.len
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {Array<any>}
|
||||||
|
*/
|
||||||
|
getContent () {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isCountable () {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {ContentDeleted}
|
||||||
|
*/
|
||||||
|
copy () {
|
||||||
|
return new ContentDeleted(this.len)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {number} offset
|
||||||
|
* @return {ContentDeleted}
|
||||||
|
*/
|
||||||
|
splice (offset) {
|
||||||
|
const right = new ContentDeleted(this.len - offset)
|
||||||
|
this.len = offset
|
||||||
|
return right
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {ContentDeleted} right
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
mergeWith (right) {
|
||||||
|
this.len += right.len
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Item} item
|
||||||
|
*/
|
||||||
|
integrate (transaction, item) {
|
||||||
|
addToDeleteSet(transaction.deleteSet, item.id, this.len)
|
||||||
|
item.deleted = true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
delete (transaction) {}
|
||||||
|
/**
|
||||||
|
* @param {StructStore} store
|
||||||
|
*/
|
||||||
|
gc (store) {}
|
||||||
|
/**
|
||||||
|
* @param {encoding.Encoder} encoder
|
||||||
|
* @param {number} offset
|
||||||
|
*/
|
||||||
|
write (encoder, offset) {
|
||||||
|
encoding.writeVarUint(encoder, this.len - offset)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRef () {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @return {ContentDeleted}
|
||||||
|
*/
|
||||||
|
export const readContentDeleted = decoder => new ContentDeleted(decoding.readVarUint(decoder))
|
92
src/structs/ContentEmbed.js
Normal file
92
src/structs/ContentEmbed.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
StructStore, Item, Transaction // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
import * as encoding from 'lib0/encoding.js'
|
||||||
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class ContentEmbed {
|
||||||
|
/**
|
||||||
|
* @param {Object} embed
|
||||||
|
*/
|
||||||
|
constructor (embed) {
|
||||||
|
this.embed = embed
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getLength () {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {Array<any>}
|
||||||
|
*/
|
||||||
|
getContent () {
|
||||||
|
return [this.embed]
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isCountable () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {ContentEmbed}
|
||||||
|
*/
|
||||||
|
copy () {
|
||||||
|
return new ContentEmbed(this.embed)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {number} offset
|
||||||
|
* @return {ContentEmbed}
|
||||||
|
*/
|
||||||
|
splice (offset) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {ContentEmbed} right
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
mergeWith (right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Item} item
|
||||||
|
*/
|
||||||
|
integrate (transaction, item) {}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
delete (transaction) {}
|
||||||
|
/**
|
||||||
|
* @param {StructStore} store
|
||||||
|
*/
|
||||||
|
gc (store) {}
|
||||||
|
/**
|
||||||
|
* @param {encoding.Encoder} encoder
|
||||||
|
* @param {number} offset
|
||||||
|
*/
|
||||||
|
write (encoder, offset) {
|
||||||
|
encoding.writeVarString(encoder, JSON.stringify(this.embed))
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRef () {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @return {ContentEmbed}
|
||||||
|
*/
|
||||||
|
export const readContentEmbed = decoder => new ContentEmbed(JSON.parse(decoding.readVarString(decoder)))
|
95
src/structs/ContentFormat.js
Normal file
95
src/structs/ContentFormat.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
Item, StructStore, Transaction // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
import * as encoding from 'lib0/encoding.js'
|
||||||
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class ContentFormat {
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {Object} value
|
||||||
|
*/
|
||||||
|
constructor (key, value) {
|
||||||
|
this.key = key
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getLength () {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {Array<any>}
|
||||||
|
*/
|
||||||
|
getContent () {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isCountable () {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {ContentFormat}
|
||||||
|
*/
|
||||||
|
copy () {
|
||||||
|
return new ContentFormat(this.key, this.value)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {number} offset
|
||||||
|
* @return {ContentFormat}
|
||||||
|
*/
|
||||||
|
splice (offset) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {ContentFormat} right
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
mergeWith (right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Item} item
|
||||||
|
*/
|
||||||
|
integrate (transaction, item) {}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
delete (transaction) {}
|
||||||
|
/**
|
||||||
|
* @param {StructStore} store
|
||||||
|
*/
|
||||||
|
gc (store) {}
|
||||||
|
/**
|
||||||
|
* @param {encoding.Encoder} encoder
|
||||||
|
* @param {number} offset
|
||||||
|
*/
|
||||||
|
write (encoder, offset) {
|
||||||
|
encoding.writeVarString(encoder, this.key)
|
||||||
|
encoding.writeVarString(encoder, JSON.stringify(this.value))
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRef () {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @return {ContentFormat}
|
||||||
|
*/
|
||||||
|
export const readContentFormat = decoder => new ContentFormat(decoding.readVarString(decoder), JSON.parse(decoding.readVarString(decoder)))
|
113
src/structs/ContentJSON.js
Normal file
113
src/structs/ContentJSON.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import {
|
||||||
|
Transaction, Item, StructStore // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
import * as encoding from 'lib0/encoding.js'
|
||||||
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class ContentJSON {
|
||||||
|
/**
|
||||||
|
* @param {Array<any>} arr
|
||||||
|
*/
|
||||||
|
constructor (arr) {
|
||||||
|
/**
|
||||||
|
* @type {Array<any>}
|
||||||
|
*/
|
||||||
|
this.arr = arr
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getLength () {
|
||||||
|
return this.arr.length
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {Array<any>}
|
||||||
|
*/
|
||||||
|
getContent () {
|
||||||
|
return this.arr
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isCountable () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {ContentJSON}
|
||||||
|
*/
|
||||||
|
copy () {
|
||||||
|
return new ContentJSON(this.arr)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {number} offset
|
||||||
|
* @return {ContentJSON}
|
||||||
|
*/
|
||||||
|
splice (offset) {
|
||||||
|
const right = new ContentJSON(this.arr.slice(offset))
|
||||||
|
this.arr = this.arr.slice(0, offset)
|
||||||
|
return right
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {ContentJSON} right
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
mergeWith (right) {
|
||||||
|
this.arr = this.arr.concat(right.arr)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Item} item
|
||||||
|
*/
|
||||||
|
integrate (transaction, item) {}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
delete (transaction) {}
|
||||||
|
/**
|
||||||
|
* @param {StructStore} store
|
||||||
|
*/
|
||||||
|
gc (store) {}
|
||||||
|
/**
|
||||||
|
* @param {encoding.Encoder} encoder
|
||||||
|
* @param {number} offset
|
||||||
|
*/
|
||||||
|
write (encoder, offset) {
|
||||||
|
const len = this.arr.length
|
||||||
|
encoding.writeVarUint(encoder, len - offset)
|
||||||
|
for (let i = offset; i < len; i++) {
|
||||||
|
const c = this.arr[i]
|
||||||
|
encoding.writeVarString(encoder, c === undefined ? 'undefined' : JSON.stringify(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRef () {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @return {ContentJSON}
|
||||||
|
*/
|
||||||
|
export const readContentJSON = decoder => {
|
||||||
|
const len = decoding.readVarUint(decoder)
|
||||||
|
const cs = []
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const c = decoding.readVarString(decoder)
|
||||||
|
if (c === 'undefined') {
|
||||||
|
cs.push(undefined)
|
||||||
|
} else {
|
||||||
|
cs.push(JSON.parse(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ContentJSON(cs)
|
||||||
|
}
|
96
src/structs/ContentString.js
Normal file
96
src/structs/ContentString.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import {
|
||||||
|
Transaction, Item, StructStore // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
import * as encoding from 'lib0/encoding.js'
|
||||||
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class ContentString {
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
*/
|
||||||
|
constructor (str) {
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.str = str
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getLength () {
|
||||||
|
return this.str.length
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {Array<any>}
|
||||||
|
*/
|
||||||
|
getContent () {
|
||||||
|
return this.str.split('')
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isCountable () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {ContentString}
|
||||||
|
*/
|
||||||
|
copy () {
|
||||||
|
return new ContentString(this.str)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {number} offset
|
||||||
|
* @return {ContentString}
|
||||||
|
*/
|
||||||
|
splice (offset) {
|
||||||
|
const right = new ContentString(this.str.slice(offset))
|
||||||
|
this.str = this.str.slice(0, offset)
|
||||||
|
return right
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {ContentString} right
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
mergeWith (right) {
|
||||||
|
this.str += right.str
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Item} item
|
||||||
|
*/
|
||||||
|
integrate (transaction, item) {}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
delete (transaction) {}
|
||||||
|
/**
|
||||||
|
* @param {StructStore} store
|
||||||
|
*/
|
||||||
|
gc (store) {}
|
||||||
|
/**
|
||||||
|
* @param {encoding.Encoder} encoder
|
||||||
|
* @param {number} offset
|
||||||
|
*/
|
||||||
|
write (encoder, offset) {
|
||||||
|
encoding.writeVarString(encoder, offset === 0 ? this.str : this.str.slice(offset))
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRef () {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @return {ContentString}
|
||||||
|
*/
|
||||||
|
export const readContentString = decoder => new ContentString(decoding.readVarString(decoder))
|
161
src/structs/ContentType.js
Normal file
161
src/structs/ContentType.js
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
readYArray,
|
||||||
|
readYMap,
|
||||||
|
readYText,
|
||||||
|
readYXmlElement,
|
||||||
|
readYXmlFragment,
|
||||||
|
readYXmlHook,
|
||||||
|
readYXmlText,
|
||||||
|
StructStore, Transaction, Item, YEvent, AbstractType // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
import * as encoding from 'lib0/encoding.js' // eslint-disable-line
|
||||||
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<function(decoding.Decoder):AbstractType<any>>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export const typeRefs = [
|
||||||
|
readYArray,
|
||||||
|
readYMap,
|
||||||
|
readYText,
|
||||||
|
readYXmlElement,
|
||||||
|
readYXmlFragment,
|
||||||
|
readYXmlHook,
|
||||||
|
readYXmlText
|
||||||
|
]
|
||||||
|
|
||||||
|
export const YArrayRefID = 0
|
||||||
|
export const YMapRefID = 1
|
||||||
|
export const YTextRefID = 2
|
||||||
|
export const YXmlElementRefID = 3
|
||||||
|
export const YXmlFragmentRefID = 4
|
||||||
|
export const YXmlHookRefID = 5
|
||||||
|
export const YXmlTextRefID = 6
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class ContentType {
|
||||||
|
/**
|
||||||
|
* @param {AbstractType<YEvent>} type
|
||||||
|
*/
|
||||||
|
constructor (type) {
|
||||||
|
this.type = type
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getLength () {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {Array<any>}
|
||||||
|
*/
|
||||||
|
getContent () {
|
||||||
|
return [this.type]
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isCountable () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {ContentType}
|
||||||
|
*/
|
||||||
|
copy () {
|
||||||
|
return new ContentType(this.type._copy())
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {number} offset
|
||||||
|
* @return {ContentType}
|
||||||
|
*/
|
||||||
|
splice (offset) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {ContentType} right
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
mergeWith (right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Item} item
|
||||||
|
*/
|
||||||
|
integrate (transaction, item) {
|
||||||
|
this.type._integrate(transaction.doc, item)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
delete (transaction) {
|
||||||
|
let item = this.type._start
|
||||||
|
while (item !== null) {
|
||||||
|
if (!item.deleted) {
|
||||||
|
item.delete(transaction)
|
||||||
|
} else {
|
||||||
|
// Whis will be gc'd later and we want to merge it if possible
|
||||||
|
// We try to merge all deleted items after each transaction,
|
||||||
|
// but we have no knowledge about that this needs to be merged
|
||||||
|
// since it is not in transaction.ds. Hence we add it to transaction._mergeStructs
|
||||||
|
transaction._mergeStructs.add(item.id)
|
||||||
|
}
|
||||||
|
item = item.right
|
||||||
|
}
|
||||||
|
this.type._map.forEach(item => {
|
||||||
|
if (!item.deleted) {
|
||||||
|
item.delete(transaction)
|
||||||
|
} else {
|
||||||
|
// same as above
|
||||||
|
transaction._mergeStructs.add(item.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
transaction.changed.delete(this.type)
|
||||||
|
transaction.changedParentTypes.delete(this.type)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {StructStore} store
|
||||||
|
*/
|
||||||
|
gc (store) {
|
||||||
|
let item = this.type._start
|
||||||
|
while (item !== null) {
|
||||||
|
item.gc(store, true)
|
||||||
|
item = item.right
|
||||||
|
}
|
||||||
|
this.type._start = null
|
||||||
|
this.type._map.forEach(/** @param {Item | null} item */ (item) => {
|
||||||
|
while (item !== null) {
|
||||||
|
item.gc(store, true)
|
||||||
|
item = item.left
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.type._map = new Map()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {encoding.Encoder} encoder
|
||||||
|
* @param {number} offset
|
||||||
|
*/
|
||||||
|
write (encoder, offset) {
|
||||||
|
this.type._write(encoder)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRef () {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @return {ContentType}
|
||||||
|
*/
|
||||||
|
export const readContentType = decoder => new ContentType(typeRefs[decoding.readVarUint(decoder)](decoder))
|
@ -21,26 +21,18 @@ export class GC extends AbstractStruct {
|
|||||||
* @param {number} length
|
* @param {number} length
|
||||||
*/
|
*/
|
||||||
constructor (id, length) {
|
constructor (id, length) {
|
||||||
super(id)
|
super(id, length)
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this._len = length
|
|
||||||
this.deleted = true
|
this.deleted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
get length () {
|
|
||||||
return this._len
|
|
||||||
}
|
|
||||||
|
|
||||||
delete () {}
|
delete () {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AbstractStruct} right
|
* @param {GC} right
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
mergeWith (right) {
|
mergeWith (right) {
|
||||||
this._len += right.length
|
this.length += right.length
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +49,7 @@ export class GC extends AbstractStruct {
|
|||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
encoding.writeUint8(encoder, structGCRefNumber)
|
encoding.writeUint8(encoder, structGCRefNumber)
|
||||||
encoding.writeVarUint(encoder, this._len - offset)
|
encoding.writeVarUint(encoder, this.length - offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,15 +67,7 @@ export class GCRef extends AbstractStructRef {
|
|||||||
/**
|
/**
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
this._len = decoding.readVarUint(decoder)
|
this.length = decoding.readVarUint(decoder)
|
||||||
}
|
|
||||||
get length () {
|
|
||||||
return this._len
|
|
||||||
}
|
|
||||||
missing () {
|
|
||||||
return [
|
|
||||||
createID(this.id.client, this.id.clock - 1)
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
@ -95,11 +79,11 @@ export class GCRef extends AbstractStructRef {
|
|||||||
if (offset > 0) {
|
if (offset > 0) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.id = createID(this.id.client, this.id.clock + offset)
|
this.id = createID(this.id.client, this.id.clock + offset)
|
||||||
this._len = this._len - offset
|
this.length -= offset
|
||||||
}
|
}
|
||||||
return new GC(
|
return new GC(
|
||||||
this.id,
|
this.id,
|
||||||
this._len
|
this.length
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,19 @@ import {
|
|||||||
replaceStruct,
|
replaceStruct,
|
||||||
addStruct,
|
addStruct,
|
||||||
addToDeleteSet,
|
addToDeleteSet,
|
||||||
ItemDeleted,
|
|
||||||
findRootTypeKey,
|
findRootTypeKey,
|
||||||
compareIDs,
|
compareIDs,
|
||||||
getItem,
|
getItem,
|
||||||
getItemType,
|
|
||||||
getItemCleanEnd,
|
getItemCleanEnd,
|
||||||
getItemCleanStart,
|
getItemCleanStart,
|
||||||
YEvent, StructStore, ID, AbstractType, Transaction // eslint-disable-line
|
readContentDeleted,
|
||||||
|
readContentBinary,
|
||||||
|
readContentJSON,
|
||||||
|
readContentString,
|
||||||
|
readContentEmbed,
|
||||||
|
readContentFormat,
|
||||||
|
readContentType,
|
||||||
|
ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
@ -27,28 +32,12 @@ import * as maplib from 'lib0/map.js'
|
|||||||
import * as set from 'lib0/set.js'
|
import * as set from 'lib0/set.js'
|
||||||
import * as binary from 'lib0/binary.js'
|
import * as binary from 'lib0/binary.js'
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {AbstractItem} left
|
|
||||||
* @param {AbstractItem} right
|
|
||||||
* @return {boolean} If true, right is removed from the linked list and should be discarded
|
|
||||||
*/
|
|
||||||
export const mergeItemWith = (left, right) => {
|
|
||||||
if (compareIDs(right.origin, left.lastId) && left.right === right && compareIDs(left.rightOrigin, right.rightOrigin)) {
|
|
||||||
left.right = right.right
|
|
||||||
if (left.right !== null) {
|
|
||||||
left.right.left = left
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split leftItem into two items
|
* Split leftItem into two items
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {AbstractItem} leftItem
|
* @param {Item} leftItem
|
||||||
* @param {number} diff
|
* @param {number} diff
|
||||||
* @return {AbstractItem}
|
* @return {Item}
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
* @private
|
* @private
|
||||||
@ -56,14 +45,15 @@ export const mergeItemWith = (left, right) => {
|
|||||||
export const splitItem = (transaction, leftItem, diff) => {
|
export const splitItem = (transaction, leftItem, diff) => {
|
||||||
const id = leftItem.id
|
const id = leftItem.id
|
||||||
// create rightItem
|
// create rightItem
|
||||||
const rightItem = leftItem.copy(
|
const rightItem = new Item(
|
||||||
createID(id.client, id.clock + diff),
|
createID(id.client, id.clock + diff),
|
||||||
leftItem,
|
leftItem,
|
||||||
createID(id.client, id.clock + diff - 1),
|
createID(id.client, id.clock + diff - 1),
|
||||||
leftItem.right,
|
leftItem.right,
|
||||||
leftItem.rightOrigin,
|
leftItem.rightOrigin,
|
||||||
leftItem.parent,
|
leftItem.parent,
|
||||||
leftItem.parentSub
|
leftItem.parentSub,
|
||||||
|
leftItem.content.splice(diff)
|
||||||
)
|
)
|
||||||
if (leftItem.deleted) {
|
if (leftItem.deleted) {
|
||||||
rightItem.deleted = true
|
rightItem.deleted = true
|
||||||
@ -80,24 +70,26 @@ export const splitItem = (transaction, leftItem, diff) => {
|
|||||||
if (rightItem.parentSub !== null && rightItem.right === null) {
|
if (rightItem.parentSub !== null && rightItem.right === null) {
|
||||||
rightItem.parent._map.set(rightItem.parentSub, rightItem)
|
rightItem.parent._map.set(rightItem.parentSub, rightItem)
|
||||||
}
|
}
|
||||||
|
leftItem.length = diff
|
||||||
return rightItem
|
return rightItem
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class that represents any content.
|
* Abstract class that represents any content.
|
||||||
*/
|
*/
|
||||||
export class AbstractItem extends AbstractStruct {
|
export class Item extends AbstractStruct {
|
||||||
/**
|
/**
|
||||||
* @param {ID} id
|
* @param {ID} id
|
||||||
* @param {AbstractItem | null} left
|
* @param {Item | null} left
|
||||||
* @param {ID | null} origin
|
* @param {ID | null} origin
|
||||||
* @param {AbstractItem | null} right
|
* @param {Item | null} right
|
||||||
* @param {ID | null} rightOrigin
|
* @param {ID | null} rightOrigin
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {string | null} parentSub
|
* @param {string | null} parentSub
|
||||||
|
* @param {AbstractContent} content
|
||||||
*/
|
*/
|
||||||
constructor (id, left, origin, right, rightOrigin, parent, parentSub) {
|
constructor (id, left, origin, right, rightOrigin, parent, parentSub, content) {
|
||||||
super(id)
|
super(id, content.getLength())
|
||||||
/**
|
/**
|
||||||
* The item that was originally to the left of this item.
|
* The item that was originally to the left of this item.
|
||||||
* @type {ID | null}
|
* @type {ID | null}
|
||||||
@ -106,12 +98,12 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
this.origin = origin
|
this.origin = origin
|
||||||
/**
|
/**
|
||||||
* The item that is currently to the left of this item.
|
* The item that is currently to the left of this item.
|
||||||
* @type {AbstractItem | null}
|
* @type {Item | null}
|
||||||
*/
|
*/
|
||||||
this.left = left
|
this.left = left
|
||||||
/**
|
/**
|
||||||
* The item that is currently to the right of this item.
|
* The item that is currently to the right of this item.
|
||||||
* @type {AbstractItem | null}
|
* @type {Item | null}
|
||||||
*/
|
*/
|
||||||
this.right = right
|
this.right = right
|
||||||
/**
|
/**
|
||||||
@ -143,9 +135,12 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
/**
|
/**
|
||||||
* If this type's effect is reundone this type refers to the type that undid
|
* If this type's effect is reundone this type refers to the type that undid
|
||||||
* this operation.
|
* this operation.
|
||||||
* @type {AbstractItem | null}
|
* @type {Item | null}
|
||||||
*/
|
*/
|
||||||
this.redone = null
|
this.redone = null
|
||||||
|
this.content = content
|
||||||
|
this.length = content.getLength()
|
||||||
|
this.countable = content.isCountable()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,7 +154,7 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
const parentSub = this.parentSub
|
const parentSub = this.parentSub
|
||||||
const length = this.length
|
const length = this.length
|
||||||
/**
|
/**
|
||||||
* @type {AbstractItem|null}
|
* @type {Item|null}
|
||||||
*/
|
*/
|
||||||
let o
|
let o
|
||||||
// set o to the first conflicting item
|
// set o to the first conflicting item
|
||||||
@ -175,11 +170,11 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
}
|
}
|
||||||
// TODO: use something like DeleteSet here (a tree implementation would be best)
|
// TODO: use something like DeleteSet here (a tree implementation would be best)
|
||||||
/**
|
/**
|
||||||
* @type {Set<AbstractItem>}
|
* @type {Set<Item>}
|
||||||
*/
|
*/
|
||||||
const conflictingItems = new Set()
|
const conflictingItems = new Set()
|
||||||
/**
|
/**
|
||||||
* @type {Set<AbstractItem>}
|
* @type {Set<Item>}
|
||||||
*/
|
*/
|
||||||
const itemsBeforeOrigin = new Set()
|
const itemsBeforeOrigin = new Set()
|
||||||
// Let c in conflictingItems, b in itemsBeforeOrigin
|
// Let c in conflictingItems, b in itemsBeforeOrigin
|
||||||
@ -238,8 +233,8 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
parent._length += length
|
parent._length += length
|
||||||
}
|
}
|
||||||
addStruct(store, this)
|
addStruct(store, this)
|
||||||
|
this.content.integrate(transaction, this)
|
||||||
maplib.setIfUndefined(transaction.changed, parent, set.create).add(parentSub)
|
maplib.setIfUndefined(transaction.changed, parent, set.create).add(parentSub)
|
||||||
// @ts-ignore
|
|
||||||
if ((parent._item !== null && parent._item.deleted) || (this.right !== null && parentSub !== null)) {
|
if ((parent._item !== null && parent._item.deleted) || (this.right !== null && parentSub !== null)) {
|
||||||
// delete if parent is deleted or if this is not the current attribute value of parent
|
// delete if parent is deleted or if this is not the current attribute value of parent
|
||||||
this.delete(transaction)
|
this.delete(transaction)
|
||||||
@ -270,29 +265,11 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an Item with the same effect as this Item (without position effect)
|
|
||||||
*
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @return {AbstractItem}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
copy (id, left, origin, right, rightOrigin, parent, parentSub) {
|
|
||||||
throw error.methodUnimplemented()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redoes the effect of this operation.
|
* Redoes the effect of this operation.
|
||||||
*
|
*
|
||||||
* @param {Transaction} transaction The Yjs instance.
|
* @param {Transaction} transaction The Yjs instance.
|
||||||
* @param {Set<AbstractItem>} redoitems
|
* @param {Set<Item>} redoitems
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -343,7 +320,7 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
right = right.right
|
right = right.right
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.redone = this.copy(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, this.parentSub)
|
this.redone = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, this.parentSub, this.content.copy())
|
||||||
this.redone.integrate(transaction)
|
this.redone.integrate(transaction)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -354,46 +331,31 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
get lastId () {
|
get lastId () {
|
||||||
return createID(this.id.client, this.id.clock + this.length - 1)
|
return createID(this.id.client, this.id.clock + this.length - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the length of this Item.
|
* Try to merge two items
|
||||||
|
*
|
||||||
|
* @param {Item} right
|
||||||
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
get length () {
|
mergeWith (right) {
|
||||||
return 1
|
if (
|
||||||
}
|
compareIDs(right.origin, this.lastId) &&
|
||||||
|
this.right === right &&
|
||||||
/**
|
compareIDs(this.rightOrigin, right.rightOrigin) &&
|
||||||
* Should return false if this Item is some kind of meta information
|
this.id.client === right.id.client &&
|
||||||
* (e.g. format information).
|
this.id.clock + this.length === right.id.clock &&
|
||||||
*
|
this.deleted === right.deleted &&
|
||||||
* * Whether this Item should be addressable via `yarray.get(i)`
|
this.content.constructor === right.content.constructor &&
|
||||||
* * Whether this Item should be counted when computing yarray.length
|
this.content.mergeWith(right.content)
|
||||||
*/
|
) {
|
||||||
get countable () {
|
this.right = right.right
|
||||||
return true
|
if (this.right !== null) {
|
||||||
}
|
this.right.left = this
|
||||||
|
}
|
||||||
/**
|
this.length += right.length
|
||||||
* Do not call directly. Always split via StructStore!
|
return true
|
||||||
*
|
}
|
||||||
* Splits this Item so that another Item can be inserted in-between.
|
return false
|
||||||
* This must be overwritten if _length > 1
|
|
||||||
* Returns right part after split
|
|
||||||
*
|
|
||||||
* (see {@link ItemJSON}/{@link ItemString} for implementation)
|
|
||||||
*
|
|
||||||
* Does not integrate the struct, nor store it in struct store.
|
|
||||||
*
|
|
||||||
* This method should only be cally by StructStore.
|
|
||||||
*
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {number} diff
|
|
||||||
* @return {AbstractItem}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
splitAt (transaction, diff) {
|
|
||||||
throw error.methodUnimplemented()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -411,16 +373,10 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
this.deleted = true
|
this.deleted = true
|
||||||
addToDeleteSet(transaction.deleteSet, this.id, this.length)
|
addToDeleteSet(transaction.deleteSet, this.id, this.length)
|
||||||
maplib.setIfUndefined(transaction.changed, parent, set.create).add(this.parentSub)
|
maplib.setIfUndefined(transaction.changed, parent, set.create).add(this.parentSub)
|
||||||
|
this.content.delete(transaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {StructStore} store
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
gcChildren (store) { }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @param {boolean} parentGCd
|
* @param {boolean} parentGCd
|
||||||
@ -431,30 +387,12 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
if (!this.deleted) {
|
if (!this.deleted) {
|
||||||
throw error.unexpectedCase()
|
throw error.unexpectedCase()
|
||||||
}
|
}
|
||||||
let r
|
this.content.gc(store)
|
||||||
if (parentGCd) {
|
if (parentGCd) {
|
||||||
r = new GC(this.id, this.length)
|
replaceStruct(store, this, new GC(this.id, this.length))
|
||||||
} else {
|
} else {
|
||||||
r = new ItemDeleted(this.id, this.left, this.origin, this.right, this.rightOrigin, this.parent, this.parentSub, this.length)
|
this.content = new ContentDeleted(this.length)
|
||||||
if (r.right !== null) {
|
|
||||||
r.right.left = r
|
|
||||||
} else if (r.parentSub !== null) {
|
|
||||||
r.parent._map.set(r.parentSub, r)
|
|
||||||
}
|
|
||||||
if (r.left !== null) {
|
|
||||||
r.left.right = r
|
|
||||||
} else if (r.parentSub === null) {
|
|
||||||
r.parent._start = r
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
replaceStruct(store, this, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {Array<any>}
|
|
||||||
*/
|
|
||||||
getContent () {
|
|
||||||
throw error.methodUnimplemented()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -465,15 +403,14 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
*
|
*
|
||||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
* @param {encoding.Encoder} encoder The encoder to write data to.
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
* @param {number} encodingRef
|
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
write (encoder, offset, encodingRef) {
|
write (encoder, offset) {
|
||||||
const origin = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin
|
const origin = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin
|
||||||
const rightOrigin = this.rightOrigin
|
const rightOrigin = this.rightOrigin
|
||||||
const parentSub = this.parentSub
|
const parentSub = this.parentSub
|
||||||
const info = (encodingRef & binary.BITS5) |
|
const info = (this.content.getRef() & binary.BITS5) |
|
||||||
(origin === null ? 0 : binary.BIT8) | // origin is defined
|
(origin === null ? 0 : binary.BIT8) | // origin is defined
|
||||||
(rightOrigin === null ? 0 : binary.BIT7) | // right origin is defined
|
(rightOrigin === null ? 0 : binary.BIT7) | // right origin is defined
|
||||||
(parentSub === null ? 0 : binary.BIT6) // parentSub is non-null
|
(parentSub === null ? 0 : binary.BIT6) // parentSub is non-null
|
||||||
@ -489,26 +426,129 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
if (parent._item === null) {
|
if (parent._item === null) {
|
||||||
// parent type on y._map
|
// parent type on y._map
|
||||||
// find the correct key
|
// find the correct key
|
||||||
// @ts-ignore we know that y exists
|
|
||||||
const ykey = findRootTypeKey(parent)
|
const ykey = findRootTypeKey(parent)
|
||||||
encoding.writeVarUint(encoder, 1) // write parentYKey
|
encoding.writeVarUint(encoder, 1) // write parentYKey
|
||||||
encoding.writeVarString(encoder, ykey)
|
encoding.writeVarString(encoder, ykey)
|
||||||
} else {
|
} else {
|
||||||
encoding.writeVarUint(encoder, 0) // write parent id
|
encoding.writeVarUint(encoder, 0) // write parent id
|
||||||
// @ts-ignore _item is defined because parent is integrated
|
|
||||||
writeID(encoder, parent._item.id)
|
writeID(encoder, parent._item.id)
|
||||||
}
|
}
|
||||||
if (parentSub !== null) {
|
if (parentSub !== null) {
|
||||||
encoding.writeVarString(encoder, parentSub)
|
encoding.writeVarString(encoder, parentSub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.content.write(encoder, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @param {number} info
|
||||||
|
*/
|
||||||
|
const readItemContent = (decoder, info) => contentRefs[info & binary.BITS5](decoder)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lookup map for reading Item content.
|
||||||
|
*
|
||||||
|
* @type {Array<function(decoding.Decoder):AbstractContent>}
|
||||||
|
*/
|
||||||
|
export const contentRefs = [
|
||||||
|
() => { throw error.unexpectedCase() }, // GC is not ItemContent
|
||||||
|
readContentDeleted,
|
||||||
|
readContentJSON,
|
||||||
|
readContentBinary,
|
||||||
|
readContentString,
|
||||||
|
readContentEmbed,
|
||||||
|
readContentFormat,
|
||||||
|
readContentType
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not implement this class!
|
||||||
|
*/
|
||||||
|
export class AbstractContent {
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getLength () {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {Array<any>}
|
||||||
|
*/
|
||||||
|
getContent () {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Should return false if this Item is some kind of meta information
|
||||||
|
* (e.g. format information).
|
||||||
|
*
|
||||||
|
* * Whether this Item should be addressable via `yarray.get(i)`
|
||||||
|
* * Whether this Item should be counted when computing yarray.length
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isCountable () {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {AbstractContent}
|
||||||
|
*/
|
||||||
|
copy () {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {number} offset
|
||||||
|
* @return {AbstractContent}
|
||||||
|
*/
|
||||||
|
splice (offset) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {AbstractContent} right
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
mergeWith (right) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Item} item
|
||||||
|
*/
|
||||||
|
integrate (transaction, item) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
delete (transaction) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {StructStore} store
|
||||||
|
*/
|
||||||
|
gc (store) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {encoding.Encoder} encoder
|
||||||
|
* @param {number} offset
|
||||||
|
*/
|
||||||
|
write (encoder, offset) {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRef () {
|
||||||
|
throw error.methodUnimplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export class AbstractItemRef extends AbstractStructRef {
|
export class ItemRef extends AbstractStructRef {
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {decoding.Decoder} decoder
|
||||||
* @param {ID} id
|
* @param {ID} id
|
||||||
@ -558,85 +598,69 @@ export class AbstractItemRef extends AbstractStructRef {
|
|||||||
if (this.parent !== null) {
|
if (this.parent !== null) {
|
||||||
missing.push(this.parent)
|
missing.push(this.parent)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @type {AbstractContent}
|
||||||
|
*/
|
||||||
|
this.content = readItemContent(decoder, info)
|
||||||
|
this.length = this.content.getLength()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {AbstractItemRef} item
|
|
||||||
* @param {number} offset
|
|
||||||
*
|
|
||||||
* @function
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const changeItemRefOffset = (item, offset) => {
|
|
||||||
item.id = createID(item.id.client, item.id.clock + offset)
|
|
||||||
item.left = createID(item.id.client, item.id.clock - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ItemParams {
|
|
||||||
/**
|
/**
|
||||||
* @param {AbstractItem?} left
|
* @param {Transaction} transaction
|
||||||
* @param {AbstractItem?} right
|
* @param {StructStore} store
|
||||||
* @param {AbstractType<YEvent>?} parent
|
* @param {number} offset
|
||||||
* @param {string|null} parentSub
|
* @return {Item|GC}
|
||||||
*/
|
*/
|
||||||
constructor (left, right, parent, parentSub) {
|
toStruct (transaction, store, offset) {
|
||||||
this.left = left
|
if (offset > 0) {
|
||||||
this.right = right
|
/**
|
||||||
this.parent = parent
|
* @type {ID}
|
||||||
this.parentSub = parentSub
|
*/
|
||||||
}
|
const id = this.id
|
||||||
}
|
this.id = createID(id.client, id.clock + offset)
|
||||||
|
this.left = createID(this.id.client, this.id.clock - 1)
|
||||||
|
this.content = this.content.splice(offset)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
const left = this.left === null ? null : getItemCleanEnd(transaction, store, this.left)
|
||||||
* Outsourcing some of the logic of computing the item params from a received struct.
|
const right = this.right === null ? null : getItemCleanStart(transaction, store, this.right)
|
||||||
* If parent === null, it is expected to gc the read struct. Otherwise apply it.
|
let parent = null
|
||||||
*
|
let parentSub = this.parentSub
|
||||||
* @param {Transaction} transaction
|
if (this.parent !== null) {
|
||||||
* @param {StructStore} store
|
const parentItem = getItem(store, this.parent)
|
||||||
* @param {ID|null} leftid
|
// Edge case: toStruct is called with an offset > 0. In this case left is defined.
|
||||||
* @param {ID|null} rightid
|
// Depending in which order structs arrive, left may be GC'd and the parent not
|
||||||
* @param {ID|null} parentid
|
// deleted. This is why we check if left is GC'd. Strictly we don't have
|
||||||
* @param {string|null} parentSub
|
// to check if right is GC'd, but we will in case we run into future issues
|
||||||
* @param {string|null} parentYKey
|
if (!parentItem.deleted && (left === null || left.constructor !== GC) && (right === null || right.constructor !== GC)) {
|
||||||
* @return {ItemParams}
|
parent = /** @type {ContentType} */ (parentItem.content).type
|
||||||
*
|
}
|
||||||
* @private
|
} else if (this.parentYKey !== null) {
|
||||||
* @function
|
parent = transaction.doc.get(this.parentYKey)
|
||||||
*/
|
} else if (left !== null) {
|
||||||
export const computeItemParams = (transaction, store, leftid, rightid, parentid, parentSub, parentYKey) => {
|
if (left.constructor !== GC) {
|
||||||
const left = leftid === null ? null : getItemCleanEnd(transaction, store, leftid)
|
parent = left.parent
|
||||||
const right = rightid === null ? null : getItemCleanStart(transaction, store, rightid)
|
parentSub = left.parentSub
|
||||||
let parent = null
|
}
|
||||||
if (parentid !== null) {
|
} else if (right !== null) {
|
||||||
const parentItem = getItemType(store, parentid)
|
if (right.constructor !== GC) {
|
||||||
switch (parentItem.constructor) {
|
parent = right.parent
|
||||||
case ItemDeleted:
|
parentSub = right.parentSub
|
||||||
case GC:
|
}
|
||||||
break
|
} else {
|
||||||
default:
|
throw error.unexpectedCase()
|
||||||
// Edge case: toStruct is called with an offset > 0. In this case left is defined.
|
|
||||||
// Depending in which order structs arrive, left may be GC'd and the parent not
|
|
||||||
// deleted. This is why we check if left is GC'd. Strictly we probably don't have
|
|
||||||
// to check if right is GC'd, but we will in case we run into future issues
|
|
||||||
if (!parentItem.deleted && (left === null || left.constructor !== GC) && (right === null || right.constructor !== GC)) {
|
|
||||||
parent = parentItem.type
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (parentYKey !== null) {
|
|
||||||
parent = transaction.doc.get(parentYKey)
|
return parent === null
|
||||||
} else if (left !== null) {
|
? new GC(this.id, this.length)
|
||||||
if (left.constructor !== GC) {
|
: new Item(
|
||||||
parent = left.parent
|
this.id,
|
||||||
parentSub = left.parentSub
|
left,
|
||||||
}
|
this.left,
|
||||||
} else if (right !== null) {
|
right,
|
||||||
if (right.constructor !== GC) {
|
this.right,
|
||||||
parent = right.parent
|
parent,
|
||||||
parentSub = right.parentSub
|
parentSub,
|
||||||
}
|
this.content
|
||||||
} else {
|
)
|
||||||
throw error.unexpectedCase()
|
|
||||||
}
|
}
|
||||||
return new ItemParams(left, right, parent, parentSub)
|
|
||||||
}
|
}
|
@ -1,99 +0,0 @@
|
|||||||
|
|
||||||
import {
|
|
||||||
AbstractItem,
|
|
||||||
AbstractItemRef,
|
|
||||||
computeItemParams,
|
|
||||||
GC,
|
|
||||||
StructStore, Transaction, AbstractType, ID // eslint-disable-line
|
|
||||||
} from '../internals.js'
|
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
import * as buffer from 'lib0/buffer.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const structBinaryRefNumber = 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemBinary extends AbstractItem {
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @param {Uint8Array} content
|
|
||||||
*/
|
|
||||||
constructor (id, left, origin, right, rightOrigin, parent, parentSub, content) {
|
|
||||||
super(id, left, origin, right, rightOrigin, parent, parentSub)
|
|
||||||
this.content = content
|
|
||||||
}
|
|
||||||
getContent () {
|
|
||||||
return [this.content]
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
*/
|
|
||||||
copy (id, left, origin, right, rightOrigin, parent, parentSub) {
|
|
||||||
return new ItemBinary(id, left, origin, right, rightOrigin, parent, parentSub, this.content)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {encoding.Encoder} encoder
|
|
||||||
* @param {number} offset
|
|
||||||
*/
|
|
||||||
write (encoder, offset) {
|
|
||||||
super.write(encoder, offset, structBinaryRefNumber)
|
|
||||||
encoding.writeVarUint8Array(encoder, this.content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemBinaryRef extends AbstractItemRef {
|
|
||||||
/**
|
|
||||||
* @param {decoding.Decoder} decoder
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {number} info
|
|
||||||
*/
|
|
||||||
constructor (decoder, id, info) {
|
|
||||||
super(decoder, id, info)
|
|
||||||
/**
|
|
||||||
* @type {Uint8Array}
|
|
||||||
*/
|
|
||||||
this.content = buffer.copyUint8Array(decoding.readVarUint8Array(decoder))
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {number} offset
|
|
||||||
* @return {ItemBinary|GC}
|
|
||||||
*/
|
|
||||||
toStruct (transaction, store, offset) {
|
|
||||||
const { left, right, parent, parentSub } = computeItemParams(transaction, store, this.left, this.right, this.parent, this.parentSub, this.parentYKey)
|
|
||||||
return parent === null
|
|
||||||
? new GC(this.id, this.length)
|
|
||||||
: new ItemBinary(
|
|
||||||
this.id,
|
|
||||||
left,
|
|
||||||
this.left,
|
|
||||||
right,
|
|
||||||
this.right,
|
|
||||||
parent,
|
|
||||||
parentSub,
|
|
||||||
this.content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
|
|
||||||
import {
|
|
||||||
AbstractItem,
|
|
||||||
AbstractItemRef,
|
|
||||||
computeItemParams,
|
|
||||||
changeItemRefOffset,
|
|
||||||
GC,
|
|
||||||
splitItem,
|
|
||||||
addToDeleteSet,
|
|
||||||
mergeItemWith,
|
|
||||||
StructStore, Transaction, ID, AbstractType // eslint-disable-line
|
|
||||||
} from '../internals.js'
|
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const structDeletedRefNumber = 2
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemDeleted extends AbstractItem {
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @param {number} length
|
|
||||||
*/
|
|
||||||
constructor (id, left, origin, right, rightOrigin, parent, parentSub, length) {
|
|
||||||
super(id, left, origin, right, rightOrigin, parent, parentSub)
|
|
||||||
this._len = length
|
|
||||||
this.deleted = true
|
|
||||||
}
|
|
||||||
get length () {
|
|
||||||
return this._len
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
*/
|
|
||||||
copy (id, left, origin, right, rightOrigin, parent, parentSub) {
|
|
||||||
return new ItemDeleted(id, left, origin, right, rightOrigin, parent, parentSub, this.length)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
*/
|
|
||||||
integrate (transaction) {
|
|
||||||
super.integrate(transaction)
|
|
||||||
addToDeleteSet(transaction.deleteSet, this.id, this.length)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {number} diff
|
|
||||||
*/
|
|
||||||
splitAt (transaction, diff) {
|
|
||||||
/**
|
|
||||||
* @type {ItemDeleted}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const right = splitItem(transaction, this, diff)
|
|
||||||
right._len -= diff
|
|
||||||
this._len = diff
|
|
||||||
return right
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ItemDeleted} right
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
mergeWith (right) {
|
|
||||||
if (mergeItemWith(this, right)) {
|
|
||||||
this._len += right._len
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {boolean} parentGCd
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
gc (store, parentGCd) {
|
|
||||||
if (parentGCd) {
|
|
||||||
super.gc(store, parentGCd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {encoding.Encoder} encoder
|
|
||||||
* @param {number} offset
|
|
||||||
*/
|
|
||||||
write (encoder, offset) {
|
|
||||||
super.write(encoder, offset, structDeletedRefNumber)
|
|
||||||
encoding.writeVarUint(encoder, this.length - offset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemDeletedRef extends AbstractItemRef {
|
|
||||||
/**
|
|
||||||
* @param {decoding.Decoder} decoder
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {number} info
|
|
||||||
*/
|
|
||||||
constructor (decoder, id, info) {
|
|
||||||
super(decoder, id, info)
|
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.len = decoding.readVarUint(decoder)
|
|
||||||
}
|
|
||||||
get length () {
|
|
||||||
return this.len
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {number} offset
|
|
||||||
* @return {ItemDeleted|GC}
|
|
||||||
*/
|
|
||||||
toStruct (transaction, store, offset) {
|
|
||||||
if (offset > 0) {
|
|
||||||
changeItemRefOffset(this, offset)
|
|
||||||
this.len = this.len - offset
|
|
||||||
}
|
|
||||||
|
|
||||||
const { left, right, parent, parentSub } = computeItemParams(transaction, store, this.left, this.right, this.parent, this.parentSub, this.parentYKey)
|
|
||||||
return parent === null
|
|
||||||
? new GC(this.id, this.length)
|
|
||||||
: new ItemDeleted(
|
|
||||||
this.id,
|
|
||||||
left,
|
|
||||||
this.left,
|
|
||||||
right,
|
|
||||||
this.right,
|
|
||||||
parent,
|
|
||||||
parentSub,
|
|
||||||
this.len
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
|
|
||||||
import {
|
|
||||||
AbstractItem,
|
|
||||||
AbstractItemRef,
|
|
||||||
computeItemParams,
|
|
||||||
GC,
|
|
||||||
Transaction, StructStore, ID, AbstractType // eslint-disable-line
|
|
||||||
} from '../internals.js'
|
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const structEmbedRefNumber = 3
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemEmbed extends AbstractItem {
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @param {Object} embed
|
|
||||||
*/
|
|
||||||
constructor (id, left, origin, right, rightOrigin, parent, parentSub, embed) {
|
|
||||||
super(id, left, origin, right, rightOrigin, parent, parentSub)
|
|
||||||
this.embed = embed
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
*/
|
|
||||||
copy (id, left, origin, right, rightOrigin, parent, parentSub) {
|
|
||||||
return new ItemEmbed(id, left, origin, right, rightOrigin, parent, parentSub, this.embed)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {encoding.Encoder} encoder
|
|
||||||
* @param {number} offset
|
|
||||||
*/
|
|
||||||
write (encoder, offset) {
|
|
||||||
super.write(encoder, offset, structEmbedRefNumber)
|
|
||||||
encoding.writeVarString(encoder, JSON.stringify(this.embed))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemEmbedRef extends AbstractItemRef {
|
|
||||||
/**
|
|
||||||
* @param {decoding.Decoder} decoder
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {number} info
|
|
||||||
*/
|
|
||||||
constructor (decoder, id, info) {
|
|
||||||
super(decoder, id, info)
|
|
||||||
/**
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
this.embed = JSON.parse(decoding.readVarString(decoder))
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {number} offset
|
|
||||||
* @return {ItemEmbed|GC}
|
|
||||||
*/
|
|
||||||
toStruct (transaction, store, offset) {
|
|
||||||
const { left, right, parent, parentSub } = computeItemParams(transaction, store, this.left, this.right, this.parent, this.parentSub, this.parentYKey)
|
|
||||||
return parent === null
|
|
||||||
? new GC(this.id, this.length)
|
|
||||||
: new ItemEmbed(
|
|
||||||
this.id,
|
|
||||||
left,
|
|
||||||
this.left,
|
|
||||||
right,
|
|
||||||
this.right,
|
|
||||||
parent,
|
|
||||||
parentSub,
|
|
||||||
this.embed
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
|
|
||||||
import {
|
|
||||||
AbstractItem,
|
|
||||||
AbstractItemRef,
|
|
||||||
computeItemParams,
|
|
||||||
GC,
|
|
||||||
Transaction, StructStore, ID, AbstractType // eslint-disable-line
|
|
||||||
} from '../internals.js'
|
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const structFormatRefNumber = 4
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemFormat extends AbstractItem {
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @param {string} key
|
|
||||||
* @param {any} value
|
|
||||||
*/
|
|
||||||
constructor (id, left, origin, right, rightOrigin, parent, parentSub, key, value) {
|
|
||||||
super(id, left, origin, right, rightOrigin, parent, parentSub)
|
|
||||||
this.key = key
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
*/
|
|
||||||
copy (id, left, origin, right, rightOrigin, parent, parentSub) {
|
|
||||||
return new ItemFormat(id, left, origin, right, rightOrigin, parent, parentSub, this.key, this.value)
|
|
||||||
}
|
|
||||||
get countable () {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {encoding.Encoder} encoder
|
|
||||||
* @param {number} offset
|
|
||||||
*/
|
|
||||||
write (encoder, offset) {
|
|
||||||
super.write(encoder, offset, structFormatRefNumber)
|
|
||||||
encoding.writeVarString(encoder, this.key)
|
|
||||||
encoding.writeVarString(encoder, JSON.stringify(this.value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemFormatRef extends AbstractItemRef {
|
|
||||||
/**
|
|
||||||
* @param {decoding.Decoder} decoder
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {number} info
|
|
||||||
*/
|
|
||||||
constructor (decoder, id, info) {
|
|
||||||
super(decoder, id, info)
|
|
||||||
/**
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.key = decoding.readVarString(decoder)
|
|
||||||
this.value = JSON.parse(decoding.readVarString(decoder))
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {number} offset
|
|
||||||
* @return {ItemFormat|GC}
|
|
||||||
*/
|
|
||||||
toStruct (transaction, store, offset) {
|
|
||||||
const { left, right, parent, parentSub } = computeItemParams(transaction, store, this.left, this.right, this.parent, this.parentSub, this.parentYKey)
|
|
||||||
return parent === null
|
|
||||||
? new GC(this.id, this.length)
|
|
||||||
: new ItemFormat(
|
|
||||||
this.id,
|
|
||||||
left,
|
|
||||||
this.left,
|
|
||||||
right,
|
|
||||||
this.right,
|
|
||||||
parent,
|
|
||||||
parentSub,
|
|
||||||
this.key,
|
|
||||||
this.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,153 +0,0 @@
|
|||||||
|
|
||||||
import {
|
|
||||||
AbstractItem,
|
|
||||||
AbstractItemRef,
|
|
||||||
computeItemParams,
|
|
||||||
splitItem,
|
|
||||||
changeItemRefOffset,
|
|
||||||
GC,
|
|
||||||
mergeItemWith,
|
|
||||||
Transaction, StructStore, ID, AbstractType // eslint-disable-line
|
|
||||||
} from '../internals.js'
|
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const structJSONRefNumber = 5
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemJSON extends AbstractItem {
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @param {Array<any>} content
|
|
||||||
*/
|
|
||||||
constructor (id, left, origin, right, rightOrigin, parent, parentSub, content) {
|
|
||||||
super(id, left, origin, right, rightOrigin, parent, parentSub)
|
|
||||||
/**
|
|
||||||
* @type {Array<any>}
|
|
||||||
*/
|
|
||||||
this.content = content
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
*/
|
|
||||||
copy (id, left, origin, right, rightOrigin, parent, parentSub) {
|
|
||||||
return new ItemJSON(id, left, origin, right, rightOrigin, parent, parentSub, this.content)
|
|
||||||
}
|
|
||||||
get length () {
|
|
||||||
return this.content.length
|
|
||||||
}
|
|
||||||
getContent () {
|
|
||||||
return this.content
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {number} diff
|
|
||||||
*/
|
|
||||||
splitAt (transaction, diff) {
|
|
||||||
/**
|
|
||||||
* @type {ItemJSON}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const right = splitItem(transaction, this, diff)
|
|
||||||
right.content = this.content.splice(diff)
|
|
||||||
return right
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ItemJSON} right
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
mergeWith (right) {
|
|
||||||
if (mergeItemWith(this, right)) {
|
|
||||||
this.content = this.content.concat(right.content)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {encoding.Encoder} encoder
|
|
||||||
* @param {number} offset
|
|
||||||
*/
|
|
||||||
write (encoder, offset) {
|
|
||||||
super.write(encoder, offset, structJSONRefNumber)
|
|
||||||
const len = this.content.length
|
|
||||||
encoding.writeVarUint(encoder, len - offset)
|
|
||||||
for (let i = offset; i < len; i++) {
|
|
||||||
const c = this.content[i]
|
|
||||||
encoding.writeVarString(encoder, c === undefined ? 'undefined' : JSON.stringify(c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemJSONRef extends AbstractItemRef {
|
|
||||||
/**
|
|
||||||
* @param {decoding.Decoder} decoder
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {number} info
|
|
||||||
*/
|
|
||||||
constructor (decoder, id, info) {
|
|
||||||
super(decoder, id, info)
|
|
||||||
const len = decoding.readVarUint(decoder)
|
|
||||||
const cs = []
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
const c = decoding.readVarString(decoder)
|
|
||||||
if (c === 'undefined') {
|
|
||||||
cs.push(undefined)
|
|
||||||
} else {
|
|
||||||
cs.push(JSON.parse(c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @type {Array<any>}
|
|
||||||
*/
|
|
||||||
this.content = cs
|
|
||||||
}
|
|
||||||
get length () {
|
|
||||||
return this.content.length
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {number} offset
|
|
||||||
* @return {ItemJSON|GC}
|
|
||||||
*/
|
|
||||||
toStruct (transaction, store, offset) {
|
|
||||||
if (offset > 0) {
|
|
||||||
changeItemRefOffset(this, offset)
|
|
||||||
this.content = this.content.slice(offset)
|
|
||||||
}
|
|
||||||
const { left, right, parent, parentSub } = computeItemParams(transaction, store, this.left, this.right, this.parent, this.parentSub, this.parentYKey)
|
|
||||||
return parent === null
|
|
||||||
? new GC(this.id, this.length)
|
|
||||||
: new ItemJSON(
|
|
||||||
this.id,
|
|
||||||
left,
|
|
||||||
this.left,
|
|
||||||
right,
|
|
||||||
this.right,
|
|
||||||
parent,
|
|
||||||
parentSub,
|
|
||||||
this.content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
|
|
||||||
import {
|
|
||||||
AbstractItem,
|
|
||||||
AbstractItemRef,
|
|
||||||
computeItemParams,
|
|
||||||
splitItem,
|
|
||||||
changeItemRefOffset,
|
|
||||||
GC,
|
|
||||||
mergeItemWith,
|
|
||||||
Transaction, StructStore, ID, AbstractType // eslint-disable-line
|
|
||||||
} from '../internals.js'
|
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
export const structStringRefNumber = 6
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemString extends AbstractItem {
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @param {string} string
|
|
||||||
*/
|
|
||||||
constructor (id, left, origin, right, rightOrigin, parent, parentSub, string) {
|
|
||||||
super(id, left, origin, right, rightOrigin, parent, parentSub)
|
|
||||||
/**
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.string = string
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
*/
|
|
||||||
copy (id, left, origin, right, rightOrigin, parent, parentSub) {
|
|
||||||
return new ItemString(id, left, origin, right, rightOrigin, parent, parentSub, this.string)
|
|
||||||
}
|
|
||||||
getContent () {
|
|
||||||
return this.string.split('')
|
|
||||||
}
|
|
||||||
get length () {
|
|
||||||
return this.string.length
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {number} diff
|
|
||||||
* @return {ItemString}
|
|
||||||
*/
|
|
||||||
splitAt (transaction, diff) {
|
|
||||||
/**
|
|
||||||
* @type {ItemString}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const right = splitItem(transaction, this, diff)
|
|
||||||
right.string = this.string.slice(diff)
|
|
||||||
this.string = this.string.slice(0, diff)
|
|
||||||
return right
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ItemString} right
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
mergeWith (right) {
|
|
||||||
if (mergeItemWith(this, right)) {
|
|
||||||
this.string += right.string
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {encoding.Encoder} encoder
|
|
||||||
* @param {number} offset
|
|
||||||
*/
|
|
||||||
write (encoder, offset) {
|
|
||||||
super.write(encoder, offset, structStringRefNumber)
|
|
||||||
encoding.writeVarString(encoder, offset === 0 ? this.string : this.string.slice(offset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemStringRef extends AbstractItemRef {
|
|
||||||
/**
|
|
||||||
* @param {decoding.Decoder} decoder
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {number} info
|
|
||||||
*/
|
|
||||||
constructor (decoder, id, info) {
|
|
||||||
super(decoder, id, info)
|
|
||||||
/**
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.string = decoding.readVarString(decoder)
|
|
||||||
}
|
|
||||||
get length () {
|
|
||||||
return this.string.length
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {number} offset
|
|
||||||
* @return {ItemString|GC}
|
|
||||||
*/
|
|
||||||
toStruct (transaction, store, offset) {
|
|
||||||
if (offset > 0) {
|
|
||||||
changeItemRefOffset(this, offset)
|
|
||||||
this.string = this.string.slice(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { left, right, parent, parentSub } = computeItemParams(transaction, store, this.left, this.right, this.parent, this.parentSub, this.parentYKey)
|
|
||||||
return parent === null
|
|
||||||
? new GC(this.id, this.length)
|
|
||||||
: new ItemString(
|
|
||||||
this.id,
|
|
||||||
left,
|
|
||||||
this.left,
|
|
||||||
right,
|
|
||||||
this.right,
|
|
||||||
parent,
|
|
||||||
parentSub,
|
|
||||||
this.string
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,199 +0,0 @@
|
|||||||
|
|
||||||
import {
|
|
||||||
AbstractItem,
|
|
||||||
AbstractItemRef,
|
|
||||||
computeItemParams,
|
|
||||||
readYArray,
|
|
||||||
readYMap,
|
|
||||||
readYText,
|
|
||||||
readYXmlElement,
|
|
||||||
readYXmlFragment,
|
|
||||||
readYXmlHook,
|
|
||||||
readYXmlText,
|
|
||||||
StructStore, GC, Transaction, ID, AbstractType // eslint-disable-line
|
|
||||||
} from '../internals.js'
|
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js' // eslint-disable-line
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const structTypeRefNumber = 7
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Array<function(decoding.Decoder):AbstractType<any>>}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const typeRefs = [
|
|
||||||
readYArray,
|
|
||||||
readYMap,
|
|
||||||
readYText,
|
|
||||||
readYXmlElement,
|
|
||||||
readYXmlFragment,
|
|
||||||
readYXmlHook,
|
|
||||||
readYXmlText
|
|
||||||
]
|
|
||||||
|
|
||||||
export const YArrayRefID = 0
|
|
||||||
export const YMapRefID = 1
|
|
||||||
export const YTextRefID = 2
|
|
||||||
export const YXmlElementRefID = 3
|
|
||||||
export const YXmlFragmentRefID = 4
|
|
||||||
export const YXmlHookRefID = 5
|
|
||||||
export const YXmlTextRefID = 6
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemType extends AbstractItem {
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @param {AbstractType<any>} type
|
|
||||||
*/
|
|
||||||
constructor (id, left, origin, right, rightOrigin, parent, parentSub, type) {
|
|
||||||
super(id, left, origin, right, rightOrigin, parent, parentSub)
|
|
||||||
this.type = type
|
|
||||||
}
|
|
||||||
|
|
||||||
getContent () {
|
|
||||||
return [this.type]
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {AbstractItem | null} left
|
|
||||||
* @param {ID | null} origin
|
|
||||||
* @param {AbstractItem | null} right
|
|
||||||
* @param {ID | null} rightOrigin
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string | null} parentSub
|
|
||||||
* @return {ItemType}
|
|
||||||
*/
|
|
||||||
copy (id, left, origin, right, rightOrigin, parent, parentSub) {
|
|
||||||
return new ItemType(id, left, origin, right, rightOrigin, parent, parentSub, this.type._copy())
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
*/
|
|
||||||
integrate (transaction) {
|
|
||||||
super.integrate(transaction)
|
|
||||||
this.type._integrate(transaction.doc, this)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {encoding.Encoder} encoder
|
|
||||||
* @param {number} offset
|
|
||||||
*/
|
|
||||||
write (encoder, offset) {
|
|
||||||
super.write(encoder, offset, structTypeRefNumber)
|
|
||||||
this.type._write(encoder)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Mark this Item as deleted.
|
|
||||||
*
|
|
||||||
* @param {Transaction} transaction The Yjs instance
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
delete (transaction) {
|
|
||||||
if (!this.deleted) {
|
|
||||||
super.delete(transaction)
|
|
||||||
let item = this.type._start
|
|
||||||
while (item !== null) {
|
|
||||||
if (!item.deleted) {
|
|
||||||
item.delete(transaction)
|
|
||||||
} else {
|
|
||||||
// Whis will be gc'd later and we want to merge it if possible
|
|
||||||
// We try to merge all deleted items after each transaction,
|
|
||||||
// but we have no knowledge about that this needs to be merged
|
|
||||||
// since it is not in transaction.ds. Hence we add it to transaction._mergeStructs
|
|
||||||
transaction._mergeStructs.add(item.id)
|
|
||||||
}
|
|
||||||
item = item.right
|
|
||||||
}
|
|
||||||
this.type._map.forEach(item => {
|
|
||||||
if (!item.deleted) {
|
|
||||||
item.delete(transaction)
|
|
||||||
} else {
|
|
||||||
// same as above
|
|
||||||
transaction._mergeStructs.add(item.id)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
transaction.changed.delete(this.type)
|
|
||||||
transaction.changedParentTypes.delete(this.type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {StructStore} store
|
|
||||||
*/
|
|
||||||
gcChildren (store) {
|
|
||||||
let item = this.type._start
|
|
||||||
while (item !== null) {
|
|
||||||
item.gc(store, true)
|
|
||||||
item = item.right
|
|
||||||
}
|
|
||||||
this.type._start = null
|
|
||||||
this.type._map.forEach(item => {
|
|
||||||
while (item !== null) {
|
|
||||||
item.gc(store, true)
|
|
||||||
// @ts-ignore
|
|
||||||
item = item.left
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this._map = new Map()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {boolean} parentGCd
|
|
||||||
*/
|
|
||||||
gc (store, parentGCd) {
|
|
||||||
this.gcChildren(store)
|
|
||||||
super.gc(store, parentGCd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export class ItemTypeRef extends AbstractItemRef {
|
|
||||||
/**
|
|
||||||
* @param {decoding.Decoder} decoder
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {number} info
|
|
||||||
*/
|
|
||||||
constructor (decoder, id, info) {
|
|
||||||
super(decoder, id, info)
|
|
||||||
const typeRef = decoding.readVarUint(decoder)
|
|
||||||
/**
|
|
||||||
* @type {AbstractType<any>}
|
|
||||||
*/
|
|
||||||
this.type = typeRefs[typeRef](decoder)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {number} offset
|
|
||||||
* @return {ItemType|GC}
|
|
||||||
*/
|
|
||||||
toStruct (transaction, store, offset) {
|
|
||||||
const { left, right, parent, parentSub } = computeItemParams(transaction, store, this.left, this.right, this.parent, this.parentSub, this.parentYKey)
|
|
||||||
return parent === null
|
|
||||||
? new GC(this.id, this.length)
|
|
||||||
: new ItemType(
|
|
||||||
this.id,
|
|
||||||
left,
|
|
||||||
this.left,
|
|
||||||
right,
|
|
||||||
this.right,
|
|
||||||
parent,
|
|
||||||
parentSub,
|
|
||||||
this.type
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,14 +4,14 @@ import {
|
|||||||
callEventHandlerListeners,
|
callEventHandlerListeners,
|
||||||
addEventHandlerListener,
|
addEventHandlerListener,
|
||||||
createEventHandler,
|
createEventHandler,
|
||||||
ItemType,
|
|
||||||
nextID,
|
nextID,
|
||||||
isVisible,
|
isVisible,
|
||||||
ItemJSON,
|
ContentType,
|
||||||
ItemBinary,
|
ContentJSON,
|
||||||
|
ContentBinary,
|
||||||
createID,
|
createID,
|
||||||
getItemCleanStart,
|
getItemCleanStart,
|
||||||
Doc, Snapshot, Transaction, EventHandler, YEvent, AbstractItem, // eslint-disable-line
|
Doc, Snapshot, Transaction, EventHandler, YEvent, Item, // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as map from 'lib0/map.js'
|
import * as map from 'lib0/map.js'
|
||||||
@ -49,17 +49,17 @@ export const callTypeObservers = (type, transaction, event) => {
|
|||||||
export class AbstractType {
|
export class AbstractType {
|
||||||
constructor () {
|
constructor () {
|
||||||
/**
|
/**
|
||||||
* @type {ItemType|null}
|
* @type {Item|null}
|
||||||
*/
|
*/
|
||||||
this._item = null
|
this._item = null
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {Map<string,AbstractItem>}
|
* @type {Map<string,Item>}
|
||||||
*/
|
*/
|
||||||
this._map = new Map()
|
this._map = new Map()
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {AbstractItem|null}
|
* @type {Item|null}
|
||||||
*/
|
*/
|
||||||
this._start = null
|
this._start = null
|
||||||
/**
|
/**
|
||||||
@ -88,7 +88,7 @@ export class AbstractType {
|
|||||||
* * Observer functions are fired
|
* * Observer functions are fired
|
||||||
*
|
*
|
||||||
* @param {Doc} y The Yjs instance
|
* @param {Doc} y The Yjs instance
|
||||||
* @param {ItemType|null} item
|
* @param {Item|null} item
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_integrate (y, item) {
|
_integrate (y, item) {
|
||||||
@ -187,7 +187,7 @@ export const typeListToArray = type => {
|
|||||||
let n = type._start
|
let n = type._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (n.countable && !n.deleted) {
|
if (n.countable && !n.deleted) {
|
||||||
const c = n.getContent()
|
const c = n.content.getContent()
|
||||||
for (let i = 0; i < c.length; i++) {
|
for (let i = 0; i < c.length; i++) {
|
||||||
cs.push(c[i])
|
cs.push(c[i])
|
||||||
}
|
}
|
||||||
@ -210,7 +210,7 @@ export const typeListToArraySnapshot = (type, snapshot) => {
|
|||||||
let n = type._start
|
let n = type._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (n.countable && isVisible(n, snapshot)) {
|
if (n.countable && isVisible(n, snapshot)) {
|
||||||
const c = n.getContent()
|
const c = n.content.getContent()
|
||||||
for (let i = 0; i < c.length; i++) {
|
for (let i = 0; i < c.length; i++) {
|
||||||
cs.push(c[i])
|
cs.push(c[i])
|
||||||
}
|
}
|
||||||
@ -234,7 +234,7 @@ export const typeListForEach = (type, f) => {
|
|||||||
let n = type._start
|
let n = type._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (n.countable && !n.deleted) {
|
if (n.countable && !n.deleted) {
|
||||||
const c = n.getContent()
|
const c = n.content.getContent()
|
||||||
for (let i = 0; i < c.length; i++) {
|
for (let i = 0; i < c.length; i++) {
|
||||||
f(c[i], index++, type)
|
f(c[i], index++, type)
|
||||||
}
|
}
|
||||||
@ -295,7 +295,7 @@ export const typeListCreateIterator = type => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// we found n, so we can set currentContent
|
// we found n, so we can set currentContent
|
||||||
currentContent = n.getContent()
|
currentContent = n.content.getContent()
|
||||||
currentContentIndex = 0
|
currentContentIndex = 0
|
||||||
n = n.right // we used the content of n, now iterate to next
|
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
|
let n = type._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (n.countable && isVisible(n, snapshot)) {
|
if (n.countable && isVisible(n, snapshot)) {
|
||||||
const c = n.getContent()
|
const c = n.content.getContent()
|
||||||
for (let i = 0; i < c.length; i++) {
|
for (let i = 0; i < c.length; i++) {
|
||||||
f(c[i], index++, type)
|
f(c[i], index++, type)
|
||||||
}
|
}
|
||||||
@ -349,7 +349,7 @@ export const typeListGet = (type, index) => {
|
|||||||
for (let n = type._start; n !== null; n = n.right) {
|
for (let n = type._start; n !== null; n = n.right) {
|
||||||
if (!n.deleted && n.countable) {
|
if (!n.deleted && n.countable) {
|
||||||
if (index < n.length) {
|
if (index < n.length) {
|
||||||
return n.getContent()[index]
|
return n.content.getContent()[index]
|
||||||
}
|
}
|
||||||
index -= n.length
|
index -= n.length
|
||||||
}
|
}
|
||||||
@ -359,7 +359,7 @@ export const typeListGet = (type, index) => {
|
|||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {AbstractItem?} referenceItem
|
* @param {Item?} referenceItem
|
||||||
* @param {Array<Object<string,any>|Array<any>|boolean|number|string|Uint8Array>} content
|
* @param {Array<Object<string,any>|Array<any>|boolean|number|string|Uint8Array>} content
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
@ -374,7 +374,7 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem,
|
|||||||
let jsonContent = []
|
let jsonContent = []
|
||||||
const packJsonContent = () => {
|
const packJsonContent = () => {
|
||||||
if (jsonContent.length > 0) {
|
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)
|
left.integrate(transaction)
|
||||||
jsonContent = []
|
jsonContent = []
|
||||||
}
|
}
|
||||||
@ -393,12 +393,12 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem,
|
|||||||
switch (c.constructor) {
|
switch (c.constructor) {
|
||||||
case Uint8Array:
|
case Uint8Array:
|
||||||
case ArrayBuffer:
|
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)
|
left.integrate(transaction)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
if (c instanceof AbstractType) {
|
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)
|
left.integrate(transaction)
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unexpected content type in insert operation')
|
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) => {
|
export const typeMapSet = (transaction, parent, key, value) => {
|
||||||
const left = parent._map.get(key) || null
|
const left = parent._map.get(key) || null
|
||||||
|
let content
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
new ItemJSON(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, [value]).integrate(transaction)
|
content = new ContentJSON([value])
|
||||||
return
|
} else {
|
||||||
}
|
switch (value.constructor) {
|
||||||
switch (value.constructor) {
|
case Number:
|
||||||
case Number:
|
case Object:
|
||||||
case Object:
|
case Boolean:
|
||||||
case Boolean:
|
case Array:
|
||||||
case Array:
|
case String:
|
||||||
case String:
|
content = new ContentJSON([value])
|
||||||
new ItemJSON(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, [value]).integrate(transaction)
|
break
|
||||||
break
|
case Uint8Array:
|
||||||
case Uint8Array:
|
content = new ContentBinary(value)
|
||||||
new ItemBinary(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, value).integrate(transaction)
|
break
|
||||||
break
|
default:
|
||||||
default:
|
if (value instanceof AbstractType) {
|
||||||
if (value instanceof AbstractType) {
|
content = new ContentType(value)
|
||||||
new ItemType(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, value).integrate(transaction)
|
} else {
|
||||||
} else {
|
throw new Error('Unexpected content type')
|
||||||
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) => {
|
export const typeMapGet = (parent, key) => {
|
||||||
const val = parent._map.get(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 = {}
|
let res = {}
|
||||||
for (const [key, value] of parent._map) {
|
for (const [key, value] of parent._map) {
|
||||||
if (!value.deleted) {
|
if (!value.deleted) {
|
||||||
res[key] = value.getContent()[value.length - 1]
|
res[key] = value.content.getContent()[value.length - 1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
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))) {
|
while (v !== null && (!snapshot.sm.has(v.id.client) || v.id.clock >= (snapshot.sm.get(v.id.client) || 0))) {
|
||||||
v = v.left
|
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>>}
|
* @return {IterableIterator<Array<any>>}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
YArrayRefID,
|
YArrayRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
Doc, Transaction, ItemType, // eslint-disable-line
|
Doc, Transaction, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||||
@ -59,14 +59,13 @@ export class YArray extends AbstractType {
|
|||||||
* * Observer functions are fired
|
* * Observer functions are fired
|
||||||
*
|
*
|
||||||
* @param {Doc} y The Yjs instance
|
* @param {Doc} y The Yjs instance
|
||||||
* @param {ItemType} item
|
* @param {Item} item
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_integrate (y, item) {
|
_integrate (y, item) {
|
||||||
super._integrate(y, item)
|
super._integrate(y, item)
|
||||||
// @ts-ignore
|
this.insert(0, /** @type {Array} */ (this._prelimContent))
|
||||||
this.insert(0, this._prelimContent)
|
|
||||||
this._prelimContent = null
|
this._prelimContent = null
|
||||||
}
|
}
|
||||||
get length () {
|
get length () {
|
||||||
@ -106,8 +105,7 @@ export class YArray extends AbstractType {
|
|||||||
typeListInsertGenerics(transaction, this, index, content)
|
typeListInsertGenerics(transaction, this, index, content)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore _prelimContent is defined because this is not yet integrated
|
/** @type {Array} */ (this._prelimContent).splice(index, 0, ...content)
|
||||||
this._prelimContent.splice(index, 0, ...content)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,8 +130,7 @@ export class YArray extends AbstractType {
|
|||||||
typeListDelete(transaction, this, index, length)
|
typeListDelete(transaction, this, index, length)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore _prelimContent is defined because this is not yet integrated
|
/** @type {Array} */ (this._prelimContent).splice(index, length)
|
||||||
this._prelimContent.splice(index, length)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,8 +172,7 @@ export class YArray extends AbstractType {
|
|||||||
* callback function
|
* callback function
|
||||||
*/
|
*/
|
||||||
map (f) {
|
map (f) {
|
||||||
// @ts-ignore
|
return typeListMap(this, /** @type {any} */ (f))
|
||||||
return typeListMap(this, f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
YMapRefID,
|
YMapRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
Doc, Transaction, ItemType, // eslint-disable-line
|
Doc, Transaction, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
import * as encoding from 'lib0/encoding.js'
|
||||||
@ -61,14 +61,13 @@ export class YMap extends AbstractType {
|
|||||||
* * Observer functions are fired
|
* * Observer functions are fired
|
||||||
*
|
*
|
||||||
* @param {Doc} y The Yjs instance
|
* @param {Doc} y The Yjs instance
|
||||||
* @param {ItemType} item
|
* @param {Item} item
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_integrate (y, item) {
|
_integrate (y, item) {
|
||||||
super._integrate(y, item)
|
super._integrate(y, item)
|
||||||
// @ts-ignore
|
for (let [key, value] of /** @type {Map<string, any>} */ (this._prelimContent)) {
|
||||||
for (let [key, value] of this._prelimContent) {
|
|
||||||
this.set(key, value)
|
this.set(key, value)
|
||||||
}
|
}
|
||||||
this._prelimContent = null
|
this._prelimContent = null
|
||||||
@ -97,7 +96,7 @@ export class YMap extends AbstractType {
|
|||||||
const map = {}
|
const map = {}
|
||||||
for (let [key, item] of this._map) {
|
for (let [key, item] of this._map) {
|
||||||
if (!item.deleted) {
|
if (!item.deleted) {
|
||||||
const v = item.getContent()[0]
|
const v = item.content.getContent()[item.length - 1]
|
||||||
map[key] = v instanceof AbstractType ? v.toJSON() : v
|
map[key] = v instanceof AbstractType ? v.toJSON() : v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,7 +118,7 @@ export class YMap extends AbstractType {
|
|||||||
* @return {Iterator<string>}
|
* @return {Iterator<string>}
|
||||||
*/
|
*/
|
||||||
values () {
|
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>}
|
* @return {IterableIterator<any>}
|
||||||
*/
|
*/
|
||||||
entries () {
|
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)
|
typeMapDelete(transaction, this, key)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Map<string, any>} */ (this._prelimContent).delete(key)
|
||||||
this._prelimContent.delete(key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,8 +164,7 @@ export class YMap extends AbstractType {
|
|||||||
typeMapSet(transaction, this, key, value)
|
typeMapSet(transaction, this, key, value)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Map<string, any>} */ (this._prelimContent).set(key, value)
|
||||||
this._prelimContent.set(key, value)
|
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@ -179,8 +176,7 @@ export class YMap extends AbstractType {
|
|||||||
* @return {T|undefined}
|
* @return {T|undefined}
|
||||||
*/
|
*/
|
||||||
get (key) {
|
get (key) {
|
||||||
// @ts-ignore
|
return /** @type {any} */ (typeMapGet(this, key))
|
||||||
return typeMapGet(this, key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,9 +5,6 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
YEvent,
|
YEvent,
|
||||||
ItemEmbed,
|
|
||||||
ItemString,
|
|
||||||
ItemFormat,
|
|
||||||
AbstractType,
|
AbstractType,
|
||||||
nextID,
|
nextID,
|
||||||
createID,
|
createID,
|
||||||
@ -16,7 +13,10 @@ import {
|
|||||||
YTextRefID,
|
YTextRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
Doc, ItemType, AbstractItem, Snapshot, StructStore, Transaction // eslint-disable-line
|
ContentEmbed,
|
||||||
|
ContentFormat,
|
||||||
|
ContentString,
|
||||||
|
Doc, Item, Snapshot, StructStore, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||||
@ -24,8 +24,8 @@ import * as encoding from 'lib0/encoding.js'
|
|||||||
|
|
||||||
export class ItemListPosition {
|
export class ItemListPosition {
|
||||||
/**
|
/**
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
*/
|
*/
|
||||||
constructor (left, right) {
|
constructor (left, right) {
|
||||||
this.left = left
|
this.left = left
|
||||||
@ -35,8 +35,8 @@ export class ItemListPosition {
|
|||||||
|
|
||||||
export class ItemTextListPosition extends ItemListPosition {
|
export class ItemTextListPosition extends ItemListPosition {
|
||||||
/**
|
/**
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {Map<string,any>} currentAttributes
|
* @param {Map<string,any>} currentAttributes
|
||||||
*/
|
*/
|
||||||
constructor (left, right, currentAttributes) {
|
constructor (left, right, currentAttributes) {
|
||||||
@ -47,8 +47,8 @@ export class ItemTextListPosition extends ItemListPosition {
|
|||||||
|
|
||||||
export class ItemInsertionResult extends ItemListPosition {
|
export class ItemInsertionResult extends ItemListPosition {
|
||||||
/**
|
/**
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {Map<string,any>} negatedAttributes
|
* @param {Map<string,any>} negatedAttributes
|
||||||
*/
|
*/
|
||||||
constructor (left, right, negatedAttributes) {
|
constructor (left, right, negatedAttributes) {
|
||||||
@ -61,8 +61,8 @@ export class ItemInsertionResult extends ItemListPosition {
|
|||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @param {Map<string,any>} currentAttributes
|
* @param {Map<string,any>} currentAttributes
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {number} count
|
* @param {number} count
|
||||||
* @return {ItemTextListPosition}
|
* @return {ItemTextListPosition}
|
||||||
*
|
*
|
||||||
@ -71,9 +71,9 @@ export class ItemInsertionResult extends ItemListPosition {
|
|||||||
*/
|
*/
|
||||||
const findNextPosition = (transaction, store, currentAttributes, left, right, count) => {
|
const findNextPosition = (transaction, store, currentAttributes, left, right, count) => {
|
||||||
while (right !== null && count > 0) {
|
while (right !== null && count > 0) {
|
||||||
switch (right.constructor) {
|
switch (right.content.constructor) {
|
||||||
case ItemEmbed:
|
case ContentEmbed:
|
||||||
case ItemString:
|
case ContentString:
|
||||||
if (!right.deleted) {
|
if (!right.deleted) {
|
||||||
if (count < right.length) {
|
if (count < right.length) {
|
||||||
// split right
|
// split right
|
||||||
@ -82,10 +82,9 @@ const findNextPosition = (transaction, store, currentAttributes, left, right, co
|
|||||||
count -= right.length
|
count -= right.length
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case ItemFormat:
|
case ContentFormat:
|
||||||
if (!right.deleted) {
|
if (!right.deleted) {
|
||||||
// @ts-ignore right is ItemFormat
|
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
|
||||||
updateCurrentAttributes(currentAttributes, right)
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -117,8 +116,8 @@ const findPosition = (transaction, store, parent, index) => {
|
|||||||
*
|
*
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {Map<string,any>} negatedAttributes
|
* @param {Map<string,any>} negatedAttributes
|
||||||
* @return {ItemListPosition}
|
* @return {ItemListPosition}
|
||||||
*
|
*
|
||||||
@ -130,21 +129,19 @@ const insertNegatedAttributes = (transaction, parent, left, right, negatedAttrib
|
|||||||
while (
|
while (
|
||||||
right !== null && (
|
right !== null && (
|
||||||
right.deleted === true || (
|
right.deleted === true || (
|
||||||
right.constructor === ItemFormat &&
|
right.content.constructor === ContentFormat &&
|
||||||
// @ts-ignore right is ItemFormat
|
(negatedAttributes.get(/** @type {ContentFormat} */ (right.content).key) === /** @type {ContentFormat} */ (right.content).value)
|
||||||
(negatedAttributes.get(right.key) === right.value)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (!right.deleted) {
|
if (!right.deleted) {
|
||||||
// @ts-ignore right is ItemFormat
|
negatedAttributes.delete(/** @type {ContentFormat} */ (right.content).key)
|
||||||
negatedAttributes.delete(right.key)
|
|
||||||
}
|
}
|
||||||
left = right
|
left = right
|
||||||
right = right.right
|
right = right.right
|
||||||
}
|
}
|
||||||
for (let [key, val] of negatedAttributes) {
|
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)
|
left.integrate(transaction)
|
||||||
}
|
}
|
||||||
return { left, right }
|
return { left, right }
|
||||||
@ -152,14 +149,13 @@ const insertNegatedAttributes = (transaction, parent, left, right, negatedAttrib
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Map<string,any>} currentAttributes
|
* @param {Map<string,any>} currentAttributes
|
||||||
* @param {ItemFormat} item
|
* @param {ContentFormat} format
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
const updateCurrentAttributes = (currentAttributes, item) => {
|
const updateCurrentAttributes = (currentAttributes, format) => {
|
||||||
const value = item.value
|
const { key, value } = format
|
||||||
const key = item.key
|
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
currentAttributes.delete(key)
|
currentAttributes.delete(key)
|
||||||
} else {
|
} else {
|
||||||
@ -168,8 +164,8 @@ const updateCurrentAttributes = (currentAttributes, item) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {Map<string,any>} currentAttributes
|
* @param {Map<string,any>} currentAttributes
|
||||||
* @param {Object<string,any>} attributes
|
* @param {Object<string,any>} attributes
|
||||||
* @return {ItemListPosition}
|
* @return {ItemListPosition}
|
||||||
@ -184,11 +180,9 @@ const minimizeAttributeChanges = (left, right, currentAttributes, attributes) =>
|
|||||||
break
|
break
|
||||||
} else if (right.deleted) {
|
} else if (right.deleted) {
|
||||||
// continue
|
// continue
|
||||||
// @ts-ignore right is ItemFormat
|
} else if (right.content.constructor === ContentFormat && (attributes[(/** @type {ContentFormat} */ (right.content)).key] || null) === /** @type {ContentFormat} */ (right.content).value) {
|
||||||
} else if (right.constructor === ItemFormat && (attributes[right.key] || null) === right.value) {
|
|
||||||
// found a format, update currentAttributes and continue
|
// found a format, update currentAttributes and continue
|
||||||
// @ts-ignore right is ItemFormat
|
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
|
||||||
updateCurrentAttributes(currentAttributes, right)
|
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -201,8 +195,8 @@ const minimizeAttributeChanges = (left, right, currentAttributes, attributes) =>
|
|||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {Map<string,any>} currentAttributes
|
* @param {Map<string,any>} currentAttributes
|
||||||
* @param {Object<string,any>} attributes
|
* @param {Object<string,any>} attributes
|
||||||
* @return {ItemInsertionResult}
|
* @return {ItemInsertionResult}
|
||||||
@ -219,7 +213,7 @@ const insertAttributes = (transaction, parent, left, right, currentAttributes, a
|
|||||||
if (currentVal !== val) {
|
if (currentVal !== val) {
|
||||||
// save negated attribute (set null if currentVal undefined)
|
// save negated attribute (set null if currentVal undefined)
|
||||||
negatedAttributes.set(key, currentVal)
|
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)
|
left.integrate(transaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,8 +223,8 @@ const insertAttributes = (transaction, parent, left, right, currentAttributes, a
|
|||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {Map<string,any>} currentAttributes
|
* @param {Map<string,any>} currentAttributes
|
||||||
* @param {string} text
|
* @param {string} text
|
||||||
* @param {Object<string,any>} attributes
|
* @param {Object<string,any>} attributes
|
||||||
@ -250,11 +244,8 @@ const insertText = (transaction, parent, left, right, currentAttributes, text, a
|
|||||||
left = insertPos.left
|
left = insertPos.left
|
||||||
right = insertPos.right
|
right = insertPos.right
|
||||||
// insert content
|
// insert content
|
||||||
if (text.constructor === String) {
|
const content = text.constructor === String ? new ContentString(text) : new ContentEmbed(text)
|
||||||
left = new ItemString(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, text)
|
left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, content)
|
||||||
} else {
|
|
||||||
left = new ItemEmbed(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, text)
|
|
||||||
}
|
|
||||||
left.integrate(transaction)
|
left.integrate(transaction)
|
||||||
return insertNegatedAttributes(transaction, parent, left, insertPos.right, insertPos.negatedAttributes)
|
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 {Transaction} transaction
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {Map<string,any>} currentAttributes
|
* @param {Map<string,any>} currentAttributes
|
||||||
* @param {number} length
|
* @param {number} length
|
||||||
* @param {Object<string,any>} attributes
|
* @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
|
// delete all formats with attributes[format.key] != null
|
||||||
while (length > 0 && right !== null) {
|
while (length > 0 && right !== null) {
|
||||||
if (right.deleted === false) {
|
if (right.deleted === false) {
|
||||||
switch (right.constructor) {
|
switch (right.content.constructor) {
|
||||||
case ItemFormat:
|
case ContentFormat:
|
||||||
// @ts-ignore right is ItemFormat
|
const { key, value } = /** @type {ContentFormat} */ (right.content)
|
||||||
const attr = attributes[right.key]
|
const attr = attributes[key]
|
||||||
if (attr !== undefined) {
|
if (attr !== undefined) {
|
||||||
// @ts-ignore right is ItemFormat
|
if (attr === value) {
|
||||||
if (attr === right.value) {
|
negatedAttributes.delete(key)
|
||||||
// @ts-ignore right is ItemFormat
|
|
||||||
negatedAttributes.delete(right.key)
|
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore right is ItemFormat
|
negatedAttributes.set(key, value)
|
||||||
negatedAttributes.set(right.key, right.value)
|
|
||||||
}
|
}
|
||||||
right.delete(transaction)
|
right.delete(transaction)
|
||||||
}
|
}
|
||||||
// @ts-ignore right is ItemFormat
|
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
|
||||||
updateCurrentAttributes(currentAttributes, right)
|
|
||||||
break
|
break
|
||||||
case ItemEmbed:
|
case ContentEmbed:
|
||||||
case ItemString:
|
case ContentString:
|
||||||
if (length < right.length) {
|
if (length < right.length) {
|
||||||
getItemCleanStart(transaction, transaction.doc.store, createID(right.id.client, right.id.clock + 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 {Transaction} transaction
|
||||||
* @param {AbstractItem|null} left
|
* @param {Item|null} left
|
||||||
* @param {AbstractItem|null} right
|
* @param {Item|null} right
|
||||||
* @param {Map<string,any>} currentAttributes
|
* @param {Map<string,any>} currentAttributes
|
||||||
* @param {number} length
|
* @param {number} length
|
||||||
* @return {ItemListPosition}
|
* @return {ItemListPosition}
|
||||||
@ -329,13 +316,12 @@ const formatText = (transaction, parent, left, right, currentAttributes, length,
|
|||||||
const deleteText = (transaction, left, right, currentAttributes, length) => {
|
const deleteText = (transaction, left, right, currentAttributes, length) => {
|
||||||
while (length > 0 && right !== null) {
|
while (length > 0 && right !== null) {
|
||||||
if (right.deleted === false) {
|
if (right.deleted === false) {
|
||||||
switch (right.constructor) {
|
switch (right.content.constructor) {
|
||||||
case ItemFormat:
|
case ContentFormat:
|
||||||
// @ts-ignore right is ItemFormat
|
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
|
||||||
updateCurrentAttributes(currentAttributes, right)
|
|
||||||
break
|
break
|
||||||
case ItemEmbed:
|
case ContentEmbed:
|
||||||
case ItemString:
|
case ContentString:
|
||||||
if (length < right.length) {
|
if (length < right.length) {
|
||||||
getItemCleanStart(transaction, transaction.doc.store, createID(right.id.client, right.id.clock + 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 () {
|
get delta () {
|
||||||
if (this._delta === null) {
|
if (this._delta === null) {
|
||||||
const y = this.target.doc
|
const y = /** @type {Doc} */ (this.target.doc)
|
||||||
// @ts-ignore
|
this._delta = []
|
||||||
transact(y, transaction => {
|
transact(y, transaction => {
|
||||||
/**
|
const delta = /** @type {Array<DeltaItem>} */ (this._delta)
|
||||||
* @type {Array<DeltaItem>}
|
|
||||||
*/
|
|
||||||
const delta = []
|
|
||||||
const currentAttributes = new Map() // saves all current attributes for insert
|
const currentAttributes = new Map() // saves all current attributes for insert
|
||||||
const oldAttributes = new Map()
|
const oldAttributes = new Map()
|
||||||
let item = this.target._start
|
let item = this.target._start
|
||||||
@ -432,7 +415,6 @@ export class YTextEvent extends YEvent {
|
|||||||
let insert = ''
|
let insert = ''
|
||||||
let retain = 0
|
let retain = 0
|
||||||
let deleteLen = 0
|
let deleteLen = 0
|
||||||
this._delta = delta
|
|
||||||
const addOp = () => {
|
const addOp = () => {
|
||||||
if (action !== null) {
|
if (action !== null) {
|
||||||
/**
|
/**
|
||||||
@ -472,14 +454,13 @@ export class YTextEvent extends YEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (item !== null) {
|
while (item !== null) {
|
||||||
switch (item.constructor) {
|
switch (item.content.constructor) {
|
||||||
case ItemEmbed:
|
case ContentEmbed:
|
||||||
if (this.adds(item)) {
|
if (this.adds(item)) {
|
||||||
if (!this.deletes(item)) {
|
if (!this.deletes(item)) {
|
||||||
addOp()
|
addOp()
|
||||||
action = 'insert'
|
action = 'insert'
|
||||||
// @ts-ignore item is ItemFormat
|
insert = /** @type {ContentEmbed} */ (item.content).embed
|
||||||
insert = item.embed
|
|
||||||
addOp()
|
addOp()
|
||||||
}
|
}
|
||||||
} else if (this.deletes(item)) {
|
} else if (this.deletes(item)) {
|
||||||
@ -496,15 +477,14 @@ export class YTextEvent extends YEvent {
|
|||||||
retain += 1
|
retain += 1
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case ItemString:
|
case ContentString:
|
||||||
if (this.adds(item)) {
|
if (this.adds(item)) {
|
||||||
if (!this.deletes(item)) {
|
if (!this.deletes(item)) {
|
||||||
if (action !== 'insert') {
|
if (action !== 'insert') {
|
||||||
addOp()
|
addOp()
|
||||||
action = 'insert'
|
action = 'insert'
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
insert += /** @type {ContentString} */ (item.content).str
|
||||||
insert += item.string
|
|
||||||
}
|
}
|
||||||
} else if (this.deletes(item)) {
|
} else if (this.deletes(item)) {
|
||||||
if (action !== 'delete') {
|
if (action !== 'delete') {
|
||||||
@ -520,59 +500,45 @@ export class YTextEvent extends YEvent {
|
|||||||
retain += item.length
|
retain += item.length
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case ItemFormat:
|
case ContentFormat:
|
||||||
|
const { key, value } = /** @type {ContentFormat} */ (item.content)
|
||||||
if (this.adds(item)) {
|
if (this.adds(item)) {
|
||||||
if (!this.deletes(item)) {
|
if (!this.deletes(item)) {
|
||||||
// @ts-ignore item is ItemFormat
|
const curVal = currentAttributes.get(key) || null
|
||||||
const curVal = currentAttributes.get(item.key) || null
|
if (curVal !== value) {
|
||||||
// @ts-ignore item is ItemFormat
|
|
||||||
if (curVal !== item.value) {
|
|
||||||
if (action === 'retain') {
|
if (action === 'retain') {
|
||||||
addOp()
|
addOp()
|
||||||
}
|
}
|
||||||
// @ts-ignore item is ItemFormat
|
if (value === (oldAttributes.get(key) || null)) {
|
||||||
if (item.value === (oldAttributes.get(item.key) || null)) {
|
delete attributes[key]
|
||||||
// @ts-ignore item is ItemFormat
|
|
||||||
delete attributes[item.key]
|
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore item is ItemFormat
|
attributes[key] = value
|
||||||
attributes[item.key] = item.value
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item.delete(transaction)
|
item.delete(transaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.deletes(item)) {
|
} else if (this.deletes(item)) {
|
||||||
// @ts-ignore item is ItemFormat
|
oldAttributes.set(key, value)
|
||||||
oldAttributes.set(item.key, item.value)
|
const curVal = currentAttributes.get(key) || null
|
||||||
// @ts-ignore item is ItemFormat
|
if (curVal !== value) {
|
||||||
const curVal = currentAttributes.get(item.key) || null
|
|
||||||
// @ts-ignore item is ItemFormat
|
|
||||||
if (curVal !== item.value) {
|
|
||||||
if (action === 'retain') {
|
if (action === 'retain') {
|
||||||
addOp()
|
addOp()
|
||||||
}
|
}
|
||||||
// @ts-ignore item is ItemFormat
|
attributes[key] = curVal
|
||||||
attributes[item.key] = curVal
|
|
||||||
}
|
}
|
||||||
} else if (!item.deleted) {
|
} else if (!item.deleted) {
|
||||||
// @ts-ignore item is ItemFormat
|
oldAttributes.set(key, value)
|
||||||
oldAttributes.set(item.key, item.value)
|
const attr = attributes[key]
|
||||||
// @ts-ignore item is ItemFormat
|
|
||||||
const attr = attributes[item.key]
|
|
||||||
if (attr !== undefined) {
|
if (attr !== undefined) {
|
||||||
// @ts-ignore item is ItemFormat
|
if (attr !== value) {
|
||||||
if (attr !== item.value) {
|
|
||||||
if (action === 'retain') {
|
if (action === 'retain') {
|
||||||
addOp()
|
addOp()
|
||||||
}
|
}
|
||||||
// @ts-ignore item is ItemFormat
|
if (value === null) {
|
||||||
if (item.value === null) {
|
attributes[key] = value
|
||||||
// @ts-ignore item is ItemFormat
|
|
||||||
attributes[item.key] = item.value
|
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore item is ItemFormat
|
delete attributes[key]
|
||||||
delete attributes[item.key]
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item.delete(transaction)
|
item.delete(transaction)
|
||||||
@ -583,26 +549,24 @@ export class YTextEvent extends YEvent {
|
|||||||
if (action === 'insert') {
|
if (action === 'insert') {
|
||||||
addOp()
|
addOp()
|
||||||
}
|
}
|
||||||
// @ts-ignore item is ItemFormat
|
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (item.content))
|
||||||
updateCurrentAttributes(currentAttributes, item)
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
item = item.right
|
item = item.right
|
||||||
}
|
}
|
||||||
addOp()
|
addOp()
|
||||||
while (this._delta.length > 0) {
|
while (delta.length > 0) {
|
||||||
let lastOp = this._delta[this._delta.length - 1]
|
let lastOp = delta[delta.length - 1]
|
||||||
if (lastOp.retain !== undefined && lastOp.attributes === undefined) {
|
if (lastOp.retain !== undefined && lastOp.attributes === undefined) {
|
||||||
// retain delta's if they don't assign attributes
|
// retain delta's if they don't assign attributes
|
||||||
this._delta.pop()
|
delta.pop()
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// @ts-ignore _delta is defined above
|
|
||||||
return this._delta
|
return this._delta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -636,15 +600,14 @@ export class YText extends AbstractType {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Doc} y
|
* @param {Doc} y
|
||||||
* @param {ItemType} item
|
* @param {Item} item
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_integrate (y, item) {
|
_integrate (y, item) {
|
||||||
super._integrate(y, item)
|
super._integrate(y, item)
|
||||||
try {
|
try {
|
||||||
// @ts-ignore this._prelimContent is still defined
|
/** @type {Array<function>} */ (this._pending).forEach(f => f())
|
||||||
this._pending.forEach(f => f())
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
@ -671,13 +634,12 @@ export class YText extends AbstractType {
|
|||||||
toString () {
|
toString () {
|
||||||
let str = ''
|
let str = ''
|
||||||
/**
|
/**
|
||||||
* @type {AbstractItem|null}
|
* @type {Item|null}
|
||||||
*/
|
*/
|
||||||
let n = this._start
|
let n = this._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (!n.deleted && n.countable && n.constructor === ItemString) {
|
if (!n.deleted && n.countable && n.content.constructor === ContentString) {
|
||||||
// @ts-ignore
|
str += /** @type {ContentString} */ (n.content).str
|
||||||
str += n.string
|
|
||||||
}
|
}
|
||||||
n = n.right
|
n = n.right
|
||||||
}
|
}
|
||||||
@ -711,8 +673,7 @@ export class YText extends AbstractType {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Array<function>} */ (this._pending).push(() => this.applyDelta(delta))
|
||||||
this._pending.push(() => this.applyDelta(delta))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,10 +693,6 @@ export class YText extends AbstractType {
|
|||||||
const ops = []
|
const ops = []
|
||||||
const currentAttributes = new Map()
|
const currentAttributes = new Map()
|
||||||
let str = ''
|
let str = ''
|
||||||
/**
|
|
||||||
* @type {AbstractItem|null}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
let n = this._start
|
let n = this._start
|
||||||
function packStr () {
|
function packStr () {
|
||||||
if (str.length > 0) {
|
if (str.length > 0) {
|
||||||
@ -762,8 +719,8 @@ export class YText extends AbstractType {
|
|||||||
}
|
}
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) {
|
if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) {
|
||||||
switch (n.constructor) {
|
switch (n.content.constructor) {
|
||||||
case ItemString:
|
case ContentString:
|
||||||
const cur = currentAttributes.get('ychange')
|
const cur = currentAttributes.get('ychange')
|
||||||
if (snapshot !== undefined && !isVisible(n, snapshot)) {
|
if (snapshot !== undefined && !isVisible(n, snapshot)) {
|
||||||
if (cur === undefined || cur.user !== n.id.client || cur.state !== 'removed') {
|
if (cur === undefined || cur.user !== n.id.client || cur.state !== 'removed') {
|
||||||
@ -779,20 +736,17 @@ export class YText extends AbstractType {
|
|||||||
packStr()
|
packStr()
|
||||||
currentAttributes.delete('ychange')
|
currentAttributes.delete('ychange')
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
str += /** @type {ContentString} */ (n.content).str
|
||||||
str += n.string
|
|
||||||
break
|
break
|
||||||
case ItemEmbed:
|
case ContentEmbed:
|
||||||
packStr()
|
packStr()
|
||||||
ops.push({
|
ops.push({
|
||||||
// @ts-ignore item is ItemFormat
|
insert: /** @type {ContentEmbed} */ (n.content).embed
|
||||||
insert: n.embed
|
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case ItemFormat:
|
case ContentFormat:
|
||||||
packStr()
|
packStr()
|
||||||
// @ts-ignore
|
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (n.content))
|
||||||
updateCurrentAttributes(currentAttributes, n)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -823,8 +777,7 @@ export class YText extends AbstractType {
|
|||||||
insertText(transaction, this, left, right, currentAttributes, text, attributes)
|
insertText(transaction, this, left, right, currentAttributes, text, attributes)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Array<function>} */ (this._pending).push(() => this.insert(index, text, attributes))
|
||||||
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)
|
insertText(transaction, this, left, right, currentAttributes, embed, attributes)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Array<function>} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes))
|
||||||
this._pending.push(() => this.insertEmbed(index, embed, attributes))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -873,8 +825,7 @@ export class YText extends AbstractType {
|
|||||||
deleteText(transaction, left, right, currentAttributes, length)
|
deleteText(transaction, left, right, currentAttributes, length)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Array<function>} */ (this._pending).push(() => this.delete(index, length))
|
||||||
this._pending.push(() => this.delete(index, length))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -899,8 +850,7 @@ export class YText extends AbstractType {
|
|||||||
formatText(transaction, this, left, right, currentAttributes, length, attributes)
|
formatText(transaction, this, left, right, currentAttributes, length, attributes)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Array<function>} */ (this._pending).push(() => this.format(index, length, attributes))
|
||||||
this._pending.push(() => this.format(index, length, attributes))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
typeMapGetAll,
|
typeMapGetAll,
|
||||||
typeListForEach,
|
typeListForEach,
|
||||||
YXmlElementRefID,
|
YXmlElementRefID,
|
||||||
Snapshot, Doc, ItemType // eslint-disable-line
|
Snapshot, Doc, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
import * as encoding from 'lib0/encoding.js'
|
||||||
@ -40,16 +40,14 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
* * Observer functions are fired
|
* * Observer functions are fired
|
||||||
*
|
*
|
||||||
* @param {Doc} y The Yjs instance
|
* @param {Doc} y The Yjs instance
|
||||||
* @param {ItemType} item
|
* @param {Item} item
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_integrate (y, item) {
|
_integrate (y, item) {
|
||||||
super._integrate(y, item)
|
super._integrate(y, item)
|
||||||
// @ts-ignore
|
this.insert(0, /** @type {Array} */ (this._prelimContent))
|
||||||
this.insert(0, this._prelimContent)
|
|
||||||
this._prelimContent = null
|
this._prelimContent = null
|
||||||
// @ts-ignore
|
;(/** @type {Map<string, any>} */ (this._prelimAttrs)).forEach((value, key) => {
|
||||||
this._prelimAttrs.forEach((value, key) => {
|
|
||||||
this.setAttribute(key, value)
|
this.setAttribute(key, value)
|
||||||
})
|
})
|
||||||
this._prelimContent = null
|
this._prelimContent = null
|
||||||
@ -105,8 +103,7 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
typeMapDelete(transaction, this, attributeName)
|
typeMapDelete(transaction, this, attributeName)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Map<string,any>} */ (this._prelimAttrs).delete(attributeName)
|
||||||
this._prelimAttrs.delete(attributeName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,8 +121,7 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
typeMapSet(transaction, this, attributeName, attributeValue)
|
typeMapSet(transaction, this, attributeName, attributeValue)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
/** @type {Map<string, any>} */ (this._prelimAttrs).set(attributeName, attributeValue)
|
||||||
this._prelimAttrs.set(attributeName, attributeValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,8 +135,7 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getAttribute (attributeName) {
|
getAttribute (attributeName) {
|
||||||
// @ts-ignore
|
return /** @type {any} */ (typeMapGet(this, attributeName))
|
||||||
return typeMapGet(this, attributeName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
YXmlFragmentRefID,
|
YXmlFragmentRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
Transaction, ItemType, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
ContentType, Transaction, Item, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
import * as encoding from 'lib0/encoding.js'
|
||||||
@ -59,10 +59,9 @@ export class YXmlTreeWalker {
|
|||||||
this._filter = f
|
this._filter = f
|
||||||
this._root = root
|
this._root = root
|
||||||
/**
|
/**
|
||||||
* @type {ItemType | null}
|
* @type {Item}
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
this._currentNode = /** @type {Item} */ (root._start)
|
||||||
this._currentNode = root._start
|
|
||||||
this._firstCall = true
|
this._firstCall = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,18 +76,21 @@ export class YXmlTreeWalker {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
next () {
|
next () {
|
||||||
|
/**
|
||||||
|
* @type {Item|null}
|
||||||
|
*/
|
||||||
let n = this._currentNode
|
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 {
|
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
|
// walk down in the tree
|
||||||
// @ts-ignore
|
n = type._start
|
||||||
n = n.type._start
|
|
||||||
} else {
|
} else {
|
||||||
// walk right or up in the tree
|
// walk right or up in the tree
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (n.right !== null) {
|
if (n.right !== null) {
|
||||||
// @ts-ignore
|
|
||||||
n = n.right
|
n = n.right
|
||||||
break
|
break
|
||||||
} else if (n.parent === this._root) {
|
} 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._firstCall = false
|
||||||
this._currentNode = n
|
|
||||||
if (n === null) {
|
if (n === null) {
|
||||||
// @ts-ignore return undefined if done=true (the expected result)
|
// @ts-ignore
|
||||||
return { value: undefined, done: true }
|
return { value: undefined, done: true }
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
this._currentNode = n
|
||||||
return { value: n.type, done: false }
|
return { value: /** @type {any} */ (n.content).type, done: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,8 @@ import {
|
|||||||
findIndexSS,
|
findIndexSS,
|
||||||
createID,
|
createID,
|
||||||
getState,
|
getState,
|
||||||
AbstractStruct, AbstractItem, StructStore, Transaction, ID // eslint-disable-line
|
splitItem,
|
||||||
|
Item, AbstractStruct, StructStore, Transaction, ID // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as math from 'lib0/math.js'
|
import * as math from 'lib0/math.js'
|
||||||
@ -11,7 +12,7 @@ import * as map from 'lib0/map.js'
|
|||||||
import * as encoding from 'lib0/encoding.js'
|
import * as encoding from 'lib0/encoding.js'
|
||||||
import * as decoding from 'lib0/decoding.js'
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
|
||||||
class DeleteItem {
|
export class DeleteItem {
|
||||||
/**
|
/**
|
||||||
* @param {number} clock
|
* @param {number} clock
|
||||||
* @param {number} len
|
* @param {number} len
|
||||||
@ -235,13 +236,13 @@ export const readDeleteSet = (decoder, transaction, store) => {
|
|||||||
let index = findIndexSS(structs, clock)
|
let index = findIndexSS(structs, clock)
|
||||||
/**
|
/**
|
||||||
* We can ignore the case of GC and Delete structs, because we are going to skip them
|
* We can ignore the case of GC and Delete structs, because we are going to skip them
|
||||||
* @type {AbstractItem}
|
* @type {Item}
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let struct = structs[index]
|
let struct = structs[index]
|
||||||
// split the first item if necessary
|
// split the first item if necessary
|
||||||
if (!struct.deleted && struct.id.clock < clock) {
|
if (!struct.deleted && struct.id.clock < clock) {
|
||||||
structs.splice(index + 1, 0, struct.splitAt(transaction, clock - struct.id.clock))
|
structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock))
|
||||||
index++ // increase we now want to use the next struct
|
index++ // increase we now want to use the next struct
|
||||||
}
|
}
|
||||||
while (index < structs.length) {
|
while (index < structs.length) {
|
||||||
@ -250,7 +251,7 @@ export const readDeleteSet = (decoder, transaction, store) => {
|
|||||||
if (struct.id.clock < clock + len) {
|
if (struct.id.clock < clock + len) {
|
||||||
if (!struct.deleted) {
|
if (!struct.deleted) {
|
||||||
if (clock + len < struct.id.clock + struct.length) {
|
if (clock + len < struct.id.clock + struct.length) {
|
||||||
structs.splice(index, 0, struct.splitAt(transaction, clock + len - struct.id.clock))
|
structs.splice(index, 0, splitItem(transaction, struct, clock + len - struct.id.clock))
|
||||||
}
|
}
|
||||||
struct.delete(transaction)
|
struct.delete(transaction)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
YMap,
|
YMap,
|
||||||
YXmlFragment,
|
YXmlFragment,
|
||||||
transact,
|
transact,
|
||||||
AbstractItem, Transaction, YEvent // eslint-disable-line
|
Item, Transaction, YEvent // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import { Observable } from 'lib0/observable.js'
|
import { Observable } from 'lib0/observable.js'
|
||||||
@ -97,7 +97,7 @@ export class Doc extends Observable {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const t = new TypeConstructor()
|
const t = new TypeConstructor()
|
||||||
t._map = type._map
|
t._map = type._map
|
||||||
type._map.forEach(/** @param {AbstractItem?} n */ n => {
|
type._map.forEach(/** @param {Item?} n */ n => {
|
||||||
for (; n !== null; n = n.left) {
|
for (; n !== null; n = n.left) {
|
||||||
n.parent = t
|
n.parent = t
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
getItem,
|
getItem,
|
||||||
getItemType,
|
|
||||||
createID,
|
createID,
|
||||||
writeID,
|
writeID,
|
||||||
readID,
|
readID,
|
||||||
compareIDs,
|
compareIDs,
|
||||||
getState,
|
getState,
|
||||||
findRootTypeKey,
|
findRootTypeKey,
|
||||||
AbstractItem,
|
Item,
|
||||||
ItemType,
|
ContentType,
|
||||||
ID, StructStore, Doc, AbstractType // eslint-disable-line
|
ID, Doc, AbstractType // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
import * as encoding from 'lib0/encoding.js'
|
||||||
@ -224,7 +223,7 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const right = getItem(store, rightID)
|
const right = getItem(store, rightID)
|
||||||
if (!(right instanceof AbstractItem)) {
|
if (!(right instanceof Item)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
index = right.deleted || !right.countable ? 0 : rightID.clock - right.id.clock
|
index = right.deleted || !right.countable ? 0 : rightID.clock - right.id.clock
|
||||||
@ -244,9 +243,9 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc) => {
|
|||||||
// type does not exist yet
|
// type does not exist yet
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const struct = getItemType(store, typeID)
|
const struct = getItem(store, typeID)
|
||||||
if (struct instanceof ItemType) {
|
if (struct instanceof Item && struct.content instanceof ContentType) {
|
||||||
type = struct.type
|
type = struct.content.type
|
||||||
} else {
|
} else {
|
||||||
// struct is garbage collected
|
// struct is garbage collected
|
||||||
return null
|
return null
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
isDeleted,
|
isDeleted,
|
||||||
DeleteSet, AbstractItem // eslint-disable-line
|
DeleteSet, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
export class Snapshot {
|
export class Snapshot {
|
||||||
@ -31,7 +31,7 @@ export class Snapshot {
|
|||||||
export const createSnapshot = (ds, sm) => new Snapshot(ds, sm)
|
export const createSnapshot = (ds, sm) => new Snapshot(ds, sm)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AbstractItem} item
|
* @param {Item} item
|
||||||
* @param {Snapshot|undefined} snapshot
|
* @param {Snapshot|undefined} snapshot
|
||||||
*
|
*
|
||||||
* @protected
|
* @protected
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
GC,
|
GC,
|
||||||
Transaction, AbstractStructRef, ID, ItemType, AbstractItem, AbstractStruct // eslint-disable-line
|
splitItem,
|
||||||
|
GCRef, ItemRef, Transaction, ID, Item, AbstractStruct // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as math from 'lib0/math.js'
|
import * as math from 'lib0/math.js'
|
||||||
@ -21,14 +22,14 @@ export class StructStore {
|
|||||||
* We could shift the array of refs instead, but shift is incredible
|
* We could shift the array of refs instead, but shift is incredible
|
||||||
* slow in Chrome for arrays with more than 100k elements
|
* slow in Chrome for arrays with more than 100k elements
|
||||||
* @see tryResumePendingStructRefs
|
* @see tryResumePendingStructRefs
|
||||||
* @type {Map<number,{i:number,refs:Array<AbstractStructRef>}>}
|
* @type {Map<number,{i:number,refs:Array<GCRef|ItemRef>}>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.pendingClientsStructRefs = new Map()
|
this.pendingClientsStructRefs = new Map()
|
||||||
/**
|
/**
|
||||||
* Stack of pending structs waiting for struct dependencies
|
* Stack of pending structs waiting for struct dependencies
|
||||||
* Maximum length of stack is structReaders.size
|
* Maximum length of stack is structReaders.size
|
||||||
* @type {Array<AbstractStructRef>}
|
* @type {Array<GCRef|ItemRef>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.pendingStack = []
|
this.pendingStack = []
|
||||||
@ -169,7 +170,7 @@ export const find = (store, id) => {
|
|||||||
*
|
*
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @param {ID} id
|
* @param {ID} id
|
||||||
* @return {AbstractItem}
|
* @return {Item}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
@ -177,43 +178,23 @@ export const find = (store, id) => {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const getItem = (store, id) => find(store, id)
|
export const getItem = (store, id) => find(store, id)
|
||||||
|
|
||||||
/**
|
|
||||||
* Expects that id is actually in store. This function throws or is an infinite loop otherwise.
|
|
||||||
*
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {ID} id
|
|
||||||
* @return {ItemType}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @function
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
export const getItemType = (store, id) => find(store, id)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expects that id is actually in store. This function throws or is an infinite loop otherwise.
|
* Expects that id is actually in store. This function throws or is an infinite loop otherwise.
|
||||||
*
|
*
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @param {ID} id
|
* @param {ID} id
|
||||||
* @return {AbstractItem}
|
* @return {Item}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const getItemCleanStart = (transaction, store, id) => {
|
export const getItemCleanStart = (transaction, store, id) => {
|
||||||
/**
|
const structs = /** @type {Array<Item>} */ (store.clients.get(id.client))
|
||||||
* @type {Array<AbstractItem>}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const structs = store.clients.get(id.client)
|
|
||||||
const index = findIndexSS(structs, id.clock)
|
const index = findIndexSS(structs, id.clock)
|
||||||
/**
|
|
||||||
* @type {AbstractItem}
|
|
||||||
*/
|
|
||||||
let struct = structs[index]
|
let struct = structs[index]
|
||||||
if (struct.id.clock < id.clock && struct.constructor !== GC) {
|
if (struct.id.clock < id.clock && struct.constructor !== GC) {
|
||||||
struct = struct.splitAt(transaction, id.clock - struct.id.clock)
|
struct = splitItem(transaction, struct, id.clock - struct.id.clock)
|
||||||
structs.splice(index + 1, 0, struct)
|
structs.splice(index + 1, 0, struct)
|
||||||
}
|
}
|
||||||
return struct
|
return struct
|
||||||
@ -225,21 +206,21 @@ export const getItemCleanStart = (transaction, store, id) => {
|
|||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @param {ID} id
|
* @param {ID} id
|
||||||
* @return {AbstractItem}
|
* @return {Item}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const getItemCleanEnd = (transaction, store, id) => {
|
export const getItemCleanEnd = (transaction, store, id) => {
|
||||||
/**
|
/**
|
||||||
* @type {Array<AbstractItem>}
|
* @type {Array<Item>}
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const structs = store.clients.get(id.client)
|
const structs = store.clients.get(id.client)
|
||||||
const index = findIndexSS(structs, id.clock)
|
const index = findIndexSS(structs, id.clock)
|
||||||
const struct = structs[index]
|
const struct = structs[index]
|
||||||
if (id.clock !== struct.id.clock + struct.length - 1 && struct.constructor !== GC) {
|
if (id.clock !== struct.id.clock + struct.length - 1 && struct.constructor !== GC) {
|
||||||
structs.splice(index + 1, 0, struct.splitAt(transaction, id.clock - struct.id.clock + 1))
|
structs.splice(index + 1, 0, splitItem(transaction, struct, id.clock - struct.id.clock + 1))
|
||||||
}
|
}
|
||||||
return struct
|
return struct
|
||||||
}
|
}
|
||||||
@ -254,10 +235,6 @@ export const getItemCleanEnd = (transaction, store, id) => {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const replaceStruct = (store, struct, newStruct) => {
|
export const replaceStruct = (store, struct, newStruct) => {
|
||||||
/**
|
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(struct.id.client))
|
||||||
* @type {Array<AbstractStruct>}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const structs = store.clients.get(struct.id.client)
|
|
||||||
structs[findIndexSS(structs, struct.id.clock)] = newStruct
|
structs[findIndexSS(structs, struct.id.clock)] = newStruct
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
getStateVector,
|
getStateVector,
|
||||||
findIndexSS,
|
findIndexSS,
|
||||||
callEventHandlerListeners,
|
callEventHandlerListeners,
|
||||||
AbstractItem,
|
Item,
|
||||||
ID, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
|
ID, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
@ -179,20 +179,15 @@ export const transact = (doc, f, origin = null) => {
|
|||||||
if (left.deleted === right.deleted && left.constructor === right.constructor) {
|
if (left.deleted === right.deleted && left.constructor === right.constructor) {
|
||||||
if (left.mergeWith(right)) {
|
if (left.mergeWith(right)) {
|
||||||
structs.splice(pos, 1)
|
structs.splice(pos, 1)
|
||||||
if (right instanceof AbstractItem && right.parentSub !== null && right.parent._map.get(right.parentSub) === right) {
|
if (right instanceof Item && right.parentSub !== null && right.parent._map.get(right.parentSub) === right) {
|
||||||
// @ts-ignore we already did a constructor check above
|
right.parent._map.set(right.parentSub, /** @type {Item} */ (left))
|
||||||
right.parent._map.set(right.parentSub, left)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// replace deleted items with ItemDeleted / GC
|
// replace deleted items with ItemDeleted / GC
|
||||||
for (const [client, deleteItems] of ds.clients) {
|
for (const [client, deleteItems] of ds.clients) {
|
||||||
/**
|
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(client))
|
||||||
* @type {Array<AbstractStruct>}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const structs = store.clients.get(client)
|
|
||||||
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
||||||
const deleteItem = deleteItems[di]
|
const deleteItem = deleteItems[di]
|
||||||
const endDeleteItemClock = deleteItem.clock + deleteItem.len
|
const endDeleteItemClock = deleteItem.clock + deleteItem.len
|
||||||
@ -205,7 +200,7 @@ export const transact = (doc, f, origin = null) => {
|
|||||||
if (deleteItem.clock + deleteItem.len <= struct.id.clock) {
|
if (deleteItem.clock + deleteItem.len <= struct.id.clock) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (struct.deleted && struct instanceof AbstractItem) {
|
if (struct.deleted && struct instanceof Item) {
|
||||||
struct.gc(store, false)
|
struct.gc(store, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,11 +209,7 @@ export const transact = (doc, f, origin = null) => {
|
|||||||
// try to merge deleted / gc'd items
|
// try to merge deleted / gc'd items
|
||||||
// merge from right to left for better efficiecy and so we don't miss any merge targets
|
// merge from right to left for better efficiecy and so we don't miss any merge targets
|
||||||
for (const [client, deleteItems] of ds.clients) {
|
for (const [client, deleteItems] of ds.clients) {
|
||||||
/**
|
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(client))
|
||||||
* @type {Array<AbstractStruct>}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const structs = store.clients.get(client)
|
|
||||||
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
||||||
const deleteItem = deleteItems[di]
|
const deleteItem = deleteItems[di]
|
||||||
// start with merging the item next to the last deleted item
|
// start with merging the item next to the last deleted item
|
||||||
@ -237,11 +228,7 @@ export const transact = (doc, f, origin = null) => {
|
|||||||
for (const [client, clock] of transaction.afterState) {
|
for (const [client, clock] of transaction.afterState) {
|
||||||
const beforeClock = transaction.beforeState.get(client) || 0
|
const beforeClock = transaction.beforeState.get(client) || 0
|
||||||
if (beforeClock !== clock) {
|
if (beforeClock !== clock) {
|
||||||
/**
|
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(client))
|
||||||
* @type {Array<AbstractStruct>}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const structs = store.clients.get(client)
|
|
||||||
// we iterate from right to left so we can safely remove entries
|
// we iterate from right to left so we can safely remove entries
|
||||||
const firstChangePos = math.max(findIndexSS(structs, beforeClock), 1)
|
const firstChangePos = math.max(findIndexSS(structs, beforeClock), 1)
|
||||||
for (let i = structs.length - 1; i >= firstChangePos; i--) {
|
for (let i = structs.length - 1; i >= firstChangePos; i--) {
|
||||||
@ -255,11 +242,7 @@ export const transact = (doc, f, origin = null) => {
|
|||||||
for (const mid of transaction._mergeStructs) {
|
for (const mid of transaction._mergeStructs) {
|
||||||
const client = mid.client
|
const client = mid.client
|
||||||
const clock = mid.clock
|
const clock = mid.clock
|
||||||
/**
|
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(client))
|
||||||
* @type {Array<AbstractStruct>}
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
const structs = store.clients.get(client)
|
|
||||||
const replacedStructPos = findIndexSS(structs, clock)
|
const replacedStructPos = findIndexSS(structs, clock)
|
||||||
if (replacedStructPos + 1 < structs.length) {
|
if (replacedStructPos + 1 < structs.length) {
|
||||||
tryToMergeWithLeft(structs, replacedStructPos + 1)
|
tryToMergeWithLeft(structs, replacedStructPos + 1)
|
||||||
|
@ -200,4 +200,3 @@ export class UndoManager {
|
|||||||
return performedRedo
|
return performedRedo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @module encoding
|
* @module encoding
|
||||||
|
*
|
||||||
|
* We use the first five bits in the info flag for determining the type of the struct.
|
||||||
|
*
|
||||||
|
* 0: GC
|
||||||
|
* 1: Item with Deleted content
|
||||||
|
* 2: Item with JSON content
|
||||||
|
* 3: Item with Binary content
|
||||||
|
* 4: Item with String content
|
||||||
|
* 5: Item with Embed content (for richtext content)
|
||||||
|
* 6: Item with Format content (a formatting marker for richtext content)
|
||||||
|
* 7: Item with Type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
findIndexSS,
|
findIndexSS,
|
||||||
GCRef,
|
GCRef,
|
||||||
ItemBinaryRef,
|
ItemRef,
|
||||||
ItemDeletedRef,
|
|
||||||
ItemEmbedRef,
|
|
||||||
ItemFormatRef,
|
|
||||||
ItemJSONRef,
|
|
||||||
ItemStringRef,
|
|
||||||
ItemTypeRef,
|
|
||||||
writeID,
|
writeID,
|
||||||
createID,
|
createID,
|
||||||
readID,
|
readID,
|
||||||
@ -21,27 +26,13 @@ import {
|
|||||||
readDeleteSet,
|
readDeleteSet,
|
||||||
writeDeleteSet,
|
writeDeleteSet,
|
||||||
createDeleteSetFromStructStore,
|
createDeleteSetFromStructStore,
|
||||||
Doc, Transaction, AbstractStruct, AbstractStructRef, StructStore, ID // eslint-disable-line
|
Doc, Transaction, AbstractStruct, StructStore, ID // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
import * as encoding from 'lib0/encoding.js'
|
||||||
import * as decoding from 'lib0/decoding.js'
|
import * as decoding from 'lib0/decoding.js'
|
||||||
import * as binary from 'lib0/binary.js'
|
import * as binary from 'lib0/binary.js'
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export const structRefs = [
|
|
||||||
GCRef,
|
|
||||||
ItemBinaryRef,
|
|
||||||
ItemDeletedRef,
|
|
||||||
ItemEmbedRef,
|
|
||||||
ItemFormatRef,
|
|
||||||
ItemJSONRef,
|
|
||||||
ItemStringRef,
|
|
||||||
ItemTypeRef
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {encoding.Encoder} encoder
|
||||||
* @param {Array<AbstractStruct>} structs All structs by `client`
|
* @param {Array<AbstractStruct>} structs All structs by `client`
|
||||||
@ -68,19 +59,19 @@ const writeStructs = (encoder, structs, client, clock) => {
|
|||||||
* @param {decoding.Decoder} decoder
|
* @param {decoding.Decoder} decoder
|
||||||
* @param {number} numOfStructs
|
* @param {number} numOfStructs
|
||||||
* @param {ID} nextID
|
* @param {ID} nextID
|
||||||
* @return {Array<AbstractStructRef>}
|
* @return {Array<GCRef|ItemRef>}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
const readStructRefs = (decoder, numOfStructs, nextID) => {
|
const readStructRefs = (decoder, numOfStructs, nextID) => {
|
||||||
/**
|
/**
|
||||||
* @type {Array<AbstractStructRef>}
|
* @type {Array<GCRef|ItemRef>}
|
||||||
*/
|
*/
|
||||||
const refs = []
|
const refs = []
|
||||||
for (let i = 0; i < numOfStructs; i++) {
|
for (let i = 0; i < numOfStructs; i++) {
|
||||||
const info = decoding.readUint8(decoder)
|
const info = decoding.readUint8(decoder)
|
||||||
const ref = new structRefs[binary.BITS5 & info](decoder, nextID, info)
|
const ref = (binary.BITS5 & info) === 0 ? new GCRef(decoder, nextID, info) : new ItemRef(decoder, nextID, info)
|
||||||
nextID = createID(nextID.client, nextID.clock + ref.length)
|
nextID = createID(nextID.client, nextID.clock + ref.length)
|
||||||
refs.push(ref)
|
refs.push(ref)
|
||||||
}
|
}
|
||||||
@ -119,14 +110,14 @@ export const writeClientsStructs = (encoder, store, _sm) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder The decoder object to read data from.
|
* @param {decoding.Decoder} decoder The decoder object to read data from.
|
||||||
* @return {Map<number,Array<AbstractStructRef>>}
|
* @return {Map<number,Array<GCRef|ItemRef>>}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const readClientsStructRefs = decoder => {
|
export const readClientsStructRefs = decoder => {
|
||||||
/**
|
/**
|
||||||
* @type {Map<number,Array<AbstractStructRef>>}
|
* @type {Map<number,Array<GCRef|ItemRef>>}
|
||||||
*/
|
*/
|
||||||
const clientRefs = new Map()
|
const clientRefs = new Map()
|
||||||
const numOfStateUpdates = decoding.readVarUint(decoder)
|
const numOfStateUpdates = decoding.readVarUint(decoder)
|
||||||
@ -254,7 +245,7 @@ export const writeStructsFromTransaction = (encoder, transaction) => writeClient
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @param {Map<number, Array<AbstractStructRef>>} clientsStructsRefs
|
* @param {Map<number, Array<GCRef|ItemRef>>} clientsStructsRefs
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
|
@ -1,36 +1,26 @@
|
|||||||
import * as t from 'lib0/testing.js'
|
import * as t from 'lib0/testing.js'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
structRefs,
|
contentRefs,
|
||||||
structGCRefNumber,
|
readContentBinary,
|
||||||
structBinaryRefNumber,
|
readContentDeleted,
|
||||||
structDeletedRefNumber,
|
readContentString,
|
||||||
structEmbedRefNumber,
|
readContentJSON,
|
||||||
structFormatRefNumber,
|
readContentEmbed,
|
||||||
structJSONRefNumber,
|
readContentType,
|
||||||
structStringRefNumber,
|
readContentFormat
|
||||||
structTypeRefNumber,
|
|
||||||
GCRef,
|
|
||||||
ItemBinaryRef,
|
|
||||||
ItemDeletedRef,
|
|
||||||
ItemEmbedRef,
|
|
||||||
ItemFormatRef,
|
|
||||||
ItemJSONRef,
|
|
||||||
ItemStringRef,
|
|
||||||
ItemTypeRef
|
|
||||||
} from '../src/internals.js'
|
} from '../src/internals.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testStructReferences = tc => {
|
export const testStructReferences = tc => {
|
||||||
t.assert(structRefs.length === 8)
|
t.assert(contentRefs.length === 8)
|
||||||
t.assert(structRefs[structGCRefNumber] === GCRef)
|
t.assert(contentRefs[1] === readContentDeleted)
|
||||||
t.assert(structRefs[structBinaryRefNumber] === ItemBinaryRef)
|
t.assert(contentRefs[2] === readContentJSON)
|
||||||
t.assert(structRefs[structDeletedRefNumber] === ItemDeletedRef)
|
t.assert(contentRefs[3] === readContentBinary)
|
||||||
t.assert(structRefs[structEmbedRefNumber] === ItemEmbedRef)
|
t.assert(contentRefs[4] === readContentString)
|
||||||
t.assert(structRefs[structFormatRefNumber] === ItemFormatRef)
|
t.assert(contentRefs[5] === readContentEmbed)
|
||||||
t.assert(structRefs[structJSONRefNumber] === ItemJSONRef)
|
t.assert(contentRefs[6] === readContentFormat)
|
||||||
t.assert(structRefs[structStringRefNumber] === ItemStringRef)
|
t.assert(contentRefs[7] === readContentType)
|
||||||
t.assert(structRefs[structTypeRefNumber] === ItemTypeRef)
|
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ import * as Y from '../src/index.js'
|
|||||||
import {
|
import {
|
||||||
createDeleteSetFromStructStore,
|
createDeleteSetFromStructStore,
|
||||||
getStateVector,
|
getStateVector,
|
||||||
AbstractItem,
|
Item,
|
||||||
DeleteSet, StructStore, Doc // eslint-disable-line
|
DeleteItem, DeleteSet, StructStore, Doc // eslint-disable-line
|
||||||
} from '../src/internals.js'
|
} from '../src/internals.js'
|
||||||
|
|
||||||
import * as t from 'lib0/testing.js'
|
import * as t from 'lib0/testing.js'
|
||||||
@ -12,6 +12,7 @@ import * as prng from 'lib0/prng.js'
|
|||||||
import * as encoding from 'lib0/encoding.js'
|
import * as encoding from 'lib0/encoding.js'
|
||||||
import * as decoding from 'lib0/decoding.js'
|
import * as decoding from 'lib0/decoding.js'
|
||||||
import * as syncProtocol from 'y-protocols/sync.js'
|
import * as syncProtocol from 'y-protocols/sync.js'
|
||||||
|
export * from '../src/internals.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TestYInstance} y // publish message created by `y` to all other online clients
|
* @param {TestYInstance} y // publish message created by `y` to all other online clients
|
||||||
@ -240,8 +241,7 @@ export const init = (tc, { users = 5 } = {}, initTestObject) => {
|
|||||||
}
|
}
|
||||||
testConnector.syncAll()
|
testConnector.syncAll()
|
||||||
result.testObjects = result.users.map(initTestObject || (() => null))
|
result.testObjects = result.users.map(initTestObject || (() => null))
|
||||||
// @ts-ignore
|
return /** @type {any} */ (result)
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -282,8 +282,7 @@ export const compare = users => {
|
|||||||
t.compare(userArrayValues[i], userArrayValues[i + 1])
|
t.compare(userArrayValues[i], userArrayValues[i + 1])
|
||||||
t.compare(userMapValues[i], userMapValues[i + 1])
|
t.compare(userMapValues[i], userMapValues[i + 1])
|
||||||
t.compare(userXmlValues[i], userXmlValues[i + 1])
|
t.compare(userXmlValues[i], userXmlValues[i + 1])
|
||||||
// @ts-ignore
|
t.compare(userTextValues[i].map(/** @param {any} a */ a => a.insert).join('').length, users[i].getText('text').length)
|
||||||
t.compare(userTextValues[i].map(a => a.insert).join('').length, users[i].getText('text').length)
|
|
||||||
t.compare(userTextValues[i], userTextValues[i + 1])
|
t.compare(userTextValues[i], userTextValues[i + 1])
|
||||||
t.compare(getStateVector(users[i].store), getStateVector(users[i + 1].store))
|
t.compare(getStateVector(users[i].store), getStateVector(users[i + 1].store))
|
||||||
compareDS(createDeleteSetFromStructStore(users[i].store), createDeleteSetFromStructStore(users[i + 1].store))
|
compareDS(createDeleteSetFromStructStore(users[i].store), createDeleteSetFromStructStore(users[i + 1].store))
|
||||||
@ -293,8 +292,8 @@ export const compare = users => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AbstractItem?} a
|
* @param {Item?} a
|
||||||
* @param {AbstractItem?} b
|
* @param {Item?} b
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
export const compareItemIDs = (a, b) => a === b || (a !== null && b != null && Y.compareIDs(a.id, b.id))
|
export const compareItemIDs = (a, b) => a === b || (a !== null && b != null && Y.compareIDs(a.id, b.id))
|
||||||
@ -306,11 +305,10 @@ export const compareItemIDs = (a, b) => a === b || (a !== null && b != null && Y
|
|||||||
export const compareStructStores = (ss1, ss2) => {
|
export const compareStructStores = (ss1, ss2) => {
|
||||||
t.assert(ss1.clients.size === ss2.clients.size)
|
t.assert(ss1.clients.size === ss2.clients.size)
|
||||||
for (const [client, structs1] of ss1.clients) {
|
for (const [client, structs1] of ss1.clients) {
|
||||||
const structs2 = ss2.clients.get(client)
|
const structs2 = /** @type {Array<Y.AbstractStruct>} */ (ss2.clients.get(client))
|
||||||
t.assert(structs2 !== undefined && structs1.length === structs2.length)
|
t.assert(structs2 !== undefined && structs1.length === structs2.length)
|
||||||
for (let i = 0; i < structs1.length; i++) {
|
for (let i = 0; i < structs1.length; i++) {
|
||||||
const s1 = structs1[i]
|
const s1 = structs1[i]
|
||||||
// @ts-ignore
|
|
||||||
const s2 = structs2[i]
|
const s2 = structs2[i]
|
||||||
// checks for abstract struct
|
// checks for abstract struct
|
||||||
if (
|
if (
|
||||||
@ -321,9 +319,9 @@ export const compareStructStores = (ss1, ss2) => {
|
|||||||
) {
|
) {
|
||||||
t.fail('Structs dont match')
|
t.fail('Structs dont match')
|
||||||
}
|
}
|
||||||
if (s1 instanceof AbstractItem) {
|
if (s1 instanceof Item) {
|
||||||
if (
|
if (
|
||||||
!(s2 instanceof AbstractItem) ||
|
!(s2 instanceof Item) ||
|
||||||
!((s1.left === null && s2.left === null) || (s1.left !== null && s2.left !== null && Y.compareIDs(s1.left.lastId, s2.left.lastId))) ||
|
!((s1.left === null && s2.left === null) || (s1.left !== null && s2.left !== null && Y.compareIDs(s1.left.lastId, s2.left.lastId))) ||
|
||||||
!compareItemIDs(s1.right, s2.right) ||
|
!compareItemIDs(s1.right, s2.right) ||
|
||||||
!Y.compareIDs(s1.origin, s2.origin) ||
|
!Y.compareIDs(s1.origin, s2.origin) ||
|
||||||
@ -349,11 +347,10 @@ export const compareStructStores = (ss1, ss2) => {
|
|||||||
export const compareDS = (ds1, ds2) => {
|
export const compareDS = (ds1, ds2) => {
|
||||||
t.assert(ds1.clients.size === ds2.clients.size)
|
t.assert(ds1.clients.size === ds2.clients.size)
|
||||||
for (const [client, deleteItems1] of ds1.clients) {
|
for (const [client, deleteItems1] of ds1.clients) {
|
||||||
const deleteItems2 = ds2.clients.get(client)
|
const deleteItems2 = /** @type {Array<DeleteItem>} */ (ds2.clients.get(client))
|
||||||
t.assert(deleteItems2 !== undefined && deleteItems1.length === deleteItems2.length)
|
t.assert(deleteItems2 !== undefined && deleteItems1.length === deleteItems2.length)
|
||||||
for (let i = 0; i < deleteItems1.length; i++) {
|
for (let i = 0; i < deleteItems1.length; i++) {
|
||||||
const di1 = deleteItems1[i]
|
const di1 = deleteItems1[i]
|
||||||
// @ts-ignore
|
|
||||||
const di2 = deleteItems2[i]
|
const di2 = deleteItems2[i]
|
||||||
if (di1.clock !== di2.clock || di1.len !== di2.len) {
|
if (di1.clock !== di2.clock || di1.len !== di2.len) {
|
||||||
t.fail('DeleteSets dont match')
|
t.fail('DeleteSets dont match')
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { init, compare, applyRandomTests, TestYInstance } from './testHelper.js' // eslint-disable-line
|
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
|
||||||
|
|
||||||
import * as Y from '../src/index.js'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing.js'
|
import * as t from 'lib0/testing.js'
|
||||||
@ -282,7 +282,7 @@ let _uniqueNumber = 0
|
|||||||
const getUniqueNumber = () => _uniqueNumber++
|
const getUniqueNumber = () => _uniqueNumber++
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Array<function(TestYInstance,prng.PRNG):void>}
|
* @type {Array<function(Doc,prng.PRNG,any):void>}
|
||||||
*/
|
*/
|
||||||
const arrayTransactions = [
|
const arrayTransactions = [
|
||||||
function insert (user, gen) {
|
function insert (user, gen) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { init, compare, applyRandomTests, TestYInstance } from './testHelper.js' // eslint-disable-line
|
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
|
||||||
|
|
||||||
import {
|
import {
|
||||||
compareIDs
|
compareIDs
|
||||||
@ -328,7 +328,7 @@ export const testYmapEventHasCorrectValueWhenSettingAPrimitiveFromOtherUser = tc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Array<function(TestYInstance,prng.PRNG):void>}
|
* @type {Array<function(Doc,prng.PRNG):void>}
|
||||||
*/
|
*/
|
||||||
const mapTransactions = [
|
const mapTransactions = [
|
||||||
function set (user, gen) {
|
function set (user, gen) {
|
||||||
|
@ -63,7 +63,7 @@ export const testBasicFormat = tc => {
|
|||||||
t.compare(text0.toDelta(), [{ insert: 'zb', attributes: { bold: true } }])
|
t.compare(text0.toDelta(), [{ insert: 'zb', attributes: { bold: true } }])
|
||||||
t.compare(delta, [{ insert: 'z', attributes: { bold: true } }])
|
t.compare(delta, [{ insert: 'z', attributes: { bold: true } }])
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
t.assert(text0._start.right.right.right.string === 'b', 'Does not insert duplicate attribute marker')
|
t.assert(text0._start.right.right.right.content.str === 'b', 'Does not insert duplicate attribute marker')
|
||||||
text0.insert(0, 'y')
|
text0.insert(0, 'y')
|
||||||
t.assert(text0.toString() === 'yzb')
|
t.assert(text0.toString() === 'yzb')
|
||||||
t.compare(text0.toDelta(), [{ insert: 'y' }, { insert: 'zb', attributes: { bold: true } }])
|
t.compare(text0.toDelta(), [{ insert: 'y' }, { insert: 'zb', attributes: { bold: true } }])
|
||||||
@ -79,11 +79,11 @@ export const testBasicFormat = tc => {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testGetDeltaWithEmbeds = tc => {
|
export const testGetDeltaWithEmbeds = tc => {
|
||||||
const { users, text0 } = init(tc, { users: 1 })
|
const { text0 } = init(tc, { users: 1 })
|
||||||
text0.applyDelta([{
|
text0.applyDelta([{
|
||||||
insert: {linebreak: "s"}
|
insert: {linebreak: 's'}
|
||||||
}])
|
}])
|
||||||
t.compare(text0.toDelta(), [{
|
t.compare(text0.toDelta(), [{
|
||||||
insert: {linebreak: "s"}
|
insert: {linebreak: 's'}
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,10 @@
|
|||||||
|
|
||||||
/* Module Resolution Options */
|
/* Module Resolution Options */
|
||||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
"paths": {
|
||||||
|
"yjs": ["./src/index.js"]
|
||||||
|
}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
// "types": [], /* Type declaration files to be included in compilation. */
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user