more work
This commit is contained in:
parent
740598cef2
commit
608a309f2c
@ -1,30 +1,33 @@
|
||||
import { decoding, encoding, error } from 'lib0'
|
||||
import {
|
||||
UpdateEncoderV1, UpdateEncoderV2, UpdateDecoderV1, UpdateDecoderV2, Transaction, Item, StructStore // eslint-disable-line
|
||||
UpdateEncoderV1, UpdateEncoderV2, UpdateDecoderV1, UpdateDecoderV2, Transaction, Item, StructStore, // eslint-disable-line
|
||||
WeakLink,
|
||||
findRootTypeKey,
|
||||
ID,
|
||||
find,
|
||||
ContentType
|
||||
} from '../internals.js'
|
||||
|
||||
export class ContentLink {
|
||||
/**
|
||||
* @param {Item} item
|
||||
* @param {WeakLink<any>|{parent:string|ID,item:string|ID}} link
|
||||
*/
|
||||
constructor (item) {
|
||||
/**
|
||||
* @type {Item}
|
||||
*/
|
||||
this.item = item
|
||||
constructor (link) {
|
||||
this.link = link
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
getLength () {
|
||||
return this.item.length
|
||||
return 1
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Array<any>}
|
||||
*/
|
||||
getContent () {
|
||||
throw new Error('not implemented')
|
||||
return [this.link]
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,7 +41,7 @@ import {
|
||||
* @return {ContentLink}
|
||||
*/
|
||||
copy () {
|
||||
return new ContentLink(this.item)
|
||||
return new ContentLink(this.link)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,7 +49,7 @@ import {
|
||||
* @return {ContentLink}
|
||||
*/
|
||||
splice (offset) {
|
||||
throw new Error('not implemented')
|
||||
throw error.methodUnimplemented()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,19 +57,57 @@ import {
|
||||
* @return {boolean}
|
||||
*/
|
||||
mergeWith (right) {
|
||||
throw new Error('not implemented')
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {Item} item
|
||||
*/
|
||||
integrate (transaction, item) {}
|
||||
integrate (transaction, item) {
|
||||
if (this.link.constructor !== WeakLink) {
|
||||
let { parent, item } = /** @type {any} */ (this.link)
|
||||
let key = null
|
||||
if (parent.constructor === ID) {
|
||||
const parentItem = find(transaction.doc.store, parent)
|
||||
if (parentItem.constructor === Item) {
|
||||
parent = /** @type {ContentType} */ (parentItem.content).type
|
||||
} else {
|
||||
parent = null
|
||||
}
|
||||
} else {
|
||||
parent = transaction.doc.share.get(parent)
|
||||
}
|
||||
|
||||
if (item.constructor === ID) {
|
||||
item = find(transaction.doc.store, item)
|
||||
} else {
|
||||
key = item
|
||||
item = parent._map.get(key)
|
||||
}
|
||||
this.link = new WeakLink(parent, item, key)
|
||||
}
|
||||
|
||||
const link = /** @type {WeakLink<any>} */ (this.link)
|
||||
if (link.item.constructor === Item) {
|
||||
if (link.item.linkedBy === null) {
|
||||
link.item.linkedBy = new Set()
|
||||
}
|
||||
link.item.linkedBy.add(link)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
*/
|
||||
delete (transaction) {}
|
||||
delete (transaction) {
|
||||
const link = /** @type {WeakLink<any>} */ (this.link)
|
||||
if (link.item.constructor === Item) {
|
||||
if (link.item.linkedBy !== null) {
|
||||
link.item.linkedBy.delete(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {StructStore} store
|
||||
@ -78,14 +119,34 @@ import {
|
||||
* @param {number} offset
|
||||
*/
|
||||
write (encoder, offset) {
|
||||
throw new Error('not implemented')
|
||||
const link = /** @type {WeakLink<any>} */ (this.link)
|
||||
let flags = 0
|
||||
const parentItem = link.source._item
|
||||
if (parentItem) {
|
||||
flags |= 1
|
||||
}
|
||||
if (link.key) {
|
||||
flags |= 2
|
||||
}
|
||||
encoding.writeVarUint(encoder.restEncoder, flags)
|
||||
if (parentItem) {
|
||||
encoder.writeLeftID(parentItem.id)
|
||||
} else {
|
||||
const ykey = findRootTypeKey(link.source)
|
||||
encoder.writeString(ykey)
|
||||
}
|
||||
if (link.key !== null) {
|
||||
encoder.writeString(link.key)
|
||||
} else {
|
||||
encoder.writeLeftID(link.item.id)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
getRef () {
|
||||
return 10
|
||||
return 11
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,6 +155,19 @@ import {
|
||||
* @return {ContentLink}
|
||||
*/
|
||||
export const readContentWeakLink = decoder => {
|
||||
throw new Error('not implemented')
|
||||
const flags = decoding.readVarUint(decoder.restDecoder)
|
||||
let parent
|
||||
let item
|
||||
if ((flags & 1) !== 0) {
|
||||
parent = decoder.readLeftID()
|
||||
} else {
|
||||
parent = decoder.readString()
|
||||
}
|
||||
if ((flags & 2) !== 0) {
|
||||
item = decoder.readString()
|
||||
} else {
|
||||
item = decoder.readLeftID()
|
||||
}
|
||||
return new ContentLink({parent, item})
|
||||
}
|
||||
|
@ -24,7 +24,9 @@ import {
|
||||
readContentType,
|
||||
addChangedTypeToTransaction,
|
||||
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
|
||||
WeakLink,
|
||||
ContentLink
|
||||
} from '../internals.js'
|
||||
|
||||
import * as error from 'lib0/error'
|
||||
@ -296,6 +298,13 @@ export class Item extends AbstractStruct {
|
||||
* @type {ID | null}
|
||||
*/
|
||||
this.redone = null
|
||||
/**
|
||||
* If this item was referenced by other weak links, here we keep the references
|
||||
* to these weak refs.
|
||||
*
|
||||
* @type {Set<WeakLink<any>> | null}
|
||||
*/
|
||||
this.linkedBy = null
|
||||
/**
|
||||
* @type {AbstractContent}
|
||||
*/
|
||||
@ -511,6 +520,7 @@ export class Item extends AbstractStruct {
|
||||
/** @type {AbstractType<any>} */ (this.parent)._map.set(this.parentSub, this)
|
||||
if (this.left !== null) {
|
||||
// this is the current attribute value of parent. delete right
|
||||
this.linkedBy = this.left.linkedBy
|
||||
this.left.delete(transaction)
|
||||
}
|
||||
}
|
||||
@ -579,6 +589,8 @@ export class Item extends AbstractStruct {
|
||||
this.deleted === right.deleted &&
|
||||
this.redone === null &&
|
||||
right.redone === null &&
|
||||
this.linkedBy === null &&
|
||||
right.linkedBy === null &&
|
||||
this.content.constructor === right.content.constructor &&
|
||||
this.content.mergeWith(right.content)
|
||||
) {
|
||||
@ -624,6 +636,7 @@ export class Item extends AbstractStruct {
|
||||
addToDeleteSet(transaction.deleteSet, this.id.client, this.id.clock, this.length)
|
||||
addChangedTypeToTransaction(transaction, parent, this.parentSub)
|
||||
this.content.delete(transaction)
|
||||
this.linkedBy = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -720,8 +733,8 @@ export const contentRefs = [
|
||||
readContentType, // 7
|
||||
readContentAny, // 8
|
||||
readContentDoc, // 9
|
||||
readContentWeakLink, // 10
|
||||
() => { error.unexpectedCase() } // 10 - Skip is not ItemContent
|
||||
() => { error.unexpectedCase() }, // 10 - Skip is not ItemContent
|
||||
readContentWeakLink // 11
|
||||
]
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
ContentAny,
|
||||
ContentBinary,
|
||||
getItemCleanStart,
|
||||
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, // eslint-disable-line
|
||||
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, WeakLink, ContentLink, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as map from 'lib0/map'
|
||||
@ -669,6 +669,10 @@ 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.integrate(transaction, 0)
|
||||
break
|
||||
case WeakLink:
|
||||
left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentLink(/** @type {WeakLink<any>} */ (c)))
|
||||
left.integrate(transaction, 0)
|
||||
break
|
||||
default:
|
||||
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))
|
||||
@ -851,6 +855,9 @@ export const typeMapSet = (transaction, parent, key, value) => {
|
||||
case Doc:
|
||||
content = new ContentDoc(/** @type {Doc} */ (value))
|
||||
break
|
||||
case WeakLink:
|
||||
content = new ContentLink(/** @type {WeakLink<any>} */ (value))
|
||||
break;
|
||||
default:
|
||||
if (value instanceof AbstractType) {
|
||||
content = new ContentType(value)
|
||||
|
@ -1,3 +1,7 @@
|
||||
import { AbstractType, GC, Item, createID } from "yjs"
|
||||
import { findMarker, typeMapGet } from "./AbstractType.js"
|
||||
import { error } from "lib0"
|
||||
import { Transaction, getItemCleanEnd, getItemCleanStart } from "src/internals.js"
|
||||
|
||||
/**
|
||||
* @template T
|
||||
@ -5,13 +9,82 @@
|
||||
* Weak link to another value stored somewhere in the document.
|
||||
*/
|
||||
export class WeakLink {
|
||||
|
||||
/**
|
||||
* Returns a reference to an underlying value existing somewhere on in the document.
|
||||
*
|
||||
* @return {T|undefined}
|
||||
*/
|
||||
deref() {
|
||||
throw new Error('not implemented')
|
||||
/**
|
||||
* @param {AbstractType<any>} source
|
||||
* @param {Item|GC} item
|
||||
* @param {string|null} key
|
||||
*/
|
||||
constructor(source, item, key) {
|
||||
this.source = source
|
||||
this.item = item
|
||||
this.key = key
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to an underlying value existing somewhere on in the document.
|
||||
*
|
||||
* @return {T|undefined}
|
||||
*/
|
||||
deref() {
|
||||
if (this.key) {
|
||||
return /** @type {T|undefined} */ (typeMapGet(this.source, this.key))
|
||||
} else {
|
||||
if (this.item.constructor === Item) {
|
||||
return this.item.content.getContent()[0]
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {WeakLink<any>}
|
||||
*/
|
||||
export const arrayWeakLink = (transaction, parent, index) => {
|
||||
const marker = findMarker(parent, index)
|
||||
let n = parent._start
|
||||
if (marker !== null) {
|
||||
n = marker.p
|
||||
index -= marker.index
|
||||
}
|
||||
for (; n !== null; n = n.right) {
|
||||
if (!n.deleted && n.countable) {
|
||||
if (index < n.length) {
|
||||
if (index > 0) {
|
||||
n = getItemCleanStart(transaction, createID(n.id.clock, n.id.clock + index))
|
||||
}
|
||||
if (n.length > 1) {
|
||||
n = getItemCleanEnd(transaction, transaction.doc.store, createID(n.id.clock, n.id.clock + 1))
|
||||
}
|
||||
return new WeakLink(parent, n, null)
|
||||
}
|
||||
index -= n.length
|
||||
}
|
||||
}
|
||||
|
||||
throw lengthExceeded
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {WeakLink} to an YMap element at given key.
|
||||
*
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {string} key
|
||||
* @return {WeakLink<any>|undefined}
|
||||
*/
|
||||
export const mapWeakLink = (parent, key) => {
|
||||
const item = parent._map.get(key)
|
||||
if (item !== undefined) {
|
||||
return new WeakLink(parent, item, key)
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
@ -17,7 +17,8 @@ import {
|
||||
callTypeObservers,
|
||||
transact,
|
||||
ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
||||
WeakLink
|
||||
WeakLink,
|
||||
arrayWeakLink
|
||||
} from '../internals.js'
|
||||
import { typeListSlice } from './AbstractType.js'
|
||||
|
||||
@ -209,7 +210,13 @@ export class YArray extends AbstractType {
|
||||
* @return {WeakLink<T>}
|
||||
*/
|
||||
link(index) {
|
||||
throw new Error('Method not implemented.')
|
||||
if (this.doc !== null) {
|
||||
return transact(this.doc, transaction => {
|
||||
return arrayWeakLink(transaction, this, index)
|
||||
})
|
||||
} else {
|
||||
throw new Error('todo')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,7 +15,8 @@ import {
|
||||
callTypeObservers,
|
||||
transact,
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
||||
WeakLink
|
||||
WeakLink,
|
||||
mapWeakLink
|
||||
} from '../internals.js'
|
||||
|
||||
import * as iterator from 'lib0/iterator'
|
||||
@ -241,7 +242,7 @@ export class YMap extends AbstractType {
|
||||
* @return {WeakLink<MapType>|undefined}
|
||||
*/
|
||||
link(key) {
|
||||
throw new Error('Method not implemented.')
|
||||
return mapWeakLink(this, key)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,8 @@ if (isBrowser) {
|
||||
log.createVConsole(document.body)
|
||||
}
|
||||
runTests({
|
||||
doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, weakLinks
|
||||
//doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions,
|
||||
weakLinks
|
||||
}).then(success => {
|
||||
/* istanbul ignore next */
|
||||
if (isNode) {
|
||||
|
@ -5,18 +5,35 @@ import { init, compare } from './testHelper.js'
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testBasic = tc => {
|
||||
export const testBasicMap = tc => {
|
||||
const doc = new Y.Doc()
|
||||
const map = doc.getMap('map')
|
||||
|
||||
const nested = new Y.Map()
|
||||
nested.set('a1', 'hello')
|
||||
map.set('a', nested)
|
||||
const link = nested.link('a')
|
||||
const link = map.link('a')
|
||||
map.set('b', link)
|
||||
|
||||
const nested2 = map.get('b')
|
||||
t.compare(nested2.toJSON(), nested.toJSON())
|
||||
const link2 = /** @type {Y.WeakLink<any>} */ (map.get('b'))
|
||||
const expected = nested.toJSON()
|
||||
const actual = link2.deref().toJSON()
|
||||
t.compare(actual, expected)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testBasicArray = tc => {
|
||||
const doc = new Y.Doc()
|
||||
const array = doc.getArray('array')
|
||||
array.insert(0, [1,2,3])
|
||||
array.insert(3, [array.link(1)])
|
||||
|
||||
t.compare(array.get(0), 1)
|
||||
t.compare(array.get(1), 2)
|
||||
t.compare(array.get(2), 3)
|
||||
t.compare(array.get(3).deref(), 2)
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user