I’m trying to create a function that will revert buffers from emacs’ *Buffer List* buffer. As far as I can tell from the documentation, there’s no way to do this quickly (in the manner of the save/mark/visit functions built in to buff-menu.el). So I’m writing some elisp. Here’s my current attempt:
(defun frobnitz ()
"Call in buffer list to revert buffer at point to file."
(interactive)
(let ((buf (buffer-menu-buffer t)))
(if (y-or-n-p (concat "Revert " (buffer-name (buf)) " ?"))
(with-current-buffer buf
(let (())
(revert-buffer t t t)
(message
(concat "Reverted " (buffer-name (buf)) "to last saved state."))
)))))
Unfortunately, the above defun doesn’t seem to work, and I’m having trouble figuring out why. If I eval the above, switch to the *Buffer List* buffer, and invoke M-: (frobnitz), then it errors out with the following.
Debugger entered--Lisp error: (void-function buffer-menu-buffer)
(buffer-menu-buffer t)
(let ((buf (buffer-menu-buffer t))) (if (y-or-n-p (concat "Revert " (buffer-name (buf)) " ?")) (with-current-buffer buf (let (nil) (revert-buffer t t t) (message (concat "Reverted " (buffer-name (buf)) "to last saved state."))))))
frobnitz()
eval((frobnitz) nil)
eval-expression((frobnitz) nil)
call-interactively(eval-expression nil nil)
It seems like that’s telling me that there’s no function buffer-menu-buffer – but that also seems gratuitously unlikely, since buffer-menu-buffer is a pretty central function in getting the buffer menu to work! For similar reasons, I’m deeply wary of messing with buffer-menu-buffer myself – I don’t want to break the buffer menu.
Bearing in mind that the answer might be “invoke this function that you overlooked,” how can I get this defun to accomplish its stated purpose of reverting a buffer directly from the buffer menu?
Update: as answerer Sean points out, the correct name of the function I was having a hard time with is Buffer-menu-buffer with a capital initial B. Having fixed that problem, I came across another:
(let (nil) (revert-buffer t t t) (message (concat "Reverted " buf-name "to last saved state.")))
(save-current-buffer (set-buffer buf) (let (nil) (revert-buffer t t t) (message (concat "Reverted " buf-name "to last saved state."))))
(with-current-buffer buf (let (nil) (revert-buffer t t t) (message (concat "Reverted " buf-name "to last saved state."))))
(if (y-or-n-p (concat "Revert " buf-name " ?")) (with-current-buffer buf (let (nil) (revert-buffer t t t) (message (concat "Reverted " buf-name "to last saved state.")))))
(let ((buf (Buffer-menu-buffer t)) (buf-name (concat "" (buffer-name (Buffer-menu-buffer t))))) (if (y-or-n-p (concat "Revert " buf-name " ?")) (with-current-buffer buf (let (nil) (revert-buffer t t t) (message (concat "Reverted " buf-name "to last saved state."))))))
frobnitz()
eval((frobnitz) nil)
eval-expression((frobnitz) nil)
call-interactively(eval-expression nil nil)
My guess is that with-current-buffer tries to save the current buffer and that’s a no-no on *Buffer List*. So now I’m looking for an alternative – maybe just switch, revert, and invoke (buffer-list) to switch back.
Update 2:
For future readers: The working function and a single-key binding to invoke it in buffer-menu-mode:
;; Enhance the buffer menu's capabilities.
(defun revert-buffer-from-buffer-list ()
"Call in buffer list to revert buffer at point to file.
Bind this to a key in `buffer-menu-mode' to use it there - not productive in
other modes because it depends on the `Buffer-menu-buffer' function. Undefined
behavior if you invoke it on a buffer not associated with a file: that's why it
has a confirmation gate. Buffers not associated with files get to play by their
own rules when it comes to `revert-buffer' (which see)."
(interactive)
(let (
(buf (Buffer-menu-buffer t))
(buf-name (concat "" (buffer-name(Buffer-menu-buffer t))))
)
(if (y-or-n-p (concat "Revert " buf-name " ?"))
(with-current-buffer buf
(let ()
(revert-buffer t t t)
(message (concat "Reverted " buf-name " to last saved state."))
)))))
(add-hook 'Buffer-menu-mode-hook
(lambda ()
(define-key Buffer-menu-mode-map (kbd "R") revert-buffer-from-buffer-list)
))
Also an exhortation to caution: add-hook is not idempotent, so if you add things to foo-mode-hook that you don’t intend to or which don’t work, you risk breaking foo-mode until you zorch foo-mode-hook or prune the broken elements out of it. Ask me how I know!
My Emacs has a function
Buffer-menu-buffer, but nobuffer-menu-buffer. I imagine that’s what’s tripping you up.EDIT:
I found two more problems with your code, after which I was able to revert buffers from the buffer menu with it.
(buf)tobufin two places.bufis a variable, not a function to call.(let (()) ...)construct causes an error. Either eliminate it, or change it to(let () ...)(although I don’t know why you’d want to).