diff --git a/src/structs/ContentMove.js b/src/structs/ContentMove.js index 8cfd6b99..985e7fa8 100644 --- a/src/structs/ContentMove.js +++ b/src/structs/ContentMove.js @@ -204,7 +204,14 @@ export class ContentMove { let { start, end } = getMovedCoords(this, transaction) while (start !== end && start != null) { if (start.moved === item) { - if (!transaction.prevMoved.has(start)) { + const prevMoved = transaction.prevMoved.get(start) + if (addsStruct(transaction, item)) { + if (prevMoved === item) { + // Edge case: Item has been moved by this move op and it has been created & deleted in the same transaction (hence no effect that should be emitted by the change computation) + transaction.prevMoved.delete(start) + } + } else if (prevMoved == null) { // && !addsStruct(tr, item) + // Normal case: item has been moved by this move and it has not been created & deleted in the same transaction transaction.prevMoved.set(start, item) } start.moved = null diff --git a/src/utils/ListIterator.js b/src/utils/ListIterator.js index 8d82d0f3..aa6c877b 100644 --- a/src/utils/ListIterator.js +++ b/src/utils/ListIterator.js @@ -16,6 +16,7 @@ import { RelativePosition, ID, AbstractContent, ContentMove, Transaction, Item, AbstractType // eslint-disable-line } from '../internals.js' import { compareRelativePositions } from './RelativePosition.js' +import * as array from 'lib0/array' const lengthExceeded = error.create('Length exceeded!') @@ -709,7 +710,7 @@ export const getMinimalListViewRanges = (tr, walker, len) => { // Move ranges must be applied in order middleMove.end = end - const normalizedRanges = ranges.map(range => { + const normalizedRanges = array.flatten(ranges.map(range => { // A subset of a range could be moved by another move with a higher priority. // If that is the case, we need to ignore those moved items. const { start, end } = getMovedCoords(range, tr) @@ -741,7 +742,7 @@ export const getMinimalListViewRanges = (tr, walker, len) => { }) } return ranges - }).flat() + })) // filter out unnecessary ranges return normalizedRanges.filter(range => !compareRelativePositions(range.start, range.end)) diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 9ff62ebe..0f232418 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -142,6 +142,8 @@ export class YEvent { * * In contrast to change.deleted, this method also returns true if the struct was added and then deleted. * + * @todo this can be removed in the next release (prefer function) + * * @param {AbstractStruct} struct * @return {boolean} */ @@ -172,7 +174,7 @@ export class YEvent { const changed = /** @type Set */ (this.transaction.changed.get(target)) if (changed.has(null)) { /** - * @type {Array<{ end: Item | null, move: Item | null, isNew : boolean }>} + * @type {Array<{ end: Item | null, move: Item | null, isNew: boolean, isDeleted: boolean }>} */ const movedStack = [] /** @@ -183,6 +185,10 @@ export class YEvent { * @type {boolean} */ let currMoveIsNew = false + /** + * @type {boolean} + */ + let currMoveIsDeleted = false /** * @type {Item | null} */ @@ -212,24 +218,26 @@ export class YEvent { for (let item = target._start; ;) { if (item === currMoveEnd && currMove) { item = currMove - const { end, move, isNew } = movedStack.pop() || { end: null, move: null, isNew: false } + const { end, move, isNew, isDeleted } = movedStack.pop() || { end: null, move: null, isNew: false, isDeleted: false } currMoveIsNew = isNew + currMoveIsDeleted = isDeleted currMoveEnd = end currMove = move } else if (item === null) { break } else if (item.content.constructor === ContentMove) { - if (item.moved === currMove) { // @todo !item.deleted || this.deletes(item) - movedStack.push({ end: currMoveEnd, move: currMove, isNew: currMoveIsNew }) + if (item.moved === currMove && (!item.deleted || (this.deletes(item) && !this.adds(item)))) { // @todo !item.deleted || this.deletes(item) + movedStack.push({ end: currMoveEnd, move: currMove, isNew: currMoveIsNew, isDeleted: currMoveIsDeleted }) const { start, end } = getMovedCoords(item.content, tr) currMove = item currMoveEnd = end currMoveIsNew = this.adds(item) || currMoveIsNew + currMoveIsDeleted = item.deleted || currMoveIsDeleted item = start continue // do not move to item.right } } else if (item.moved !== currMove) { - if (!currMoveIsNew && item.countable && (!item.deleted || this.deletes(item)) && !this.adds(item) && (item.moved === null || isMovedByNew(item)) && (this.transaction.prevMoved.get(item) || null) === currMove) { + if (!currMoveIsNew && item.countable && (!item.deleted || this.deletes(item)) && !this.adds(item) && (item.moved === null || isMovedByNew(item) || currMoveIsDeleted) && (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 ff1ba336..c7306525 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -569,7 +569,6 @@ export const testMoveDeletions = tc => { ydoc.transact(tr => { /** @type {Item} */ (yarray._start).delete(tr) }) - debugger t.compare(lastDelta, [{ delete: 1 }, { retain: 2 }, { insert: [3] }]) t.compareArrays(yarray.toArray(), [1, 2, 3]) t.compareArrays(yarray.toArray(), array)