YouTube Summaries

← All summaries

Controlling buffer placement with display-buffer-alist

2024-02-08 Thu ⏱ 29 min protesilaos

Prot walks through Emacs's window-management machinery, showing how to take control of where buffers appear by customizing display-buffer-alist. Starting from the (often annoying) default behavior, he incrementally builds rules using buffer matchers, ordered lists of display functions, and action parameters, ending with a peek at the more advanced setup in his own configuration.

The problem with the defaults

With display-buffer-alist set to nil, a command like occur splits the frame and shows its buffer below when there is a single window — acceptable. But with two side-by-side windows, the same command takes over the other window, destroying your layout. For ancillary buffers like *Occur*, which exist to support what you are doing in the current buffer, this is undesirable.

Anatomy of an entry

Each entry in the alist has three parts: a buffer matcher (how to identify the buffer being displayed), a list of display functions, and optional parameters. The matcher can be a regular expression against the buffer name (e.g. matching *Occur* with escaped asterisks) or a derived-mode condition against the buffer's major mode; multiple conditions can be combined with or.

Display functions and ordering

The display functions run in order and the first that succeeds wins, so sequence matters. A sensible default pattern: display-buffer-reuse-mode-window first (if a window already shows a buffer of that mode, reuse it), then a fallback like display-buffer-below-selected. With this rule, occur splits below the current window instead of stealing the neighboring one.

Parameters

Parameters are cons cells appended to the entry:

  • window-height :: an absolute line count or a function like fit-window-to-buffer. The latter shrinks small buffers nicely but lets large ones (e.g. an occur for a common word) swallow nearly the whole frame — Prot handles this in his own config with a custom function enforcing a maximum.
  • dedicated :: dedicates the buffer to its window. With a dedicated *Occur* window, C-x b no longer replaces it; Emacs reuses or creates another window instead. Prot's mode line shows an indicator for dedicated windows.
  • body-function :: a function receiving the window, run after display. Examples: select-window to move focus straight into the occur buffer (so it can be acted on or dismissed immediately), or delete-other-windows so that opening his notmuch email buffer clears the rest of the frame.

Window parameters and examples from his config

The window-parameters entry can set things like (mode-line-format . none) — demonstrated by hiding the mode line of the *Completions* buffer. Other rules from his configuration: async shell command output matched by regexp with a no-window action (the buffer exists but is never displayed), side windows for certain buffers, preserve-size to keep window dimensions across frame resizes, and a fully custom display function plus body function for occur that picks side-by-side vs. below placement depending on whether the frame can be split, with sensible min/max sizing.

A note on quoting

The alist uses a backquoted list with comma operators so that variables (like a function stored in a variable) are evaluated rather than taken literally — a backtick, not a plain quote, is required for that to work.

Sample code and links accompany the video on his site: https://protesilaos.com/codelog/2024-02-08-emacs-window-rules-display-buffer-alist/