]> git.eshelyaron.com Git - emacs.git/commitdiff
Prioritize split along the longest edge by default.
authorNicolas Despres <nicolas.despres@gmail.com>
Sat, 21 Dec 2024 11:45:12 +0000 (12:45 +0100)
committerEshel Yaron <me@eshelyaron.com>
Sun, 9 Feb 2025 08:45:05 +0000 (09:45 +0100)
Currently, `split-window-sensibly' prefers to try to split
vertically first, disregarding the actual shape of the frame
or the user preferences.  This is a good default when Emacs
is taller than wider.  However, when Emacs is in full-screen
(landscape screen layout), trying to split vertically may not
be what the user expected, since there is plenty of space
available on the right.

Typical scenario: Emacs is in landscape layout, one buffer is
open in a window covering the entire frame.  Another buffer is
opened in a second window (C-x 4 f). Both splits are feasible
but users may prefer the horizontal one.

This patch preserves the behavior of the `split-height-threshold'
and `split-width-threshold' variables.  Splitting continues not
to be permitted if the edge length is below the threshold.

* lisp/window.el (split-window-sensibly): First tried split
direction follows user preferences.

* etc/NEWS: Add an entry for new variable
`split-window-preferred-direction'.
* doc/emacs/windows.texi: Document new variable.

(cherry picked from commit 77ff0df588b6691db38be810210225e84d15eca3)

doc/emacs/windows.texi
lisp/window.el

index 0abb3fbdc752da9dadb430383c19a9abdf01a970..94296a9ae7d979e2e8eaf8542c398d6787ca5d21 100644 (file)
@@ -511,6 +511,7 @@ selected frame, and display the buffer in that new window.
 
 @vindex split-height-threshold
 @vindex split-width-threshold
+@vindex split-window-preferred-direction
 The split can be either vertical or horizontal, depending on the
 variables @code{split-height-threshold} and
 @code{split-width-threshold}.  These variables should have integer
@@ -519,7 +520,9 @@ window's height, the split puts the new window below.  Otherwise, if
 @code{split-width-threshold} is smaller than the window's width, the
 split puts the new window on the right.  If neither condition holds,
 Emacs tries to split so that the new window is below---but only if the
-window was not split before (to avoid excessive splitting).
+window was not split before (to avoid excessive splitting).  Whether
+Emacs tries first to split vertically or horizontally, is
+determined by the value of @code{split-window-preferred-direction}.
 
 @item
 Otherwise, display the buffer in a window previously showing it.
index 53e0ddf5e84c96badd888ce9facd4252de945cd5..b3081b0aec5ba64420deda930c18cad1b588f84f 100644 (file)
@@ -7342,20 +7342,64 @@ hold:
                      (* 2 (max window-min-height
                                (if mode-line-format 2 1))))))))))
 
+(defcustom split-window-preferred-direction 'vertical
+  "The first direction tried when Emacs needs to split a window.
+This variable controls in which order `split-window-sensibly' will try to
+split the window.  That order specially matters when both dimensions of
+the frame are long enough to be split according to
+`split-width-threshold' and `split-height-threshold'.  If this is set to
+`vertical' (the default), `split-window-sensibly' tries to split
+vertically first and then horizontally.  If set to `horizontal' it does
+the opposite.  If set to `longest', the first direction tried
+depends on the frame shape: in landscape orientation it will be like
+`horizontal', but in portrait it will be like `vertical'.  Basically,
+the longest of the two dimension is split first.
+
+If both `split-width-threshold' and `split-height-threshold' cannot be
+satisfied, it will fallback to split vertically.
+
+See `split-window-preferred-function' for more control of the splitting
+strategy."
+  :type '(radio
+          (const :tag "Try to split vertically first"
+                 vertical)
+          (const :tag "Try to split horizontally first"
+                 horizontal)
+          (const :tag "Try to split along the longest edge first"
+                 longest))
+  :version "31.1"
+  :group 'windows)
+
+(defun window--try-vertical-split (window)
+  "Helper function for `split-window-sensibly'"
+  (when (window-splittable-p window)
+    (with-selected-window window
+      (split-window-below))))
+
+(defun window--try-horizontal-split (window)
+  "Helper function for `split-window-sensibly'"
+  (when (window-splittable-p window t)
+    (with-selected-window window
+      (split-window-right))))
+
 (defun split-window-sensibly (&optional window)
   "Split WINDOW in a way suitable for `display-buffer'.
-WINDOW defaults to the currently selected window.
-If `split-height-threshold' specifies an integer, WINDOW is at
-least `split-height-threshold' lines tall and can be split
-vertically, split WINDOW into two windows one above the other and
-return the lower window.  Otherwise, if `split-width-threshold'
-specifies an integer, WINDOW is at least `split-width-threshold'
-columns wide and can be split horizontally, split WINDOW into two
-windows side by side and return the window on the right.  If this
-can't be done either and WINDOW is the only window on its frame,
-try to split WINDOW vertically disregarding any value specified
-by `split-height-threshold'.  If that succeeds, return the lower
-window.  Return nil otherwise.
+The variable `split-window-preferred-direction' prescribes an order of
+directions in which Emacs should try to split WINDOW.  If that order
+mandates starting with a vertical split, and `split-height-threshold'
+specifies an integer that is at least as large a WINDOW's height, split
+WINDOW into two windows one below the other and return the lower one.
+If that order mandates starting with a horizontal split, and
+`split-width-threshold' specifies an integer that is at least as large
+as WINDOW's width, split WINDOW into two windows side by side and return
+the one on the right.
+
+In either case, if the first attempt to split WINDOW fails, try to split
+the window in the other direction in the same manner as described above.
+If that attempt fails too, and WINDOW is the only window on its frame,
+try splitting WINDOW into two windows, one below the other, disregarding
+the value of `split-height-threshold' and return the window on the
+bottom.
 
 By default `display-buffer' routines call this function to split
 the largest or least recently used window.  To change the default
@@ -7375,14 +7419,14 @@ Have a look at the function `window-splittable-p' if you want to
 know how `split-window-sensibly' determines whether WINDOW can be
 split."
   (let ((window (or window (selected-window))))
-    (or (and (window-splittable-p window)
-            ;; Split window vertically.
-            (with-selected-window window
-              (split-window-below)))
-       (and (window-splittable-p window t)
-            ;; Split window horizontally.
-            (with-selected-window window
-              (split-window-right)))
+    (or (if (or
+             (eql split-window-preferred-direction 'horizontal)
+             (and (eql split-window-preferred-direction 'longest)
+                  (> (frame-width) (frame-height))))
+            (or (window--try-horizontal-split window)
+                (window--try-vertical-split window))
+          (or (window--try-vertical-split window)
+              (window--try-horizontal-split window)))
        (and
          ;; If WINDOW is the only usable window on its frame (it is
          ;; the only one or, not being the only one, all the other
@@ -7400,10 +7444,8 @@ split."
                                 frame nil 'nomini)
               t)))
         (not (window-minibuffer-p window))
-        (let ((split-height-threshold 0))
-          (when (window-splittable-p window)
-            (with-selected-window window
-              (split-window-below))))))))
+         (let ((split-height-threshold 0))
+           (window--try-vertical-split window))))))
 
 (defun window--try-to-split-window (window &optional alist)
   "Try to split WINDOW.