2

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?

1

5 Answers 5

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)))
2
  • 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. Jun 10, 2021 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, 2021 at 0: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)))))


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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