| Title: | Manage the History of Any Object with Undo/Redo Operations |
|---|---|
| Description: | Track the history of any R object and move through it with undo and redo operations. Anything can be stored, from a single number to a data frame or an entire application state. A manager can restrict its history to specific classes and cap how many items it keeps, and it can be reactive to integrate with 'Shiny'. |
| Authors: | Dean Attali [aut, cre] (ORCID: <https://orcid.org/0000-0002-5645-3493>) |
| Maintainer: | Dean Attali <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.0 |
| Built: | 2026-11-01 10:14:00 UTC |
| Source: | https://github.com/cran/undomanager |
Two managers are equal when they hold the same value, the same undo and redo
history, and the same type restriction. Internal bookkeeping, such as the
counter used to trigger shiny reactivity, is ignored, so two managers that
reached the same state by different routes compare as equal.
## S3 method for class 'UndoManager' all.equal(target, current, ...)## S3 method for class 'UndoManager' all.equal(target, current, ...)
target, current
|
The two |
... |
Passed on to |
Note that identical() cannot be used to compare managers: R6 objects are
environments, so identical() always reports two separate managers as
different regardless of their contents.
TRUE if the two managers are equal, otherwise a character vector
describing the differences.
Launch a small pixel art editor that demonstrates how to use
an UndoManager inside a Shiny app. The whole canvas is stored as a single
matrix and every stroke is one item in the manager's history, with buttons
and a timeline to move through it. The source is in
system.file("examples-shiny/pixelart", package = "undomanager").
run_example(...)run_example(...)
... |
Passed on to |
Requires the shiny package.
Does not return while the app is running.
if (interactive()) { run_example() }if (interactive()) { run_example() }
Create a new UndoManager to track the history of an object and move
through it with undo and redo operations. The manager starts out empty; the
first call to $do() gives it a value.
undomanager(type = NULL, allow_null = FALSE, max_size = Inf)undomanager(type = NULL, allow_null = FALSE, max_size = Inf)
type |
The permitted classes of the items ( |
allow_null |
Whether |
max_size |
The maximum number of items to keep. Once the history
grows past this, the oldest items are dropped. Use |
This is the recommended way to create a manager. It is equivalent to
UndoManager$new(), which is also available for anyone who wants to work
with the R6 class directly, such as to subclass it.
A manager is an R6 object, so it has reference semantics: its methods modify the manager in place instead of returning a modified copy, and assigning a manager to a second variable does not copy it.
A new UndoManager object.
UndoManager for the full list of methods and active bindings.
nums <- undomanager() nums$do(5) nums$do(7) nums$do(10) nums$undo()$value nums$redo()$value # operations return the manager, so they can be chained undomanager()$do(1)$do(2)$do(3)$undo(2)$value undomanager(type = "numeric") undomanager(type = "numeric", allow_null = TRUE) undomanager(max_size = 10)nums <- undomanager() nums$do(5) nums$do(7) nums$do(10) nums$undo()$value nums$redo()$value # operations return the manager, so they can be chained undomanager()$do(1)$do(2)$do(3)$undo(2)$value undomanager(type = "numeric") undomanager(type = "numeric", allow_null = TRUE) undomanager(max_size = 10)
With the undo manager, you can manage the history of an object by using undo and redo operations.
Managers are usually created with undomanager() rather than by calling
UndoManager$new() directly. The class itself is exported for anyone who
wants to work with it directly, such as to subclass it.
valueGet the value
is_emptyWhether the manager holds no value at all
can_undoWhether there are any undo operations available
can_redoWhether there are any redo operations available
undo_sizeGet the number of undo operations
redo_sizeGet the number of redo operations
UndoManager$new()Create a new undo manager. The manager starts out empty; the first
call to do() gives it a value. Most users should call
undomanager() instead, which is equivalent.
UndoManager$new(type = NULL, allow_null = FALSE, max_size = Inf)
typeThe permitted classes of the items (NULL to allow any
object). An item is accepted when any of these classes appears among
the classes R would dispatch on, so "numeric" also accepts integers
and numeric matrices.
allow_nullWhether NULL values are allowed. Only used when
type is given; an untyped manager accepts any object, including NULL.
max_sizeThe maximum number of items to keep. Once the history
grows past this, the oldest items are dropped. Use Inf for no limit.
A new UndoManager object.
UndoManager$new() UndoManager$new(type = "numeric") UndoManager$new(type = "numeric", allow_null = TRUE) UndoManager$new(max_size = 10)
UndoManager$reactive()Get a shiny reactive for this manager, so that it can be used inside a reactive context. The returned reactive invalidates whenever the value changes, and calling it returns the manager itself.
UndoManager$reactive()
A shiny reactive expression that returns the manager.
if (requireNamespace("shiny", quietly = TRUE)) {
nums <- undomanager()$do(5)
rx <- nums$reactive()
shiny::isolate(rx()$value)
}
UndoManager$print()Print the manager: the classes it accepts, how many undo and redo operations are available, the current item, and the items in the undo and redo history.
UndoManager$print(n = Inf, ...)
nThe maximum number of undo items, and of redo items, to show.
The items closest to the current one are shown first, and a line says
how many more were left out. Use Inf (the default) to show them all.
...Not used.
The manager, invisibly.
undomanager()$do(5)$do(7)$do(10)$do(12)$do(45)$undo()$print() # only show the nearest item on each side of the current one undomanager()$do(5)$do(7)$do(10)$do(12)$do(45)$undo()$print(n = 1)
UndoManager$undo()Move back in the history, making the previous item current. Undoing when there is nothing left to undo does nothing.
UndoManager$undo(n = 1)
nThe number of undo operations to perform. If n is larger
than the number of available undo operations, all of them are
performed. Use Inf to undo the entire history.
The manager, invisibly.
nums <- undomanager()$do(5)$do(7)$do(10) nums$undo()$value nums$undo(Inf)$value
UndoManager$redo()Move forward in the history, reversing an undo. Any redo history is
discarded as soon as a new item is added with do(). Redoing when
there is nothing left to redo does nothing.
UndoManager$redo(n = 1)
nThe number of redo operations to perform. If n is larger
than the number of available redo operations, all of them are
performed. Use Inf to redo the entire history.
The manager, invisibly.
nums <- undomanager()$do(5)$do(7)$do(10)$undo(2) nums$redo()$value nums$redo(Inf)$value
UndoManager$do()Add an item and make it the current value. The item that was current becomes the most recent undo, and any redo history is discarded.
UndoManager$do(item)
itemThe item to add. It must satisfy the manager's type, if
one was given.
The manager, invisibly.
nums <- undomanager() nums$do(5) nums$do(7)$value nums$undo()$do(99)$redo_size
UndoManager$clear()Forget the undo and redo history, keeping the current value.
UndoManager$clear(clear_value = FALSE)
clear_valueWhether to also discard the current value, leaving the manager empty.
The manager, invisibly.
nums <- undomanager()$do(5)$do(7) nums$clear()$undo_size nums$value nums$clear(clear_value = TRUE)$is_empty
undomanager(), the recommended way to create a manager.
nums <- undomanager() nums$do(5) nums$do(7) nums$do(10) nums$undo()$value nums$redo()$value # operations return the manager, so they can be chained undomanager()$do(1)$do(2)$do(3)$undo(2)$value ## ------------------------------------------------ ## Method `UndoManager$new()` ## ------------------------------------------------ UndoManager$new() UndoManager$new(type = "numeric") UndoManager$new(type = "numeric", allow_null = TRUE) UndoManager$new(max_size = 10) ## ------------------------------------------------ ## Method `UndoManager$reactive()` ## ------------------------------------------------ if (requireNamespace("shiny", quietly = TRUE)) { nums <- undomanager()$do(5) rx <- nums$reactive() shiny::isolate(rx()$value) } ## ------------------------------------------------ ## Method `UndoManager$print()` ## ------------------------------------------------ undomanager()$do(5)$do(7)$do(10)$do(12)$do(45)$undo()$print() # only show the nearest item on each side of the current one undomanager()$do(5)$do(7)$do(10)$do(12)$do(45)$undo()$print(n = 1) ## ------------------------------------------------ ## Method `UndoManager$undo()` ## ------------------------------------------------ nums <- undomanager()$do(5)$do(7)$do(10) nums$undo()$value nums$undo(Inf)$value ## ------------------------------------------------ ## Method `UndoManager$redo()` ## ------------------------------------------------ nums <- undomanager()$do(5)$do(7)$do(10)$undo(2) nums$redo()$value nums$redo(Inf)$value ## ------------------------------------------------ ## Method `UndoManager$do()` ## ------------------------------------------------ nums <- undomanager() nums$do(5) nums$do(7)$value nums$undo()$do(99)$redo_size ## ------------------------------------------------ ## Method `UndoManager$clear()` ## ------------------------------------------------ nums <- undomanager()$do(5)$do(7) nums$clear()$undo_size nums$value nums$clear(clear_value = TRUE)$is_emptynums <- undomanager() nums$do(5) nums$do(7) nums$do(10) nums$undo()$value nums$redo()$value # operations return the manager, so they can be chained undomanager()$do(1)$do(2)$do(3)$undo(2)$value ## ------------------------------------------------ ## Method `UndoManager$new()` ## ------------------------------------------------ UndoManager$new() UndoManager$new(type = "numeric") UndoManager$new(type = "numeric", allow_null = TRUE) UndoManager$new(max_size = 10) ## ------------------------------------------------ ## Method `UndoManager$reactive()` ## ------------------------------------------------ if (requireNamespace("shiny", quietly = TRUE)) { nums <- undomanager()$do(5) rx <- nums$reactive() shiny::isolate(rx()$value) } ## ------------------------------------------------ ## Method `UndoManager$print()` ## ------------------------------------------------ undomanager()$do(5)$do(7)$do(10)$do(12)$do(45)$undo()$print() # only show the nearest item on each side of the current one undomanager()$do(5)$do(7)$do(10)$do(12)$do(45)$undo()$print(n = 1) ## ------------------------------------------------ ## Method `UndoManager$undo()` ## ------------------------------------------------ nums <- undomanager()$do(5)$do(7)$do(10) nums$undo()$value nums$undo(Inf)$value ## ------------------------------------------------ ## Method `UndoManager$redo()` ## ------------------------------------------------ nums <- undomanager()$do(5)$do(7)$do(10)$undo(2) nums$redo()$value nums$redo(Inf)$value ## ------------------------------------------------ ## Method `UndoManager$do()` ## ------------------------------------------------ nums <- undomanager() nums$do(5) nums$do(7)$value nums$undo()$do(99)$redo_size ## ------------------------------------------------ ## Method `UndoManager$clear()` ## ------------------------------------------------ nums <- undomanager()$do(5)$do(7) nums$clear()$undo_size nums$value nums$clear(clear_value = TRUE)$is_empty