Use-package: How to bind keys with use-package bind in mode maps?

Created on 3 Sep 2014  路  41Comments  路  Source: jwiegley/use-package

I would like to use use-package to bind keys in markdown-mode, but markdown-mode uses a strange markdown-mode-map variable to manage its keybindings. Is there a way to detect this oddity and work around it in use-package?

bind-key enhancement

Most helpful comment

The following now works:

(use-package term
  :bind (:map term-mode-map
         ("M-p" . term-send-up)
         ("M-n" . term-send-down)
         :map term-raw-map
         ("M-o" . other-window)
         ("M-p" . term-send-up)
         ("M-n" . term-send-down)))

All 41 comments

markdown-mode uses a strange markdown-mode-map variable to manage its keybindings.

It's an Emacs convention to use a major mode specific map, see Major Mode Conventions. You can set keys in the map by passing it to bind-key:

(use-package markdown-mode
  :defer t ; don't access `markdown-mode-map' until `markdown-mode' is loaded
  :config (bind-key "<some-key>" 'some-function markdown-mode-map))

@mcandre This feature will be coming shortly, and will probably look like this:

(use-package foo
  :bind (:map markdown-mode-map
            ("<some-key>" . some-function)))

Anything before a :map is global, anything after a :map applies to that keymap, multiple keymaps can be specified.

The above syntax should now work, as use-package is now using bind-keys under the hood, rather than just bind-key. It means you can do other nifty stuff too, like create your own prefix maps on the fly.

That syntax doesn't work for me. I get ":bind wants a string, (string . symbol) or list of these."

Oh, whoops, I didn't extend the normalization check yet.

Also, it's undocumented, as is bind-keymap.

Because I tried patch use-package to support the format (:map mapname (string . symbol)) but failed. So I wrote an extension to support it , you can use it like this, and when @jwiegley finished the work, you can migrate it easily.

What is the status on this?

Perhaps @jwiegley could mentor @bolasblack to get this fixed here upstream?

I started work on this, then it got shelved due to other pressing matters. I'd be happy to mentor someone to finish it up.

Binding multiple keys for a keymap should already work, we just need to fix the validator.

Actually, for now the easiest solution will be just to disable the validator. I can do that today.

This doesn't seem to have happened. I've done the change if you want it, but I suspect you've done it and just forgot to push :)

can you PR me? I'm not sure what state my version of this is in anymore.

Once I get it working, sure :) I thought it was working, but it's, er, not. I'll get back to it later tonight.

OK, that seems to work. PR #234 created.

Well, that didn't work at all. Should work better now: PR #235 doesn't break anything I've tried, allows your syntax, and does pretty much what you suggested (though the handler had to change too, to avoid considering non-list-elements to be commands that needed autoloading.)

:bind (:map map (("key" . function)("key" . function)("key" . function))) 

Seems to be working for me now.

I get
mapcar: Wrong type argument: listp, :map
when evaluating:

  :bind (("key" . function)
         :map map
         (("key" . function)
          ("key" . function)
          ("key" . function)))

@Bost you would need to separate out your global and :map entries as separate lists.

@Bost (I think!)

To define both global and map bindings it looks like you can use multiple :bind entries, like so:

(use-package counsel
  :bind (("M-x" . counsel-M-x)                           ; global
         ("C-x C-f" . counsel-find-file))
  :bind (:map help-map                                   ; help-map
              ("f" . counsel-describe-function)
              ("v" . counsel-describe-variable)
              ("C-l" . counsel-info-lookup-symbol)))

This is also meant to work (though may not today):

(use-package counsel
  :bind (("M-x" . counsel-M-x)
         ("C-x C-f" . counsel-find-file)
         :map help-map
         ("f" . counsel-describe-function)
         ("v" . counsel-describe-variable)
         ("C-l" . counsel-info-lookup-symbol)))

I get mapcar: Wrong type argument: listp, :map with a config similar to the one above:

(use-package swiper                     ; Isearch with an overview
  :ensure t
  :bind (("C-c s s" . swiper)
         ("C-c s S" . swiper-all)
         :map isearch-mode-map
         ("M-i"     . swiper-from-isearch)))

@manuel-uberti It just hasn't been implemented yet.

Oh I see. Forget my comment, then. :-)

@jwiegley is anyone working on this/are there plans to work on this?

The following now works:

(use-package term
  :bind (:map term-mode-map
         ("M-p" . term-send-up)
         ("M-n" . term-send-down)
         :map term-raw-map
         ("M-o" . other-window)
         ("M-p" . term-send-up)
         ("M-n" . term-send-down)))

Next step is to autoload the keymaps.

@jwiegley Dang you have nice timing. I was just looking for this to setup some evil-mode "state" specific bindings :smile:

Thanks a lot, John!

Just what I was looking for. Thanks John!

great work!

:+1:

@jwiegley

Next step is to autoload the keymaps.

I take it that's not done yet? Would that mean that, for example, right now when I try something like this:

(use-package evil
  :bind (:map evil-emacs-state-map
              ("C-w" . evil-window-map))
  ...)

I get an error saying that the symbol evil-emacs-state-map isn't defined or something. I imagine because it's trying to add it to the map before evil has even loaded? Is that what your quote, once implemented, would resolve?

I get the same with as @blaenk - it looks like it's trying to add bindings to maps before they are defined. I tried using :defines to fix it but it didn't make any difference.

Ditto.

(use-package python
  :bind
  (:map python-mode-map ("C-c C-c" . tvaughan/python-mode-map)))

Does not work.

I forgot to mention that after encountering this issue I found out about general.el which is more or less an alternative to bind-key. I suggested to the author of it to implement use-package integration and he was gracious enough to do so. For my uses general.el is much more useful and indeed handles this situation perfectly.

So something like what @carrete wrote might look like this with general:

(use-package python
  :general
  (:keymaps 'python-mode-map
    "C-c C-c" 'tvaughan/python-mode-map))

You can also do the reverse, where you essentially "mount" python's keymap to another bind like C-x p for python, i.e. you press C-x p and you would then be "within" python-mode-map. The problem there is that python-mode-map won't exist yet because it's deferred/autoloaded. You can make it so that pressing C-x p triggers the autoload of python-mode (and thus python-mode-map) on-the-fly by doing something like this:

(use-package python
  :general
  ("C-x p" '(:keymap python-mode-map))

general.el sounds like a nice alternative for those who need support that bind-key doesn't presently offer.

Thanks for the tip @blaenk!

I don't know, I don't use flycheck to check emacs-lisp code.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

endoffile78 picture endoffile78  路  11Comments

jtecca picture jtecca  路  9Comments

sheepduke picture sheepduke  路  3Comments

cswarth picture cswarth  路  5Comments

komolovf picture komolovf  路  8Comments