Arranging Emacs windows

Dealing with windows in Emacs can be a bit annoying, until someone explains you the philosophy behind it and you dig a bit into the manual for display-buffer and display-buffer-alist. Even then, the problem that package managers do not follow the Emacs canon, or that this has changed a bit over time, makes it difficult to configure some pieces of software.

I leave here my customizations as of Nov. 2019, in case they may help someone. They are part of an init.org file that is converted into an init.el automatically when saving it, but it should be clear as standalone explanation.

First, we add some functions that I will use to select buffers based on modes. This is not usual: I just need it for buffers whose name does not really follow any rule; otherwise the method from display-buffer-alist just works.

(defun jjgr-dedicated-window (fun)
  (lambda (buffer &optional alist)
    (let ((window (funcall fun buffer alist)))
      (when window
        (set-window-dedicated-p window t))
      window)))

(defun jjgr-rx-mode-name (mode-regexp)
  (lambda (buffer &rest optional)
    (with-current-buffer buffer
      (string-match mode-regexp mode-name))))

Then I do the configuration itself. I have basically three scenarios where I use Emacs: email, coding and writing. I also have three sets of windows: the ones I open (my files, a new shell), temporary windows (captures, new emails), and informative windows (logs, error messages, help windows).

(setq
 ;; Kill a frame when quitting its only window
 frame-auto-hide-function 'delete-frame
 ;; Maximum number of side-windows to create on (left top right bottom)
 window-sides-slots '(0 1 1 1)
 ;; Default rules
 display-buffer-alist
 `(;; Display *Help* buffer at the bottom-most slot
   ("*\\(Help\\|trace-\\|Backtrace\\|RefTeX.*\\)"
    (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-in-side-window)
    (side . right)
    (slot . 0)
    (window-width . 0.33)
    (reusable-frames . visible))
   ("^\\*info"
    (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-pop-up-frame)
    (pop-up-frame-parameters
      (width . 80)
      (left . 1.0)
      (fullscreen . fullheight)))
   ;; Open new edited messages in a right-hand frame
   ;; For this to close the frame, add
   ;; (add-hook 'wl-draft-kill-pre-hook 'quit-window)
   ("\\(\\*draft\\*\\|Draft/\\)"
    (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-pop-up-frame)
    (pop-up-frame-parameters
      (width . 80)
      (left . 1.0)
      (fullscreen . fullheight)))
   ;; TeX output buffers to bottom, with 10 lines
   (,(jjgr-rx-mode-name "^\\(TeX Output\\|TeX\\)")
    (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-in-side-window)
    (side . bottom)
    (slot . 0)
    (window-height . 10)
    (reusable-frames . visible))
   ;; Display *BBDB* buffer on the bottom frame
   ("*BBDB"
    (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-in-side-window)
    (side . bottom)
    (slot . 0)
    (window-height . 10)
    (reusable-frames . visible))
   ;; Split shells at the bottom
   ("^\\*[e]shell"
    (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-below-selected)
    (window-min-height . 20)
    (reusable-frames . visible)
    )
   )
 )

I find the syntax of display-buffer-alist rather self explanatory. Helper buffers, I tend to place on a side-window that pops to the right. The exception are long \*info\* buffers with Emacs manuals, that I like to read on their own frame (i.e. window from the operating system). The database of contacts, \*BBDB\*, pops at the bottom of the screen in a small window with ten lines. Shells are split from the current window, at the bottom, instead of overriding the content of the window (the default).

Note how I have to modify how Wanderlust (the email client) works. I instruct Emacs to open email editing buffers on a new operating system window (frame). However I need to use quit-window to once the draft is finished or abandoned, because otherwise the frame will not be closed automatically. This is because Wanderlust uses delete-window instead.