Quick Emacs Buffer Switching with ace-jump-mode

Update 6/18/13

I recently put in the effort to turn this into a more convenient Emacs lisp package. Enhancements include:

  • No longer are your pre-existing ace-jump-mode and bs settings clobbered. perspective mode is optional.
  • When the ace-jump-buffer menu is activated, C-g or any non-found ace-jump key-binding will clear ace-jump-mode and dismiss buffer menu.

This article’s code should only be entertained as a reference to the initial thought experiment. Grab the latest version on Github:

https://github.com/waymondo/ace-jump-buffer

Or install directly from MELPA:

1
package-install ace-jump-buffer

Beyond typing and text-editing related commands, switching buffers is my most common Emacs action. I’m constantly hopping back and forth between scripts and stylesheets and templates, popping into shells, etc. ido-mode’s fuzzy-match choosing is typically the de facto standard of this kind of interface: after triggering an appropriate binding you are usually several keystrokes from naming and selecting your target. But life is short, can we do this faster?

There are also buffer flipping managers like iflip that behave much like beloved ⌘+TAB application switching, but when buffers start to pile up it can take a handful of repeated commands to get where you are going.

ace-jump-mode got me thinking. I’m continually finding it to be super useful in my day to day workflow. In just 2 or 3 keystrokes you can put your cursor anywhere in your Emacs environment. Then I realized I could bend it to allow me to hop to any buffer in 1 character. Here it is what I came up with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(require 'bs)
(require 'ace-jump-mode)
(require 'perspective)

;; simple layout, increase window height
(setq bs-max-window-height 30
      bs-attributes-list (quote (("" 2 2 left " ")
                                 ("" 1 1 left bs--get-marked-string)
                                 ("" 1 1 left " ")
                                 ("Buffer" bs--get-name-length 10 left bs--get-name))))

;; filter buffers to current perspective
(add-to-list 'bs-configurations
             '("persp" nil nil nil
               (lambda (buf)
                 (with-current-buffer buf
                   (not (member buf (persp-buffers persp-curr)))))) nil)

;; on end of ace jump, select buffer
(defun bs-ace-jump-end-hook ()
  (if (string-match (buffer-name) "*buffer-selection*")
      (bs-select)))

(add-hook 'ace-jump-mode-end-hook 'bs-ace-jump-end-hook)

(defun ace-jump-buffer ()
  (interactive)
  (bs--show-with-configuration "persp")
  (setq ace-jump-mode-scope 'window)
  (beginning-of-buffer)
  (call-interactively 'ace-jump-line-mode))

So what’s going on here? bs is a lightweight, built-in emacs lisp package for buffer switching, seemingly a predecessor to buffer-menu and ibuffer. I choose it for its simplicity and configuration. As I rely heavily on perspective for keeping my workspaces (projects) organized, I created a configuration to only display buffers in the current one.

Upon triggering ace-jump-buffer, the bs buffer switching popup appears and ace-jump-line is called, placing a character at the beginning of each line. The buffer ordering is from most recently viewed to backwards in time, so you can assume the current buffer is c, the previous buffer is d, and so on. Pressing the key that matches the line’s character selects the buffer.

After about a week I’m finding it to be quite effective. Perhaps I’ll wrap it up and submit it to MELPA if anyone else does to. You can browse my entire Emacs setup here.

Comments