GNU Emacs is editor of my choice for decade and a half. My usual work flow is to have 3 windows in one singe frame. Most of the time I use GNU Emacs in terminal, so multiple frames are not an option. Also, with limited space in terminal, 3 windows are just enough.

This is how I split frame into 3 windows:

-----------------------------
| Main editing area | shell |
|                   |-------|
|                   | rest  |
-----------------------------

'Main editing area' is window where I actually edit files. 'Shell' is where I run eshell with guard, gulp watch or other automated build/test process. Last window, labeled 'rest', is what this article is about. That window I use for displaying help, flycheck errors, GNU Emacs completions, ELDoc, etc…

Splitting windows

This is part of my .emacs configuration (comments added for clarification):

(defun sasa/split-windows()
  "Split windows my way."
  (interactive)
  ;; Create new window right of the current one
  ;; Current window is 80 characters (columns) wide
  (split-window-right 80)
  ;; Go to next window
  (other-window 1)
  ;; Create new window below current one
  (split-window-below)
  ;; Start eshell in current window
  (eshell)
  ;; Go to previous window
  (other-window -1)
  ;; never open any buffer in window with shell
  (set-window-dedicated-p (nth 1 (window-list)) t))

Special buffers for display-buffer-alist

Before GNU Emacs 24.X i have used special-display-regexp and special-display-function, but those are deprecated now. New way to choose where to display specific buffer is to use display-buffer-alist. Unfortunately, documentation is not clear and good examples are almost impossible to find. Here is what I manage to create after some research and testing.

(defun sasa/display-buffer (buffer &optional alist)
  "Select window for BUFFER (need to use word ALIST on the first line).
Returns thirth visible window if there are three visible windows, nil otherwise.
Minibuffer is ignored."
  (let ((wnr (if (active-minibuffer-window) 3 2)))
    (when (= (+ wnr 1) (length (window-list)))
      (let ((window (nth wnr (window-list))))
        (set-window-buffer window buffer)
        window)))
  )

(defvar sasa/help-temp-buffers '("^\\*Flycheck errors\\*$"
                                 "^\\*Completions\\*$"
                                 "^\\*Help\\*$"
                                 ;; Other buffers names...
                                 "^\\*Colors\\*$"
                                 "^\\*Async Shell Command\\*$"))

(while sasa/help-temp-buffers
  (add-to-list 'display-buffer-alist
               `(,(car sasa/help-temp-buffers)
                 (display-buffer-reuse-window
                  sasa/display-buffer
                  display-buffer-in-side-window)
                 (reusable-frames     . visible)
                 (side                . bottom)
                 (window-height       . 0.33)
                 ))
  (setq sasa/help-temp-buffers (cdr sasa/help-temp-buffers)))

Lets ignore sasa/display-buffer function for a moment and concentrate on the rest of the code. sasa/help-temp-buffers is a list of regular expressions for buffer names. In while loop I'm adding those regular expressions into display-buffer-alist list. That list takes, except regular expression, list of display functions and display options.

display-buffer-reuse-window will try to display buffer in the window that already displays buffer with a same name. if not, next display function will be called.

sasa/display-buffer will display buffer in window that I previously called rest, but only if there are 3 windows. Sometimes, there are actually 4 windows. In *Completion*, for example, mini-buffer is also in window-list, always in first place. In that case we need to open buffer on the fourth window, not third. If there are no three windows, function will return nil and next display function is called.

display-buffer-in-side-window will pop up new window, based on provided options: on the bottom of the frame and take about 33% of available height.

Share on: