fixed pos.rel cases

This commit is contained in:
Kevin Jahns 2021-11-21 19:53:53 +01:00
parent 40c3be1732
commit 2a33507c00
4 changed files with 65 additions and 9 deletions

View File

@ -55,11 +55,19 @@ export const useSearchMarker = (tr, yarray, index, f) => {
searchMarker.push(fsm)
}
const diff = fsm.index - index
// @todo create fresh marker if diff > index
if (diff > 0) {
fsm.backward(tr, diff)
} else {
fsm.forward(tr, -diff)
}
// @todo remove this tests
const otherTesting = new ListIterator(yarray)
otherTesting.forward(tr, index)
if (otherTesting.nextItem !== fsm.nextItem || otherTesting.index !== fsm.index || otherTesting.reachedEnd !== fsm.reachedEnd) {
throw new Error('udtirane')
}
const result = f(fsm)
if (fsm.reachedEnd) {
fsm.reachedEnd = false
@ -93,7 +101,7 @@ export const useSearchMarker = (tr, yarray, index, f) => {
* @param {Array<ListIterator>} searchMarker
* @param {number} index
* @param {number} len If insertion, len is positive. If deletion, len is negative.
* @param {ListIterator} origSearchMarker Do not update this searchmarker because it is the one we used to manipulate.
* @param {ListIterator|null} origSearchMarker Do not update this searchmarker because it is the one we used to manipulate. @todo !=null for improved perf in ytext
*/
export const updateMarkerChanges = (searchMarker, index, len, origSearchMarker) => {
for (let i = searchMarker.length - 1; i >= 0; i--) {

View File

@ -27,6 +27,7 @@ import {
updateMarkerChanges,
ContentType,
useSearchMarker,
findIndexCleanStart,
ListIterator, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction // eslint-disable-line
} from '../internals.js'
@ -127,9 +128,27 @@ const findPosition = (transaction, parent, index) => {
const currentAttributes = new Map()
if (parent._searchMarker) {
return useSearchMarker(transaction, parent, index, listIter => {
let left, right
if (listIter.rel > 0) {
// must exist because rel > 0
const nextItem = /** @type {Item} */ (listIter.nextItem)
if (listIter.rel === nextItem.length) {
left = nextItem
right = left.right
} else {
const structs = /** @type {Array<Item|GC>} */ (transaction.doc.store.clients.get(nextItem.id.client))
const after = /** @type {Item} */ (structs[findIndexCleanStart(transaction, structs, nextItem.id.clock + listIter.rel)])
listIter.nextItem = after
listIter.rel = 0
left = listIter.left
right = listIter.right
}
} else {
left = listIter.left
right = listIter.right
}
// @todo this should simply split if .rel > 0
const pos = new ItemTextListPosition(listIter.left, listIter.right, listIter.index, currentAttributes)
return findNextPosition(transaction, pos, index - listIter.index + listIter.rel)
return new ItemTextListPosition(left, right, index, currentAttributes)
})
} else {
const pos = new ItemTextListPosition(null, parent._start, 0, currentAttributes)
@ -266,7 +285,7 @@ const insertText = (transaction, parent, currPos, text, attributes) => {
const content = text.constructor === String ? new ContentString(/** @type {string} */ (text)) : (text instanceof AbstractType ? new ContentType(text) : new ContentEmbed(text))
let { left, right, index } = currPos
if (parent._searchMarker) {
updateMarkerChanges(transaction, parent._searchMarker, currPos.index, content.getLength())
updateMarkerChanges(parent._searchMarker, currPos.index, content.getLength(), null)
}
right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, content)
right.integrate(transaction, 0)
@ -471,7 +490,7 @@ const deleteText = (transaction, currPos, length) => {
}
const parent = /** @type {AbstractType<any>} */ (/** @type {Item} */ (currPos.left || currPos.right).parent)
if (parent._searchMarker) {
updateMarkerChanges(transaction, parent._searchMarker, currPos.index, -startLength + length)
updateMarkerChanges(parent._searchMarker, currPos.index, -startLength + length, null)
}
return currPos
}

View File

@ -162,13 +162,18 @@ export class ListIterator {
/**
* @param {Transaction} tr
* @param {number} len
* @return {ListIterator}
*/
backward (tr, len) {
if (this.index - len < 0) {
throw lengthExceeded
}
let item = this.nextItem && this.nextItem.left
this.index -= len
if (this.rel > len) {
this.rel -= len
return this
}
let item = this.nextItem && this.nextItem.left
if (this.rel) {
len -= this.rel
this.rel = 0
@ -177,8 +182,10 @@ export class ListIterator {
if (item.countable && !item.deleted && item.moved === this.currMove) {
len -= item.length
if (len < 0) {
this.rel = item.length + len
this.rel = -len
len = 0
}
if (len === 0) {
break
}
} else if (item.content.constructor === ContentMove && item.moved === this.currMove) {
@ -201,8 +208,7 @@ export class ListIterator {
}
item = item.left
}
this.index -= len
this.nextItem = item && item.right
this.nextItem = item
return this
}

View File

@ -373,6 +373,29 @@ export const compare = users => {
t.compare(Y.encodeStateVector(users[i]), Y.encodeStateVector(users[i + 1]))
compareDS(Y.createDeleteSetFromStructStore(users[i].store), Y.createDeleteSetFromStructStore(users[i + 1].store))
compareStructStores(users[i].store, users[i + 1].store)
// test list-iterator
{
const user = users[0]
user.transact(tr => {
const type = user.getArray('array')
Y.useSearchMarker(tr, type, type.length, walker => {
for (let i = type.length; i >= 0; i--) {
const otherWalker = new Y.ListIterator(type)
otherWalker.forward(tr, walker.index)
otherWalker.forward(tr, 0)
walker.forward(tr, 0)
t.assert(walker.index === i)
t.assert(walker.left === otherWalker.left)
t.assert(walker.right === otherWalker.right)
t.assert(walker.nextItem === otherWalker.nextItem)
t.assert(walker.reachedEnd === otherWalker.reachedEnd)
if (i > 0) {
walker.backward(tr, 1)
}
}
})
})
}
}
users.map(u => u.destroy())
}