This page is about the bs library, which provides a comfortable way to switch buffers. It is part of Emacs. Also known as bs and BSBufferSelection.


Here’s how to install it:

    (global-set-key (kbd "C-x C-b") 'bs-show)

It has font-locking, and various configurations.

Here are two configurations to limit the list of buffers to just channels or to just buffers that have a default target (ie. channels and queries for nicks in ERC:

    (require 'bs)
    (add-to-list 'bs-configurations
                 '("channels" nil nil "^[^#]" nil nil))
    (add-to-list 'bs-configurations
                 '("targets" nil nil nil
                   (lambda (buf)
                      (with-current-buffer buf
                        (not (erc-default-target)))) nil))

And here is a configuration to limit the list of buffers to SQL-related stuff (cf. SqlMode):

    (add-to-list 'bs-configurations
                 '("SQL" nil nil nil
                   (lambda (buf)
                      (with-current-buffer buf
                         (not (memq major-mode
                            '(sql-interactive-mode sql-mode))))) nil))

And here is one for all your dired buffers (before calling kill-all-dired-buffers from KillingBuffers, for example):

    (add-to-list 'bs-configurations
                 '("dired" nil nil nil
                   (lambda (buf)
                     (with-current-buffer buf
                       (not (eq major-mode 'dired-mode)))) nil))

If you are using GnuClient, then you will notice that you cannot kill buffers from the selection using ‘d’. The reason is that server-kill-buffer has a subtle bug that will return nil even if the killing of the buffer was successful. Here is some code to fix it:

    (fset 'kill-buffer 'my-server-kill-buffer)
    (defun my-server-kill-buffer (buffer)
      "Call `server-kill-buffer' but make sure to return the correct value."
      (interactive "bKill buffer ")
      (server-kill-buffer (get-buffer buffer))
      (not (buffer-name (get-buffer buffer))))


bs-ext.el contains extensions to Buffer Selection which allows you to bind keys to configs, and match buffers by regular expressions, along with a few other conveniences.

Problems, questions and hopefully some answers too

Buffer List Empty/Truncated

No buffers are showing up in the buffer list, or buffers are missing even though they shouldn’t be filtered out when the customization regexes are unchanged from the default.

There’s apparently a problem in the bs script when the mode is XML or HTML. There’s a discussion about it over on the BufferMenu+ page, with the backtrace provided at http://gist.github.com/458598. The analysis of the backtrace that leads to bs is:

Your backtrace suggests that the problem is in ‘bs’, not in buff-menu+.el. The code in the backtrace is all from `bs.elโ€™, it seems. Debugger entered--Lisp error: (wrong-type-argument characterp sgml-xml-mode) concat(" " (sgml-xml-mode "XHTML" "HTML")) (if (eq (quote right) align) (concat (make-string ... 32) string) (concat string (make-string ... 32))) (if (>= length len) string (if (eq ... align) (concat ... string) (concat string ...))) (let ((length ...)) (if (>= length len) string (if ... ... ...))) bs--format-aux((sgml-xml-mode "XHTML" "HTML") right 12) Something is passing ‘(sgml-xml-mode “XHTML” “HTML”) to ‘concat’. It tries to process that list as a list of chars, which it is not (it is a list of a symbol and two strings). โ€“ DrewAdams


It turns out there’s a bug in the bs.el script that shows up mostly with XML, HTML, and SGML files. It was assumed that the mode-name variable, which indicates the major mode of the buffer, would always be a string, but reading the documentation it says it can be a string, a variable containing a string, or a list mixing both. In the case of XML, SGML, and HTML files it is set to (sgml-xml-mode "XML" "SGML"). The correct way to access the human-readable name of this is documented to be with the format-mode-line function that has a defined way of parsing these variables. The fix is therefore to change the parsing to use (format-mode-line mode-name) instead of (mode-name) in the bs--get-mode-name defun. I’m seeing that this is causing a different result to be returned from the format-mode-line call though when it’s put into bs--get-mode-name than if it’s just evaluated manually from the same buffer. It does guarantee a valid string result regardless though, so this seems to at least fix the list showing up empty even if the buffers with multiple possible mode names don’t get the same name listed as with the other standard buffer listing functions.

Solution: On line 1379 of bs.el, in the bs--get-mode-name function, change mode-name to (format-mode-line mode-name).



Setting the above variable to a regexp doesn’t seem to do what I (mistakenly?) believe it should: viz. not showing the buffers matched by the regexp. Even though it is set from my .emacs, its value is nil when I ‘C-h v bs-dont-show-regexp RET’. What am I missing/misunderstanding?

You should set `bs-configurations`, not `bs-dont-show-regexp`. This confused me too. --AmitPatel

Thank you. 😊

See also:

  • CategoryBufferSwitching for information on other ways to select buffers.
  • Icicles, which also has buffer configurations – see command ‘icicle-buffer-config’ and user option ‘icicle-buffer-configs’. It also lets you match buffer names using regexps, even multiple regexps.
  • KillingBuffers, for an example of using Icicles to write a command to kill all Dired buffers. The same command also shows you all Dired buffers (via ‘S-TAB’) – no need for a separate Dired-buffers configuration.