From 29fa60ccf9fd537352de088fdf362e7df25fa076 Mon Sep 17 00:00:00 2001
From: Kevin Jahns <kevin.jahns@protonmail.com>
Date: Thu, 29 Feb 2024 14:46:43 +0100
Subject: [PATCH] [Undo] add UndoManager.currStackItem

---
 package.json             |  3 +-
 src/utils/UndoManager.js | 22 +++++----------
 tests/undo-redo.tests.js | 61 +++++++++-------------------------------
 3 files changed, 23 insertions(+), 63 deletions(-)

diff --git a/package.json b/package.json
index d0133c49..16544493 100644
--- a/package.json
+++ b/package.json
@@ -12,9 +12,10 @@
     "url": "https://github.com/sponsors/dmonad"
   },
   "scripts": {
+    "clean": "rm -rf dist docs",
     "test": "npm run dist && node ./dist/tests.cjs --repetition-time 50",
     "test-extensive": "npm run lint && npm run dist && node ./dist/tests.cjs --production --repetition-time 10000",
-    "dist": "rm -rf dist && rollup -c && tsc",
+    "dist": "npm run clean && rollup -c && tsc",
     "watch": "rollup -wc",
     "lint": "markdownlint README.md && standard && tsc",
     "docs": "rm -rf docs; jsdoc --configure ./.jsdoc.json --verbose --readme ./README.md --package ./package.json || true",
diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js
index c0dca63d..16ccb7ae 100644
--- a/src/utils/UndoManager.js
+++ b/src/utils/UndoManager.js
@@ -52,11 +52,6 @@ const clearUndoManagerStackItem = (tr, um, stackItem) => {
  * @return {StackItem?}
  */
 const popStackItem = (undoManager, stack, eventType) => {
-  /**
-   * Whether a change happened
-   * @type {StackItem?}
-   */
-  let result = null
   /**
    * Keep a reference to the transaction so we can fire the event with the changedParentTypes
    * @type {any}
@@ -65,7 +60,7 @@ const popStackItem = (undoManager, stack, eventType) => {
   const doc = undoManager.doc
   const scope = undoManager.scope
   transact(doc, transaction => {
-    while (stack.length > 0 && result === null) {
+    while (stack.length > 0 && undoManager.currStackItem === null) {
       const store = doc.store
       const stackItem = /** @type {StackItem} */ (stack.pop())
       /**
@@ -113,7 +108,7 @@ const popStackItem = (undoManager, stack, eventType) => {
           performedChange = true
         }
       }
-      result = performedChange ? stackItem : null
+      undoManager.currStackItem = performedChange ? stackItem : null
     }
     transaction.changed.forEach((subProps, type) => {
       // destroy search marker if necessary
@@ -123,11 +118,12 @@ const popStackItem = (undoManager, stack, eventType) => {
     })
     _tr = transaction
   }, undoManager)
-  if (result != null) {
+  if (undoManager.currStackItem != null) {
     const changedParentTypes = _tr.changedParentTypes
-    undoManager.emit('stack-item-popped', [{ stackItem: result, type: eventType, changedParentTypes }, undoManager])
+    undoManager.emit('stack-item-popped', [{ stackItem: undoManager.currStackItem, type: eventType, changedParentTypes }, undoManager])
+    undoManager.currStackItem = null
   }
-  return result
+  return undoManager.currStackItem
 }
 
 /**
@@ -196,7 +192,7 @@ export class UndoManager extends Observable {
      *
      * @type {StackItem|null}
      */
-    this.doingStackItem = null
+    this.currStackItem = null
     this.lastChange = 0
     this.ignoreRemoteMapChanges = ignoreRemoteMapChanges
     this.captureTimeout = captureTimeout
@@ -337,12 +333,10 @@ export class UndoManager extends Observable {
    */
   undo () {
     this.undoing = true
-    this.doingStackItem = array.last(this.undoStack) ?? null
     let res
     try {
       res = popStackItem(this, this.undoStack, 'undo')
     } finally {
-      this.doingStackItem = null
       this.undoing = false
     }
     return res
@@ -355,12 +349,10 @@ export class UndoManager extends Observable {
    */
   redo () {
     this.redoing = true
-    this.doingStackItem = array.last(this.redoStack) ?? null
     let res
     try {
       res = popStackItem(this, this.redoStack, 'redo')
     } finally {
-      this.doingStackItem = null
       this.redoing = false
     }
     return res
diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js
index 82c98129..2fb2ff2e 100644
--- a/tests/undo-redo.tests.js
+++ b/tests/undo-redo.tests.js
@@ -719,62 +719,29 @@ export const testUndoDeleteInMap = (tc) => {
 /**
  * It should expose the StackItem being processed if undoing
  *
- * @param {t.TestCase} tc
+ * @param {t.TestCase} _tc
  */
-export const testUndoDoingStackItem = async (tc) => {
+export const testUndoDoingStackItem = async (_tc) => {
   const doc = new Y.Doc()
   const text = doc.getText('text')
   const undoManager = new Y.UndoManager([text])
-
   undoManager.on('stack-item-added', /** @param {any} event */ event => {
     event.stackItem.meta.set('str', '42')
   })
-
-  const meta = new Promise((resolve) => {
-    setTimeout(() => resolve('ABORTED'), 50)
-    text.observe((event) => {
-      const /** @type {Y.UndoManager} */ origin = event.transaction.origin
-      if (origin === undoManager && origin.undoing) {
-        resolve(origin.doingStackItem?.meta.get('str'))
-      }
-    })
+  let metaUndo = /** @type {any} */ (null)
+  let metaRedo = /** @type {any} */ (null)
+  text.observe((event) => {
+    const /** @type {Y.UndoManager} */ origin = event.transaction.origin
+    if (origin === undoManager && origin.undoing) {
+      metaUndo = origin.currStackItem?.meta.get('str')
+    } else if (origin === undoManager && origin.redoing) {
+      metaRedo = origin.currStackItem?.meta.get('str')
+    }
   })
-
-  text.insert(0, 'abc')
-  undoManager.undo()
-
-  t.compare(await meta, '42')
-  t.compare(undoManager.doingStackItem, null)
-}
-
-/**
- * It should expose the StackItem being processed if redoing
- *
- * @param {t.TestCase} tc
- */
-export const testRedoDoingStackItem = async (tc) => {
-  const doc = new Y.Doc()
-  const text = doc.getText('text')
-  const undoManager = new Y.UndoManager([text])
-
-  undoManager.on('stack-item-added', /** @param {any} event */ event => {
-    event.stackItem.meta.set('str', '42')
-  })
-
-  const meta = new Promise(resolve => {
-    setTimeout(() => resolve('ABORTED'), 50)
-    text.observe((event) => {
-      const /** @type {Y.UndoManager} */ origin = event.transaction.origin
-      if (origin === undoManager && origin.redoing) {
-        resolve(origin.doingStackItem?.meta.get('str'))
-      }
-    })
-  })
-
   text.insert(0, 'abc')
   undoManager.undo()
   undoManager.redo()
-
-  t.compare(await meta, '42')
-  t.compare(undoManager.doingStackItem, null)
+  t.compare(metaUndo, '42', 'currStackItem is accessible while undoing')
+  t.compare(metaRedo, '42', 'currStackItem is accessible while redoing')
+  t.compare(undoManager.currStackItem, null, 'currStackItem is null after observe/transaction')
 }