From 3a0e65403f32e1bedd989dfb48d592846844a9fc Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 9 Nov 2017 17:31:58 -0800 Subject: [PATCH] fix undo scope --- examples/html-editor/index.js | 5 ++- src/Util/UndoManager.js | 80 ++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/examples/html-editor/index.js b/examples/html-editor/index.js index 8eb4c56d..6766fed6 100644 --- a/examples/html-editor/index.js +++ b/examples/html-editor/index.js @@ -17,11 +17,12 @@ window.onload = function () { window.yXmlType.bindToDom(document.body) } window.undoManager = new Y.utils.UndoManager(window.yXmlType, { - captureTimeout: 1000 + captureTimeout: 0 }) document.onkeydown = function interceptUndoRedo (e) { - if (e.keyCode === 90 && e.ctrlKey) { + if (e.keyCode === 90 && e.metaKey) { + console.log('uidtaren') if (!e.shiftKey) { console.info('Undo!') window.undoManager.undo() diff --git a/src/Util/UndoManager.js b/src/Util/UndoManager.js index 7c535d6d..4e2a5190 100644 --- a/src/Util/UndoManager.js +++ b/src/Util/UndoManager.js @@ -24,10 +24,45 @@ function isStructInScope (y, struct, scope) { return false } +function applyReverseOperation (y, scope, reverseBuffer) { + let performedUndo = false + y.transact(() => { + while (!performedUndo && reverseBuffer.length > 0) { + let undoOp = reverseBuffer.pop() + // make sure that it is possible to iterate {from}-{to} + y.os.getItemCleanStart(undoOp.fromState) + y.os.getItemCleanEnd(undoOp.toState) + y.os.iterate(undoOp.fromState, undoOp.toState, op => { + if (!op._deleted && isStructInScope(y, op, scope)) { + performedUndo = true + op._delete(y) + } + }) + for (let op of undoOp.deletedStructs) { + if ( + isStructInScope(y, op, scope) && + op._parent !== y && + !op._parent._deleted && + ( + op._parent._id.user !== y.userID || + op._parent._id.clock < undoOp.fromState.clock || + op._parent._id.clock > undoOp.fromState.clock + ) + ) { + performedUndo = true + op = op._copy(undoOp.deletedStructs) + op._integrate(y) + } + } + } + }) + return performedUndo +} + export default class UndoManager { constructor (scope, options = {}) { this.options = options - options.captureTimeout = options.captureTimeout || 0 + options.captureTimeout = options.captureTimeout == null ? 500 : options.captureTimeout this._undoBuffer = [] this._redoBuffer = [] this._scope = scope @@ -36,11 +71,11 @@ export default class UndoManager { const y = scope._y this.y = y y.on('afterTransaction', (y, remote) => { - if (!remote && (y._transaction.beforeState.has(y.userID) || y._transaction.deletedStructs.size > 0)) { + if (!remote && y._transaction.changedParentTypes.has(scope)) { let reverseOperation = new ReverseOperation(y) if (!this._undoing) { let lastUndoOp = this._undoBuffer.length > 0 ? this._undoBuffer[this._undoBuffer.length - 1] : null - if (lastUndoOp !== null && lastUndoOp.created - reverseOperation.created <= options.captureTimeout) { + if (lastUndoOp !== null && reverseOperation.created - lastUndoOp.created <= options.captureTimeout) { console.log('appending', lastUndoOp, reverseOperation) lastUndoOp.created = reverseOperation.created lastUndoOp.toState = reverseOperation.toState @@ -60,45 +95,14 @@ export default class UndoManager { undo () { console.log('undoing') this._undoing = true - this._applyReverseOperation(this._undoBuffer) + const performedUndo = applyReverseOperation(this.y, this._scope, this._undoBuffer) this._undoing = false + return performedUndo } redo () { this._redoing = true - this._applyReverseOperation(this._redoBuffer) + const performedRedo = applyReverseOperation(this.y, this._scope, this._redoBuffer) this._redoing = false - } - _applyReverseOperation (reverseBuffer) { - this.y.transact(() => { - let performedUndo = false - while (!performedUndo && reverseBuffer.length > 0) { - let undoOp = reverseBuffer.pop() - // make sure that it is possible to iterate {from}-{to} - this.y.os.getItemCleanStart(undoOp.fromState) - this.y.os.getItemCleanEnd(undoOp.toState) - this.y.os.iterate(undoOp.fromState, undoOp.toState, op => { - if (!op._deleted && isStructInScope(this.y, op, this._scope)) { - performedUndo = true - op._delete(this.y) - } - }) - for (let op of undoOp.deletedStructs) { - if ( - isStructInScope(this.y, op, this._scope) && - op._parent !== this.y && - !op._parent._deleted && - ( - op._parent._id.user !== this.y.userID || - op._parent._id.clock < undoOp.fromState.clock || - op._parent._id.clock > undoOp.fromState.clock - ) - ) { - performedUndo = true - op = op._copy(undoOp.deletedStructs) - op._integrate(this.y) - } - } - } - }) + return performedRedo } }