reimplemented WeakLink as an AbstractType
This commit is contained in:
parent
7b0d5fede5
commit
2c60c4a130
@ -40,6 +40,5 @@ export * from './structs/ContentJSON.js'
|
|||||||
export * from './structs/ContentAny.js'
|
export * from './structs/ContentAny.js'
|
||||||
export * from './structs/ContentString.js'
|
export * from './structs/ContentString.js'
|
||||||
export * from './structs/ContentType.js'
|
export * from './structs/ContentType.js'
|
||||||
export * from './structs/ContentLink.js'
|
|
||||||
export * from './structs/Item.js'
|
export * from './structs/Item.js'
|
||||||
export * from './structs/Skip.js'
|
export * from './structs/Skip.js'
|
||||||
|
@ -1,183 +0,0 @@
|
|||||||
import { decoding, encoding, error } from 'lib0'
|
|
||||||
import {
|
|
||||||
UpdateEncoderV1, UpdateEncoderV2, UpdateDecoderV1, UpdateDecoderV2, Transaction, Item, StructStore, // eslint-disable-line
|
|
||||||
YWeakLink,
|
|
||||||
AbstractType,
|
|
||||||
getItemCleanStart,
|
|
||||||
createID,
|
|
||||||
getItemCleanEnd
|
|
||||||
} from '../internals.js'
|
|
||||||
|
|
||||||
export class ContentLink {
|
|
||||||
/**
|
|
||||||
* @param {YWeakLink<any>} link
|
|
||||||
*/
|
|
||||||
constructor (link) {
|
|
||||||
this.link = link
|
|
||||||
/**
|
|
||||||
* @type {Item|null}
|
|
||||||
*/
|
|
||||||
this._item = null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
getLength () {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {Array<any>}
|
|
||||||
*/
|
|
||||||
getContent () {
|
|
||||||
return [this.link]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
isCountable () {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {ContentLink}
|
|
||||||
*/
|
|
||||||
copy () {
|
|
||||||
return new ContentLink(this.link)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} offset
|
|
||||||
* @return {ContentLink}
|
|
||||||
*/
|
|
||||||
splice (offset) {
|
|
||||||
throw error.methodUnimplemented()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {ContentLink} right
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
mergeWith (right) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {Item} item
|
|
||||||
*/
|
|
||||||
integrate (transaction, item) {
|
|
||||||
let sourceItem = this.link.item !== null ? this.link.item : getItemCleanStart(transaction, this.link.id)
|
|
||||||
if (sourceItem.constructor === Item && sourceItem.parentSub !== null) {
|
|
||||||
// for maps, advance to most recent item
|
|
||||||
while (sourceItem.right !== null) {
|
|
||||||
sourceItem = sourceItem.right
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!sourceItem.deleted && sourceItem.length > 1) {
|
|
||||||
sourceItem = getItemCleanEnd(transaction, transaction.doc.store, createID(sourceItem.id.client, sourceItem.id.clock + 1))
|
|
||||||
}
|
|
||||||
this.link.item = sourceItem
|
|
||||||
this._item = item
|
|
||||||
if (!sourceItem.deleted) {
|
|
||||||
const src = /** @type {Item} */ (sourceItem)
|
|
||||||
if (src.linkedBy === null) {
|
|
||||||
src.linkedBy = new Set()
|
|
||||||
}
|
|
||||||
src.linkedBy.add(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
*/
|
|
||||||
delete (transaction) {
|
|
||||||
if (this._item !== null && this.link !== null && this.link.item !== null && !this.link.item.deleted) {
|
|
||||||
const item = /** @type {Item} */ (this.link.item)
|
|
||||||
if (item.linkedBy !== null) {
|
|
||||||
item.linkedBy.delete(this._item)
|
|
||||||
}
|
|
||||||
this.link.item = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {StructStore} store
|
|
||||||
*/
|
|
||||||
gc (store) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
||||||
* @param {number} offset
|
|
||||||
*/
|
|
||||||
write (encoder, offset) {
|
|
||||||
const flags = 0 // flags that could be used in the future
|
|
||||||
encoding.writeUint8(encoder.restEncoder, flags)
|
|
||||||
encoder.writeLeftID(this.link.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
getRef () {
|
|
||||||
return 11
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
|
||||||
* @return {ContentLink}
|
|
||||||
*/
|
|
||||||
export const readContentWeakLink = decoder => {
|
|
||||||
const flags = decoding.readUint8(decoder.restDecoder)
|
|
||||||
const id = decoder.readLeftID()
|
|
||||||
return new ContentLink(new YWeakLink(id, null))
|
|
||||||
}
|
|
||||||
|
|
||||||
const lengthExceeded = error.create('Length exceeded!')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {WeakLink} to an YArray element at given index.
|
|
||||||
*
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {number} index
|
|
||||||
* @return {YWeakLink<any>}
|
|
||||||
*/
|
|
||||||
export const arrayWeakLink = (transaction, parent, index) => {
|
|
||||||
let item = parent._start
|
|
||||||
for (; item !== null; item = item.right) {
|
|
||||||
if (!item.deleted && item.countable) {
|
|
||||||
if (index < item.length) {
|
|
||||||
if (index > 0) {
|
|
||||||
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + index))
|
|
||||||
}
|
|
||||||
if (item.length > 1) {
|
|
||||||
item = getItemCleanEnd(transaction, transaction.doc.store, createID(item.id.client, item.id.clock + 1))
|
|
||||||
}
|
|
||||||
return new YWeakLink(item.id, item)
|
|
||||||
}
|
|
||||||
index -= item.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw lengthExceeded
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {WeakLink} to an YMap element at given key.
|
|
||||||
*
|
|
||||||
* @param {AbstractType<any>} parent
|
|
||||||
* @param {string} key
|
|
||||||
* @return {YWeakLink<any>|undefined}
|
|
||||||
*/
|
|
||||||
export const mapWeakLink = (parent, key) => {
|
|
||||||
const item = parent._map.get(key)
|
|
||||||
if (item !== undefined) {
|
|
||||||
return new YWeakLink(item.id, item)
|
|
||||||
} else {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,8 @@ import {
|
|||||||
readYXmlFragment,
|
readYXmlFragment,
|
||||||
readYXmlHook,
|
readYXmlHook,
|
||||||
readYXmlText,
|
readYXmlText,
|
||||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item, YEvent, AbstractType // eslint-disable-line
|
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item, YEvent, AbstractType, // eslint-disable-line
|
||||||
|
readYWeakLink
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as error from 'lib0/error'
|
import * as error from 'lib0/error'
|
||||||
@ -23,7 +24,8 @@ export const typeRefs = [
|
|||||||
readYXmlElement,
|
readYXmlElement,
|
||||||
readYXmlFragment,
|
readYXmlFragment,
|
||||||
readYXmlHook,
|
readYXmlHook,
|
||||||
readYXmlText
|
readYXmlText,
|
||||||
|
readYWeakLink
|
||||||
]
|
]
|
||||||
|
|
||||||
export const YArrayRefID = 0
|
export const YArrayRefID = 0
|
||||||
@ -33,6 +35,7 @@ export const YXmlElementRefID = 3
|
|||||||
export const YXmlFragmentRefID = 4
|
export const YXmlFragmentRefID = 4
|
||||||
export const YXmlHookRefID = 5
|
export const YXmlHookRefID = 5
|
||||||
export const YXmlTextRefID = 6
|
export const YXmlTextRefID = 6
|
||||||
|
export const YWeakLinkRefID = 7
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -104,6 +107,7 @@ export class ContentType {
|
|||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
*/
|
*/
|
||||||
delete (transaction) {
|
delete (transaction) {
|
||||||
|
this.type._delete(transaction) // call custom destructor on AbstractType
|
||||||
let item = this.type._start
|
let item = this.type._start
|
||||||
while (item !== null) {
|
while (item !== null) {
|
||||||
if (!item.deleted) {
|
if (!item.deleted) {
|
||||||
|
@ -18,15 +18,13 @@ import {
|
|||||||
readContentString,
|
readContentString,
|
||||||
readContentEmbed,
|
readContentEmbed,
|
||||||
readContentDoc,
|
readContentDoc,
|
||||||
readContentWeakLink,
|
|
||||||
createID,
|
createID,
|
||||||
readContentFormat,
|
readContentFormat,
|
||||||
readContentType,
|
readContentType,
|
||||||
addChangedTypeToTransaction,
|
addChangedTypeToTransaction,
|
||||||
isDeleted,
|
isDeleted,
|
||||||
StackItem, DeleteSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line
|
StackItem, DeleteSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line
|
||||||
YWeakLink,
|
YWeakLink
|
||||||
ContentLink
|
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as error from 'lib0/error'
|
import * as error from 'lib0/error'
|
||||||
@ -302,7 +300,7 @@ export class Item extends AbstractStruct {
|
|||||||
* If this item was referenced by other weak links, here we keep the references
|
* If this item was referenced by other weak links, here we keep the references
|
||||||
* to these weak refs.
|
* to these weak refs.
|
||||||
*
|
*
|
||||||
* @type {Set<Item> | null}
|
* @type {Set<YWeakLink<any>> | null}
|
||||||
*/
|
*/
|
||||||
this.linkedBy = null
|
this.linkedBy = null
|
||||||
/**
|
/**
|
||||||
@ -386,10 +384,10 @@ export class Item extends AbstractStruct {
|
|||||||
if (this.parent && this.parent.constructor === ID && this.id.client !== this.parent.client && this.parent.clock >= getState(store, this.parent.client)) {
|
if (this.parent && this.parent.constructor === ID && this.id.client !== this.parent.client && this.parent.clock >= getState(store, this.parent.client)) {
|
||||||
return this.parent.client
|
return this.parent.client
|
||||||
}
|
}
|
||||||
if (this.content.constructor === ContentLink) {
|
if (this.content.constructor === ContentType && /** @type {ContentType} */ (this.content).type.constructor === YWeakLink) {
|
||||||
const content = /** @type {ContentLink} */ (this.content)
|
const content = /** @type {any} */ (this.content).type
|
||||||
if (content.link.id.client !== this.id.client) {
|
if (content._id.client !== this.id.client) {
|
||||||
return content.link.id.client
|
return content._id.client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,7 +538,7 @@ export class Item extends AbstractStruct {
|
|||||||
addChangedTypeToTransaction(transaction, /** @type {AbstractType<any>} */ (this.parent), this.parentSub)
|
addChangedTypeToTransaction(transaction, /** @type {AbstractType<any>} */ (this.parent), this.parentSub)
|
||||||
if (this.linkedBy !== null) {
|
if (this.linkedBy !== null) {
|
||||||
for (let link of this.linkedBy) {
|
for (let link of this.linkedBy) {
|
||||||
addChangedTypeToTransaction(transaction, /** @type {AbstractType<any>} */ (link.parent), link.parentSub)
|
addChangedTypeToTransaction(transaction, link, this.parentSub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((/** @type {AbstractType<any>} */ (this.parent)._item !== null && /** @type {AbstractType<any>} */ (this.parent)._item.deleted) || (this.parentSub !== null && this.right !== null)) {
|
if ((/** @type {AbstractType<any>} */ (this.parent)._item !== null && /** @type {AbstractType<any>} */ (this.parent)._item.deleted) || (this.parentSub !== null && this.right !== null)) {
|
||||||
@ -647,7 +645,12 @@ export class Item extends AbstractStruct {
|
|||||||
addToDeleteSet(transaction.deleteSet, this.id.client, this.id.clock, this.length)
|
addToDeleteSet(transaction.deleteSet, this.id.client, this.id.clock, this.length)
|
||||||
addChangedTypeToTransaction(transaction, parent, this.parentSub)
|
addChangedTypeToTransaction(transaction, parent, this.parentSub)
|
||||||
this.content.delete(transaction)
|
this.content.delete(transaction)
|
||||||
this.linkedBy = null
|
if (this.linkedBy !== null) {
|
||||||
|
for (let link of this.linkedBy) {
|
||||||
|
addChangedTypeToTransaction(transaction, link, this.parentSub)
|
||||||
|
}
|
||||||
|
this.linkedBy = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -744,8 +747,7 @@ export const contentRefs = [
|
|||||||
readContentType, // 7
|
readContentType, // 7
|
||||||
readContentAny, // 8
|
readContentAny, // 8
|
||||||
readContentDoc, // 9
|
readContentDoc, // 9
|
||||||
() => { error.unexpectedCase() }, // 10 - Skip is not ItemContent
|
() => { error.unexpectedCase() } // 10 - Skip is not ItemContent
|
||||||
readContentWeakLink // 11
|
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
ContentAny,
|
ContentAny,
|
||||||
ContentBinary,
|
ContentBinary,
|
||||||
getItemCleanStart,
|
getItemCleanStart,
|
||||||
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, YWeakLink, ContentLink, // eslint-disable-line
|
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, YWeakLink, // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as map from 'lib0/map'
|
import * as map from 'lib0/map'
|
||||||
@ -309,6 +309,11 @@ export class AbstractType {
|
|||||||
this._item = item
|
this._item = item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
_delete (transaction) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {AbstractType<EventType>}
|
* @return {AbstractType<EventType>}
|
||||||
*/
|
*/
|
||||||
@ -669,10 +674,6 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem,
|
|||||||
left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentDoc(/** @type {Doc} */ (c)))
|
left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentDoc(/** @type {Doc} */ (c)))
|
||||||
left.integrate(transaction, 0)
|
left.integrate(transaction, 0)
|
||||||
break
|
break
|
||||||
case YWeakLink:
|
|
||||||
left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentLink(/** @type {YWeakLink<any>} */ (c)))
|
|
||||||
left.integrate(transaction, 0)
|
|
||||||
break
|
|
||||||
default:
|
default:
|
||||||
if (c instanceof AbstractType) {
|
if (c instanceof AbstractType) {
|
||||||
left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentType(c))
|
left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentType(c))
|
||||||
@ -855,9 +856,6 @@ export const typeMapSet = (transaction, parent, key, value) => {
|
|||||||
case Doc:
|
case Doc:
|
||||||
content = new ContentDoc(/** @type {Doc} */ (value))
|
content = new ContentDoc(/** @type {Doc} */ (value))
|
||||||
break
|
break
|
||||||
case YWeakLink:
|
|
||||||
content = new ContentLink(/** @type {YWeakLink<any>} */ (value))
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
if (value instanceof AbstractType) {
|
if (value instanceof AbstractType) {
|
||||||
content = new ContentType(value)
|
content = new ContentType(value)
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
import { AbstractType, GC, ID, Item, Transaction, YEvent } from "yjs"
|
import { decoding, encoding, error } from "lib0"
|
||||||
|
import {
|
||||||
|
YEvent, Transaction, ID, GC, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item,
|
||||||
|
transact,
|
||||||
|
getItemCleanEnd,
|
||||||
|
createID,
|
||||||
|
getItemCleanStart,
|
||||||
|
callTypeObservers,
|
||||||
|
YWeakLinkRefID
|
||||||
|
} from "../internals.js"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T extends AbstractType<any>
|
* @template T extends AbstractType<any>
|
||||||
@ -9,27 +18,27 @@ export class YWeakLinkEvent extends YEvent {
|
|||||||
/**
|
/**
|
||||||
* @param {YWeakLink<T>} ylink The YWeakLink to which this event was propagated to.
|
* @param {YWeakLink<T>} ylink The YWeakLink to which this event was propagated to.
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {YEvent<any>} source Source event that has been propagated to ylink.
|
|
||||||
*/
|
*/
|
||||||
constructor (ylink, transaction, source) {
|
constructor (ylink, transaction) {
|
||||||
super(ylink, transaction)
|
super(ylink, transaction)
|
||||||
this.source = source
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
|
* @extends AbstractType<YWeakLinkEvent<T>>
|
||||||
*
|
*
|
||||||
* Weak link to another value stored somewhere in the document.
|
* Weak link to another value stored somewhere in the document.
|
||||||
*/
|
*/
|
||||||
export class YWeakLink {
|
export class YWeakLink extends AbstractType {
|
||||||
/**
|
/**
|
||||||
* @param {ID} id
|
* @param {ID} id
|
||||||
* @param {Item|GC|null} item
|
* @param {Item|GC|null} item
|
||||||
*/
|
*/
|
||||||
constructor(id, item) {
|
constructor(id, item) {
|
||||||
this.id = id
|
super()
|
||||||
this.item = item
|
this._id = id
|
||||||
|
this._linkedItem = item
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,14 +47,14 @@ export class YWeakLink {
|
|||||||
* @return {T|undefined}
|
* @return {T|undefined}
|
||||||
*/
|
*/
|
||||||
deref() {
|
deref() {
|
||||||
if (this.item !== null && this.item.constructor === Item) {
|
if (this._linkedItem !== null && this._linkedItem.constructor === Item) {
|
||||||
let item = this.item
|
let item = this._linkedItem
|
||||||
if (item.parentSub !== null) {
|
if (item.parentSub !== null) {
|
||||||
// for map types go to the most recent one
|
// for map types go to the most recent one
|
||||||
while (item.right !== null) {
|
while (item.right !== null) {
|
||||||
item = item.right
|
item = item.right
|
||||||
}
|
}
|
||||||
this.item = item
|
this._linkedItem = item
|
||||||
}
|
}
|
||||||
if (!item.deleted) {
|
if (!item.deleted) {
|
||||||
return item.content.getContent()[0]
|
return item.content.getContent()[0]
|
||||||
@ -53,4 +62,155 @@ export class YWeakLink {
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integrate this type into the Yjs instance.
|
||||||
|
*
|
||||||
|
* * Save this struct in the os
|
||||||
|
* * This type is sent to other client
|
||||||
|
* * Observer functions are fired
|
||||||
|
*
|
||||||
|
* @param {Doc} y The Yjs instance
|
||||||
|
* @param {Item|null} item
|
||||||
|
*/
|
||||||
|
_integrate (y, item) {
|
||||||
|
super._integrate(y, item)
|
||||||
|
if (item !== null) {
|
||||||
|
transact(y, (transaction) => {
|
||||||
|
let sourceItem = this._linkedItem !== null ? this._linkedItem : getItemCleanStart(transaction, this._id)
|
||||||
|
if (sourceItem.constructor === Item && sourceItem.parentSub !== null) {
|
||||||
|
// for maps, advance to most recent item
|
||||||
|
while (sourceItem.right !== null) {
|
||||||
|
sourceItem = sourceItem.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!sourceItem.deleted && sourceItem.length > 1) {
|
||||||
|
sourceItem = getItemCleanEnd(transaction, transaction.doc.store, createID(sourceItem.id.client, sourceItem.id.clock + 1))
|
||||||
|
}
|
||||||
|
this._linkedItem = sourceItem
|
||||||
|
if (!sourceItem.deleted) {
|
||||||
|
const src = /** @type {Item} */ (sourceItem)
|
||||||
|
if (src.linkedBy === null) {
|
||||||
|
src.linkedBy = new Set()
|
||||||
|
}
|
||||||
|
src.linkedBy.add(this)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
_delete (transaction) {
|
||||||
|
if (this._item !== null && this._linkedItem !== null && !this._linkedItem.deleted) {
|
||||||
|
const item = /** @type {Item} */ (this._linkedItem)
|
||||||
|
if (item.linkedBy !== null) {
|
||||||
|
item.linkedBy.delete(this)
|
||||||
|
}
|
||||||
|
this._linkedItem = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {YWeakLink<T>}
|
||||||
|
*/
|
||||||
|
_copy () {
|
||||||
|
return new YWeakLink(this._id, this._linkedItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {YWeakLink<T>}
|
||||||
|
*/
|
||||||
|
clone () {
|
||||||
|
return new YWeakLink(this._id, this._linkedItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates YWeakLinkEvent and calls observers.
|
||||||
|
*
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
|
||||||
|
*/
|
||||||
|
_callObserver (transaction, parentSubs) {
|
||||||
|
super._callObserver(transaction, parentSubs)
|
||||||
|
callTypeObservers(this, transaction, new YWeakLinkEvent(this, transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
||||||
|
*/
|
||||||
|
_write (encoder) {
|
||||||
|
encoder.writeTypeRef(YWeakLinkRefID)
|
||||||
|
const flags = 0 // flags that could be used in the future
|
||||||
|
encoding.writeUint8(encoder.restEncoder, flags)
|
||||||
|
encoder.writeLeftID(this._id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
||||||
|
* @return {YWeakLink<any>}
|
||||||
|
*/
|
||||||
|
export const readYWeakLink = decoder => {
|
||||||
|
const flags = decoding.readUint8(decoder.restDecoder)
|
||||||
|
const id = decoder.readLeftID()
|
||||||
|
return new YWeakLink(id, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const lengthExceeded = error.create('Length exceeded!')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {WeakLink} to an YArray element at given index.
|
||||||
|
*
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {AbstractType<any>} parent
|
||||||
|
* @param {number} index
|
||||||
|
* @return {YWeakLink<any>}
|
||||||
|
*/
|
||||||
|
export const arrayWeakLink = (transaction, parent, index) => {
|
||||||
|
let item = parent._start
|
||||||
|
for (; item !== null; item = item.right) {
|
||||||
|
if (!item.deleted && item.countable) {
|
||||||
|
if (index < item.length) {
|
||||||
|
if (index > 0) {
|
||||||
|
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + index))
|
||||||
|
}
|
||||||
|
if (item.length > 1) {
|
||||||
|
item = getItemCleanEnd(transaction, transaction.doc.store, createID(item.id.client, item.id.clock))
|
||||||
|
}
|
||||||
|
const link = new YWeakLink(item.id, item)
|
||||||
|
if (item.linkedBy === null) {
|
||||||
|
item.linkedBy = new Set()
|
||||||
|
}
|
||||||
|
item.linkedBy.add(link)
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
index -= item.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lengthExceeded
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {WeakLink} to an YMap element at given key.
|
||||||
|
*
|
||||||
|
* @param {AbstractType<any>} parent
|
||||||
|
* @param {string} key
|
||||||
|
* @return {YWeakLink<any>|undefined}
|
||||||
|
*/
|
||||||
|
export const mapWeakLink = (parent, key) => {
|
||||||
|
const item = parent._map.get(key)
|
||||||
|
if (item !== undefined) {
|
||||||
|
const link = new YWeakLink(item.id, item)
|
||||||
|
if (item.linkedBy === null) {
|
||||||
|
item.linkedBy = new Set()
|
||||||
|
}
|
||||||
|
item.linkedBy.add(link)
|
||||||
|
return link
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
@ -118,55 +118,69 @@ export const testDeleteSource = tc => {
|
|||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testObserveMapLinkArrayRemove = tc => {
|
export const testObserveMapUpdate = tc => {
|
||||||
const doc = new Y.Doc()
|
const { testConnector, users, map0, map1 } = init(tc, { users: 2 })
|
||||||
const map = doc.getMap('map')
|
map0.set('a', 'value')
|
||||||
const array = doc.getArray('array')
|
const link0 = /** @type {Y.WeakLink<String>} */ (map0.link('a'))
|
||||||
|
|
||||||
array.insert(0, [1])
|
|
||||||
const link = array.link(0)
|
|
||||||
map.set('key', link)
|
|
||||||
/**
|
/**
|
||||||
* @type {any}
|
* @type {any}
|
||||||
*/
|
*/
|
||||||
let keys = null
|
let target0
|
||||||
map.observe((e) => {
|
link0.observe((e) => target0 = e.target)
|
||||||
console.log('map received event', e)
|
map0.set('b', link0)
|
||||||
keys = e.keys
|
|
||||||
})
|
|
||||||
|
|
||||||
array.delete(0)
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
t.compare(keys.get('key'), { action:'delete', oldValue: 1, newValue: null })
|
let link1 = /** @type {Y.WeakLink<String>} */ (map1.get('b'))
|
||||||
}
|
t.compare(link1.deref(), 'value')
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {t.TestCase} tc
|
|
||||||
*/
|
|
||||||
export const testObserveMapLinkMapUpdate = tc => {
|
|
||||||
const doc = new Y.Doc()
|
|
||||||
const map1 = doc.getMap('map1')
|
|
||||||
const map2 = doc.getMap('map2')
|
|
||||||
/**
|
/**
|
||||||
* @type {Map<string, { action: 'add' | 'update' | 'delete', oldValue: any, newValue: any }>}
|
* @type {any}
|
||||||
*/
|
*/
|
||||||
let keys
|
let target1
|
||||||
map1.observe((e) => keys = e.keys)
|
link1.observe((e) => target1 = e.target)
|
||||||
|
|
||||||
map2.set('key', 'value1')
|
map0.set('a', 'value2')
|
||||||
const link = map2.link('key')
|
t.compare(target0.deref(), 'value2')
|
||||||
map1.set('other-key', link)
|
|
||||||
|
|
||||||
keys = /** @type {any} */ (null)
|
testConnector.flushAllMessages()
|
||||||
map2.set('key', 'value2')
|
t.compare(target1.deref(), 'value2')
|
||||||
|
|
||||||
t.compare(keys.get('key'), { action:'update', oldValue: 'value1', newValue: 'value2' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testObserveMapLinkMapRemove = tc => {
|
export const testObserveMapDelete = tc => {
|
||||||
|
const { testConnector, users, map0, map1 } = init(tc, { users: 2 })
|
||||||
|
map0.set('a', 'value')
|
||||||
|
const link0 = /** @type {Y.WeakLink<String>} */ (map0.link('a'))
|
||||||
|
/**
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
|
let target0
|
||||||
|
link0.observe((e) => target0 = e.target)
|
||||||
|
map0.set('b', link0)
|
||||||
|
|
||||||
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
|
let link1 = /** @type {Y.WeakLink<String>} */ (map1.get('b'))
|
||||||
|
t.compare(link1.deref(), 'value')
|
||||||
|
/**
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
|
let target1
|
||||||
|
link1.observe((e) => target1 = e.target)
|
||||||
|
|
||||||
|
map0.delete('a')
|
||||||
|
t.compare(target0.deref(), undefined)
|
||||||
|
|
||||||
|
testConnector.flushAllMessages()
|
||||||
|
t.compare(target1.deref(), undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} tc
|
||||||
|
*/
|
||||||
|
const testObserveMapLinkMapRemove = tc => {
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
const map1 = doc.getMap('map1')
|
const map1 = doc.getMap('map1')
|
||||||
const map2 = doc.getMap('map2')
|
const map2 = doc.getMap('map2')
|
||||||
@ -189,7 +203,7 @@ export const testObserveMapLinkMapRemove = tc => {
|
|||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testObserveArrayLinkMapRemove = tc => {
|
const testObserveArrayLinkMapRemove = tc => {
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
const array = doc.getArray('array')
|
const array = doc.getArray('array')
|
||||||
const map = doc.getMap('map')
|
const map = doc.getMap('map')
|
||||||
@ -212,7 +226,7 @@ export const testObserveArrayLinkMapRemove = tc => {
|
|||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testObserveArrayLinkMapUpdate = tc => {
|
const testObserveArrayLinkMapUpdate = tc => {
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
const array = doc.getArray('array')
|
const array = doc.getArray('array')
|
||||||
const map = doc.getMap('map')
|
const map = doc.getMap('map')
|
||||||
@ -235,7 +249,7 @@ export const testObserveArrayLinkMapUpdate = tc => {
|
|||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testObserveTransitive = tc => {
|
const testObserveTransitive = tc => {
|
||||||
// test observers in a face of linked chains of values
|
// test observers in a face of linked chains of values
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
const map1 = doc.getMap('map1')
|
const map1 = doc.getMap('map1')
|
||||||
@ -262,7 +276,7 @@ export const testObserveTransitive = tc => {
|
|||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testDeepObserveMap = tc => {
|
const testDeepObserveMap = tc => {
|
||||||
// test observers in a face of linked chains of values
|
// test observers in a face of linked chains of values
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
const map = doc.getMap('map')
|
const map = doc.getMap('map')
|
||||||
@ -297,7 +311,7 @@ export const testDeepObserveMap = tc => {
|
|||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testDeepObserveArray = tc => {
|
const testDeepObserveArray = tc => {
|
||||||
// test observers in a face of linked chains of values
|
// test observers in a face of linked chains of values
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
const map = doc.getMap('map')
|
const map = doc.getMap('map')
|
||||||
@ -332,7 +346,7 @@ export const testDeepObserveArray = tc => {
|
|||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testDeepObserveRecursive = tc => {
|
const testDeepObserveRecursive = tc => {
|
||||||
// test observers in a face of linked chains of values
|
// test observers in a face of linked chains of values
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
const root = doc.getArray('array')
|
const root = doc.getArray('array')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user