diff --git a/src/structs/Item.js b/src/structs/Item.js index 392321e0..44e9aeef 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -28,6 +28,7 @@ import { import * as error from 'lib0/error' import * as binary from 'lib0/binary' +import { ContentMove } from './ContentMove.js' /** * @todo This should return several items @@ -381,9 +382,19 @@ 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)) { return this.parent.client } + if (this.content.constructor === ContentMove) { + const c = /** @type {ContentMove} */ (this.content) + const start = c.start.item + const end = c.isCollapsed() ? null : c.end.item + if (start && start.clock >= getState(store, start.client)) { + return start.client + } + if (end && end.clock >= getState(store, end.client)) { + return end.client + } + } // We have all missing ids, now find the items - if (this.origin) { this.left = getItemCleanEnd(transaction, this.origin) this.origin = this.left.lastId @@ -413,6 +424,7 @@ export class Item extends AbstractStruct { this.parent = /** @type {ContentType} */ (parentItem.content).type } } + return null } @@ -640,6 +652,7 @@ export class Item extends AbstractStruct { if (!this.deleted) { throw error.unexpectedCase() } + this.moved = null this.content.gc(store) if (parentGCd) { replaceStruct(store, this, new GC(this.id, this.length)) diff --git a/src/utils/ListIterator.js b/src/utils/ListIterator.js index 4fd830c8..4404f23c 100644 --- a/src/utils/ListIterator.js +++ b/src/utils/ListIterator.js @@ -286,8 +286,8 @@ export class ListIterator { const startLength = len const sm = this.type._searchMarker let item = this.nextItem - while (len > 0 && !this.reachedEnd) { - while (item && !item.deleted && item.countable && !this.reachedEnd && len > 0) { + while (len > 0) { + while (item && !item.deleted && item.countable && !this.reachedEnd && len > 0 && item.moved === this.currMove && item !== this.currMoveEnd) { if (this.rel > 0) { item = getItemCleanStart(tr, createID(item.id.client, item.id.clock + this.rel)) this.rel = 0 @@ -303,7 +303,7 @@ export class ListIterator { this.reachedEnd = true } } - if (item && !this.reachedEnd && len > 0) { + if (len > 0) { this.nextItem = item this.forward(tr, 0) item = this.nextItem diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index f50b9d70..e583fed3 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -195,13 +195,15 @@ export class YEvent { delta.push(lastOp) } } - for (let item = target._start; item !== null;) { - if (item === currMoveEnd) { + for (let item = target._start; ;) { + if (item === currMoveEnd && currMove) { item = currMove const { end, move, isNew } = movedStack.pop() || { end: null, move: null, isNew: false } currMoveIsNew = isNew currMoveEnd = end currMove = move + } else if (item === null) { + break } else if (item.content.constructor === ContentMove) { if (item.moved === currMove) { movedStack.push({ end: currMoveEnd, move: currMove, isNew: currMoveIsNew }) @@ -213,7 +215,7 @@ export class YEvent { continue // do not move to item.right } } else if (item.moved !== currMove) { - if (!currMoveIsNew && item.countable && item.moved && !this.adds(item) && !this.adds(item.moved) && (this.transaction.prevMoved.get(item) || null) === currMove) { + if (!currMoveIsNew && item.countable && item.moved && !this.adds(item) && this.adds(item.moved) && (this.transaction.prevMoved.get(item) || null) === currMove) { if (lastOp === null || lastOp.delete === undefined) { packOp() lastOp = { delete: 0 } diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 3c5ff268..0d0e84fa 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -472,6 +472,46 @@ export const testMove = tc => { compare(users) } +/** + * @param {t.TestCase} tc + */ +export const testMove2 = tc => { + { + // move in uninitialized type + const yarr = new Y.Array() + yarr.insert(0, [1, 2]) + yarr.move(1, 0) + // @ts-ignore + t.compare(yarr._prelimContent, [2, 1]) + } + const { array0, array1, users } = init(tc, { users: 3 }) + /** + * @type {any} + */ + let event0 = null + /** + * @type {any} + */ + let event1 = null + array0.observe(event => { + event0 = event + }) + array1.observe(event => { + event1 = event + }) + array0.insert(0, [1, 2]) + array0.move(1, 0) + t.compare(array0.toArray(), [2, 1]) + t.compare(event0.delta, [{ insert: [2] }, { retain: 1 }, { delete: 1 }]) + Y.applyUpdate(users[1], Y.encodeStateAsUpdate(users[0])) + t.compare(array1.toArray(), [2, 1]) + t.compare(event1.delta, [{ insert: [2, 1] }]) + array0.move(0, 2) + t.compare(array0.toArray(), [1, 2]) + t.compare(event0.delta, [{ delete: 1 }, { retain: 1 }, { insert: [2] }]) + compare(users) +} + /** * @param {t.TestCase} tc */ @@ -613,7 +653,7 @@ const compareTestobjects = cmp => { * @param {t.TestCase} tc */ export const testRepeatGeneratingYarrayTests6 = tc => { - compareTestobjects(applyRandomTests(tc, arrayTransactions, 3, monitorArrayTestObject)) + compareTestobjects(applyRandomTests(tc, arrayTransactions, 7, monitorArrayTestObject)) } /**