1

I have this Elisp code that allows me to use ace-window to open a file in a selected window from Dired:

  (defun find-file-dired-ace-window ()
    "Use ace window to select a window for opening a file from dired."
    (interactive)
    (let ((file (dired-get-file-for-visit)))
      (if (> (length (aw-window-list)) 1)
          (aw-select "" (lambda (window)
                          (aw-switch-to-window window)
                          (find-file file)))
    (find-file-other-window file))))

This works well, however I find it annoying that the focus switches to the newly opened buffer. I would like the focus to stay in the Dired buffer.

I have thought to use save-window-excursion for this, however now the file does not get opened at all, so it is totally useless:

  (defun save-excursion-dired-ace-window ()
    "Use ace window to select a window for opening a file from dired."
    (interactive)
    (save-window-excursion
      (let ((file (dired-get-file-for-visit)))
        (if (> (length (aw-window-list)) 1)
            (aw-select "" (lambda (window)
                            (aw-switch-to-window window)
                            (find-file file)))
          (find-file-other-window file)))))

How can I can get the desired behavior?

Drew
  • 75,699
  • 9
  • 109
  • 225

5 Answers5

0

Don't use find-file (or find-file-other-window).

Use find-file-noselect to obtain a buffer visiting the file. Then use display-buffer-pop-up-window to display it in another window.

There are other buffer-display functions you can use, if you want to show it in some existing window. C-h f display-buffer tells you about them.

Dunno what aw-switch-to-window does. If that selects the window then maybe you don't want to do that. But the overall point is to use display-buffer (or one of its sidekicks), not find-file (or one of its sidekicks). And use find-file-noselect to get you the file-visiting buffer to display.

(defun find-file-dired-ace-window ()
    "Use ace window to select a window for opening a file from dired."
    (interactive)
    (let* ((file (dired-get-file-for-visit))
           (buf  (find-file-noselect file)))
      (if (> (length (aw-window-list)) 1)
             (aw-select "" (lambda (window)
                             (aw-switch-to-window window)
                             (display-buffer buf)
                             buf))
       (display-buffer-pop-up-window buf nil)))
Drew
  • 75,699
  • 9
  • 109
  • 225
  • Unfortunately this does not work. Whenever I select a buffer with this command I get the error "Invalid buffer", and I get switched to the selected window without the new buffer being opened. – Nicholas Hubbard Jun 10 '21 at 21:43
  • I had a typo: `file` instead of `buf` was passed to `display-buffer`. And maybe your `aw-select` expects its function arg to return a buffer (like `find-file` does). Try what I have now. If that doesn't help, then at least see if the other branch works, i.e., when `aw-window-list` returns less than 2. – Drew Jun 11 '21 at 00:27
0

This may be hacky but it works! I save the dired buffer at the beginning and then switch back to it right at the end.

 (defun find-file-dired-ace-window ()
    "Use ace window to select a window for opening a file from dired."
    (interactive)
    (let ((file (dired-get-file-for-visit))
          (dired-buf (current-buffer)))
      (if (> (length (aw-window-list)) 1)
          (progn
            (aw-select "" (lambda (window)
                            (aw-switch-to-window window)
                            (find-file file)))
            (let ((dired-window (get-buffer-window dired-buf)))
              (when dired-window
                (select-window dired-window))))
    (find-file-other-window file))))
0

Workflow is ...

  1. Declare your intention ...
  2. Do the command
  3. Pick the window
  4. Profit!

It is not "natural". May be it is just a question of getting used to this sort of workflow.


  1. Copy below snippet to ~/scratch.el
(package-initialize)

(require 'ace-window)

(ace-window-display-mode 1)

(define-key ctl-x-4-map "5" 'ace-window-prefix)

(defun ace-window-prefix ()
  "See `other-window-prefix."
  (interactive)
  (display-buffer-override-next-command
   (lambda (buffer _alist)
     "No clue what is happening here.  May require further tuning."
     (cons (let ((aw-dispatch-when-more-than 1)
         (aw-dispatch-always nil))
         (aw-select nil))
       'reuse))
   ;; Switch to window you started with
   (lambda (oldw neww)
     (unless (eq oldw neww)
       (select-window oldw)))
   "[ace-window]")
  (message "Display next command buffer in an existing window ..."))

  1. Quit Emacs. Restart with emacs -Q.

  2. C-x 4 C-f ~/scratch.el and do M-x eval-buffer RET

  3. C-x 4 C-f RET. This will open your Home directory, and place the cursor on the first file or dir there.

  4. Do C-x 4 5 RET.

    (Take care that there is no key presses between C-x 4 5 and RET. This is important.)

  5. ace-window will prompt you for the window to select. Select the window.

  6. The RET-ed file will be opened in the selected window.

  7. .... and you will be in whatever window you started with.

0

Here is another approach ... But before that, some remarks upfront.


The key function call you are looking for is aw-flip-window.


  1. The workflow with o (= my-aw-dired-find-file-other-window-noselect, which overrides the vanilla dired-find-file-other-window) is fairly intuitive.

    You pick the window, instead of Emacs picking some "random" window for you. This is great!

  2. The workflow with C-x 4 C-f (= my-aw-find-file-other-window-noselect, which overrides the vanilla find-file-other-window) is:

    • Select the window
    • Select the file

    Which again is quite intuitive. Great!

In both the workflows, the clutter is in the glue code.


  1. Copy the below snippet to ~/scratch-1.el
(require 'package)
(package-initialize)

(require 'ace-window)

(defmacro ace-window-do-other-window-noselect (fn1 fn2 &rest body)
  `(defun ,(intern (format "my-aw-%s-noselect" (symbol-name fn1))) ()
     (interactive)
     (let ((value (progn ,@body))
       (current-window (selected-window)))
       (cond
    ((> (length (aw-window-list)) 2)
     (aw-switch-to-window (aw-select nil))
     (cond
      ((null ',body)
       (call-interactively ',fn2))
      (t (funcall ',fn2 value))))
    (t (call-interactively ',fn1)))
       (unless (eq current-window (selected-window))
     (aw-flip-window)))))

(require 'dired)
(define-key dired-mode-map (kbd "o")
        (ace-window-do-other-window-noselect
         dired-find-file-other-window
         find-file (dired-get-file-for-visit)))

(global-set-key (kbd "C-x 4 C-f")
        (ace-window-do-other-window-noselect
         find-file-other-window
         find-file))

  1. Quit Emacs with C-x C-c, and restart with emacs -Q.

  2. C-x C-f ~/scratch-1.el, and do M-x eval-buffer.

  3. C-x 4 C-f ~/ RET. This will open your Home directory. Your cursor will still be in ~/scratch-1.el buffer.

    Note that the C-x 4 C-f used here is a redefined one.

  4. C-x o, and switch to dired buffer, that is displaying your Home dir.

  5. The cursor is on the first file / dir in your Home dir. Press o. It will open the chosen file / dir in another window.

  6. There is no magic so far. There are only 2 windows, and there is no confusion about what the other window is.

  7. C-x 3. There are 3 windows, and the same dired buffer is displayed in 2 different windows.

  8. Do a couple of C-ns and move to some other file/dir. This is so that you have variety of buffers. Press o. ace-window will prompt you for a window. Input the window choice.

  9. Note that the file / dir on which you pressed o, opened in window of your choosing, and you are still in the same window dired window you started with.

  10. Do a C-x 4 C-f. ace-window will prompt you for a window. Input the window choice.

  11. Now find-file will prompt you for the file name. Input, say ~/scratch-1.el.

  12. Note that the ~/scratch-1.el is in opened in selected window, and you are still in the same window you started with.

0

This is the TLDR version of my response here https://emacs.stackexchange.com/a/72468/31220.

Install the below snippet. The snippet overrides/redefines o in dired-mode-map, in such a way that you get to pick what the other window is. FWIW, o in dired-mode-map maps to dired-find-file-other-window. You can see that this is the pp-macroexpand-last-sexp version of whatever you see in that response.

(require 'package)
(package-initialize)

(require 'ace-window)
(require 'dired)
(define-key dired-mode-map (kbd "o")
            (defun my-aw-dired-find-file-other-window-noselect ()
              (interactive)
              (let ((value (dired-get-file-for-visit))
                    (current-window (selected-window)))
                (cond
                 ((> (length (aw-window-list)) 2)
                  (aw-switch-to-window (aw-select nil))
                  (funcall 'find-file value))
                 (t (call-interactively 'dired-find-file-other-window)))
                (unless (eq current-window (selected-window))
                  (aw-flip-window)))))