Skip to content

MArpogaus/auto-side-windows

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

https://img.shields.io/github/contributors/MArpogaus/auto-side-windows.svg?style=flat-square https://img.shields.io/github/forks/MArpogaus/auto-side-windows.svg?style=flat-square https://img.shields.io/github/stars/MArpogaus/auto-side-windows.svg?style=flat-square https://img.shields.io/github/issues/MArpogaus/auto-side-windows.svg?style=flat-square https://img.shields.io/github/license/MArpogaus/auto-side-windows.svg?style=flat-square https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555

auto-side-windows

About The Project

screenshot.png

Motivation

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.

Layout Concept

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.

Inspirations

This package builds upon the excellent work and explanations of others in the Emacs community:

Thank you all!

Getting Started

Prerequisites

  • Emacs 30.1 or later.

Example Configuration

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))

Usage

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.

Customization

You can customize the behavior through the following variables (accessible via `M-x customize-group RET auto-side-windows RET`):

OptionTypeDescription
auto-side-windows-{top,bottom,left,right}-buffer-nameslist (string)Regexps matching buffer names for each side.
auto-side-windows-{top,bottom,left,right}-buffer-modeslist (symbol)Major modes for buffers for each side.
auto-side-windows-{top,bottom,left,right}-extra-conditionslist (sexp)Additional conditions (e.g., `(category . …)` for display-buffer) for matching.
auto-side-windows-{top,bottom,left,right}-window-parametersalistWindow parameters (see window-parameters) applied to windows on this side.
auto-side-windows-{top,bottom,left,right}-alistalistAction alist properties (e.g., window-height) applied for this side.
auto-side-windows-common-window-parametersalistWindow parameters applied to all side windows created by this package.
auto-side-windows-common-alistalistAction alist properties (e.g., `(dedicated . t)`) applied for all side windows.
auto-side-windows-reuse-mode-windowalistSpecify sides (e.g., `((right . t))`) where windows can be reused for same major mode.
auto-side-windows-{before,after}-display-hookhookHooks run before/after displaying a buffer in a side window.
auto-side-windows-{before,after}-toggle-hookhookHooks run before/after toggling a buffer as a side window.

Commands

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 option switch-to-buffer-obey-display-actions should be customized to a non-nil value to respect the display buffer actions defined by this package.

Integration with Other Packages

auto-side-windows plays well with other common window and buffer management packages.

Popper Integration

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 Integration

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.

Enhancing magit and org-mode compatibility

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.

Comparison with Other Packages

While several packages address window management, auto-side-windows has a specific focus:

Shackle

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

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.

Core Emacs (display-buffer-alist)

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.

Bonus: Boxes around top windows

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

Contributions to the auto-side-windows package are greatly appreciated! Feel free to check the issues page or submit a pull request.

License

Distributed under the GPLv3 License.

Contact

Marcel Arpogaus - znepry.necbtnhf@tznvy.pbz (encrypted with [ROT13](https://rot13.com/))

Project Link: https://github.com/MArpogaus/auto-side-windows

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published