7

Thanks to @itsjeyd, I've come to love ace-window, but now I want more.

I use C-x 4 f and C-x 4 b to invoke the commands ido-find-file-other-window and ido-switch-buffer-other-window, but I want to pick the window to be used. That is, I want to use ido to pick the file or buffer name and (probably) something like ace-window to select the window to be used.

Today, I have to:

  1. Invoke ace-window to select the window I want to change.
  2. Invoke ido-find-file or ido-switch-to-buffer.
  3. Invoke ace-window to go back to the original window.

Is there a better way? If not, I will likely try to write something to do what I want and put it on github.

Colin Fraizer
  • 740
  • 4
  • 12

1 Answers1

5

How about this:

(defun ace-command-other-window (cmd &optional one-win-cmd)
  "Execute CMD in another window.
If provided, call ONE-WIN-CMD instead when there is only one window."
  (interactive "CM-x (other window) ")
  (catch 'done
    (when (and one-win-cmd
               (not (window-parent)))
      (call-interactively one-win-cmd)
      (throw 'done t))
    (let ((start-window (selected-window)))
      (unwind-protect
           (progn
             (aw-switch-to-window
              (aw-select  " Ace - Command "))
             (call-interactively cmd))
        (aw-switch-to-window start-window)))))

(defun ace-find-file ()
  "Find a file in another window."
  (interactive)
  (ace-command-other-window #'ido-find-file
                            #'ido-find-file-other-window))

(defun ace-switch-buffer ()
  "Switch buffers in another window."
  (interactive)
  (ace-command-other-window #'ido-switch-buffer
                            #'ido-switch-buffer-other-window))

When you have only one window, these call the normal ido commands to split the window and switch to the specified file or buffer. If you have two windows, they will switch file/buffer in the other window but keep the current window active. With three or more windows, you'll be prompted to choose a target window using ace.

Suggested bindings: C-x 4 M-x for ace-command-other-window, and then rebind C-x 4 f and C-x 4 b for the other two.

Update

My original approach was to use ace to switch to an arbitrary window and then call the desired command. This doesn't behave as you might expect. For example, if you call ace-find-file you end up starting in the default directory of the target window rather than the currently active one.

Here is an alternative implementation that flips things around to choose the target file/buffer first, then displays it in an ace-selected window.

(defun ace-switch-buffer ()
  "Switch to another buffer in another window."
  (interactive)
  (if (not (window-parent))
      (ido-switch-buffer-other-window)
      (let ((start-win (selected-window))
            (buf (ido-read-buffer "Buffer: "))
            (win (aw-select " Ace Buffer: ")))
        (unwind-protect
             (progn
               (aw-switch-to-window win)
               (switch-to-buffer buf))
          (aw-switch-to-window start-win)))))



(defun ace-find-file ()
  "Find a file and display it in another window."
  (interactive)
  (if (not (window-parent))
      (ido-find-file-other-window)
      (let ((start-win (selected-window))
            (buf (find-file-noselect (ido-read-file-name "File: ")))
            (win (aw-select " Ace File: ")))
        (unwind-protect
             (progn
               (aw-switch-to-window win)
               (switch-to-buffer buf))
          (aw-switch-to-window start-win)))))
glucas
  • 20,175
  • 1
  • 51
  • 83
  • 1
    I love these functions, but I found two problems: 1. They leave you with the wrong window selected if you `quit` (`C-g`). 2. They don't work right if you use the global (multi-frame) `ace-window` option to pick a window in a different frame. My fix is to add `unwind-protect` around the `select-window` and `ido-find-file` and to use `select-frame-set-input-focus` if `(not (eq (window-frame start-window) (window-frame new-win))`. – Colin Fraizer Feb 20 '15 at 20:52
  • I re-select the original frame as a "cleanup" of the `unwind-protect` and moved the `(aw-switch-to-window start-window)` to a cleanup too. – Colin Fraizer Feb 20 '15 at 20:59
  • Nice, I only tested with one frame. Note that `aw-switch-to-window` is already calling `select-frame-set-input-focus`, so you may not need that part. – glucas Feb 20 '15 at 21:12
  • The problem I fixed---probably inelegantly---was: if focus was in frame A and `aw-select` picked a window in frame B, ido would try to use frame B's minibuffer, but keyboard input went into frame A's selected window. A better solution---which I didn't work out---would be to use frame A's minibuffer for ido regardless. My versions of the functions are [here](https://gist.github.com/cfraizer/798447f23baf0ac0d491). – Colin Fraizer Feb 20 '15 at 21:20
  • Right -- if you use `aw-switch-to-window` it will also switch frames, so in my latest update I use that everywhere. – glucas Feb 20 '15 at 21:24
  • Nice. I like the new implementation. – Colin Fraizer Feb 20 '15 at 21:25
  • 1
    Just for grins, here's the variation I'm using now: https://gist.github.com/glucas/09aea018251cf85c7290 With this you can also call `ace-command-other-window` to interactively run an arbitrary command in another window. – glucas Feb 21 '15 at 19:38
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/21329/discussion-between-glucas-and-colin-fraizer). – glucas Feb 21 '15 at 19:39