mirror of
https://github.com/yjs/yjs.git
synced 2026-02-23 19:49:59 +01:00
started refactor expose a single Yjs type only using lib0@v1 delta
This commit is contained in:
@@ -24,7 +24,7 @@ export {
|
||||
ContentAny,
|
||||
ContentString,
|
||||
ContentType,
|
||||
AbstractType,
|
||||
YType as AbstractType,
|
||||
getTypeChildren,
|
||||
createRelativePositionFromTypeIndex,
|
||||
createRelativePositionFromJSON,
|
||||
|
||||
@@ -19,16 +19,7 @@ export * from './utils/StructSet.js'
|
||||
export * from './utils/IdMap.js'
|
||||
export * from './utils/AttributionManager.js'
|
||||
export * from './utils/delta-helpers.js'
|
||||
|
||||
export * from './types/AbstractType.js'
|
||||
export * from './types/YArray.js'
|
||||
export * from './types/YMap.js'
|
||||
export * from './types/YText.js'
|
||||
export * from './types/YXmlFragment.js'
|
||||
export * from './types/YXmlElement.js'
|
||||
export * from './types/YXmlHook.js'
|
||||
export * from './types/YXmlText.js'
|
||||
|
||||
export * from './ytype.js'
|
||||
export * from './structs/AbstractStruct.js'
|
||||
export * from './structs/GC.js'
|
||||
export * from './structs/ContentBinary.js'
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
readContentType,
|
||||
addChangedTypeToTransaction,
|
||||
addStructToIdSet,
|
||||
IdSet, StackItem, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line
|
||||
IdSet, StackItem, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, YType, Transaction, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as error from 'lib0/error'
|
||||
@@ -282,7 +282,7 @@ export class Item extends AbstractStruct {
|
||||
* @param {ID | null} origin
|
||||
* @param {Item | null} right
|
||||
* @param {ID | null} rightOrigin
|
||||
* @param {AbstractType<any,any>|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it.
|
||||
* @param {YType<any,any>|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it.
|
||||
* @param {string | null} parentSub
|
||||
* @param {AbstractContent} content
|
||||
*/
|
||||
@@ -309,7 +309,7 @@ export class Item extends AbstractStruct {
|
||||
*/
|
||||
this.rightOrigin = rightOrigin
|
||||
/**
|
||||
* @type {AbstractType<any,any>|ID|null}
|
||||
* @type {YType<any,any>|ID|null}
|
||||
*/
|
||||
this.parent = parent
|
||||
/**
|
||||
@@ -696,7 +696,7 @@ export class Item extends AbstractStruct {
|
||||
encoder.writeRightID(rightOrigin)
|
||||
}
|
||||
if (origin === null && rightOrigin === null) {
|
||||
const parent = /** @type {AbstractType<any>} */ (this.parent)
|
||||
const parent = /** @type {YType<any>} */ (this.parent)
|
||||
if (parent._item !== undefined) {
|
||||
const parentItem = parent._item
|
||||
if (parentItem === null) {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { ObservableV2 } from 'lib0/observable'
|
||||
|
||||
import {
|
||||
Doc // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
/**
|
||||
* This is an abstract interface that all Connectors should implement to keep them interchangeable.
|
||||
*
|
||||
* @note This interface is experimental and it is not advised to actually inherit this class.
|
||||
* It just serves as typing information.
|
||||
*
|
||||
* @extends {ObservableV2<any>}
|
||||
*/
|
||||
export class AbstractConnector extends ObservableV2 {
|
||||
/**
|
||||
* @param {Doc} ydoc
|
||||
* @param {any} awareness
|
||||
*/
|
||||
constructor (ydoc, awareness) {
|
||||
super()
|
||||
this.doc = ydoc
|
||||
this.awareness = awareness
|
||||
}
|
||||
}
|
||||
116
src/utils/Doc.js
116
src/utils/Doc.js
@@ -4,12 +4,7 @@
|
||||
|
||||
import {
|
||||
StructStore,
|
||||
AbstractType,
|
||||
YArray,
|
||||
YText,
|
||||
YMap,
|
||||
YXmlElement,
|
||||
YXmlFragment,
|
||||
YType,
|
||||
transact,
|
||||
applyUpdate,
|
||||
ContentDoc, Item, Transaction, // eslint-disable-line
|
||||
@@ -24,13 +19,6 @@ import * as promise from 'lib0/promise'
|
||||
|
||||
export const generateNewClientId = random.uint32
|
||||
|
||||
/**
|
||||
* @typedef {import('../utils/types.js').YTypeConstructors} YTypeConstructors
|
||||
*/
|
||||
/**
|
||||
* @typedef {import('../utils/types.js').YType} YType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} DocOpts
|
||||
* @property {boolean} [DocOpts.gc=true] Disable garbage collection (default: gc=true)
|
||||
@@ -212,105 +200,17 @@ export class Doc extends ObservableV2 {
|
||||
* Define all types right after the Y.Doc instance is created and store them in a separate object.
|
||||
* Also use the typed methods `getText(name)`, `getArray(name)`, ..
|
||||
*
|
||||
* @template {YTypeConstructors} [TypeC=typeof AbstractType]
|
||||
* @example
|
||||
* const ydoc = new Y.Doc(..)
|
||||
* const appState = {
|
||||
* document: ydoc.getText('document')
|
||||
* comments: ydoc.getArray('comments')
|
||||
* }
|
||||
* @param {string} key
|
||||
* @param {string?} name Type-name
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {TypeC} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ...
|
||||
* @return {AbstractType} The created type. Constructed with TypeConstructor
|
||||
*
|
||||
* @public
|
||||
* @return {YType}
|
||||
*/
|
||||
get (name, TypeConstructor = /** @type {any} */ (AbstractType)) {
|
||||
const type = map.setIfUndefined(this.share, name, () => {
|
||||
// @ts-ignore
|
||||
const t = new TypeConstructor()
|
||||
get (key, name = null) {
|
||||
return map.setIfUndefined(this.share, key, () => {
|
||||
const t = new YType(name)
|
||||
t._integrate(this, null)
|
||||
return t
|
||||
})
|
||||
const Constr = type.constructor
|
||||
// @ts-ignore
|
||||
if (TypeConstructor !== AbstractType && Constr !== TypeConstructor) {
|
||||
if (Constr === AbstractType) {
|
||||
// @ts-ignore
|
||||
const t = new TypeConstructor()
|
||||
t._map = type._map
|
||||
type._map.forEach(/** @param {Item?} n */ n => {
|
||||
for (; n !== null; n = n.left) {
|
||||
// @ts-ignore
|
||||
n.parent = t
|
||||
}
|
||||
})
|
||||
t._start = type._start
|
||||
for (let n = t._start; n !== null; n = n.right) {
|
||||
n.parent = t
|
||||
}
|
||||
t._length = type._length
|
||||
this.share.set(name, t)
|
||||
t._integrate(this, null)
|
||||
return /** @type {InstanceType<TypeC>} */ (t)
|
||||
} else {
|
||||
throw new Error(`Type with the name ${name} has already been defined with a different constructor`)
|
||||
}
|
||||
}
|
||||
return /** @type {InstanceType<TypeC>} */ (type)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {string} [name]
|
||||
* @return {YArray<T>}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getArray (name = '') {
|
||||
return /** @type {YArray<any>} */ (this.get(name, YArray))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name]
|
||||
* @return {YText}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getText (name = '') {
|
||||
return /** @type {YText} */ (this.get(name, YText))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {string} [name]
|
||||
* @return {YMap<T>}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getMap (name = '') {
|
||||
return /** @type {YMap<T>} */ (this.get(name, YMap))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name]
|
||||
* @return {YXmlElement}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getXmlElement (name = '') {
|
||||
return /** @type {YXmlElement<{[key:string]:string}>} */ (this.get(name, YXmlElement))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name]
|
||||
* @return {YXmlFragment}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getXmlFragment (name = '') {
|
||||
return /** @type {YXmlFragment} */ (this.get(name, YXmlFragment))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,11 +226,9 @@ export class Doc extends ObservableV2 {
|
||||
* @type {Object<string, any>}
|
||||
*/
|
||||
const doc = {}
|
||||
|
||||
this.share.forEach((value, key) => {
|
||||
doc[key] = value.toJSON()
|
||||
})
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbstractType } from '../internals.js' // eslint-disable-line
|
||||
import { YType } from '../internals.js' // eslint-disable-line
|
||||
|
||||
import * as decoding from 'lib0/decoding'
|
||||
import * as encoding from 'lib0/encoding'
|
||||
@@ -72,7 +72,7 @@ export const readID = decoder =>
|
||||
* `type` does not store any information about the `keyname`.
|
||||
* This function finds the correct `keyname` for `type` and throws otherwise.
|
||||
*
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {YType<any>} type
|
||||
* @return {string}
|
||||
*
|
||||
* @private
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ContentType,
|
||||
followRedone,
|
||||
getItem,
|
||||
StructStore, ID, Doc, AbstractType, noAttributionsManager, // eslint-disable-line
|
||||
StructStore, ID, Doc, YType, noAttributionsManager, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding'
|
||||
@@ -106,13 +106,13 @@ export const createRelativePositionFromJSON = json => new RelativePosition(json.
|
||||
|
||||
export class AbsolutePosition {
|
||||
/**
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {YType<any>} type
|
||||
* @param {number} index
|
||||
* @param {number} [assoc]
|
||||
*/
|
||||
constructor (type, index, assoc = 0) {
|
||||
/**
|
||||
* @type {AbstractType<any>}
|
||||
* @type {YType<any>}
|
||||
*/
|
||||
this.type = type
|
||||
/**
|
||||
@@ -124,7 +124,7 @@ export class AbsolutePosition {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {YType<any>} type
|
||||
* @param {number} index
|
||||
* @param {number} [assoc]
|
||||
*
|
||||
@@ -133,7 +133,7 @@ export class AbsolutePosition {
|
||||
export const createAbsolutePosition = (type, index, assoc = 0) => new AbsolutePosition(type, index, assoc)
|
||||
|
||||
/**
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {YType<any>} type
|
||||
* @param {ID|null} item
|
||||
* @param {number} [assoc]
|
||||
*
|
||||
@@ -153,7 +153,7 @@ export const createRelativePosition = (type, item, assoc) => {
|
||||
/**
|
||||
* Create a relativePosition based on a absolute position.
|
||||
*
|
||||
* @param {AbstractType} type The base type (e.g. YText or YArray).
|
||||
* @param {YType} type The base type (e.g. YText or YArray).
|
||||
* @param {number} index The absolute position.
|
||||
* @param {number} [assoc]
|
||||
* @param {import('../utils/AttributionManager.js').AbstractAttributionManager} attributionManager
|
||||
@@ -304,7 +304,7 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc, followUndo
|
||||
if (!(right instanceof Item)) {
|
||||
return null
|
||||
}
|
||||
type = /** @type {AbstractType<any>} */ (right.parent)
|
||||
type = /** @type {YType<any>} */ (right.parent)
|
||||
if (type._item === null || !type._item.deleted) {
|
||||
index = attributionManager.contentLength(right) === 0 ? 0 : (res.diff + (assoc >= 0 ? 0 : 1)) // adjust position based on left association if necessary
|
||||
let n = right.left
|
||||
|
||||
@@ -9,10 +9,12 @@ import {
|
||||
Item,
|
||||
generateNewClientId,
|
||||
createID,
|
||||
cleanupYTextAfterTransaction,
|
||||
IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
|
||||
iterateStructsByIdSet,
|
||||
ContentFormat,
|
||||
IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractStruct, YEvent, Doc // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import {YType} from '../ytype.js'
|
||||
import * as error from 'lib0/error'
|
||||
import * as map from 'lib0/map'
|
||||
import * as math from 'lib0/math'
|
||||
@@ -84,13 +86,13 @@ export class Transaction {
|
||||
* All types that were directly modified (property added or child
|
||||
* inserted/deleted). New types are not included in this Set.
|
||||
* Maps from type to parentSubs (`item.parentSub = null` for YArray)
|
||||
* @type {Map<import('../utils/types.js').YType,Set<String|null>>}
|
||||
* @type {Map<YType,Set<String|null>>}
|
||||
*/
|
||||
this.changed = new Map()
|
||||
/**
|
||||
* Stores the events for the types that observe also child elements.
|
||||
* It is mainly used by `observeDeep`.
|
||||
* @type {Map<import('../utils/types.js').YType,Array<YEvent<any>>>}
|
||||
* @type {Map<YType,Array<YEvent<any>>>}
|
||||
*/
|
||||
this.changedParentTypes = new Map()
|
||||
/**
|
||||
@@ -198,7 +200,7 @@ export const nextID = transaction => {
|
||||
* did not change, it was just added and we should not fire events for `type`.
|
||||
*
|
||||
* @param {Transaction} transaction
|
||||
* @param {import('../utils/types.js').YType} type
|
||||
* @param {YType} type
|
||||
* @param {string|null} parentSub
|
||||
*/
|
||||
export const addChangedTypeToTransaction = (transaction, type, parentSub) => {
|
||||
@@ -220,8 +222,8 @@ const tryToMergeWithLefts = (structs, pos) => {
|
||||
for (; i > 0; right = left, left = structs[--i - 1]) {
|
||||
if (left.deleted === right.deleted && left.constructor === right.constructor) {
|
||||
if (left.mergeWith(right)) {
|
||||
if (right instanceof Item && right.parentSub !== null && /** @type {AbstractType<any>} */ (right.parent)._map.get(right.parentSub) === right) {
|
||||
/** @type {AbstractType<any>} */ (right.parent)._map.set(right.parentSub, /** @type {Item} */ (left))
|
||||
if (right instanceof Item && right.parentSub !== null && /** @type {YType} */ (right.parent)._map.get(right.parentSub) === right) {
|
||||
/** @type {YType} */ (right.parent)._map.set(right.parentSub, /** @type {Item} */ (left))
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -300,6 +302,200 @@ export const tryGc = (tr, idset, gcFilter) => {
|
||||
tryMerge(idset, tr.doc.store)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {Item | null} item
|
||||
*/
|
||||
const cleanupContextlessFormattingGap = (transaction, item) => {
|
||||
if (!transaction.doc.cleanupFormatting) return 0
|
||||
// iterate until item.right is null or content
|
||||
while (item && item.right && (item.right.deleted || !item.right.countable)) {
|
||||
item = item.right
|
||||
}
|
||||
const attrs = new Set()
|
||||
// iterate back until a content item is found
|
||||
while (item && (item.deleted || !item.countable)) {
|
||||
if (!item.deleted && item.content.constructor === ContentFormat) {
|
||||
const key = /** @type {ContentFormat} */ (item.content).key
|
||||
if (attrs.has(key)) {
|
||||
item.delete(transaction)
|
||||
transaction.cleanUps.add(item.id.client, item.id.clock, item.length)
|
||||
} else {
|
||||
attrs.add(key)
|
||||
}
|
||||
}
|
||||
item = item.left
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Map<string,any>} currentAttributes
|
||||
* @param {ContentFormat} format
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
const updateCurrentAttributes = (currentAttributes, { key, value }) => {
|
||||
if (value === null) {
|
||||
currentAttributes.delete(key)
|
||||
} else {
|
||||
currentAttributes.set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call this function after string content has been deleted in order to
|
||||
* clean up formatting Items.
|
||||
*
|
||||
* @param {Transaction} transaction
|
||||
* @param {Item} start
|
||||
* @param {Item|null} curr exclusive end, automatically iterates to the next Content Item
|
||||
* @param {Map<string,any>} startAttributes
|
||||
* @param {Map<string,any>} currAttributes
|
||||
* @return {number} The amount of formatting Items deleted.
|
||||
*
|
||||
* @function
|
||||
*/
|
||||
export const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => {
|
||||
if (!transaction.doc.cleanupFormatting) return 0
|
||||
/**
|
||||
* @type {Item|null}
|
||||
*/
|
||||
let end = start
|
||||
/**
|
||||
* @type {Map<string,ContentFormat>}
|
||||
*/
|
||||
const endFormats = map.create()
|
||||
while (end && (!end.countable || end.deleted)) {
|
||||
if (!end.deleted && end.content.constructor === ContentFormat) {
|
||||
const cf = /** @type {ContentFormat} */ (end.content)
|
||||
endFormats.set(cf.key, cf)
|
||||
}
|
||||
end = end.right
|
||||
}
|
||||
let cleanups = 0
|
||||
let reachedCurr = false
|
||||
while (start !== end) {
|
||||
if (curr === start) {
|
||||
reachedCurr = true
|
||||
}
|
||||
if (!start.deleted) {
|
||||
const content = start.content
|
||||
switch (content.constructor) {
|
||||
case ContentFormat: {
|
||||
const { key, value } = /** @type {ContentFormat} */ (content)
|
||||
const startAttrValue = startAttributes.get(key) ?? null
|
||||
if (endFormats.get(key) !== content || startAttrValue === value) {
|
||||
// Either this format is overwritten or it is not necessary because the attribute already existed.
|
||||
start.delete(transaction)
|
||||
transaction.cleanUps.add(start.id.client, start.id.clock, start.length)
|
||||
cleanups++
|
||||
if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) {
|
||||
if (startAttrValue === null) {
|
||||
currAttributes.delete(key)
|
||||
} else {
|
||||
currAttributes.set(key, startAttrValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!reachedCurr && !start.deleted) {
|
||||
updateCurrentAttributes(currAttributes, /** @type {ContentFormat} */ (content))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
start = /** @type {Item} */ (start.right)
|
||||
}
|
||||
return cleanups
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function is experimental and subject to change / be removed.
|
||||
*
|
||||
* Ideally, we don't need this function at all. Formatting attributes should be cleaned up
|
||||
* automatically after each change. This function iterates twice over the complete YText type
|
||||
* and removes unnecessary formatting attributes. This is also helpful for testing.
|
||||
*
|
||||
* This function won't be exported anymore as soon as there is confidence that the YText type works as intended.
|
||||
*
|
||||
* @param {YType} type
|
||||
* @return {number} How many formatting attributes have been cleaned up.
|
||||
*/
|
||||
export const cleanupYTextFormatting = type => {
|
||||
if (!type.doc?.cleanupFormatting) return 0
|
||||
let res = 0
|
||||
transact(/** @type {Doc} */ (type.doc), transaction => {
|
||||
let start = /** @type {Item} */ (type._start)
|
||||
let end = type._start
|
||||
let startAttributes = map.create()
|
||||
const currentAttributes = map.copy(startAttributes)
|
||||
while (end) {
|
||||
if (end.deleted === false) {
|
||||
switch (end.content.constructor) {
|
||||
case ContentFormat:
|
||||
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (end.content))
|
||||
break
|
||||
default:
|
||||
res += cleanupFormattingGap(transaction, start, end, startAttributes, currentAttributes)
|
||||
startAttributes = map.copy(currentAttributes)
|
||||
start = end
|
||||
break
|
||||
}
|
||||
}
|
||||
end = end.right
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This will be called by the transaction once the event handlers are called to potentially cleanup
|
||||
* formatting attributes.
|
||||
*
|
||||
* @param {Transaction} transaction
|
||||
*/
|
||||
export const cleanupYTextAfterTransaction = transaction => {
|
||||
/**
|
||||
* @type {Set<YType>}
|
||||
*/
|
||||
const needFullCleanup = new Set()
|
||||
// check if another formatting item was inserted
|
||||
const doc = transaction.doc
|
||||
iterateStructsByIdSet(transaction, transaction.insertSet, (item) => {
|
||||
if (
|
||||
!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC
|
||||
) {
|
||||
needFullCleanup.add(/** @type {any} */ (item).parent)
|
||||
}
|
||||
})
|
||||
// cleanup in a new transaction
|
||||
transact(doc, (t) => {
|
||||
iterateStructsByIdSet(transaction, transaction.deleteSet, item => {
|
||||
if (item instanceof GC || !(/** @type {YType} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YType} */ (item.parent))) {
|
||||
return
|
||||
}
|
||||
const parent = /** @type {YType} */ (item.parent)
|
||||
if (item.content.constructor === ContentFormat) {
|
||||
needFullCleanup.add(parent)
|
||||
} else {
|
||||
// If no formatting attribute was inserted or deleted, we can make due with contextless
|
||||
// formatting cleanups.
|
||||
// Contextless: it is not necessary to compute currentAttributes for the affected position.
|
||||
cleanupContextlessFormattingGap(t, item)
|
||||
}
|
||||
})
|
||||
// If a formatting item was inserted, we simply clean the whole type.
|
||||
// We need to compute currentAttributes for the current position anyway.
|
||||
for (const yText of needFullCleanup) {
|
||||
cleanupYTextFormatting(yText)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<Transaction>} transactionCleanups
|
||||
* @param {number} i
|
||||
@@ -352,7 +548,12 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
||||
.sort((event1, event2) => event1.path.length - event2.path.length)
|
||||
// We don't need to check for events.length
|
||||
// because we know it has at least one element
|
||||
callEventHandlerListeners(type._dEH, events, transaction)
|
||||
|
||||
/**
|
||||
* @type {YEvent<any>}
|
||||
*/
|
||||
const deepEventHandler = events.find(event => event.target === type) || new YEvent(type, transaction, new Set(null))
|
||||
callEventHandlerListeners(type._dEH, deepEventHandler, transaction)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
isParentOf,
|
||||
followRedone,
|
||||
getItemCleanStart,
|
||||
YEvent, Transaction, Doc, Item, GC, IdSet, AbstractType // eslint-disable-line
|
||||
YEvent, Transaction, Doc, Item, GC, IdSet, YType // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as time from 'lib0/time'
|
||||
@@ -273,7 +273,7 @@ export class UndoManager extends ObservableV2 {
|
||||
ytypes.forEach(ytype => {
|
||||
if (!tmpSet.has(ytype)) {
|
||||
tmpSet.add(ytype)
|
||||
if (ytype instanceof AbstractType ? ytype.doc !== this.doc : ytype !== this.doc) logging.warn('[yjs#509] Not same Y.Doc') // use MultiDocUndoManager instead. also see https://github.com/yjs/yjs/issues/509
|
||||
if (ytype instanceof YType ? ytype.doc !== this.doc : ytype !== this.doc) logging.warn('[yjs#509] Not same Y.Doc') // use MultiDocUndoManager instead. also see https://github.com/yjs/yjs/issues/509
|
||||
this.scope.push(ytype)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
diffIdSet,
|
||||
mergeIdSets,
|
||||
noAttributionsManager,
|
||||
Doc, AbstractAttributionManager, Item, AbstractType, Transaction, AbstractStruct, // eslint-disable-line
|
||||
YType, Doc, AbstractAttributionManager, Item, Transaction, AbstractStruct, // eslint-disable-line
|
||||
createAbsolutePositionFromRelativePosition,
|
||||
createRelativePosition,
|
||||
AbsolutePosition
|
||||
@@ -18,19 +18,19 @@ import * as set from 'lib0/set'
|
||||
*/
|
||||
export class YEvent {
|
||||
/**
|
||||
* @param {AbstractType<DConf>} target The changed type.
|
||||
* @param {YType<DConf>} target The changed type.
|
||||
* @param {Transaction} transaction
|
||||
* @param {Set<any>?} subs The keys that changed
|
||||
*/
|
||||
constructor (target, transaction, subs) {
|
||||
/**
|
||||
* The type on which this event was created on.
|
||||
* @type {AbstractType<DConf>}
|
||||
* @type {YType<DConf>}
|
||||
*/
|
||||
this.target = target
|
||||
/**
|
||||
* The current target on which the observe callback is called.
|
||||
* @type {AbstractType<any>}
|
||||
* @type {YType<any>}
|
||||
*/
|
||||
this.currentTarget = target
|
||||
/**
|
||||
@@ -39,11 +39,11 @@ export class YEvent {
|
||||
*/
|
||||
this.transaction = transaction
|
||||
/**
|
||||
* @type {delta.Delta<DConf>|null}
|
||||
* @type {import('../ytype.js').DeltaConfTypesToDelta<DConf>|null}
|
||||
*/
|
||||
this._delta = null
|
||||
/**
|
||||
* @type {import('../types/AbstractType.js').DeltaConfTypesToDeltaDelta<DConf>|null}
|
||||
* @type {delta.Delta<DConf>|null}
|
||||
*/
|
||||
this._deltaDeep = null
|
||||
/**
|
||||
@@ -125,7 +125,7 @@ export class YEvent {
|
||||
/**
|
||||
* @todo this should be done only one in the transaction step
|
||||
*
|
||||
* @type {Map<import('./types.js').YType,Set<string|null>>|null}
|
||||
* @type {Map<YType,Set<string|null>>|null}
|
||||
*/
|
||||
let modified = this.transaction.changed
|
||||
if (deep) {
|
||||
@@ -184,8 +184,8 @@ export class YEvent {
|
||||
* console.log(path) // might look like => [2, 'key1']
|
||||
* child === type.get(path[0]).get(path[1])
|
||||
*
|
||||
* @param {AbstractType} parent
|
||||
* @param {AbstractType} child target
|
||||
* @param {YType} parent
|
||||
* @param {YType} child target
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @return {Array<string|number>} Path to the target
|
||||
*
|
||||
@@ -205,7 +205,7 @@ export const getPathTo = (parent, child, am = noAttributionsManager) => {
|
||||
const apos = /** @type {AbsolutePosition} */ (createAbsolutePositionFromRelativePosition(createRelativePosition(parent, child._item.id), doc, false, am))
|
||||
path.unshift(apos.index)
|
||||
}
|
||||
child = /** @type {AbstractType} */ (child._item.parent)
|
||||
child = /** @type {YType} */ (child._item.parent)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
diffIdSet,
|
||||
mergeIdSets,
|
||||
Item,
|
||||
AbstractType, Doc, // eslint-disable-line
|
||||
YType, Doc, // eslint-disable-line
|
||||
iterateStructsByIdSet
|
||||
} from '../internals.js'
|
||||
import * as delta from 'lib0/delta'
|
||||
@@ -28,12 +28,12 @@ export const diffDocsToDelta = (v1, v2, { am = createAttributionManagerFromDiff(
|
||||
const deletesOnly = diffIdSet(deleteDiff, insertDiff)
|
||||
const itemsToRender = mergeIdSets([insertsOnly, deleteDiff])
|
||||
/**
|
||||
* @type {Map<AbstractType, Set<string|null>>}
|
||||
* @type {Map<YType, Set<string|null>>}
|
||||
*/
|
||||
const changedTypes = new Map()
|
||||
iterateStructsByIdSet(tr, itemsToRender, /** @param {any} item */ item => {
|
||||
while (item instanceof Item) {
|
||||
const parent = /** @type {AbstractType} */ (item.parent)
|
||||
const parent = /** @type {YType} */ (item.parent)
|
||||
const conf = map.setIfUndefined(changedTypes, parent, set.create)
|
||||
if (conf.has(item.parentSub)) break // has already been marked as modified
|
||||
conf.add(item.parentSub)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { AbstractType, Item } from '../internals.js' // eslint-disable-line
|
||||
import { Item } from '../internals.js' // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Check if `parent` is a parent of `child`.
|
||||
*
|
||||
* @param {import('../utils/types.js').YType} parent
|
||||
* @param {import('../ytype.js').YType} parent
|
||||
* @param {Item|null} child
|
||||
* @return {Boolean} Whether `parent` is a parent of `child`.
|
||||
*
|
||||
@@ -15,7 +15,7 @@ export const isParentOf = (parent, child) => {
|
||||
if (child.parent === parent) {
|
||||
return true
|
||||
}
|
||||
child = /** @type {AbstractType<any>} */ (child.parent)._item
|
||||
child = /** @type {import('../ytype.js').YType} */ (child.parent)._item
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
AbstractType // eslint-disable-line
|
||||
YType // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
/**
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
*
|
||||
* Do not use in productive systems as the output can be immense!
|
||||
*
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {YType<any>} type
|
||||
*/
|
||||
export const logType = type => {
|
||||
const res = []
|
||||
|
||||
@@ -1,28 +1,3 @@
|
||||
/**
|
||||
* @typedef {import('../types/YArray.js').YArray<any>
|
||||
* | import('../types/YMap.js').YMap<any>
|
||||
* | import('../types/YText.js').YText<any>
|
||||
* | import('../types/YXmlFragment.js').YXmlFragment<any,any>
|
||||
* | import('../types/YXmlElement.js').YXmlElement<any,any>
|
||||
* | import('../types/YXmlHook.js').YXmlHook
|
||||
* | import('../types/YXmlText.js').YXmlText} YValueType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object<string,any>|Array<any>|number|null|string|Uint8Array|BigInt|YValueType} YValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('../types/AbstractType.js').AbstractType<any,any>} YType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {typeof import('../types/YArray.js').YArray<any>
|
||||
* | typeof import('../types/YMap.js').YMap<any>
|
||||
* | typeof import('../types/YText.js').YText<any>
|
||||
* | typeof import('../types/YXmlFragment.js').YXmlFragment<any,any>
|
||||
* | typeof import('../types/YXmlElement.js').YXmlElement<any,any>
|
||||
* | typeof import('../types/YXmlHook.js').YXmlHook
|
||||
* | typeof import('../types/YXmlText.js').YXmlText
|
||||
* | typeof import('../types/AbstractType.js').AbstractType} YTypeConstructors
|
||||
* @typedef {Object<string,any>|Array<any>|number|null|string|Uint8Array|BigInt|import('./').YType} YValue
|
||||
*/
|
||||
|
||||
@@ -33,8 +33,6 @@ import {
|
||||
UpdateEncoderV1,
|
||||
UpdateEncoderV2,
|
||||
writeIdSet,
|
||||
YXmlElement,
|
||||
YXmlHook,
|
||||
createIdSet
|
||||
} from '../internals.js'
|
||||
|
||||
@@ -617,12 +615,8 @@ const createObfuscator = ({ formatting = true, subdocs = true, yxml = true } = {
|
||||
case ContentType: {
|
||||
if (yxml) {
|
||||
const type = /** @type {ContentType} */ (content).type
|
||||
if (type instanceof YXmlElement) {
|
||||
type.nodeName = map.setIfUndefined(nodeNameCache, type.nodeName, () => 'node-' + i)
|
||||
}
|
||||
// @ts-ignore
|
||||
if (type instanceof YXmlHook) {
|
||||
type.hookName = map.setIfUndefined(nodeNameCache, type.hookName, () => 'hook-' + i)
|
||||
if (type.name != null) {
|
||||
type.name = map.setIfUndefined(nodeNameCache, type.nodeName, () => 'typename-' + i)
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
2065
src/ytype.js
Normal file
2065
src/ytype.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user