Posts tagged "general.el":

26 Jul 2023

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.

Tags: emacs general.el
09 Jul 2023

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.

Tags: emacs general.el
Other posts