Skip to content

Commit

Permalink
Fix bugs in evil-with-undo' and evil-undo-pop' with undo-tree.
Browse files Browse the repository at this point in the history
Addresses #1074

- evil-with-undo:
  nconc'ing onto front of buffer-undo-list here can corrupt buffer-undo-list
  when in undo-tree-mode in rare circumstances (see issue #1074). Leave
  standard undo machinery to work as usual when undo is enabled. Deal with
  disabled undo by temporarily enabling then disabling undo, and transferring
  any undo changes to evil-temporary-undo.

- evil-undo-pop:
  This function called `undo' directly from Elisp, which is wrong when in
  undo-tree-mode. Fix this by calling undo-tree-undo instead when in
  undo-tree-mode.
  • Loading branch information
tsc25 committed Mar 6, 2021
1 parent f5ab7ff commit 2cf534a
Showing 1 changed file with 37 additions and 33 deletions.
70 changes: 37 additions & 33 deletions evil-common.el
Original file line number Diff line number Diff line change
Expand Up @@ -3526,23 +3526,24 @@ If undo is disabled in the current buffer, the undo information
is stored in `evil-temporary-undo' instead of `buffer-undo-list'."
(declare (indent defun)
(debug t))
`(unwind-protect
(let (buffer-undo-list)
(unwind-protect
(progn ,@body)
(setq evil-temporary-undo buffer-undo-list)
;; ensure evil-temporary-undo starts with exactly one undo
;; boundary marker, i.e. nil
(unless (null (car-safe evil-temporary-undo))
(push nil evil-temporary-undo))))
(unless (eq buffer-undo-list t)
;; undo is enabled, so update the global buffer undo list
(setq buffer-undo-list
;; prepend new undos (if there are any)
(if (cdr evil-temporary-undo)
(nconc evil-temporary-undo buffer-undo-list)
buffer-undo-list)
evil-temporary-undo nil))))
(let ((undo-list (make-symbol "undo-list")))
`(let ((,undo-list buffer-undo-list))
(when (eq ,undo-list t) (setq buffer-undo-list nil))
(unwind-protect
(unwind-protect
(progn ,@body)
;; ensure any new undo changes we've accumulated start with
;; exactly one undo boundary marker, i.e. nil
(unless (or (and (eq ,undo-list t) (null buffer-undo-list))
(eq ,undo-list buffer-undo-list))
(push nil buffer-undo-list)))
(if (eq ,undo-list t)
;; undo is disabled, so store undo information in
;; evil-temporary-undo
(setq evil-temporary-undo buffer-undo-list
buffer-undo-list t)
;; undo is enabled, so ensure evil-temporary-undo is null
(setq evil-temporary-undo nil))))))

(defmacro evil-with-single-undo (&rest body)
"Execute BODY as a single undo step."
Expand All @@ -3562,22 +3563,25 @@ is stored in `evil-temporary-undo' instead of `buffer-undo-list'."
Removes the last undo information from `buffer-undo-list'.
If undo is disabled in the current buffer, use the information
in `evil-temporary-undo' instead."
(let ((paste-undo (list nil)))
(let ((undo-list (if (eq buffer-undo-list t)
evil-temporary-undo
buffer-undo-list)))
(when (or (not undo-list) (car undo-list))
(user-error "Can't undo previous change"))
(while (and undo-list (null (car undo-list)))
(pop undo-list)) ; remove nil
(while (and undo-list (car undo-list))
(push (pop undo-list) paste-undo))
(let ((buffer-undo-list (nreverse paste-undo)))
(evil-save-echo-area
(undo)))
(if (eq buffer-undo-list t)
(setq evil-temporary-undo nil)
(setq buffer-undo-list undo-list)))))
(if (and (eq evil-undo-system 'undo-tree)
(not (eq buffer-undo-list t)))
(undo-tree-undo)
(let ((paste-undo (list nil)))
(let ((undo-list (if (eq buffer-undo-list t)
evil-temporary-undo
buffer-undo-list)))
(when (or (not undo-list) (car undo-list))
(user-error "Can't undo previous change"))
(while (and undo-list (null (car undo-list)))
(pop undo-list)) ; remove nil
(while (and undo-list (car undo-list))
(push (pop undo-list) paste-undo))
(let ((buffer-undo-list (nreverse paste-undo)))
(evil-save-echo-area
(undo)))
(if (eq buffer-undo-list t)
(setq evil-temporary-undo nil)
(setq buffer-undo-list undo-list))))))

;;; Search
(defun evil-transform-regexp (regexp replacements-alist)
Expand Down

0 comments on commit 2cf534a

Please sign in to comment.