How can I make Emacs retain its undo history for my buffer after doing revert-buffer or using auto-revert-mode?
In Vim, if a file that is open in a buffer is changed on disc, Vim prompts me to reload the file. I can then simply click 'u' to undo the reload if I so wish and even go back further from then. Emacs seems to trash all the undo information the moment I revert the buffer.
I guess the obvious approach would be a function which kills the current buffer content, and then calls insert-file
to read in the current content from the file.
If the changes to the file included changes to the character encoding, there might be problems? I haven't tested that.
Here's my current attempt. It's a little hairy IMO, but it works okay.
;; Allow buffer reverts to be undone
(defun my-revert-buffer (&optional ignore-auto noconfirm preserve-modes)
"Revert buffer from file in an undo-able manner."
(interactive)
(when (buffer-file-name)
;; Based upon `delphi-save-state':
;; Ensure that any buffer modifications do not have any side
;; effects beyond the actual content changes.
(let ((buffer-read-only nil)
(inhibit-read-only t)
(before-change-functions nil)
(after-change-functions nil))
(unwind-protect
(progn
;; Prevent triggering `ask-user-about-supersession-threat'
(set-visited-file-modtime)
;; Kill buffer contents and insert from associated file.
(widen)
(kill-region (point-min) (point-max))
(insert-file-contents (buffer-file-name))
;; Mark buffer as unmodified.
(set-buffer-modified-p nil))))))
(defadvice ask-user-about-supersession-threat
(around my-supersession-revert-buffer)
"Use my-revert-buffer in place of revert-buffer."
(let ((real-revert-buffer (symbol-function 'revert-buffer)))
(fset 'revert-buffer 'my-revert-buffer)
;; Note that `ask-user-about-supersession-threat' calls
;; (signal 'file-supersession ...), so we need to handle
;; the error in order to restore revert-buffer.
(unwind-protect
ad-do-it
(fset 'revert-buffer real-revert-buffer))))
(ad-activate 'ask-user-about-supersession-threat)
Annoyingly, I've only just noticed all the relevant-looking information in the revert-buffer
docs, so there's probably a much simpler way to do this.
If the value of
revert-buffer-function
is non-nil, it is called to do all the work for this command. Otherwise, the hooksbefore-revert-hook
andafter-revert-hook
are run at the beginning and the end, and ifrevert-buffer-insert-file-contents-function
is non-nil, it is called instead of rereading visited file contents.