Skip to content

Commit

Permalink
Facilitate referencing UndoManager StackItem inside Type observers
Browse files Browse the repository at this point in the history
  • Loading branch information
mylesj committed Feb 29, 2024
1 parent a9dc72f commit 917261a
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/utils/UndoManager.js
Expand Up @@ -191,6 +191,12 @@ export class UndoManager extends Observable {
*/
this.undoing = false
this.redoing = false
/**
* The currently popped stack item if UndoManager.undoing or UndoManager.redoing
*
* @type {StackItem|null}
*/
this.doingStackItem = null
this.lastChange = 0
this.ignoreRemoteMapChanges = ignoreRemoteMapChanges
this.captureTimeout = captureTimeout
Expand Down Expand Up @@ -331,10 +337,12 @@ 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
Expand All @@ -347,10 +355,12 @@ 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
Expand Down
63 changes: 63 additions & 0 deletions tests/undo-redo.tests.js
Expand Up @@ -715,3 +715,66 @@ export const testUndoDeleteInMap = (tc) => {
undoManager.undo()
t.compare(map0.toJSON(), { a: 'a' })
}

/**
* It should expose the StackItem being processed if undoing
*
* @param {t.TestCase} 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'))
}
})
})

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)
}

0 comments on commit 917261a

Please sign in to comment.