basics of range based quotations
This commit is contained in:
parent
337cc1e202
commit
02367b612b
@ -113,13 +113,15 @@ export class ContentType {
|
||||
// when removing weak links, remove references to them
|
||||
// from type they're pointing to
|
||||
const type = /** @type {WeakLink<any>} */ (this.type);
|
||||
if (type._linkedItem !== null && !type._linkedItem.deleted) {
|
||||
const item = /** @type {Item} */ (type._linkedItem)
|
||||
for (let item = type._firstItem; item !== null; item = item.right) {
|
||||
if (item.linked) {
|
||||
unlinkFrom(transaction, item, type)
|
||||
}
|
||||
type._linkedItem = null
|
||||
if (item === type._lastItem) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
type._firstItem = type._lastItem = null
|
||||
}
|
||||
|
||||
let item = this.type._start
|
||||
|
@ -396,9 +396,15 @@ export class Item extends AbstractStruct {
|
||||
}
|
||||
if (this.content.constructor === ContentType && /** @type {ContentType} */ (this.content).type.constructor === YWeakLink) {
|
||||
// make sure that linked content is integrated first
|
||||
const linkSource = /** @type {any} */ (this.content).type._id
|
||||
if (linkSource.clock >= getState(store, linkSource.client)) {
|
||||
return linkSource.client
|
||||
const content = /** @type {ContentType} */ (this.content)
|
||||
const link = /** @type {YWeakLink<any>} */ (content.type)
|
||||
const start = link._quoteStart.item
|
||||
if (start !== null && start.clock >= getState(store, start.client)) {
|
||||
return start.client
|
||||
}
|
||||
const end = link._quoteEnd.item
|
||||
if (end !== null && end.clock >= getState(store, end.client)) {
|
||||
return end.client
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ export class YArray extends AbstractType {
|
||||
return arrayWeakLink(transaction, this, index)
|
||||
})
|
||||
} else {
|
||||
throw new Error('todo')
|
||||
throw new Error('cannot create a link to an YArray that has not been integrated into YDoc')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,8 @@ import {
|
||||
callTypeObservers,
|
||||
YWeakLinkRefID,
|
||||
writeID,
|
||||
readID
|
||||
readID,
|
||||
RelativePosition
|
||||
} from "../internals.js"
|
||||
|
||||
/**
|
||||
@ -36,13 +37,26 @@ export class YWeakLinkEvent extends YEvent {
|
||||
*/
|
||||
export class YWeakLink extends AbstractType {
|
||||
/**
|
||||
* @param {ID} id
|
||||
* @param {Item|GC|null} item
|
||||
* @param {RelativePosition} start
|
||||
* @param {RelativePosition} end
|
||||
* @param {Item|null} firstItem
|
||||
* @param {Item|null} lastItem
|
||||
*/
|
||||
constructor(id, item) {
|
||||
constructor(start, end, firstItem, lastItem) {
|
||||
super()
|
||||
this._id = id
|
||||
this._linkedItem = item
|
||||
this._quoteStart = start
|
||||
this._quoteEnd = end
|
||||
this._firstItem = firstItem
|
||||
this._lastItem = lastItem
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current link contains only a single element.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSingle() {
|
||||
return this._quoteStart.item === this._quoteEnd.item
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,21 +65,40 @@ export class YWeakLink extends AbstractType {
|
||||
* @return {T|undefined}
|
||||
*/
|
||||
deref() {
|
||||
if (this._linkedItem !== null && this._linkedItem.constructor === Item) {
|
||||
let item = this._linkedItem
|
||||
if (this._firstItem !== null) {
|
||||
let item = this._firstItem
|
||||
if (item.parentSub !== null) {
|
||||
// for map types go to the most recent one
|
||||
while (item.right !== null) {
|
||||
item = item.right
|
||||
}
|
||||
this._linkedItem = item
|
||||
// we don't support quotations over maps
|
||||
this._firstItem = item
|
||||
}
|
||||
if (!item.deleted) {
|
||||
return item.content.getContent()[0]
|
||||
if (!this._firstItem.deleted) {
|
||||
return this._firstItem.content.getContent()[0]
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of references to all elements quoted by current weak link.
|
||||
*
|
||||
* @return {Array<any>}
|
||||
*/
|
||||
unqote() {
|
||||
let result = /** @type {Array<any>} */ ([])
|
||||
let item = this._firstItem
|
||||
//TODO: moved elements
|
||||
while (item !== null && item !== this._lastItem) {
|
||||
if (!item.deleted) {
|
||||
result = result.concat(item.content.getContent())
|
||||
}
|
||||
item = item.right
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Integrate this type into the Yjs instance.
|
||||
@ -84,19 +117,25 @@ export class YWeakLink extends AbstractType {
|
||||
// link may refer to a single element in multi-element block
|
||||
// in such case we need to cut of the linked element into a
|
||||
// separate block
|
||||
let sourceItem = this._linkedItem !== null ? this._linkedItem : getItemCleanStart(transaction, this._id)
|
||||
if (sourceItem.constructor === Item && sourceItem.parentSub !== null) {
|
||||
let firstItem = this._firstItem !== null ? this._firstItem : getItemCleanStart(transaction, /** @type {ID} */ (this._quoteStart.item))
|
||||
let lastItem = this._lastItem !== null ? this._lastItem : getItemCleanEnd(transaction, y.store, /** @type {ID} */(this._quoteEnd.item))
|
||||
if (firstItem.parentSub !== null) {
|
||||
// for maps, advance to most recent item
|
||||
while (sourceItem.right !== null) {
|
||||
sourceItem = sourceItem.right
|
||||
while (firstItem.right !== null) {
|
||||
firstItem = firstItem.right
|
||||
}
|
||||
lastItem = firstItem
|
||||
}
|
||||
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) {
|
||||
createLink(transaction, /** @type {Item} */ (sourceItem), this)
|
||||
this._firstItem = firstItem
|
||||
this._lastItem = lastItem
|
||||
|
||||
/** @type {Item|null} */
|
||||
let item = firstItem
|
||||
for (; item !== null; item = item.right) {
|
||||
createLink(transaction, item, this)
|
||||
if (item === lastItem) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -106,14 +145,14 @@ export class YWeakLink extends AbstractType {
|
||||
* @return {YWeakLink<T>}
|
||||
*/
|
||||
_copy () {
|
||||
return new YWeakLink(this._id, this._linkedItem)
|
||||
return new YWeakLink(this._quoteStart, this._quoteEnd, this._firstItem, this._lastItem)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {YWeakLink<T>}
|
||||
*/
|
||||
clone () {
|
||||
return new YWeakLink(this._id, this._linkedItem)
|
||||
return new YWeakLink(this._quoteStart, this._quoteEnd, this._firstItem, this._lastItem)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,9 +171,13 @@ export class YWeakLink extends AbstractType {
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoder.writeTypeRef(YWeakLinkRefID)
|
||||
const flags = 0 // flags that could be used in the future
|
||||
encoding.writeUint8(encoder.restEncoder, flags)
|
||||
writeID(encoder.restEncoder, this._id)
|
||||
const isSingle = this.isSingle()
|
||||
const info = (isSingle ? 0 : 1) | (this._quoteStart.assoc >= 0 ? 2 : 0) | (this._quoteEnd.assoc >= 0 ? 4 :0)
|
||||
encoding.writeUint8(encoder.restEncoder, info)
|
||||
writeID(encoder.restEncoder, /** @type {ID} */ (this._quoteStart.item))
|
||||
if (!isSingle) {
|
||||
writeID(encoder.restEncoder, /** @type {ID} */ (this._quoteEnd.item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,9 +187,14 @@ export class YWeakLink extends AbstractType {
|
||||
* @return {YWeakLink<any>}
|
||||
*/
|
||||
export const readYWeakLink = decoder => {
|
||||
const flags = decoding.readUint8(decoder.restDecoder)
|
||||
const id = readID(decoder.restDecoder)
|
||||
return new YWeakLink(id, null)
|
||||
const info = decoding.readUint8(decoder.restDecoder)
|
||||
const isSingle = (info & 1) !== 1
|
||||
const startAssoc = (info & 2) === 2 ? 0 : -1
|
||||
const endAssoc = (info & 4) === 4 ? 0 : -1
|
||||
const startID = readID(decoder.restDecoder)
|
||||
const start = new RelativePosition(null, null, startID, startAssoc)
|
||||
const end = new RelativePosition(null, null, isSingle ? startID : readID(decoder.restDecoder), endAssoc)
|
||||
return new YWeakLink(start, end, null, null)
|
||||
}
|
||||
|
||||
const lengthExceeded = error.create('Length exceeded!')
|
||||
@ -159,27 +207,43 @@ const lengthExceeded = error.create('Length exceeded!')
|
||||
* @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) {
|
||||
export const arrayWeakLink = (transaction, parent, index, length = 1) => {
|
||||
let startItem = parent._start
|
||||
for (;startItem !== null; startItem = startItem.right) {
|
||||
if (!startItem.deleted && startItem.countable) {
|
||||
if (index < startItem.length) {
|
||||
if (index > 0) {
|
||||
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + index))
|
||||
startItem = getItemCleanStart(transaction, createID(startItem.id.client, startItem.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 (parent.doc !== null) {
|
||||
const source = /** @type {Item} */ (item)
|
||||
transact(parent.doc, (transaction) => {
|
||||
createLink(transaction, source, link)
|
||||
})
|
||||
}
|
||||
return link
|
||||
break;
|
||||
}
|
||||
index -= item.length
|
||||
index -= startItem.length
|
||||
}
|
||||
}
|
||||
|
||||
if (startItem !== null) {
|
||||
let endItem = startItem
|
||||
let remaining = length
|
||||
for (;endItem !== null && endItem.right !== null && endItem.length > remaining; endItem = endItem.right) {
|
||||
// iterate over the items to reach the last block in the quoted range
|
||||
remaining -= endItem.length
|
||||
}
|
||||
if (endItem.length >= remaining) {
|
||||
endItem = getItemCleanEnd(transaction, transaction.doc.store, createID(startItem.id.client, startItem.id.clock + remaining - 1))
|
||||
const start = new RelativePosition(null, null, startItem.id, 0)
|
||||
const end = new RelativePosition(null, null, endItem.lastId, -1)
|
||||
const link = new YWeakLink(start, end, startItem, endItem)
|
||||
if (parent.doc !== null) {
|
||||
transact(parent.doc, (transaction) => {
|
||||
for (let item = link._firstItem; item !== null; item = item = item.right) {
|
||||
createLink(transaction, item, link)
|
||||
if (item === link._lastItem) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return link
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +260,9 @@ export const arrayWeakLink = (transaction, parent, index) => {
|
||||
export const mapWeakLink = (parent, key) => {
|
||||
const item = parent._map.get(key)
|
||||
if (item !== undefined) {
|
||||
const link = new YWeakLink(item.id, item)
|
||||
const start = new RelativePosition(null, null, item.id, 0)
|
||||
const end = new RelativePosition(null, null, item.id, -1)
|
||||
const link = new YWeakLink(start, end, item, item)
|
||||
if (parent.doc !== null) {
|
||||
transact(parent.doc, (transaction) => {
|
||||
createLink(transaction, item, link)
|
||||
|
Loading…
x
Reference in New Issue
Block a user