Skip to content

Commit 2645ae1

Browse files
committed
* lisp/simple.el (undo-redo): New command
(undo--last-change-was-undo-p): New function. * test/lisp/simple-tests.el (simple-tests--exec): New function. (simple-tests--undo): New test.
1 parent fe903c5 commit 2645ae1

File tree

4 files changed

+72
-0
lines changed

4 files changed

+72
-0
lines changed

doc/emacs/fixit.texi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ changes have already been undone, the undo command signals an error.
6666

6767
@cindex redo
6868
@findex undo-only
69+
@findex undo-redo
6970
Any command other than an undo command breaks the sequence of undo
7071
commands. Starting from that moment, the entire sequence of undo
7172
commands that you have just performed are themselves placed into the
@@ -77,6 +78,8 @@ undo commands.
7778
Alternatively, if you want to resume undoing, without redoing
7879
previous undo commands, use @kbd{M-x undo-only}. This is like
7980
@code{undo}, but will not redo changes you have just undone.
81+
To complement it @kbd{M-x undo-redo} will undo previous undo commands
82+
(and will not record itself as an undoable command).
8083

8184
If you notice that a buffer has been modified accidentally, the
8285
easiest way to recover is to type @kbd{C-/} repeatedly until the stars

etc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ dimension.
7373

7474
* Editing Changes in Emacs 28.1
7575

76+
+++
77+
** New command 'undo-redo'
78+
7679
+++
7780
** 'read-number' now has its own history variable.
7881
Additionally, the function now accepts a HIST argument which can be

lisp/simple.el

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,6 +2662,30 @@ Contrary to `undo', this will not redo a previous undo."
26622662
(interactive "*p")
26632663
(let ((undo-no-redo t)) (undo arg)))
26642664

2665+
(defun undo--last-change-was-undo-p (undo-list)
2666+
(while (and (consp undo-list) (eq (car undo-list) nil))
2667+
(setq undo-list (cdr undo-list)))
2668+
(gethash undo-list undo-equiv-table))
2669+
2670+
(defun undo-redo (&optional arg)
2671+
"Undo the last ARG undos."
2672+
(interactive "*p")
2673+
(cond
2674+
((not (undo--last-change-was-undo-p buffer-undo-list))
2675+
(user-error "No undo to undo"))
2676+
(t
2677+
(let* ((ul buffer-undo-list)
2678+
(new-ul
2679+
(let ((undo-in-progress t))
2680+
(while (and (consp ul) (eq (car ul) nil))
2681+
(setq ul (cdr ul)))
2682+
(primitive-undo arg ul)))
2683+
(new-pul (undo--last-change-was-undo-p new-ul)))
2684+
(message "Redo%s" (if undo-in-region " in region" ""))
2685+
(setq this-command 'undo)
2686+
(setq pending-undo-list new-pul)
2687+
(setq buffer-undo-list new-ul)))))
2688+
26652689
(defvar undo-in-progress nil
26662690
"Non-nil while performing an undo.
26672691
Some change-hooks test this variable to do something different.")

test/lisp/simple-tests.el

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,48 @@ See bug#35036."
392392
(should (equal ?\s (char-syntax ?\n))))))
393393

394394

395+
;;; undo tests
396+
397+
(defun simple-tests--exec (cmds)
398+
(dolist (cmd cmds)
399+
(setq last-command this-command)
400+
(setq this-command cmd)
401+
(run-hooks 'pre-command-hook)
402+
(command-execute cmd)
403+
(run-hooks 'post-command-hook)
404+
(undo-boundary)))
405+
406+
(ert-deftest simple-tests--undo ()
407+
(with-temp-buffer
408+
(buffer-enable-undo)
409+
(dolist (x '("a" "b" "c" "d" "e"))
410+
(insert x)
411+
(undo-boundary))
412+
(should (equal (buffer-string) "abcde"))
413+
(simple-tests--exec '(undo undo))
414+
(should (equal (buffer-string) "abc"))
415+
(simple-tests--exec '(backward-char undo))
416+
(should (equal (buffer-string) "abcd"))
417+
(simple-tests--exec '(undo))
418+
(should (equal (buffer-string) "abcde"))
419+
(simple-tests--exec '(backward-char undo undo))
420+
(should (equal (buffer-string) "abc"))
421+
(simple-tests--exec '(backward-char undo-redo))
422+
(should (equal (buffer-string) "abcd"))
423+
(simple-tests--exec '(undo))
424+
(should (equal (buffer-string) "abc"))
425+
(simple-tests--exec '(backward-char undo-redo undo-redo))
426+
(should (equal (buffer-string) "abcde"))
427+
(simple-tests--exec '(undo undo))
428+
(should (equal (buffer-string) "abc"))
429+
(simple-tests--exec '(backward-char undo-only undo-only))
430+
(should (equal (buffer-string) "a"))
431+
(simple-tests--exec '(backward-char undo-redo undo-redo))
432+
(should (equal (buffer-string) "abc"))
433+
(simple-tests--exec '(backward-char undo-redo undo-redo))
434+
(should (equal (buffer-string) "abcde"))
435+
))
436+
395437
;;; undo auto-boundary tests
396438
(ert-deftest undo-auto-boundary-timer ()
397439
(should

0 commit comments

Comments
 (0)