tags:

views:

522

answers:

4

Scenario:

  • I start to type M-x to type a command
  • I switch to another emacs window/buffer because I realise I'm executing the command in the wrong window
  • I start to type M-x again to execute the command in the correct window

Result: I get the dreaded "Command attempted to use minibuffer while in minibuffer"

This happens to me multiple times a day while using emacs, and not just in this scenario. This behaviour is highly user-hostile (ref. Modes and Pseudo-modes in The Humane Interface by Jef Raskin)

Is there a way to customize emacs behaviour so that instead of giving this error, it just cancels the first minibuffer and replaces it with a new one?

+2  A: 

I'm not sure if there is such a customization, but the way I avoid this is hitting ctrl-g to cancel the command I was in the middle of writing in the minibuffer.

Brian Campbell
Thanks, I'm aware of ctrl-g and use it a lot, but I can't seem to incorporate it into my workflow in this scenario - I think the reason is that I don't want to cancel the command, just redirect it to another window.
EoghanM
+4  A: 

You can set the variable enable-recursive-minibuffers, which will prevent that error message from coming up. But it just enables multiple calls to the minibuffer - it doesn't redirect the current minibuffer's command to the new buffer. You can give this a try, but I think it'll be more confusing because the original action is still pending...

M-x is bound to 'execute-extended-command, and re-hosting (changing the original buffer) for that command is kind of like programming with continuation. i.e. you call a subroutine from location X, but instead of returning to X when done, you return to Y. I personally think it'd open up more confusion than it'd solve. But I understand the frustration (and know others who have the same frustration).

Trey Jackson
+3  A: 

Since my first answer doesn't directly give you what you want, I thought I'd come up with a real solution. This is what I have:

(defvar my-execute-extended-command-source-buffer nil
  "var holding the buffer to which the extended-execute-command should apply")
(defvar in-my-execute-extended-command nil
  "internal use - indicates whether we're in a 'recursive edit' of sorts")
(defun my-execute-extended-command (command)
  "home-grown version of execute-extended-command that supports re-hosting the buffer"
  (interactive (list (if in-my-execute-extended-command
                   nil
                 (let ((in-my-execute-extended-command t))
                   (setq my-execute-extended-command-source-buffer (current-buffer))
                   (completing-read "My-x " obarray 'commandp t nil 'extended-command-history nil nil)))))
  (if in-my-execute-extended-command
      (progn (setq my-execute-extended-command-source-buffer (current-buffer))
             (select-window (minibuffer-window)))
    (switch-to-buffer my-execute-extended-command-source-buffer)
    (call-interactively (symbol-function (intern command)))))

I've tested it this way. I bound it to a key (F10 in my case b/c I didn't want to lose M-x). Then, with two windows open, each showing a different buffer (say A and B):

  1. From window showing buffer A: F10 isearch-for
  2. Switch from minibuffer to window showing A: C-x o
  3. Switch from window showing A to that showing B: C-x o
  4. "re-host" the command from buffer B: F10
  5. Now back in the minibuffer, finish the command ward RET

When I started typing a search term, the search applied to buffer B.

This only replaces the M-x functionality, not the commands invoked from M-x. Also, this version does not support the prefix argument.

Hopefully this is what you want.

Trey Jackson
Thanks Trey, I'll bind this to M-x and see how I get on. I see one immediate problem that the after execution of the command at step 5, buffer A is replaced by buffer B (so now I have two B buffers in both windows). Maybe that's something quirky with my setup - you've given me enough to continue to develop an answer that suits me.
EoghanM
The buffer changing happens with me as well, I don't know of a way to fix that. I think the handling of `interactive` forces the command to originate from the window from which you start. But that's a guess. I wasn't able to solve that problem with `'save-window-excursion` (which was the first thing that came to mind).
Trey Jackson
+1  A: 

Can anyone improve on the following?

I've given up and just want to set \C-w to cancel any previous minibuffer before opening a new one (like doing \C-g\C-w)

So far thanks to Trey I've got:

(defun cancel-completing-read ()
  (if (> (minibuffer-depth) 0) (exit-minibuffer))
   (completing-read "My-x " obarray 'commandp t nil 'extended-command-history nil nil))

(defun cancel-and-execute-command (command)
  (interactive (list (cancel-completing-read)))
  (call-interactively (symbol-function (intern command))))

(global-set-key "\M-x" 'cancel-and-execute-command)

What command should I use in the place of exit-minibuffer above?

I've tried

keyboard-escape-quit
exit-minibuffer
keyboard-quit
EoghanM