diff --git a/README.md b/README.md index f33c138c..2579b6d6 100644 --- a/README.md +++ b/README.md @@ -614,8 +614,9 @@ parameter that is stored on transaction.origin and toJSON():any
Deprecated: It is recommended to call toJSON directly on the shared types. -Converts the entire document into a js object, recursively traversing each yjs type. Doesn't -log types that have not been defined (using ydoc.getType(..)). +Converts the entire document into a js object, recursively traversing each yjs +type. Doesn't log types that have not been defined (using +ydoc.getType(..)).
get(string, Y.[TypeClass]):[Type]
Define a shared type.
diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 3a01000b..022d8985 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -5,11 +5,11 @@ import { transact, createID, redoItem, - iterateStructs, isParentOf, followRedone, getItemCleanStart, - getState, + isDeleted, + addToDeleteSet, Transaction, Doc, Item, GC, DeleteSet, AbstractType, YEvent // eslint-disable-line } from '../internals.js' @@ -18,14 +18,12 @@ import { Observable } from 'lib0/observable.js' class StackItem { /** - * @param {DeleteSet} ds - * @param {Map} beforeState - * @param {Map} afterState + * @param {DeleteSet} deletions + * @param {DeleteSet} insertions */ - constructor (ds, beforeState, afterState) { - this.ds = ds - this.beforeState = beforeState - this.afterState = afterState + constructor (deletions, insertions) { + this.insertions = insertions + this.deletions = deletions /** * Use this to save and restore metadata like selection range */ @@ -65,48 +63,26 @@ const popStackItem = (undoManager, stack, eventType) => { */ const itemsToDelete = [] let performedChange = false - stackItem.afterState.forEach((endClock, client) => { - const startClock = stackItem.beforeState.get(client) || 0 - const len = endClock - startClock - // @todo iterateStructs should not need the structs parameter - const structs = /** @type {Array} */ (store.clients.get(client)) - if (startClock !== endClock) { - // make sure structs don't overlap with the range of created operations [stackItem.start, stackItem.start + stackItem.end) - // this must be executed before deleted structs are iterated. - getItemCleanStart(transaction, createID(client, startClock)) - if (endClock < getState(doc.store, client)) { - getItemCleanStart(transaction, createID(client, endClock)) - } - iterateStructs(transaction, structs, startClock, len, struct => { - if (struct instanceof Item) { - if (struct.redone !== null) { - let { item, diff } = followRedone(store, struct.id) - if (diff > 0) { - item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff)) - } - if (item.length > len) { - getItemCleanStart(transaction, createID(item.id.client, endClock)) - } - struct = item - } - if (!struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) { - itemsToDelete.push(struct) - } + iterateDeletedStructs(transaction, stackItem.insertions, struct => { + if (struct instanceof Item) { + if (struct.redone !== null) { + let { item, diff } = followRedone(store, struct.id) + if (diff > 0) { + item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff)) } - }) + struct = item + } + if (!struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) { + itemsToDelete.push(struct) + } } }) - iterateDeletedStructs(transaction, stackItem.ds, struct => { - const id = struct.id - const clock = id.clock - const client = id.client - const startClock = stackItem.beforeState.get(client) || 0 - const endClock = stackItem.afterState.get(client) || 0 + iterateDeletedStructs(transaction, stackItem.deletions, struct => { if ( struct instanceof Item && scope.some(type => isParentOf(type, struct)) && - // Never redo structs in [stackItem.start, stackItem.start + stackItem.end) because they were created and deleted in the same capture interval. - !(clock >= startClock && clock < endClock) + // Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval. + !isDeleted(stackItem.insertions, struct.id) ) { itemsToRedo.add(struct) } @@ -201,17 +177,23 @@ export class UndoManager extends Observable { // neither undoing nor redoing: delete redoStack this.redoStack = [] } - const beforeState = transaction.beforeState - const afterState = transaction.afterState + const insertions = new DeleteSet() + transaction.afterState.forEach((endClock, client) => { + const startClock = transaction.beforeState.get(client) || 0 + const len = endClock - startClock + if (len > 0) { + addToDeleteSet(insertions, client, startClock, len) + } + }) const now = time.getUnixTime() if (now - this.lastChange < captureTimeout && stack.length > 0 && !undoing && !redoing) { // append change to last stack op const lastOp = stack[stack.length - 1] - lastOp.ds = mergeDeleteSets([lastOp.ds, transaction.deleteSet]) - lastOp.afterState = afterState + lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet]) + lastOp.insertions = mergeDeleteSets([lastOp.insertions, insertions]) } else { // create a new stack op - stack.push(new StackItem(transaction.deleteSet, beforeState, afterState)) + stack.push(new StackItem(transaction.deleteSet, insertions)) } if (!undoing && !redoing) { this.lastChange = now @@ -232,7 +214,7 @@ export class UndoManager extends Observable { * @param {StackItem} stackItem */ const clearItem = stackItem => { - iterateDeletedStructs(transaction, stackItem.ds, item => { + iterateDeletedStructs(transaction, stackItem.deletions, item => { if (item instanceof Item && this.scope.some(type => isParentOf(type, item))) { keepItem(item, false) }