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?
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.
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.
Most helpful comment
The following now works: