fix several issues of supporting deleted move ops

This commit is contained in:
Kevin Jahns 2022-07-08 21:36:36 +02:00
parent 0ce40596d1
commit 19723670c4
4 changed files with 24 additions and 9 deletions

View File

@ -204,7 +204,14 @@ export class ContentMove {
let { start, end } = getMovedCoords(this, transaction) let { start, end } = getMovedCoords(this, transaction)
while (start !== end && start != null) { while (start !== end && start != null) {
if (start.moved === item) { 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) transaction.prevMoved.set(start, item)
} }
start.moved = null start.moved = null

View File

@ -16,6 +16,7 @@ import {
RelativePosition, ID, AbstractContent, ContentMove, Transaction, Item, AbstractType // eslint-disable-line RelativePosition, ID, AbstractContent, ContentMove, Transaction, Item, AbstractType // eslint-disable-line
} from '../internals.js' } from '../internals.js'
import { compareRelativePositions } from './RelativePosition.js' import { compareRelativePositions } from './RelativePosition.js'
import * as array from 'lib0/array'
const lengthExceeded = error.create('Length exceeded!') const lengthExceeded = error.create('Length exceeded!')
@ -709,7 +710,7 @@ export const getMinimalListViewRanges = (tr, walker, len) => {
// Move ranges must be applied in order // Move ranges must be applied in order
middleMove.end = end 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. // 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. // If that is the case, we need to ignore those moved items.
const { start, end } = getMovedCoords(range, tr) const { start, end } = getMovedCoords(range, tr)
@ -741,7 +742,7 @@ export const getMinimalListViewRanges = (tr, walker, len) => {
}) })
} }
return ranges return ranges
}).flat() }))
// filter out unnecessary ranges // filter out unnecessary ranges
return normalizedRanges.filter(range => !compareRelativePositions(range.start, range.end)) return normalizedRanges.filter(range => !compareRelativePositions(range.start, range.end))

View File

@ -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. * 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 * @param {AbstractStruct} struct
* @return {boolean} * @return {boolean}
*/ */
@ -172,7 +174,7 @@ export class YEvent {
const changed = /** @type Set<string|null> */ (this.transaction.changed.get(target)) const changed = /** @type Set<string|null> */ (this.transaction.changed.get(target))
if (changed.has(null)) { 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 = [] const movedStack = []
/** /**
@ -183,6 +185,10 @@ export class YEvent {
* @type {boolean} * @type {boolean}
*/ */
let currMoveIsNew = false let currMoveIsNew = false
/**
* @type {boolean}
*/
let currMoveIsDeleted = false
/** /**
* @type {Item | null} * @type {Item | null}
*/ */
@ -212,24 +218,26 @@ export class YEvent {
for (let item = target._start; ;) { for (let item = target._start; ;) {
if (item === currMoveEnd && currMove) { if (item === currMoveEnd && currMove) {
item = 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 currMoveIsNew = isNew
currMoveIsDeleted = isDeleted
currMoveEnd = end currMoveEnd = end
currMove = move currMove = move
} else if (item === null) { } else if (item === null) {
break break
} else if (item.content.constructor === ContentMove) { } else if (item.content.constructor === ContentMove) {
if (item.moved === currMove) { // @todo !item.deleted || this.deletes(item) 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 }) movedStack.push({ end: currMoveEnd, move: currMove, isNew: currMoveIsNew, isDeleted: currMoveIsDeleted })
const { start, end } = getMovedCoords(item.content, tr) const { start, end } = getMovedCoords(item.content, tr)
currMove = item currMove = item
currMoveEnd = end currMoveEnd = end
currMoveIsNew = this.adds(item) || currMoveIsNew currMoveIsNew = this.adds(item) || currMoveIsNew
currMoveIsDeleted = item.deleted || currMoveIsDeleted
item = start item = start
continue // do not move to item.right continue // do not move to item.right
} }
} else if (item.moved !== currMove) { } 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) { if (lastOp === null || lastOp.delete === undefined) {
packOp() packOp()
lastOp = { delete: 0 } lastOp = { delete: 0 }

View File

@ -569,7 +569,6 @@ export const testMoveDeletions = tc => {
ydoc.transact(tr => { ydoc.transact(tr => {
/** @type {Item} */ (yarray._start).delete(tr) /** @type {Item} */ (yarray._start).delete(tr)
}) })
debugger
t.compare(lastDelta, [{ delete: 1 }, { retain: 2 }, { insert: [3] }]) t.compare(lastDelta, [{ delete: 1 }, { retain: 2 }, { insert: [3] }])
t.compareArrays(yarray.toArray(), [1, 2, 3]) t.compareArrays(yarray.toArray(), [1, 2, 3])
t.compareArrays(yarray.toArray(), array) t.compareArrays(yarray.toArray(), array)