9

I know Emacs tries to be intellectual and opens its helper buffers depending on which dimension of the window is bigger, so it may appear in vertical split window if current width is bigger than height, and in horizontal split otherwise.

But I’d prefer it to open that list always in horizontal split, because there are long paths I can’t see when the buffer is placed in vertical split. How can I do this?

tijagi
  • 1,124
  • 1
  • 13
  • 31
  • Tangentially, give `M-x ibuffer` a try. I highly recommend using it as a replacement for `list-buffers`. I prefer its default behaviours, but it's also extremely powerful. `C-h m` in the `ibuffer` buffer will give you a basic idea. – phils Feb 04 '14 at 12:52
  • Related (but definitely not a duplicate): [Reverse evaluation order of split-height-threshold and split-width-threshold in Emacs for display-buffer](http://stackoverflow.com/q/23659909/1199226) – itsjeyd May 14 '14 at 21:59

4 Answers4

7

I believe you've got the horizontal/vertical split terminology back to front (I can never remember which is which either), but as your goal was to retain the original window's width, I'm forcing a vertical split.

See C-hf split-window-sensibly RET. It tells you what to do:

You can enforce this function to not split WINDOW horizontally,
by setting (or binding) the variable `split-width-threshold' to
nil.  If, in addition, you set `split-height-threshold' to zero,
chances increase that this function does split WINDOW vertically.

So as a permanent setting:

(setq split-width-threshold nil)
(setq split-height-threshold 0)

For just a specific function, you can advise that function (but see Edit 2 below!):

(defadvice list-buffers (around list-buffers-split-vertically)
  "Always split vertically when displaying the buffer list.
See `split-window-sensibly'."
  (let ((split-width-threshold nil)
        (split-height-threshold 0))
    ad-do-it))
(ad-activate 'list-buffers)

Edit: Actually, in this instance I suspect you're only concerned with the interactive case, in which case it's preferable to define a function and remap the bindings:

(defun my-list-buffers-vertical-split ()
  "`list-buffers', but forcing a vertical split.
    See `split-window-sensibly'."
  (interactive)
  (let ((split-width-threshold nil)
        (split-height-threshold 0))
    (call-interactively 'list-buffers)))

(global-set-key [remap list-buffers] 'my-list-buffers-vertical-split)

Edit 2: And Stefan points out that display-buffer-alist facilitates such things without advising functions (and of course avoiding unnecessary advice is always a good thing). I believe we still need a custom action, so:

(defun my-display-buffer-pop-up-same-width-window (buffer alist)
  "A `display-buffer' ACTION forcing a vertical window split.
    See `split-window-sensibly' and `display-buffer-pop-up-window'."
  (let ((split-width-threshold nil)
        (split-height-threshold 0))
    (display-buffer-pop-up-window buffer alist)))

(add-to-list 'display-buffer-alist
             '("\\*Buffer List\\*" my-display-buffer-pop-up-same-width-window))
phils
  • 71,335
  • 11
  • 153
  • 198
  • 1
    Rather than advise `list-buffers` you can add an entry to `display-buffer-alist` for the `*Buffer List*` buffer. – Stefan Feb 04 '14 at 13:41
  • Though it doesn’t retain window width, if the window was already split, but I think it is another question. – tijagi Feb 04 '14 at 16:14
  • Well it retains the width of the window you are splitting :) There are certainly solutions to modify and restore the frame's window configuration if you want to make more drastic changes -- but as you say, that's a different question. – phils Feb 04 '14 at 22:25
0

You can remove (5) if you prefer not to select the window after it is displayed -- i.e., remove (select-window (get-buffer-window (buffer-name buffer))). I like the bottom window to be reserved for a 3-month calendar, so that's why I have a condition to use the window above (if it exists) -- you can remove that condition if you are so inclined. Actually, it's your function so you can modify everything as you see fit now that you see how it works. The alist would be used like this: '((window-width . 33)) if you wanted to control certain aspects of the target window, etc. I find myself always going back to this document page because it is the only scanty formal example I've found . . . and, of course, the source itself window.el: http://www.gnu.org/software/emacs/manual/html_node/elisp/Display-Action-Functions.html

(defun lawlist-list-buffers-left (&optional arg)
  "Display a list of existing buffers.
The list is displayed in a buffer named \"*Buffer List*\".
See `buffer-menu' for a description of the Buffer Menu.
    By default, all buffers are listed except those whose names start
with a space (which are for internal use).  With prefix argument
ARG, show only buffers that are visiting files."
  (interactive "P")
  (lawlist-display-buffer-left (list-buffers-noselect arg) nil))

(defun lawlist-list-buffers-right (&optional arg)
  "Display a list of existing buffers.
The list is displayed in a buffer named \"*Buffer List*\".
See `buffer-menu' for a description of the Buffer Menu.
    By default, all buffers are listed except those whose names start
with a space (which are for internal use).  With prefix argument
ARG, show only buffers that are visiting files."
  (interactive "P")
  (lawlist-display-buffer-right (list-buffers-noselect arg) nil))

(defun lawlist-display-buffer-left (buffer alist)
"(1) If `buffer` is already displayed, then display it again in the same window.
(2) If `buffer` is not already displayed, and if there is a window to the left,
then display that `buffer` in said window. (3) If `buffer` is not already
displayed, and if there is a window to the right, then use the selected window.
(4) If all else fails, then create a new window to the left and display `buffer` there.
(5) Select the target window which displays `buffer`."
 (let (
    (window
      (cond
        ((get-buffer-window buffer (selected-frame)))
        ((window-in-direction 'above))
        ((window-in-direction 'left))
        ((window-in-direction 'right)
          (selected-window))
        (t
          (split-window (selected-window) nil 'left)))))
  (window--display-buffer buffer window 'window alist display-buffer-mark-dedicated)
  ;; OPTIONAL -- uncomment to select the target window.
  ;; (select-window (get-buffer-window (buffer-name buffer)))
  ))

(defun lawlist-display-buffer-right (buffer alist)
"(1) If `buffer` is already displayed, then display it again in the same window.
(2) If `buffer` is not already displayed, and if there is a window to the right,
then display that `buffer` in said window. (3) If `buffer` is not already
displayed, and if there is a window to the left, then use the selected window.
(4) If all else fails, then create a new window to the right and display `buffer` there.
(5) Select the target window which displays `buffer`."
 (let (
    (window
      (cond
        ((get-buffer-window buffer (selected-frame)))
        ((window-in-direction 'above))
        ((window-in-direction 'right))
        ((window-in-direction 'left)
          (selected-window))
        (t
          (split-window (selected-window) nil 'right)))))
  (window--display-buffer buffer window 'window alist display-buffer-mark-dedicated)
  ;; OPTIONAL -- uncomment to select the target window.
  ;; (select-window (get-buffer-window (buffer-name buffer)))
  ))
lawlist
  • 13,099
  • 3
  • 49
  • 158
0

If horizontally or vertically, presently neither split-height-threshold nor split-width-threshold seem reliable WRT to expected kind of split. Which looks like a bug, resp. design issue.

As a work-around call M-x split-window-horizontally resp. -vertically before, or advise the function with it.

Andreas Röhler
  • 4,804
  • 14
  • 18
  • Is there an example of it failing? I guess a really easy alternative to `let` binding those variables would be to `flet` the function we don't want to the function we do want :) `(require 'cl) (flet ((split-window-right (&rest ignore) (split-window-below))) ...)` – phils Feb 04 '14 at 23:01
  • I wrote my own functions because I don't believe `split-window-preferred-function` (aka `split-window-sensibly`) is used by `display-buffer`. It is used by some other functions, e.g., whenever `window--try-to-split-window` is used -- *not* `display-buffer`, but instead functions like: `display-buffer-pop-up-window`; `display-buffer-below-selected`; `display-buffer-at-bottom`. See `display-buffer-fallback-action` for the functions that are used . . . – lawlist Feb 05 '14 at 00:52
0

As suggested at https://www.emacswiki.org/emacs/HorizontalSplitting , I have this line in my .emacs file and it works for me:

(setq split-width-threshold 9999)
Peter B
  • 453
  • 4
  • 15