- About The Project
- Getting Started
- Usage
- Customization
- Commands
- Integration with Other Packages
- Comparison with Other Packages
- Bonus: Boxes around top windows
- Contributions
- License
- Contact
I found the default behavior of display-buffer
often unpredictable, leading to new buffers appearing in unexpected windows.
Digging into Emacs’ window management led me to realize the power of display-buffer-alist
, but configuring it directly can be complicated and confusing especially for new users.
auto-side-windows
simplifies this process.
It provides a straightforward way to define rules based on buffer names or major modes, ensuring that specific buffers consistently open in designated side windows (top, bottom, left, or right).
The goal is to achieve a predictable, organized workspace, similar to layouts often found in IDEs, keeping the main editing area clear while providing easy access to complementary information.
The package helps achieve layouts where side windows display non-primary information, keeping the central area focused on editing. A conceptual layout might look like this:
_________________________________________________ | top: compilation, grep, occur | |_________________________________________________| | left: | | right: | | - dired | | - Help | | - treemacs | | - Info | | - outline | Main Window Area | - Magit | | | (Editing) | | | | | | | | | | |____________|_______________________|____________| | bottom: REPLs, shells | |_________________________________________________| | Echo Area | |_________________________________________________|
This allows buffers like help, compilation logs, shells, or project trees to be accessible without disrupting the primary editing flow.
This package builds upon the excellent work and explanations of others in the Emacs community:
- Emacs Window Management Almanac by u/karthinks
- Demystifying Emacs’s Window Manager by u/mickeyp
- Karthink’s setup-windows.el for a practical examples.
Thank you all!
- Emacs 30.1 or later.
Here’s an example configuration using use-package
:
(use-package auto-side-windows
:load-path "/path/to/auto-side-windows/"
:custom
;; Respects display actions when switching buffers
(switch-to-buffer-obey-display-actions t)
;; Top side window configurations
(auto-side-windows-top-buffer-names
'("^\\*Backtrace\\*$"
"^\\*Async-native-compile-log\\*$"
"^\\*Compile-Log\\*$"
"^\\*Multiple Choice Help\\*$"
"^\\*Quick Help\\*$"
"^\\*TeX Help\\*$"
"^\\*TeX errors\\*$"
"^\\*Warnings\\*$"
"^\\*Process List\\*$"))
(auto-side-windows-top-buffer-modes
'(flymake-diagnostics-buffer-mode
locate-mode
occur-mode
grep-mode
xref--xref-buffer-mode))
;; Bottom side window configurations
(auto-side-windows-bottom-buffer-names
'("^\\*eshell\\*$"
"^\\*shell\\*$"
"^\\*term\\*$"))
(auto-side-windows-bottom-buffer-modes
'(eshell-mode
shell-mode
term-mode
comint-mode
debugger-mode))
;; Right side window configurations
(auto-side-windows-right-buffer-names
'("^\\*eldoc.*\\*$"
"^\\*info\\*$"
"^\\*Metahelp\\*$"))
(auto-side-windows-right-buffer-modes
'(Info-mode
TeX-output-mode
eldoc-mode
help-mode
helpful-mode
shortdoc-mode))
;; Example: Custom parameters for top windows (e.g., fit height to buffer)
;; (auto-side-windows-top-alist '((window-height . fit-window-to-buffer)))
;; (auto-side-windows-top-window-parameters '((mode-line-format . ...))) ;; Adjust mode-line
;; Maximum number of side windows on the left, top, right and bottom
(window-sides-slots '(1 1 1 1)) ; Example: Allow one window per side
;; Force left and right side windows to occupy full frame height
(window-sides-vertical t)
;; Make changes to tab-/header- and mode-line-format persistent when toggling windows visibility
(window-persistent-parameters
(append window-persistent-parameters
'((tab-line-format . t)
(header-line-format . t)
(mode-line-format . t))))
:bind ;; Example keybindings (adjust prefix as needed)
(:map global-map ; Or your preferred keymap prefix
("C-c w t" . auto-side-windows-display-buffer-top)
("C-c w b" . auto-side-windows-display-buffer-bottom)
("C-c w l" . auto-side-windows-display-buffer-left)
("C-c w r" . auto-side-windows-display-buffer-right)
("C-c w w" . auto-side-windows-switch-to-buffer)
("C-c w t" . window-toggle-side-windows) ; Toggle all side windows
("C-c w T" . auto-side-windows-toggle-side-window)) ; Toggle current buffer in/out of side window
:hook
(after-init . auto-side-windows-mode))
Once auto-side-windows-mode
is enabled and configured, buffers matching your defined rules (by name regexp or major mode) will automatically be displayed in the specified side window.
You can customize the behavior through the following variables (accessible via `M-x customize-group RET auto-side-windows RET`):
Option | Type | Description |
---|---|---|
auto-side-windows-{top,bottom,left,right}-buffer-names | list (string) | Regexps matching buffer names for each side. |
auto-side-windows-{top,bottom,left,right}-buffer-modes | list (symbol) | Major modes for buffers for each side. |
auto-side-windows-{top,bottom,left,right}-extra-conditions | list (sexp) | Additional conditions (e.g., `(category . …)` for display-buffer ) for matching. |
auto-side-windows-{top,bottom,left,right}-window-parameters | alist | Window parameters (see window-parameters ) applied to windows on this side. |
auto-side-windows-{top,bottom,left,right}-alist | alist | Action alist properties (e.g., window-height ) applied for this side. |
auto-side-windows-common-window-parameters | alist | Window parameters applied to all side windows created by this package. |
auto-side-windows-common-alist | alist | Action alist properties (e.g., `(dedicated . t)`) applied for all side windows. |
auto-side-windows-reuse-mode-window | alist | Specify sides (e.g., `((right . t))`) where windows can be reused for same major mode. |
auto-side-windows-{before,after}-display-hook | hook | Hooks run before/after displaying a buffer in a side window. |
auto-side-windows-{before,after}-toggle-hook | hook | Hooks run before/after toggling a buffer as a side window. |
The package provides the following commands:
auto-side-windows-mode
: Global minor mode. Enable this to activate the automatic side window management based on your rules.auto-side-windows-toggle-side-window
: If the current buffer is in a side window created by this package, move it to a normal window. If it was just moved out, move it back to its side window configuration. Useful for temporarily maximizing a side window buffer.auto-side-windows-display-buffer-{top,bottom,left,right}
: Manually force the current buffer to be displayed in a side window on the specified side, overriding normal rules.auto-side-windows-display-buffer-on-side
: Manually force the current buffer to be displayed in a side window on the specified side, overriding normal rules. Compared to the command above the user is prompted side in the minibuffer.auto-side-windows-switch-to-buffer
: Switch to a side buffer. The optionswitch-to-buffer-obey-display-actions
should be customized to a non-nil value to respect the display buffer actions defined by this package.
auto-side-windows
plays well with other common window and buffer management packages.
You can configure Popper to manage the side windows created by auto-side-windows
. Define Popper’s reference buffers using the same lists you use for auto-side-windows
:
(use-package popper
:after auto-side-windows ; Ensure auto-side-windows variables are defined
:hook (auto-side-windows-mode . popper-mode) ; Activate popper alongside
:custom
;; Tell Popper to consider buffers matching auto-side-windows rules as popups
(popper-reference-buffers
(append auto-side-windows-top-buffer-names auto-side-windows-top-buffer-modes
auto-side-windows-left-buffer-names auto-side-windows-left-buffer-modes
auto-side-windows-right-buffer-names auto-side-windows-right-buffer-modes
auto-side-windows-bottom-buffer-names auto-side-windows-bottom-buffer-modes))
;; Optional: Don't let Popper decide where to display, auto-side-windows handles that
(popper-display-control nil) ; Or 'user if you prefer popper commands for display
:config
(popper-mode +1) ; Enable popper-mode
(popper-echo-mode +1) ; Optional: echo area notifications
:bind ;; Example bindings
(:map your-prefix-map ;; e.g. my/toggle-map
("p" . popper-toggle) ; Toggle last popup
("P" . popper-toggle-type) ; Toggle popups of specific type
("C-p" . popper-cycle))) ; Cycle through visible popups
With this setup, you can use Popper commands (like popper-toggle
or popper-cycle
) to quickly hide/show/cycle through the side windows managed by auto-side-windows
.
Additionally, the following custom command allows you to switch to arbitrary buried popup buffers:
(defun my/popper-switch-to-buried-buffer (buffer)
"Switch to buried popup BUFFER."
(interactive
(list
(when-let ((buried-popups (progn (popper--find-buried-popups)
(mapcar #'cdr
(alist-get (funcall popper-group-function)
popper-buried-popup-alist nil nil 'equal))))
(pred (lambda (b)
(if (consp b) (setq b (car b)))
(setq b (get-buffer b))
(member b buried-popups))))
(read-buffer "Switch to popup: " nil t pred))))
(if buffer (display-buffer buffer)
(message "No buried popups.")))
Just add it to the :preface
section of the above use-package
declaration and optionally bind it to a key.
Ace Window can serve as a fallback mechanism for selecting a window when display-buffer
doesn’t find a suitable existing window or rule (including those from auto-side-windows
).
Modify display-buffer-base-action
to include ace-display-buffer
. This tells Emacs to use Ace Window to ask you where to put a buffer if other methods fail.
(use-package ace-window
:custom
(display-buffer-base-action '(display-buffer-reuse-mode-window
display-buffer-in-previous-window
ace-display-buffer))) ; Ask user via ace-window
Now, if a command tries to display a buffer and no auto-side-windows
rule applies and standard reuse fails, ace-display-buffer
will trigger, letting you pick the window.
To further enhance the Emacs experience with auto-side-windows
, consider adding these configurations:
;; Org mode: Ensure agenda buffers open in the top side window for easy access
(setopt org-src-window-setup 'plain)
(setopt auto-side-windows-top-buffer-names '("^\\*Org Agenda\\*$"
"^\\*Org Src.*\\*"
"^\\*Org-Babel Error Output\\*"
...)))
;; Magit: Display Magit diff/status in the right side window, edit commit msg on top
(setopt magit-display-buffer-function #'display-buffer
magit-commit-diff-inhibit-same-window t)
(setopt auto-side-windows-right-buffer-names '("^magit-diff:.*$"
"^magit-process:.*$"
...))
(setopt auto-side-windows-right-buffer-modes '(magit-status-mode
magit-log-mode
magit-diff-mode
magit-process-mode
...))
(setopt auto-side-windows-top-buffer-names
'("^COMMIT_EDITMSG$"
...))
These configurations integrate well with auto-side-windows
and enhance buffer management for specific use cases in Emacs.
While several packages address window management, auto-side-windows
has a specific focus:
Shackle is a powerful package for finely controlling how and where popup windows appear, including options for creating new frames or specifying precise window parameters. It’s very flexible and can involve more complex rule definitions.
Difference: auto-side-windows
focuses specifically on simplifying the common use case of assigning buffers to side windows within the current frame based on simple name/mode matching, acting as a high-level interface to display-buffer-alist
for this purpose.
Popper excels at managing buffers that are already considered “popups”. It allows you to quickly toggle, cycle through, and manage the visibility of these popup windows.
Difference: auto-side-windows
defines which buffers should become side windows in the first place. Popper can then be used to manage these side windows created by auto-side-windows
rules (see Popper Integration), by cycling through them or separating them by project. They work well together.
Emacs’ built-in display-buffer-alist
is the underlying mechanism for controlling buffer display. It’s extremely powerful but requires writing potentially complex alist structures.
Difference: auto-side-windows
acts as a configuration layer on top of display-buffer-alist
. It generates the necessary alist entries for side window rules based on user-friendly customization variables, reducing the need to write the raw alist entries manually for this common pattern.
Inspired by u/Nicolas-Rougier’s post, you can add customized header lines to side windows for better visual distinction. To resemble the example shown in the screenshot add this to your configuration:
(use-package auto-side-windows
:ensure (:host github :repo "MArpogaus/auto-side-windows")
:preface
(defun my/get-header-line-icon-for-buffer (buffer)
(with-current-buffer buffer
(unless (boundp 'header-line-icon)
(setq-local header-line-icon
(cond
((buffer-match-p "Warning" buffer) '(" ! " . warning))
((buffer-match-p '(or "^\\*Backtrace\\*$" ".*[Ee]rror.*") buffer) '(" ! " . error))
((buffer-match-p '(or "^COMMIT_EDITMSG$" "^\\*diff-hl\\*$") buffer) '(" " . success))
((buffer-match-p "^\\*Org Src.*\\*" buffer) '(" " . mode-line-emphasis))
((buffer-match-p "^\\*Org Agenda\\*$" buffer) '(" " . mode-line-emphasis))
(t '(" ? " . mode-line-emphasis)))))
header-line-icon))
(defun my/install-top-side-window-face-remaps (buffer foreground background)
(with-current-buffer buffer
(unless (bound-and-true-p top-side-window-face-remaps-cookies)
(setq-local top-side-window-face-remaps-cookies
(list
(face-remap-add-relative 'header-line
`(:box nil :underline nil :overline ,background))
(face-remap-add-relative 'fringe
`(:background ,background))
(face-remap-add-relative 'mode-line-active
`(:overline ,background :underline nil :height 0))
(face-remap-add-relative 'mode-line-inactive
`(:overline ,background :underline nil :height 0))
)))))
(defvar my/header-line-format-top
'(:eval
(let*
((buffer (current-buffer))
(prefix-and-face (my/get-header-line-icon-for-buffer buffer))
(prefix (car prefix-and-face))
(background (face-foreground (cdr prefix-and-face)))
(foreground (face-background (cdr prefix-and-face) nil 'default))
(prefix-face (list :inherit 'bold :background background :foreground foreground))
(buffer-face (list :inherit 'bold :foreground background)))
(set-window-fringes nil 1 1 t)
(my/install-top-side-window-face-remaps buffer foreground background)
(list
(propertize prefix 'face prefix-face 'display '(space-width 0.7))
(propertize (format-mode-line " %b ") 'face buffer-face)
(propertize " " 'display `(space :align-to right))
(propertize " " 'face prefix-face 'display '(space-width 1))))))
:custom
;; Draw boxes around top side windows
(auto-side-windows-top-window-parameters `((mode-line-format . t)
(header-line-format . ,my/header-line-format-top)))
(auto-side-windows-before-display-hook '((lambda (buffer)
(with-current-buffer buffer
(when (bound-and-true-p top-side-window-face-remaps-cookies)
(dolist (cookie top-side-window-face-remaps-cookies)
(face-remap-remove-relative cookie))
(kill-local-variable 'top-side-window-face-remaps-cookies))))))
(auto-side-windows-before-toggle-hook auto-side-windows-before-display-hook))
Note: Complex face remapping in header lines might have side effects (e.g., affecting Corfu popup fringes).
Contributions to the auto-side-windows
package are greatly appreciated!
Feel free to check the issues page or submit a pull request.
Distributed under the GPLv3 License.
Marcel Arpogaus - znepry.necbtnhf@tznvy.pbz (encrypted with [ROT13](https://rot13.com/))
Project Link: https://github.com/MArpogaus/auto-side-windows