diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index fa1135b8026..937680c200d 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -1506,6 +1506,11 @@ continuing to undo. This function does not bind @code{undo-in-progress}. @end defun +@defmac with-undo-amalgamate body@dots{} +This macro removes all the undo boundaries inserted during the +execution of @var{body} so that it can be undone as a single step. +@end defmac + Some commands leave the region active after execution in such a way that it interferes with selective undo of that command. To make @code{undo} ignore the active region when invoked immediately after such a command, diff --git a/etc/NEWS b/etc/NEWS index a297948a11a..874af33c752 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -598,6 +598,10 @@ Use 'exif-parse-file' and 'exif-field' instead. * Lisp Changes in Emacs 29.1 ++++ +*** New macro 'with-undo-amalgamate' +It records a particular sequence of operations as a single undo step + +++ *** New command 'yank-media'. This command supports yanking non-plain-text media like images and diff --git a/lisp/subr.el b/lisp/subr.el index f6dbd00532e..5a5842d4287 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3542,6 +3542,29 @@ user can undo the change normally." (accept-change-group ,handle) (cancel-change-group ,handle)))))) +(defmacro with-undo-amalgamate (&rest body) + "Like `progn' but perform BODY with amalgamated undo barriers. + +This allows multiple operations to be undone in a single step. +When undo is disabled this behaves like `progn'." + (declare (indent 0) (debug t)) + (let ((handle (make-symbol "--change-group-handle--"))) + `(let ((,handle (prepare-change-group)) + ;; Don't truncate any undo data in the middle of this, + ;; otherwise Emacs might truncate part of the resulting + ;; undo step: we want to mimic the behavior we'd get if the + ;; undo-boundaries were never added in the first place. + (undo-outer-limit nil) + (undo-limit most-positive-fixnum) + (undo-strong-limit most-positive-fixnum)) + (unwind-protect + (progn + (activate-change-group ,handle) + ,@body) + (progn + (accept-change-group ,handle) + (undo-amalgamate-change-group ,handle)))))) + (defun prepare-change-group (&optional buffer) "Return a handle for the current buffer's state, for a change group. If you specify BUFFER, make a handle for BUFFER's state instead.