The more I remove code from my init.el to rely on Emacs defaults, the more I am getting used to it. This means I stopped using the likes of nswbuff and counsel-projectile, but it doesn’t mean there isn’t room for small tweaks.

The basic switch-to-buffer (C-x b ) does exactly what it says: it lets you switch to a buffer of your choice. Now, if you play around in Emacs for a while you end up with many open buffers such as *Messages*, *Flymake log*, *Help*, *Ibuffer*, etc. Usually when I want to switch to a buffer I start typing and fido-mode gets me there, but the output of vanilla switch-to-buffer is still a bit cluttered with buffers I never jump to.

(defvar mu-ignored-buffers
  '("\\` " "^\\*Async" "^\\*Compile-Log" "^\\*Completions" "^\\*Flymake"
    "^\\*Messages" "^\\*eldoc" "^\\*envrc" "\\*tramp" "^\\*xref")
  "A list of predicates for buffers to ignore.")
(defun mu-switch-to-buffer (buffer-or-name)
  "Switch to BUFFER-OR-NAME but ignore specific buffers."
   (list (read-buffer "Switch to: "
                      (other-buffer (current-buffer))
                      (lambda (buf)
                          mu-ignored-buffers (car buf) #'string-match-p))))))
  (switch-to-buffer buffer-or-name))

The key point in mu-switch-to-buffer is the fourth argument passed to read-buffer.

Optional arg PREDICATE, if non-nil, is a function limiting the buffers that can be considered. It will be called with each potential candidate, in the form of either a string or a cons cell whose `car’ is a string, and should return non-nil to accept the candidate for completion, nil otherwise.

Note that I am using mu-ignored-buffers only in mu-switch-to-buffer. Ibuffer still shows the ignored buffers to me. If I’ll ever get particularly angry at those buffers, I could hide them in Ibuffer as well by adding them to ibuffer-never-show-predicates. These days, though, teenage angst is far behind myself.