Concisely, the question is: in a window, how do I switch quickly to a buffer previously visited in that window, even if it's already opened in another window?
A more detailed description follows.
Normally, in order to switch window to previous buffer one just types C-x b RET
. That is, the default argument to switch-to-buffer
(or ido-switch-buffer
) is the previous buffer.
This is not, however, the case when that (previous) buffer is already shown in another window. That's exactly what bugs me.
Let's consider an example. Suppose I have three buffers (A
, B
and C
) and two windows showing buffers A
and B
(C
is not visible at this point).
Then I open buffer A
in the second window, too. So, now I have buffer A
shown in both windows. Then I switch (C-x b RET
) to B
again. After that, C-x b RET
will bring me not to A
, but to C
because A
is already shown in the other window.
How do I make C-x b RET
behave more consistently?
After this problem had been solved, I realized I needed more: namely, for point position to be remembered per-window, not per buffer. Luckily, there're ready-made solutions:
They're quite similar; for a discussion of differences see here.
To move between the buffers, type C-x b. Emacs shows you a default buffer name. Press Enter if that's the buffer you want, or type the first few characters of the correct buffer name and press Tab. Emacs fills in the rest of the name.
Buffers in Emacs editing are objects that have distinct names and hold text that can be edited. Buffers appear to Lisp programs as a special data type. You can think of the contents of a buffer as a string that you can extend; insertions and deletions may occur in any part of the buffer.
I've found a fix for switch-to-buffer
. It eventually calls
(other-buffer (current-buffer))
while in order to fix your problem, the call needs to look like this:
(other-buffer (current-buffer) t)
i.e. the visible-ok
argument needs to be t
.
Here's an advice to have it always at t
. Hopefully it won't break other stuff that uses other-buffer
:
(defadvice other-buffer (around fix-switch-to-buffer
(&optional buffer visible-ok frame) activate)
(setq visible-ok t)
ad-do-it)
Note that ido-switch-to-buffer
uses a different machinery, so a different method is needed to fix it.
ido-switch-to-buffer
I needed to re-define ido-make-buffer-list
:
(defun ido-make-buffer-list (default)
(let* ((ido-current-buffers (list (buffer-name (current-buffer))))
(ido-temp-list (ido-make-buffer-list-1 (selected-frame) ido-current-buffers)))
(if ido-temp-list
(nconc ido-temp-list ido-current-buffers)
(setq ido-temp-list ido-current-buffers))
(if default
(setq ido-temp-list
(cons default (delete default ido-temp-list))))
(if (bound-and-true-p ido-enable-virtual-buffers)
(ido-add-virtual-buffers-to-list))
(run-hooks 'ido-make-buffer-list-hook)
ido-temp-list))
The diff is just one line, but it's too messy to advice it.
other-buffer
The old stuff should still work for quite a while, but here's the new approach:
(defun other-buffer-advice (orig-fun &optional buffer visible-ok frame)
(funcall orig-fun buffer t frame))
(advice-add 'other-buffer :around #'other-buffer-advice)
;; (advice-remove 'other-buffer :around #'other-buffer-advice)
Instead of advising the built-in function other-buffer
, you can pre-select visible buffers using a package.
If you're using Ivy, you can use abo-abo's approach to override the lower-use function ivy-switch-buffer
.
(defun user/ivy-switch-buffer ()
"Switch to another buffer with visible-ok preselection."
(interactive)
(ivy-read "Switch to buffer: " #'internal-complete-buffer
:keymap ivy-switch-buffer-map
:preselect (buffer-name (other-buffer (current-buffer) t))
:action #'ivy--switch-buffer-action
:matcher #'ivy--switch-buffer-matcher
:caller 'ivy-switch-buffer))
(advice-add 'ivy-switch-buffer :override #'user/ivy-switch-buffer)
If by "window" you really mean "frame" (i.e., you'd like to ido-switch-buffer
to a buffer that is currently shown in another frame), then ido-mode
gives you the behavior you're looking for when you change ido-default-buffer-method
from its default value of raise-frame
to selected-window
:
(setq ido-default-buffer-method 'selected-window)
Emacs constructs an independent buffer list for each frame, so the only thing you have to do is to configure Ido to avoid jumping to another frame when you switch buffers.
To get this behavior across windows within the same frame, you should hook a function that reorders the buffer list onto ido-make-buffer-list-hook
.
From ido.el
:
;; Changing the list of files
;; --------------------------
;; By default, the list of current files is most recent first,
;; oldest last, with the exception that the files visible in the
;; current frame are put at the end of the list. A hook exists to
;; allow other functions to order the list. For example, if you add:
;;
;; (add-hook 'ido-make-buffer-list-hook 'ido-summary-buffers-to-end)
;;
;; then all files matching "Summary" are moved to the end of the
;; list. (I find this handy for keeping the INBOX Summary and so on
;; out of the way.) It also moves files matching "output\*$" to the
;; end of the list (these are created by AUCTeX when compiling.)
;; Other functions could be made available which alter the list of
;; matching files (either deleting or rearranging elements.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With