el/dot-emacs.el: Override the default `display-buffer' logic.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 6 Dec 2023 14:25:28 +0000 (14:25 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 6 Dec 2023 14:25:28 +0000 (14:25 +0000)
Just use the `next' window in a fairly obvious sort of way.  The
objective here is to be less surprising and random.

el/dot-emacs.el

index abe67b0..a04ac26 100644 (file)
@@ -253,6 +253,12 @@ fringes is not taken out of the allowance for WIDTH, unlike
       (other-window 1))
     (select-window win)))
 
+(defun mdw-frame-width-quantized-p (frame-width column-width)
+  "Return whether the FRAME-WIDTH was chosen specifically for COLUMN-WIDTH."
+  (let ((sb-width (mdw-horizontal-window-overhead)))
+    (zerop (mod (+ frame-width sb-width)
+               (+ column-width sb-width)))))
+
 (defun mdw-frame-width-for-columns (columns width)
   "Return the preferred width for a frame with so many COLUMNS of WIDTH."
   (let ((sb-width (mdw-horizontal-window-overhead)))
@@ -753,6 +759,68 @@ Pretend they don't exist.  They might be on other display devices."
 
 (setq display-buffer-reuse-frames nil)
 
+(defun mdw-last-window-in-frame-p (window)
+  "Return whether WINDOW is the last in its frame."
+  (catch 'done
+    (while window
+      (let ((next (window-next-sibling window)))
+       (while (and next (window-minibuffer-p next))
+         (setq next (window-next-sibling next)))
+       (if next (throw 'done nil)))
+      (setq window (window-parent window)))
+    t))
+
+(defun mdw-display-buffer-in-tolerable-window (buffer alist)
+  "Try finding a tolerable window in which to display BUFFER.
+Begone, foul DWIMmerlaik!
+
+This is all totally subject to arbitrary change in the future, but the
+emphasis is on predictability rather than crazy DWIMmery."
+  (let* ((selected (selected-window)) chosen
+        (full-height-p (window-full-height-p selected))
+        (full-width-p (window-full-width-p selected)))
+    (cond
+
+     ((and full-height-p full-width-p)
+      ;; We're basically the only window in the frame.  If we want to get
+      ;; anywhere, we'll have to split the window.
+
+      (let ((width (window-width selected))
+           (preferred-width (mdw-preferred-column-width)))
+       (if (and (>= width (mdw-frame-width-for-columns 2 preferred-width))
+                (mdw-frame-width-quantized-p width preferred-width))
+           (setq chosen (split-window-right preferred-width))
+         (setq chosen (split-window-below)))
+       (display-buffer-record-window 'window chosen buffer)))
+
+     ((mdw-last-window-in-frame-p selected)
+      ;; This is the last window in the frame.  I don't think I want to
+      ;; clobber the first window, so rebound and clobber the previous one
+      ;; instead.  (This obviously has the same effect if there are only two
+      ;; windows, but seems more useful if there are three.)
+
+      (setq chosen (previous-window selected 'never nil))
+      (display-buffer-record-window 'reuse chosen buffer))
+
+     (t
+      ;; There's another window in front of us.  Let's use that one.
+      (setq chosen (next-window selected 'never nil)))
+      (display-buffer-record-window 'reuse chosen buffer))
+
+    (if (eq chosen selected)
+       (error "Failed to select a different window!"))
+
+    (when chosen
+      (with-selected-window chosen (switch-to-buffer buffer)))
+    chosen))
+
+;; Hack the display actions so that they do something sensible.
+(setq display-buffer-fallback-action
+       '((display-buffer--maybe-same-window
+          display-buffer-reuse-window
+          display-buffer-pop-up-window
+          mdw-display-buffer-in-tolerable-window)))
+
 ;; Rename buffers along with files.
 
 (defvar mdw-inhibit-rename-buffer nil