1

I like binding M-n to make-frame, so I have the following in my init file:

(bind-key* "M-n" 'make-frame)

The * form of bind-key allows me to override other minor-mode M-n bindings. But when I'm using the minibuffer (e.g. find-file), I'd rather M-n retained it's original behavior and ran next-history-element.

I'm currently using a workaround, but I'm hoping there's a better solution.

(defun ivan-unbind-make-frame () (bind-key* "M-n" nil))
(defun ivan-rebind-make-frame () (bind-key* "M-n" 'make-frame))

(add-to-list 'minibuffer-setup-hook #'ivan-unbind-make-frame)
(add-to-list 'minibuffer-exit-hook  #'ivan-rebind-make-frame)

Another approach I considered was to create a keymap of the minibuffer bindings I want to take precedence over bind-key* bindings and put it in an entry at the front of emulation-mode-map-alists.

(defvar overriding-minibuffer-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "M-n") #'next-history-element)
    map))

(defvar minibuffer-active-p nil)

(add-to-list 'emulation-mode-map-alists
             `((minibuffer-active-p . ,overriding-minibuffer-map)))

(defun minibuffer-active-on  () (setq minibuffer-active-p t))
(defun minibuffer-active-off () (setq minibuffer-active-p nil))

(add-to-list 'minibuffer-setup-hook #'minibuffer-active-on)
(add-to-list 'minibuffer-exit-hook  #'minibuffer-active-off)

This is more complicated and still doesn't free me from using minibuffer setup/exit hooks (unless there's an existing variable that reflects whether the minibuffer is active, but I couldn't find one). It also makes the assumption that M-n runs next-history-element in all minibuffer use-cases, and I'm not sure that's true.

Is there a better way to handle this, or should I stick with my current solution?

ivan
  • 1,928
  • 10
  • 20

1 Answers1

4

You can use the PREDICATE argument of bind-key*

(bind-key* KEY-NAME COMMAND &optional PREDICATE)

Similar to ‘bind-key’, but overrides any mode-specific bindings.

(bind-key KEY-NAME COMMAND &optional KEYMAP PREDICATE)
[...]

If PREDICATE is non-nil, it is a form evaluated to determine when
a key should be bound. It must return non-nil in such cases.[...]

Therefore (bind-key* "M-n" 'make-frame (not (minibufferp))) should do what you want.

The predicate parameter is a wrapper around Emacs' filtered keybindings (see (elisp) Extended Menu Items, Define context-aware keys in Emacs, Elisp: Conditionally change keybinding).

npostavs
  • 9,033
  • 1
  • 21
  • 53
  • Wow, that's perfect (I should've rtfd :D) -- thanks. I'm actually using `bind-keys*` (plural), so I ended up with: `(bind-keys* :filter (not (minibufferp)) ("M-n" . make-frame))`. – ivan Apr 16 '17 at 20:55