diff --git a/src/structs/Item.js b/src/structs/Item.js index 3abfa325..10e0e841 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -35,6 +35,8 @@ import * as set from 'lib0/set.js' import * as binary from 'lib0/binary.js' /** + * @todo This should return several items + * * @param {StructStore} store * @param {ID} id * @return {{item:Item, diff:number}} diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 21c3b694..667cc9ac 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -75,15 +75,15 @@ const popStackItem = (undoManager, stack, eventType) => { itemsToRedo.add(struct) } }) - itemsToRedo.forEach(item => { - performedChange = redoItem(transaction, item, itemsToRedo) !== null || performedChange + itemsToRedo.forEach(struct => { + performedChange = redoItem(transaction, struct, itemsToRedo) !== null || performedChange }) /** * @type {Array} */ const itemsToDelete = [] iterateStructs(transaction, structs, stackStartClock, stackItem.len, struct => { - if (struct instanceof Item && !struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) { + if (struct instanceof Item) { if (struct.redone !== null) { let { item, diff } = followRedone(store, struct.id) if (diff > 0) { @@ -94,7 +94,9 @@ const popStackItem = (undoManager, stack, eventType) => { } struct = item } - itemsToDelete.push(struct) + if (!struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) { + itemsToDelete.push(struct) + } } }) // We want to delete in reverse order so that children are deleted before diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index cb85233e..349dcf7f 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -13,10 +13,23 @@ import * as t from 'lib0/testing.js' export const testUndoText = tc => { const { testConnector, text0, text1 } = init(tc, { users: 3 }) const undoManager = new UndoManager(text0) + + // items that are added & deleted in the same transaction won't be undo text0.insert(0, 'test') text0.delete(0, 4) undoManager.undo() t.assert(text0.toString() === '') + + // follow redone items + text0.insert(0, 'a') + undoManager.stopCapturing() + text0.delete(0, 1) + undoManager.stopCapturing() + undoManager.undo() + t.assert(text0.toString() === 'a') + undoManager.undo() + t.assert(text0.toString() === '') + text0.insert(0, 'abc') text1.insert(0, 'xyz') testConnector.syncAll()