Exterminate Magit buffers

Some say that Emacs users rarely kill buffers. Instead they smoothly dance away to another buffer using their preferred switching mechanism, and just keep on working with no hiccups whatsoever. In my case ivy-switch-buffer is usually doing all the the dancing, with Ibuffer coming in when the situation gets out of hand.

However, I do not enjoy having more open buffers than what I really need. Call it obsessive-compulsive disorder, call it whatever you like, I want my buffers list clean and on point.

Magit has a penchant for open buffers. Jonas Bernoulli already explained the reasoning behind this behaviour, but he also suggested a solution to clean up Magit-related buffers when the work is done.

Following Jonas’ tip, this is what I devised:

(defun mu-magit-kill-buffers (param)
  "Restore window configuration and kill all Magit buffers."
  (let ((buffers (magit-mode-get-buffers)))
    (mapc #'kill-buffer buffers)))

(validate-setq magit-bury-buffer-function #'mu-magit-kill-buffers)

The function is simple: first it collects the available Magit buffers, then restores the window configuration as it was before calling magit-status, and finally applies kill-buffer on the buffers previously collected. kill-buffer will not ask for confirmation, so now pressing q in magit-status has the desired effect.

Something is weird in that function, though. Why pass param to mu-magit-kill-buffers if there is no need for it in the function body?

Let’s break this down. The value of magit-bury-buffer-function is a function1.

(defcustom magit-bury-buffer-function 'magit-restore-window-configuration
  "The function used to bury or kill the current Magit buffer."
  :package-version '(magit . "2.3.0")
  :group 'magit-buffers
  :type '(radio (function-item quit-window)
                (function-item magit-mode-quit-window)
                (function-item magit-restore-window-configuration)
                (function :tag "Function")))

Looking at the code of magit-restore-window-configuration, we can see that it accepts an optional argument.

(defun magit-restore-window-configuration (&optional kill-buffer)
  "Bury or kill the current buffer and restore previous window configuration."
  (let ((winconf magit-previous-window-configuration)
        (buffer (current-buffer))
        (frame (selected-frame)))
    (quit-window kill-buffer (selected-window))
    (when (and winconf (equal frame (window-configuration-frame winconf)))
      (set-window-configuration winconf)
      (when (buffer-live-p buffer)
        (with-current-buffer buffer
          (setq magit-previous-window-configuration nil))))))

Thus param in mu-magit-kill-buffers is only there to match the signature of the function passed to magit-bury-buffer-function. If I leave it out, I will get this error message:

magit-mode-bury-buffer: Wrong number of arguments: ((t) nil "Kill all Magit buffers." (let ((buffers (magit-mode-get-buffers))) (magit-restore-window-configuration) (mapc #'kill-buffer buffers))), 1

If you’re wondering why I am using the sharp quote (#') when setting the value of magit-bury-buffer-function, you can simply trust the sage advice of Artur Malabarba.

There is still one problem to solve. Setting magit-bury-buffer-function this way makes q always kill every Magit buffer. This is not what I need when I am using, for instance, magit-log-buffer-file.

Hence it’s better to make my custom function interactive and apply it only for magit-status-mode-map:

(defun mu-magit-kill-buffers ()
  "Restore window configuration and kill all Magit buffers."
  (let ((buffers (magit-mode-get-buffers)))
    (mapc #'kill-buffer buffers)))

(bind-key "q" #'mu-magit-kill-buffers magit-status-mode-map)

Bonus point: the unused param is gone.

  1. Actually it is a symbol, which in this case is the quoted name of a function.