Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make `C-x b RET` switch to previous buffer even if it's already shown in another window?

Tags:

emacs

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?

Update

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:

  • winpoint
  • per-window-point

They're quite similar; for a discussion of differences see here.

like image 947
shakurov Avatar asked Dec 26 '14 18:12

shakurov


People also ask

How do I switch between buffers in Emacs?

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.

What is a buffer in Emacs?

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.


2 Answers

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.

update: fix for 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.

update: use new advice system for 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) 
like image 130
abo-abo Avatar answered Oct 20 '22 22:10

abo-abo


Instead of advising the built-in function other-buffer, you can pre-select visible buffers using a package.

1 Using Ivy

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)

2 Using Ido mode

2.1 Switching to a buffer shown in another frame

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.

2.2 Switching to a buffer that is shown in another window inside the same frame

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.)
like image 41
aparkerlue Avatar answered Oct 20 '22 23:10

aparkerlue