diff options
Diffstat (limited to 'src/client/util/UndoManager.ts')
-rw-r--r-- | src/client/util/UndoManager.ts | 73 |
1 files changed, 46 insertions, 27 deletions
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index d0aec45a6..b59af6656 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -1,9 +1,10 @@ import { observable, action, runInAction } from 'mobx'; +import { Field } from '../../fields/Doc'; import { Without } from '../../Utils'; function getBatchName(target: any, key: string | symbol): string { const keyName = key.toString(); - if (target && target.constructor && target.constructor.name) { + if (target?.constructor?.name) { return `${target.constructor.name}.${keyName}`; } return keyName; @@ -34,6 +35,17 @@ function propertyDecorator(target: any, key: string | symbol) { }); } +export function undoable(fn: (...args: any[]) => any, batchName: string): (...args: any[]) => any { + return function () { + const batch = UndoManager.StartBatch(batchName); + try { + return fn.apply(undefined, arguments as any); + } finally { + batch.end(); + } + }; +} + export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any; export function undoBatch(fn: (...args: any[]) => any): (...args: any[]) => any; export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any { @@ -73,15 +85,18 @@ export namespace UndoManager { } type UndoBatch = UndoEvent[]; + export let undoStackNames: string[] = observable([]); + export let redoStackNames: string[] = observable([]); export let undoStack: UndoBatch[] = observable([]); export let redoStack: UndoBatch[] = observable([]); let currentBatch: UndoBatch | undefined; - export let batchCounter = 0; + export let batchCounter = observable.box(0); let undoing = false; - let tempEvents: UndoEvent[] | undefined = undefined; + export let tempEvents: UndoEvent[] | undefined = undefined; - export function AddEvent(event: UndoEvent): void { - if (currentBatch && batchCounter && !undoing) { + export function AddEvent(event: UndoEvent, value?: any): void { + if (currentBatch && batchCounter.get() && !undoing) { + console.log(' '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + (value instanceof Array ? value.map(val => Field.toScriptString(val)).join(',') : Field.toScriptString(value))); currentBatch.push(event); tempEvents?.push(event); } @@ -135,11 +150,13 @@ export namespace UndoManager { private dispose = (cancel: boolean) => { if (this.disposed) { - throw new Error('Cannot dispose an already disposed batch'); + console.log('WARNING: undo batch already disposed'); + return false; + } else { + this.disposed = true; + openBatches.splice(openBatches.indexOf(this)); + return EndBatch(this.batchName, cancel); } - this.disposed = true; - openBatches.splice(openBatches.indexOf(this)); - return EndBatch(cancel); }; end = () => this.dispose(false); @@ -147,22 +164,23 @@ export namespace UndoManager { } export function StartBatch(batchName: string): Batch { - // console.log("Start " + batchCounter + " " + batchName); - batchCounter++; - if (batchCounter > 0 && currentBatch === undefined) { + console.log(' '.slice(0, batchCounter.get()) + 'Start ' + batchCounter + ' ' + batchName); + runInAction(() => batchCounter.set(batchCounter.get() + 1)); + if (currentBatch === undefined) { currentBatch = []; } return new Batch(batchName); } - const EndBatch = action((cancel: boolean = false) => { - batchCounter--; - // console.log("End " + batchCounter); - if (batchCounter === 0 && currentBatch?.length) { - // console.log("------ended----") + const EndBatch = action((batchName: string, cancel: boolean = false) => { + runInAction(() => batchCounter.set(batchCounter.get() - 1)); + console.log(' '.slice(0, batchCounter.get()) + 'End ' + batchName + ' (' + currentBatch?.length + ')'); + if (batchCounter.get() === 0 && currentBatch?.length) { if (!cancel) { undoStack.push(currentBatch); + undoStackNames.push(batchName ?? '???'); } + redoStackNames.length = 0; redoStack.length = 0; currentBatch = undefined; return true; @@ -170,15 +188,11 @@ export namespace UndoManager { return false; }); - export function RunInTempBatch<T>(fn: () => T) { + export function StartTempBatch() { tempEvents = []; - try { - const success = runInAction(fn); - if (!success) UndoManager.UndoTempBatch(); - return success; - } finally { - tempEvents = undefined; - } + } + export function EndTempBatch<T>(success: boolean) { + UndoManager.UndoTempBatch(success); } //TODO Make this return the return value export function RunInBatch<T>(fn: () => T, batchName: string) { @@ -189,10 +203,11 @@ export namespace UndoManager { batch.end(); } } - export const UndoTempBatch = action(() => { - if (tempEvents) { + export const UndoTempBatch = action((success: any) => { + if (tempEvents && !success) { undoing = true; for (let i = tempEvents.length - 1; i >= 0; i--) { + currentBatch?.includes(tempEvents[i]) && currentBatch.splice(currentBatch.indexOf(tempEvents[i])); tempEvents[i].undo(); } undoing = false; @@ -204,6 +219,7 @@ export namespace UndoManager { return; } + const names = undoStackNames.pop(); const commands = undoStack.pop(); if (!commands) { return; @@ -215,6 +231,7 @@ export namespace UndoManager { } undoing = false; + redoStackNames.push(names ?? '???'); redoStack.push(commands); }); @@ -223,6 +240,7 @@ export namespace UndoManager { return; } + const names = redoStackNames.pop(); const commands = redoStack.pop(); if (!commands) { return; @@ -234,6 +252,7 @@ export namespace UndoManager { } undoing = false; + undoStackNames.push(names ?? '???'); undoStack.push(commands); }); } |