Friday, July 14, 2023

Ergonomic Buffer Selection On The Emacs Audio Desktop

An Ergonomic Buffer Select With Minimal Chording

1. Background

Buffers are central to Emacs as is evinced by the various buffer-selection schemes that have been created over time. As someone who has lived for over 30 years in Emacs, my own set of buffer-selection tools have evolved and I've settled on a combination of ido and fuzzy matching over the last few years. This article describes a new tool that puts all of these together, but with an emphasis on ergonomics and minimized chording.

2. Setting The Context

Buffer selection tools in Emacs vary along the following feature axis, with many providing enhancements on one ore of these axis:

  • Match Strategy: Selecting the buffer name from the available choices,
  • UI: Displaying those matches,
  • And finally displaying the buffer.

    Command emacspeak-buffer-select focuses exclusively on invocation and keyboard commands for moving through the choices and selecting the buffer.

    • So Why Is Ido Not Sufficient?

      Package ido is still my tool of choice and has served me well over the years. The problem emacspeak-buffer-select solves is along the invocation axis; it can be traced back to my desire to avoid chording, and in that context, I found that C-x b was becoming particularly irksome. The solution described below is specifically optimized to my current configuration using

XCape, where a single tap on the CTRL/CapsLock key produces Emacspeak prefix C-e.

3. Design Goals

  • No chording.
  • Enable moving through list of buffers with pairs of related keys.
  • Enable various types of ordering of the available choices e.g., navigate by major-mode.
  • In the spirit of ido, enable falling through to switch-to-buffer and find-file when needed.
  • Following on from above, enable relevant actions like killing buffers.

4. Use set-transient-map To Implement The Behavior

In the past I would have implemented the above using package hydra or transient; But both felt overweight for this case. My final solution uses Emacs builtin set-transient-map.

5. Final Behavior

  • Command emacspeak-buffer-select is invoked via keys C-e ,, C-e ., C-e n, and C-e p.
  • That command moves to the next/previous buffer.
  • The key pair , . use Emacs commands previous-buffer and next-buffer; keys n and p pick the previous or next buffer that uses the current buffer's major-mode.
  • In addition, while active, the transient-map binds:
    • b: _switch-to-buffer
    • k: kill-buffer
    • o: other-window

With the above in place, my most common workflows look like:

  • Press , or . repeatedly to cycles through next/previous buffers.
  • Press n or p repeatedly to cycle through buffers in the same mode, especially useful when programming, or using EWW to browse the Web.
  • Press b or f when cycling doesn't yield the target in a couple of steps.
  • Opportunistically clean up unwanted buffers by pressing k.

You can see the final implementation at emacspeak-buffer-select. Note that despite the naming there is little that is specific to Emacspeak in the above.

5.1. Emacspeak Specific Features

  • Uses Auditory icons to indicate that a transient map is active.
  • Produces an auditory icon when the transient map goes away
  • Uses call-interactively to invoke subcommands so that they automatically produce auditory feedback via Emacspeak.