A generic mechanism for running a command repeatedly without cluttering your undo history.
Where each successive call undoes the previous result before applying the next. Consumers build cycling, completion, or other repeat-with-redo behaviors on top.
Note that this is middle-ware since it's intended for other packages to use.
Available via melpa.
This package exposes:
with-command-redomacro.with-command-redo-fnfunction variant.with-command-redo-active-pquery if a redo-chain is active, takes a singleidsymbol.
Both with-command-redo and with-command-redo-fn accept:
:id SYMBOL- Identifies the chain. Multiple commands sharing the same id continue each other's chain. Required.
:on-other-command FUNCTION- Called from
post-command-hookwhen a non-modifying external command runs while the chain is active. Receives the cache as its sole argument. Side effects (messaging, cache mutation) are permitted. Return non-nil to break the chain, nil to keep it alive.
The body (macro) or callback (function) receives a plist with:
:count- The execution count (0 on the first call).
:cache- nil on the first call. Use
plist-putto pass a value to the next call. :result- Defaults to t. Use
plist-putto set nil when the operation has no result.
Cycle through a list of strings, each call undoes the previous insertion:
;; Macro variant.
(defun my-cycle-insert ()
(interactive)
(with-command-redo (list :id 'my-cycle) props
(let ((count (plist-get props :count))
(items '("alpha" "beta" "gamma")))
(insert (nth (mod count (length items)) items)))))
;; Function variant (for conditional use without macro expansion).
(defun my-cycle-insert ()
(interactive)
(with-command-redo-fn
(list :id 'my-cycle)
(lambda (props)
(let ((count (plist-get props :count))
(items '("alpha" "beta" "gamma")))
(insert (nth (mod count (length items)) items))))))(use-package with-command-redo)