added tests for quoting multiple elements in arrays

This commit is contained in:
Bartosz Sypytkowski 2023-07-07 11:11:22 +02:00
parent 5415565cf0
commit 535bcc3424
3 changed files with 118 additions and 40 deletions

View File

@ -10,7 +10,8 @@ import {
readYXmlText,
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item, YEvent, AbstractType, // eslint-disable-line
readYWeakLink,
unlinkFrom
unlinkFrom,
ID
} from '../internals.js'
import * as error from 'lib0/error'
@ -113,15 +114,17 @@ export class ContentType {
// when removing weak links, remove references to them
// from type they're pointing to
const type = /** @type {WeakLink<any>} */ (this.type);
const end = /** @type {ID} */ (type._quoteEnd.item)
for (let item = type._firstItem; item !== null; item = item.right) {
if (item.linked) {
unlinkFrom(transaction, item, type)
}
if (item === type._lastItem) {
const lastId = item.lastId
if (lastId.client === end.client && lastId.clock === end.clock) {
break;
}
}
type._firstItem = type._lastItem = null
type._firstItem = null
}
let item = this.type._start

View File

@ -40,14 +40,12 @@ export class YWeakLink extends AbstractType {
* @param {RelativePosition} start
* @param {RelativePosition} end
* @param {Item|null} firstItem
* @param {Item|null} lastItem
*/
constructor(start, end, firstItem, lastItem) {
constructor(start, end, firstItem) {
super()
this._quoteStart = start
this._quoteEnd = end
this._firstItem = firstItem
this._lastItem = lastItem
}
/**
@ -90,11 +88,16 @@ export class YWeakLink extends AbstractType {
unqote() {
let result = /** @type {Array<any>} */ ([])
let item = this._firstItem
const end = /** @type {ID} */ (this._quoteEnd.item)
//TODO: moved elements
while (item !== null && item !== this._lastItem) {
while (item !== null) {
if (!item.deleted) {
result = result.concat(item.content.getContent())
}
const lastId = item.lastId
if (lastId.client === end.client && lastId.clock === end.clock) {
break;
}
item = item.right
}
return result
@ -118,22 +121,22 @@ export class YWeakLink extends AbstractType {
// in such case we need to cut of the linked element into a
// separate block
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))
getItemCleanEnd(transaction, y.store, /** @type {ID} */(this._quoteEnd.item))
if (firstItem.parentSub !== null) {
// for maps, advance to most recent item
while (firstItem.right !== null) {
firstItem = firstItem.right
}
lastItem = firstItem
}
this._firstItem = firstItem
this._lastItem = lastItem
/** @type {Item|null} */
let item = firstItem
for (; item !== null; item = item.right) {
let end = /** @type {ID} */ (this._quoteEnd.item)
for (;item !== null; item = item.right) {
createLink(transaction, item, this)
if (item === lastItem) {
const lastId = item.lastId
if (lastId.client === end.client && lastId.clock === end.clock) {
break;
}
}
@ -145,14 +148,14 @@ export class YWeakLink extends AbstractType {
* @return {YWeakLink<T>}
*/
_copy () {
return new YWeakLink(this._quoteStart, this._quoteEnd, this._firstItem, this._lastItem)
return new YWeakLink(this._quoteStart, this._quoteEnd, this._firstItem)
}
/**
* @return {YWeakLink<T>}
*/
clone () {
return new YWeakLink(this._quoteStart, this._quoteEnd, this._firstItem, this._lastItem)
return new YWeakLink(this._quoteStart, this._quoteEnd, this._firstItem)
}
/**
@ -194,7 +197,7 @@ export const readYWeakLink = decoder => {
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)
return new YWeakLink(start, end, null)
}
const invalidQuotedRange = error.create('Invalid quoted range length.')
@ -223,33 +226,36 @@ export const arrayWeakLink = (transaction, parent, index, length = 1) => {
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;
}
}
})
let endItem = startItem
let remaining = length
for (;endItem !== null; endItem = endItem.right) {
if (!endItem.deleted && endItem.countable) {
if (remaining > endItem.length) {
remaining -= endItem.length
} else {
endItem = getItemCleanEnd(transaction, transaction.doc.store, createID(endItem.id.client, endItem.id.clock + remaining - 1))
break;
}
return link
}
}
if (startItem !== null && endItem !== null) {
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)
if (parent.doc !== null) {
transact(parent.doc, (transaction) => {
const end = /** @type {ID} */ (link._quoteEnd.item)
for (let item = link._firstItem; item !== null; item = item = item.right) {
createLink(transaction, item, link)
const lastId = item.lastId
if (lastId.client === end.client && lastId.clock === end.clock) {
break;
}
}
})
}
return link
}
throw invalidQuotedRange
}
@ -265,7 +271,7 @@ export const mapWeakLink = (parent, key) => {
if (item !== undefined) {
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)
const link = new YWeakLink(start, end, item)
if (parent.doc !== null) {
transact(parent.doc, (transaction) => {
createLink(transaction, item, link)

View File

@ -42,6 +42,75 @@ export const testBasicArray = tc => {
t.compare(array1.get(3).deref(), 2)
}
/**
* @param {t.TestCase} tc
*/
export const testArrayQuoteMultipleElements = tc => {
const { testConnector, array0, array1 } = init(tc, {users:2})
const nested = new Y.Map([['key', 'value']])
array0.insert(0, [1, 2, nested, 3])
array0.insert(0, [array0.quote(1, 3)])
const link0 = array0.get(0)
t.compare(link0.unqote(), [2, nested, 3])
t.compare(array0.get(1), 1)
t.compare(array0.get(2), 2)
t.compare(array0.get(3), nested)
t.compare(array0.get(4), 3)
testConnector.flushAllMessages()
const link1 = array1.get(0)
let unqoted = link1.unqote()
t.compare(unqoted[0], 2)
t.compare(unqoted[1].toJSON(), {'key':'value'})
t.compare(unqoted[2], 3)
t.compare(array1.get(1), 1)
t.compare(array1.get(2), 2)
t.compare(array1.get(3).toJSON(), {'key':'value'})
t.compare(array1.get(4), 3)
array1.insert(3, ['A', 'B'])
unqoted = link1.unqote()
t.compare(unqoted[0], 2)
t.compare(unqoted[1], 'A')
t.compare(unqoted[2], 'B')
t.compare(unqoted[3].toJSON(), {'key':'value'})
t.compare(unqoted[4], 3)
testConnector.flushAllMessages()
t.compare(array0.get(0).unqote(), [2, 'A', 'B', nested, 3])
}
/**
* @param {t.TestCase} tc
*/
export const testSelfQuotation = tc => {
const { testConnector, array0, array1 } = init(tc, {users:2})
array0.insert(0, [1, 2, 3, 4])
const link0 = array0.quote(0, 3)
array0.insert(1, [link0]) // link is inserted into its own range
t.compare(link0.unqote(), [1, link0, 2, 3])
t.compare(array0.get(0), 1)
t.compare(array0.get(1), link0)
t.compare(array0.get(2), 2)
t.compare(array0.get(3), 3)
t.compare(array0.get(4), 4)
testConnector.flushAllMessages()
const link1 = array1.get(1)
let unqoted = link1.unqote()
t.compare(unqoted, [1, link1, 2, 3])
t.compare(array1.get(0), 1)
t.compare(array1.get(1), link1)
t.compare(array1.get(2), 2)
t.compare(array1.get(3), 3)
t.compare(array1.get(4), 4)
}
/**
* @param {t.TestCase} tc
*/