I have a main window displaying buffer A
, and bottom window displaying some project-wide search results
.
┌───────────┐
│ A │
│ │
├───────────┤
│ results │
└───────────┘
Hitting enter on one of the search results runs compile-goto-error, which ends up calling pop-to-buffer, splitting my main window, and displaying the result on the right:
┌─────┬─────┐
│ A │ R1 │
│ │ │
├─────┴─────┤
│ results │
└───────────┘
Swithing back to the results
window, hitting enter an another result displays it on the left:
┌─────┬─────┐
│ R2 │ R1 │
│ │ │
├─────┴─────┤
│ results │
└───────────┘
This annoys me to no end, as I wanted to keep A
visible and continue browsing the results on the right. I don't have a strong left/right preference, I just want pop-to-buffer to be consistent in its choice of window. It's currently choosing the least recently used window, and while I can see the reasoning behind that, it forces me into a game of whacka-mole, bouncing between windows, switching between buffers, etc.
The same problem arises when I hit enter on push-button links in a help buffer.
I'm coming from Vim, where the righthand window is consistently used in such scenarios. I'd like to configure Emacs to make this workflow less disorienting. I have something like this in mind:
- if the relevant buffer is already visible in an existing window, use that window
- if a result was previously displayed, use the same window again if the window is still live
- if the main window has not been split (i.e. there's only one window other than the
results
window), use split-window-sensibly and display buffer in the right/lower split - if the main window has already been split, default to the right/lower split
I'm also open to suggestions for other approaches entirely. I realize that by trying to reproduce a workflow I use in Vim, I might be missing out on a better approach that's achievable in Emacs.
I've written a sort of hacky workaround, though seems to work so far.
(defvar pop-target-window)
(make-variable-buffer-local 'pop-target-window)
(advice-add 'compilation-goto-locus :around #'ivan-around-compilation-goto-locus)
(defun ivan-around-compilation-goto-locus (orig-func &rest args)
(advice-add 'pop-to-buffer :override #'ivan-pop-to-buffer)
(apply orig-func args))
(defun ivan-pop-to-buffer (buffer &optional action norecord)
(advice-remove 'pop-to-buffer #'ivan-pop-to-buffer)
(let ((from-buffer (current-buffer))
(reused-window (display-buffer-reuse-window buffer nil)))
(cond (reused-window
(select-window reused-window norecord))
((and (bound-and-true-p pop-target-window)
(window-live-p pop-target-window))
(window--display-buffer buffer pop-target-window 'reuse)
(select-window pop-target-window norecord))
(t
(pop-to-buffer buffer action norecord)
(with-current-buffer from-buffer
(setq-local pop-target-window (selected-window)))))))