Posts tagged "general.el":
Making keymaps prettier with general.el
After my previous post on defining keymaps using general.el I revisited some of
my setup in order to make some package keymaps prettier when displayed by
which-key. The keymaps defined by packages are typically don't contain any
description of the bindings, so which-key
ends up displaying the name of the
function bound to the key. It's not always easy to remember what a function does
based just on its name, and sometimes the names are so long that which-key
cuts the name short and all you see is a bunch of keybindings for seemingly the
same function.
The only remedy I've found is to re-define the keybindings decorating them with descriptions that (hopefully) are easier to understand than the function name. (At least, this way I only have myself to blame if it's a bad description.)
One such keymap that I use somewhat frequently, and where the function names
often confuse me is evil-mc-key-map
from evil-mc. The keymap is bound at g .
in evil's normal and visual modes, and it also contains a few bindings using
control (C-
) and meta (M-
) which I think would be better placed under
sub-keymaps.
I decided to group the "make and go" functions under g . m
and the "skip and
go" function under g . s
. The rest I'm just giving descriptions. general.el
makes it easy both to overwrite already existing bindings, but adding
descriptions, and to add new ones, all in one call to general-def
:
(general-def evil-mc-key-map :states '(normal visual) "g.A" '("make end sel" . evil-mc-make-cursor-in-visual-selection-end) "g.I" '("make beg sel" . evil-mc-make-cursor-in-visual-selection-beg) "g.a" '("make all" . evil-mc-make-all-cursors) "g.q" '("undo all" . evil-mc-undo-all-cursors) "g.u" '("undo last" . evil-mc-undo-last-added-cursor) "g. RET" '("make here" . evil-mc-make-cursor-here) "g.p" '("pause" . evil-mc-pause-cursors) "g.r" '("resume" . evil-mc-resume-cursors) "g.m" '(:ignore t :wk "make & go") "g.m$" '("to last cur" . evil-mc-make-and-goto-last-cursor) "g.m0" '("to first cur" . evil-mc-make-and-goto-first-cursor) "g.mC" '("to prev cur" . evil-mc-make-and-goto-prev-cursor) "g.mc" '("to next cur" . evil-mc-make-and-goto-next-cursor) "g.mh" '("to prev match" . evil-mc-make-and-goto-prev-match) "g.mj" '("to next line" . evil-mc-make-cursor-move-next-line) "g.mk" '("to prev line" . evil-mc-make-cursor-move-prev-line) "g.ml" '("to next match" . evil-mc-make-and-goto-next-match) "g.s" '(:ignore t :wk "skip & go") "g.sC" '("to prev cur" . evil-mc-skip-and-goto-prev-cursor) "g.sc" '("to next cur" . evil-mc-skip-and-goto-next-cursor) "g.sh" '("to prev match" . evil-mc-skip-and-goto-prev-match) "g.sl" '("to next match" . evil-mc-skip-and-goto-next-match))
Finally I want to remove the bindings that weren't overwritten. general.el
makes that easy too with general-undbind
:
(general-unbind '(normal visual) evil-mc-key-map "g.$" "g.0" "g. C-n" "g. C-S-n" "g. C-u" "g. C-S-u" "g. C-p" "g. C-r" "g. M-N" "g. M-n" "g.N" "g.O" "g.n" "g.o")
Yes, this is a bit of work, and so far I've only done this for a few keymaps (those containing bindings I use frequently and/or find difficult to remember). So far I've found it worth the cost.
general.el and two ways to define keybindings
When I abandoned spacemacs I really wanted to duplicate its keybindings using
SPC
as leader key and per-mode bindings available by pressing ,
. I found a
nice setup using general.el in Tianshu Wang's Emacs config. I made only minor
modification and ended up with the following setup.
(use-package general :after (evil evil-easymotion) :config (general-evil-setup) (general-auto-unbind-keys) (general-define-key :states '(normal insert motion visual emacs) :keymaps 'override :prefix-map 'tyrant-map :prefix "SPC" :non-normal-prefix "M-SPC") (general-create-definer mes/tyrant-def :keymaps 'tyrant-map) (mes/tyrant-def "" nil) (general-create-definer mes/despot-def :states '(normal insert motion visual emacs) :keymaps 'override :major-modes t :prefix "," :non-normal-prefix "M-,") (mes/despot-def "" nil) (general-def universal-argument-map "SPC u" 'universal-argument-more))
One slightly surprising thing I found out is that two different ways to define keybindings can be used, one seems to work on both top and mode level, the other only on mode level.
Top-level keybindings (SPC
)
At the top-level, i.e. when using mes/tyrant-def
, I need to use a cons
based
configuration. This is part of my top-level keybindings:
(mes/tyrant-def "SPC" '("M-x" . execute-extended-command) "TAB" '("latest buffer" . mode-line-other-buffer) "!" 'shell-command "/" '("search" . consult-ripgrep) "u" 'universal-argument "b" (cons "bufs" (make-sparse-keymap)) "bb" '("switch" . consult-buffer) "bc" '("close" . kill-this-buffer) "be" '("erase" . erase-buffer) "bs" '("scratch" . scratch-buffer) ;; .... )
When it's done this way which-key picks up the descriptive strings.
It did take me a while to figure out that this config style was necessary. Again Tianshu Wang's Emacs config was what led me right.
Mode-level keybindings (,
)
At the mode-level, i.e. using mes/despot-def
, I've found that I can, almost
always, define keybindings following the documentation of general.el. Here's the
keybindings I have for nix-mode
:
(mes/despot-def nix-mode-map "=" '(:ignore t :wk "format") "=b" '(nix-format-buffer :wk "buffer"))
Just the other day I found that AUCTeX must be doing something with it's keymaps
and I have to use the cons
-based configuration style for its keymaps.