6

TL;DR: Any suggestions as to what function(s) can help me restore (to any given window) the buffer that was visible most recently? It's important that the currently visible buffer is not killed, just moved to the back.

This is partly an exercise in Elisp for me so my preference is for building such a function myself instead of using off-the-shelf packages.

The long version:

I'm trying to create a function which effectively "throws" a newly created buffer to the other window. I've captured some but not all of the necessary functionality.

An example:

  • Let's say that my current frame is split vertically into two windows, |Window 1|Window 2|; I'm editing foo.el in Window 1 and some-junk.el in Window 2.
  • Now, let's say that I open a file bar.el which I want to see alongside foo.el (via ido, helm, etc.).
  • However, I accidentally opened bar.el in in Window 1, thereby covering up foo.el.
  • (I find myself in similar non-accidental situations, hence the motivation for such a function)

The goal is to have a function which will "throw" bar.el to Window 2, i.e., two steps: (i) have Window 2 show bar.el and (ii) have Window 1 show foo.el.

So far, I've been able to come up with an implementation of (i) but am stuck on (ii). in other words, I can get Window 2 to show bar.el but I'm having trouble getting Window 1 to revert to foo.el:

(defun dc/move-top-buffer-to-other-window ()
  (interactive)
  (let* ((current (selected-window))
         (other   (next-window))
     (current-buffer (window-buffer current)))
     (set-window-buffer other current-buffer)
     (other-window 1)))

I thought there might be a variable that represents a notion of "buffer-order" for each window (e.g., in the example 1, such a variable for Window 2 would hold a list of buffers in the following order, as per the example above, [bar.el foo.el ...]), since Emacs is able to "remember" which buffer I had open last. But I haven't been able to find it.

Any suggestions?

5

Here's a simple, no-frills function that does what you're asking. No need to let-bind anything:

(defun switcheroo ()
  "Throw current buffer to other window, display previous buffer
in this window."
  (interactive)
  (save-selected-window
    (switch-to-buffer-other-window (current-buffer)))
  (switch-to-buffer (other-buffer)))
3

Take a look at source of winner-mode. It provides some of the functionality you are looking for, and it may give some hints on how to track a ring of buffers per window.

After enabling winner-mode, by default, C-c <left> and C-c <right> are bound to winner-undo, and winner-redo. It then tracks which buffer was in each window, and what windows were visible in the frame. So if you switch to another buffer in either window, and then run winner-undo the buffer will return to point in the original window.

The fun part is that if a windows is split again, executing winner-undo will return to two visible windows. After executing winner-redo, the third window will re-open with the last buffer it held.

Alternatively, if the functionality needed is to simply display the current (new) buffer in the next window, transpose-window might be all you need to swap visible buffers:

 (defun transpose-windows (arg)
   "Transpose the buffers shown in two windows."
   (interactive "p")
   (let ((selector (if (>= arg 0) 'next-window 'previous-window)))
     (while (/= arg 0)
       (let ((this-win (window-buffer))
             (next-win (window-buffer (funcall selector))))
         (set-window-buffer (selected-window) next-win)
         (set-window-buffer (funcall selector) this-win)
         (select-window (funcall selector)))
       (setq arg (if (plusp arg) (1- arg) (1+ arg))))))

 (define-key ctl-x-4-map (kbd "t") 'transpose-windows)
  • Thanks, I'm going to add transpose-windows to my arsenal. That said, transpose-windows doesn't really accomplish what I was looking to do (i.e., moving one buffer to another window vs. swapping two buffers). I'd initially given the check-mark without a sufficiently close read, my fault. – iceman Oct 29 '14 at 22:52
2

My alternate solution for any who are interested. It turns out I was one line away from the desired functionality! (The key function was bury-buffer.) Full function below, as reference.

Dan's version above is better, though - it's shorter and sweeter since it leverages the save-selected-window function (which I'm only just learning about!)

Also, for readers: I am a big fan of winner-mode (as per the other post) and would recommend it also. Part of the motivation behind creating this function was because winner-mode is more for undoing/redoing changes to your windows/buffers and not for shifting around your current windows/buffers.

(defun dc/move-top-buffer-to-other-window ()
  "If you're viewing 'foo.el' and 'some-junk.el' in two-window
frame (e.g., |Window 1|Window 2|) as
follows (|foo.el|some-junk.el|) and you (possibly accidentally)
open another buffer, 'bar.el' in Window 1, then calling this
function will move 'bar.el' to Window 2 and will restore 'foo.el'
to Window 1."
  (interactive)
  (let* ((current (selected-window))
         (other (next-window))
         (current-buffer (window-buffer current)))
    (set-window-buffer other current-buffer)
    (bury-buffer)
    (other-window 1)))
  • 1
    I wanted to tell you about bury-buffer, but you seemingly discovered it yourself. – mbork Oct 29 '14 at 23:03
  • +1, by the way, for figuring out a solution yourself in the process. Welcome to the club! – Dan Oct 29 '14 at 23:16

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.