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))
           (pop-to-buffer buffer action norecord)
           (with-current-buffer from-buffer
             (setq-local pop-target-window (selected-window)))))))
  • I realize that your question is specifically related to forcing pop-to-buffer to behave the way you want (and your work-around suggests that you have already achieved what you desire), but there are probably other solutions that could use different functions and one can always create new functions to do exactly what one desires ....
    – lawlist
    May 14 '17 at 5:40
  • @lawlist I was trying to avoid writing a new function because there's so much going on in compilation-next-error-function and compilation-goto-locus. I suspect I just need to bite the bullet and dive into those functions to better understand what it would take to write the function I want.
    – ivan
    May 14 '17 at 11:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.