A general is a leader. – onioncheese
About
general.el provides a more convenient way to bind keys in emacs for both evil and non-evil users. general-define-key allows defining multiple keys at once, implicitly wrapping key strings with (kbd ...), having named prefix key sequences (like the leader key in vim), and much more.
This package was initially created due to frustration with the popularity of evil-leader despite its strange/poor design and implementation (e.g. unnecessarily relying on a global minor mode, unnecessarily re-implementing some of evil-define-key, not allowing the user to choose the specific states keys are bound in, having a weird syntax and implementation of mode-specific keybindings, etc.). However, this package is now more comparable to bind-key (only with a lot more functionality).
Key Features
- Provides a single function,
general-define-key, that is usable for all key definition; wrappers are provided as well - Does not hide important details of key definition (unlike
evil-leader.el); users should be familiar withdefine-keyand other definers (e.g.evil-define-key(*)for evil users) before using this package - Uses a syntax similar to
setqfor key definitions (likeevil-define-key,bind-map,evil-leader.el, etc.; unlikebind-key) - Provides tight (and optional) integration with evil (unlike
bind-key) general-defcan act as a drop-in replacement for the following definers (see the documentation below for a minor caveat) (unique):general-define-keyandglobal-set-key(no positional keymap argument)define-keyandevil-global-set-key(positional argument for keymap)evil-define-key(positional argument for state and keymap)
- With the
:definerkeyword,general-define-keycan be extended to wrap any key definition function (e.g.evil-define-minor-mode-key,lispy-define-key, etc.) (unique) - Allows bindings keys in multiple keymaps/states at once (unlike
bind-key) - Automatically wraps string keys and definitions with
kbd(this behavior can be turned off for compatibility withdefine-key) - Allows using an arbitrary number of prefix keys or “leaders” of any length (but does not require prefix keys like) (unlike
evil-leader.el) - Allows for automatically creating prefix commands (but does not require creating them like
bind-keydoes) - Allows for buffer-local keybindings (unlike
local-set-key) - Allows deferring keybindings until the specified keymap exists (no need to use
(with-)eval-after-load) (likeevil-define-key) - Allows displaying defined keys (like
bind-key.el) - Provides integration with other packages such as
key-chord.elandwhich-key.el(unique) - Provides other helpers for keybindings (unique):
- A method for creating “autoloaded” keymaps (like
bind-key.el) - A potentially better way to simulate keypresses (works with prefix args and for incomplete key sequences, i.e. a key bound to a keymap)
- A method for binding under non-prefix keys with an optional timeout (like in vim; e.g. bind
jkin insert mode without losingj) - A helper to create a menu item to dispatch to different definitions based on predicates
- A method for creating “autoloaded” keymaps (like
- Provides other helpers for configuration (e.g. more convenient functions for hooks and advice)
- Is well tested (unlike
bind-key.elandevil-leader.el)
Basic Setup
Since general provides variables for defaults to the various keyword arguments, you can use it in a very specific way. For example, you could set general-default-prefix to a prefix of your choosing and only use it for defining prefix keys. I personally don’t change any of these variables and use it in a more general way for all my keybindings. The main advantage of using general-define-key (or a wrapper for it) even in cases where its extra functionality may be not used is that all keybindings are recorded and can be displayed later with general-describe-keybindings.
Non-evil
(require 'general)
(setq my-leader1 "C-c")
;; without :keymaps, general-define-key acts similarly to global-set-key
;; bind "C-c a" and "C-c b" globally
(general-define-key :prefix my-leader1
"a" 'some-command
"b" 'another-command)
;; or without a prefix
(general-define-key
"C-c a" 'some-command
"C-c b" 'another-command)
;; bind a key in a specific keymap (keymaps must be quoted)
(general-define-key :keymaps 'org-mode-map
"TAB" 'org-cycle)
;; if you prefer an explicit (kbd) or don't want (kbd) at all:
(setq general-implicit-kbd nil)
(general-define-key
(kbd "C-c a") 'some-command
(kbd "C-c b") 'another-command)Evil
(require 'general)
;; bind a key globally in normal state; keymaps must be quoted
(setq general-default-keymaps 'evil-normal-state-map)
;; bind j and k in normal state globally
(general-define-key
"j" 'evil-next-visual-line
"k" 'evil-previous-visual-line)
;; bind gj and gk
(general-define-key :prefix "g"
"j" 'evil-next-line
"k" 'evil-previous-line)
;; named prefix key
(setq my-leader1 ",")
(general-define-key :prefix my-leader1
"f" 'find-file)
;; a default prefix sequence
(setq general-default-prefix ",")
(general-define-key "f" 'find-file)
;; bind a key in multiple states
(general-define-key :keymaps 'org-mode-map
:states '(insert emacs)
"<tab>" 'org-cycle)Vim-like definitions:
(general-evil-setup)
;; all keywords arguments are still supported
(general-nmap :prefix "SPC"
"p" 'helm-mini)
;; bind in motion state (inherited by the normal, visual, and operator states)
(general-mmap "j" 'evil-next-visual-line
"k" 'evil-previous-visual-line)
;; alternatively, for shorter names
(general-evil-setup t)
(mmap "j" 'evil-next-visual-line
"k" 'evil-previous-visual-line)More Details
This package provides one main function, general-define-key, for key definitions for both evil and non-evil users. If you do not like keyword arguments or would like to create your own key-defining functions, this package also allows for these things.
Settings and Keyword Arguments
general-implicit-kbd can be set to nil if you want to manually use (kbd "keys") or if you don’t want to use kbd at all.
general-default-prefix, general-default-states, and general-default-keymaps determine the defaults for the corresponding keyword arguments :prefix, :states, and :keymaps. By default, there is no prefix or state (each is nil), and the keymap is (quote global). Each keymap can either be a quoted keymap or (quote global) or (quote local). When the keymap is local, the key will be bound only in the current buffer (see Buffer Local Keybindings). When the keymap is global, the key will be bound in (current-global-map).
general-default-states and general-default-keymaps can be lists or a single element, allowing the user to define keys for multiple evil states or keymaps simultaneously. This can be useful in certain situations to prevent redundant keybindings.
Using a different prefix for the insert and emacs states (or any state in general-non-normal-states) can be done with :non-normal-prefix or :global-prefix. By default, :prefix will apply to all keys, but if one (or both) of the other prefix keywords is specified, :prefix will only apply to evil states not listed in general-non-normal-states. This is also the case for the global evil keymaps such as evil-normal-state-map. :non-normal-prefix will always only apply to the non-normal states. :global-prefix will always apply to all keys. The corresponding default variables are general-default-non-normal-prefix and general-default-global-prefix. For example, this command will bind SPC / to swiper in normal state and M-SPC / to swiper in emacs and insert state:
(general-define-key :states '(normal insert emacs)
:keymaps 'text-mode-map
:prefix "SPC"
:non-normal-prefix "M-SPC"
"/" 'swiper)If you would like to create a named prefix keymap for your prefix keys, you can also specify :prefix-command which will be passed to define-prefix-command. All prefix keys will then be bound to the prefix command in the correct keymaps. You can additionally specify :prefix-map and :prefix-name (which will be passed as the last two arguments to define-prefix-command).
(general-define-key :states '(normal insert emacs)
:keymaps 'text-mode-map
:prefix "SPC"
:non-normal-prefix "M-SPC"
:prefix-command 'my-prefix-command
:prefix-map 'my-prefix-map
"/" 'swiper)General is flexible in allowing you to choose how you write things, so if the above would be something you’d use often, you could create a function with the above state and prefix keyword arguments as defaults using general-create-definer and write the definition like this:
(my-normal-and-insert-define-key "/" 'swiper)The :infix keyword can be used to sandwich keys in between all of the specified prefix keys and the keys in each mapping. This is mainly useful when using multiple prefix keywords and especially when using wrappers. For example, if you wanted to define several keys that were prefixed with SPC g in normal state and M-SPC g in insert state, you could use the previous wrapper with :infix instead of re-specifying both :prefix and :non-normal-prefix:
(my-normal-and-insert-define-key :infix "g" <maps...>)There is also a :predicate keyword for giving a condition under which a map should be active.
Displaying Keybindings
General keeps track of all your keybindings and allows presenting them as tables in an org buffer using general-describe-keybindings. By default, they will be displayed in this order:
- Buffer local keybindings (i.e.
:keymaps 'local) - Global keybindings (i.e.
:keymaps 'global) - Global evil keybindings (e.g.
:keymaps 'evil-normal-state-map) - Other keybindings
Within these categories keymaps, states, and keybindings will be presented in the order they were created in. For each keybinding created, this command will display the key, the definition, and the previous definition. The previous definition will only be updated when the definition changes by default. To have it only be updated when the key was previously unbound, the user can set general-describe-update-previous-definition to nil.
The order in which keybindings are displayed is customizable. All keymaps listed in general-describe-priority-keymaps will be displayed first. The rest can optionally be sorted by setting general-describe-keymap-sort-function (nil by default). The order evil states are displayed in can be altered either by changing general-describe-state-sort-function or changing the order of states in general-describe-evil-states. Keybindings can also be sorted if the user sets general-describe-keybinding-sort-function. Here is an example that will sort everything alphabetically:
(setq general-describe-priority-keymaps nil
general-describe-keymap-sort-function #'general-sort-by-car
general-describe-state-sort-function #'general-sort-by-car)
;; sort keybindings alphabetically by key
(setq general-describe-keybinding-sort-function #'general-sort-by-car)
;; sort keybindings alphabetically by definition
(setq general-describe-keybinding-sort-function #'general-sort-by-cadr)For reference, keybindings are stored in an alist. Here is what is passed to each sorting function:
;; `general-keybindings' - an alist of keymap to state alist
;; passed to `general-describe-keymap-sort-function'
((keymap-name . state-alist) ...)
;; a state alist (state name is nil if there is no state)
;; passed to `general-describe-state-sort-function'
((state-name . keybindings) ...)
;; the list of keybindings is passed to `general-describe-keybinding-sort-function'
(("key after kbd applied" 'def 'previous-def) ...)To actually change how the keybinding table is printed, the user could override general--print-map.
Positional Argument Wrappers
When you’re defining keys in specific keymaps and states, using positional arguments can be shorter. General has two macros that can basically act as drop-in replacements for define-key and evil-define-key. They are general-emacs-define-key and general-evil-define-key. These are simply wrappers for general-define-key that pass the positional arguments to the corresponding keywords. However, for compatibility with define-key and evil-define-key, it is not necessary to quote keymaps. Both keymaps and states can be left quoted or unquoted (regardless of whether they are lists).
For example, the following are equivalent:
(general-define-key :keymaps 'org-mode-map
"M-n" 'org-next-visible-heading
"M-p" 'org-previous-visible-heading)
(general-emacs-define-key org-mode-map
"M-n" 'org-next-visibl-heading
"M-p" 'org-previous-visible-heading)
;; rough equivalent with define-key
(with-eval-after-load 'org-mode
(define-key org-mode-map (kbd "M-n") 'org-next-visible-heading)
(define-key org-mode-map (kbd "M-p") 'org-previous-visible-heading))And the following are equivalent:
(general-define-key :states '(normal visual)
:keymaps 'org-mode-map
"gj" 'org-next-visible-heading
"gk" 'org-previous-visible-heading)
(general-evil-define-key '(normal visual) org-mode-map
"gj" 'org-next-visible-heading
"gk" 'org-previous-visible-heading)
;; equivalent with evil-define-key
(evil-define-key '(normal visual) org-mode-map
"gj" 'org-next-visible-heading
"gk" 'org-previous-visible-heading)The actual behavior of these two macros is the same as general-define-key. You can still use general-define-key’s keyword arguments after the positional arguments (however, :keymaps and :states will not override the positional arguments):
;; these are both valid
(general-emacs-define-key 'global
:prefix "C-c"
"/" 'swiper)
(general-evil-define-key 'normal org-mode-map
:prefix "SPC"
"g" 'worf-goto)As for global-set-key and global-evil-set-key, wrappers are not needed. By default general-define-key acts like global-set-key, and general-emacs-define-key can also act like global-evil-set-key using the symbols for evil’s states.
A third macro, general-def, is provided for those who would prefer to use a single, succinctly named definer for all of the previous cases. It will act the same as general-define-key, general-emacs-define-key, or general-evil-define-key depending on the number of positional arguments.
;; use general-define-key
(general-def
"key" 'def
...)
;; use general-emacs-define-key
(general-def org-mode-map
"key" 'def
...)
(general-def 'normal
"key" 'def
...)
;; use general-evil-define-key
(general-def 'normal org-mode-map
"key" 'def
...)Note that if you want to use variables to hold keys (e.g. key-var 'def), you should use general-define-key (if those were the first arguments to general-def, it would consider them a state and keymap). Doing this isn’t recommended and probably isn’t useful. If you want to use a variable specifically with :prefix or another keyword argument, that is still supported by general-def.
Note for Evil Users
When :states is specified (or general-default-states is non-nil), general-define-key will act as a wrapper around evil-define-key. This means that the following are equivalent:
(general-define-key :states '(normal visual)
"j" 'my-j)
(evil-define-key '(normal visual) (current-global-map)
"j" 'my-j)In general, you should avoid using :states like this if you don’t have a reason to. The standard way to define global keybindings for an evil state is as follows:
(define-key evil-normal-state-map "j" 'my-j)
(define-key evil-visual-state-map "j" 'my-j)
;; or
(evil-global-set-key 'normal "j" 'my-j)
(evil-global-set-key 'visual "j" 'my-j)These are the equivalents with general.el:
(general-define-key :keymaps '(evil-normal-state-map evil-visual-state-map)
"j" 'my-j)
;; using the shorthand symbols
(general-define-key :keymaps '(normal visual)
"j" 'my-j)
;; using a vim definer
(general-evil-setup)
(general-nvmap "j" 'my-j)Note that keybindings made for normal state in the global map will override keybindings made for evil-normal-state-map. The keybinding may not change immediately if you do this in a buffer though. The potentially practical use for binding in the global map is to prevent certain keys from being overriden. If you are using general-def (or a definer built on general-def), you will need to specify the =’global= if you want this behavior:
(general-def 'normal 'global ...)
;; or
(general-nmap 'global ...)Keymap/State Aliases
To prevent the need to type out long keymap names like evil-inner-text-objects-map, general allows the user to specify shorthand names for keymaps by altering general-keymap-aliases (and for states by altering general-state-aliases). These are alists of either an alias or a list of aliases to the full keymap name:
(push '(help . help-map) general-keymap-aliases)
;; or
(push '((h help) . help-map) general-keymap-aliases)
;; now
(general-define-key :keymaps 'help ...)
;; is the same as
(general-define-key :keymaps 'help-map ...)By default, the global evil state and text object keymaps have aliases. This allows for using the same syntax as evil-global-set-key and evil-define-key:
(general-define-key :keymaps 'motion ...)
;; or
(general-define-key :keymaps 'm ...)Note that this is different from using :states 'motion (see Notes for Evil Users). See general-keymap-aliases for all default aliases.
All keymap symbols are immediately processed by general--unalias. By overriding this function, it would be possible to, for example, automatically append -map or -mode-map to keymap names that don’t end in -map or do something more complicated to create a generic shorthand without having manually specify all aliases. This is not recommended as it could potentially become confusing (and would currently break :definer 'minor-mode), but if anyone would find this useful, feel free to make an issue, and I’ll consider adding it as an option.
Vim-like Wrappers
general-evil-setup is used to generate key definition functions that are named similarly to vim’s. Currently, the following functions are created:
general-imapgeneral-emapgeneral-nmapgeneral-vmapgeneral-omapgeneral-mmapgeneral-rmapgeneral-otomapgeneral-itomapgeneral-iemapgeneral-nvmapgeneral-tomap
These are wrappers around general-def that set a default :keymaps (or a default :states). You can see the help text for each for a more specific description. general-evil-setup takes two optional arguments. If the first is non-nil, shorter aliases for these functions such as nmap will be created. If the second is non-nil, the function will default to setting :states (if a default exists) and using the global map instead of :keymaps (see Notes for Evil Users). This can be altered later by setting general-vim-definer-default.
:states will automatically be set instead of :keymaps if the user manually specifies keymaps and a default for :states was specified (there is no inner text object state, for example):
;; define in evil-normal-state-map (set default :keymaps)
(general-nmap ...)
;; define in the normal state auxiliary map for org-mode-map (set default :states)
(general-nmap org-mode-map ...)
;; or
(general-nmap :keymaps 'org-mode-map ...)If there is some other combination you would like to use, you can use general-create-vim-definer or general-create-dual-vim-definer.
Override Keymaps and Buffer Local Keybindings
General.el provides the equivalent of bind-key’s override-global-map as general-override-mode-map (keymap alias is =’override=). When general-override-mode is enabled, keys bound in general=override-map will take precedence over keys bound in any other minor mode keymaps.
Note that binding directly in general-override-mode-map is only useful for non-evil keybindings. Evil keybindings already override almost all normal keybindings using the same method used here. On the other hand, if you want to override evil keybindings, you have a few options. For reference, first review the precedence for evil keymaps. If you want a global evil keybinding to not be overridden by any evil overriding maps (used by evil-integration.el for some modes by default), you can use the previously mentioned method and bind that key in an auxiliary keymap since auxiliary maps have precedence over overriding maps (which in turn have precedence over the normal global evil keymaps). Doing this is mainly useful if you want to use evil-make-overriding-map for specific modes but want to prevent certain global keys from ever being overwritten (e.g. a prefix key for window/file/buffer management). However, if you use evil packages that make keybindings with evil-define-key, this method is not sufficient. If you want your global keybinding to not be overridden by keybindings in any auxiliary maps, you can use an intercept keymap. You can make any keymap an intercept keymap, but it may be convenient to just use general-override-mode-map for this purpose since the necessary setup (evil-make-intercept-map) has already been performed:
(general-define-key
:states 'normal
:keymaps 'override
...)
;; has precedence over
(general-define-key
:states 'normal
:keymaps 'org-mode-map)General also provides a local equivalent called general-override-local-mode which is used to add support for buffer-local keybindings (with higher precedence than minor mode keybindings) by specifying :keymaps 'local. Unlike with the global override mode, :keymaps 'local should always be used instead of the actual keymap name since :keymaps 'local will cause general.el to automatically turn on the corresponding minor mode and perform some necessary extra setup. Note that this is not the same as using local-set-key (which will bind the key for the current buffer’s major mode). When :states is specified with :keymaps 'local, evil-local-set-key will be used instead.
Predicates
The user can use the :predicate keyword to specify a condition under which the map(s) should be active. For example:
(general-define-key :keymaps 'local
:predicate '(eobp)
"<right>" 'beginning-of-buffer)<right> will now behave normally except at the end of the buffer where it will jump to the beginning of the buffer. Note that with :predicate, you can still only have a key bound once in a single keymap. If you want to have a key take different actions depending on conditions in a single keymap, see Choosing Definition Based on Predicates.
See this post for more information about how this works.
Functions/Macros to Aid Key Definition
Simulating Keypresses
General provides a macro called general-simulate-keys that can be used to simulate a key sequence. In some cases, this can be used similarly to a keyboard macro, but it has some advantages. Unlike with a keyboard macro, prefix arguments will work for the command the key simulates. The key simulated does not have to correspond to the full key sequence for a command. In these cases which-key will show the keys bound under the simulated prefix. For example:
(general-nmap "SPC" (general-simulate-keys "C-c"))general-simulate-key can take an optional argument (for use with evil only) that will cause the keys to be simulated in emacs state. This allows something like the following to work:
(general-nmap "j" (general-simulate-keys "C-n" t))The key section can also be replaced by a list of a command and keys (e.g. (general-simulate-keys (#'evil-delete "iw"))). See the next section for a reasonable use case for this feature.
Also note that general-simulate-keys creates a named function with a docstring, so which-key and describe-key will work properly for keys bound to a command created with it. The automatically generated function name and docstring can be replaced with optional arguments:
(general-nmap "SPC" (general-simulate-keys
"C-c" t
"Simulate C-c in emacs state with SPC."
general-SPC-simulates-C-c))Make sure that you don’t bind a key to simulate itself (e.g. (general-emap "C-n" (general-simulate-keys "C-n" t))) as it wouldn’t do anything (and would cause an infinite loop).
Another thing to be aware of is that if a command name is not specified, the resulting general-simulate-... command will always be repeated with evil-repeat (since the name of the command that will end up running is not necessarily known). If you would like to change this, you can use evil-declare-not-repeat with the name of the resulting simulate command. On the other hand, if a command name is specified, the simulate command will be repeated depending on the repeat property of that command.
Mapping Under Non-prefix Keys
This functionality is mainly targeted at evil users, but it could potentially be useful for non-evil users as well. In vim you can bind something like cow without a problem. With evil, c is bound to evil-change, so you can’t bind directly to cow. A workaround for this case is to bind a key in evil-operator-state-map, but this won’t work, for example, if you wanted to bind ctb or cw to something special. I’ve come up with a more general workaround, general-key-dispatch. Consider the following example:
(general-nmap "c"
(general-key-dispatch 'evil-change
"ow" 'toggle-word-wrap
"w" (general-simulate-keys ('evil-change "iw"))
"tb" 'some-command
"c" 'evil-change-whole-line
;; could be used for other operators where there
;; isn't an existing command for the linewise version:
;; "c" (general-simulate-keys ('evil-change "c"))
))
(general-vmap "c" 'evil-change)In this example, the function created will execute any of the mapped key sequences or fall back to evil-change. For example, ow is mapped, so cow will run toggle-word-wrap. On the other hand, b is not mapped, so cb will act the same as cb would by default. Counts and repeating should still work for both the mapped keys and fallback command. Because evil handles cc differently (since c is not a motion), c must be explicitly bound to evil-change-whole-line (or to simulate “cc”) to keep its behavior. In visual state, c is not actually bound by default and will use the normal state command, so to keep c working the same in visual state, you should explicitly bind it to evil-change.
Another thing to note is that you can’t bind a key in the general-key-dispatch section to simulate the base key (in this case c). For this example, you cant’t bind w to (general-simulate-keys "ciw"). While this won’t cause an infinite loop, it won’t work either, so you have to use the command name instead. Also, if you use a count in the middle (i.e c2w), it will act as c2w and not c2iw. If anyone cares about this, I could probably add an option to allow changing the count in the middle without immediately falling back to the default command.
Another possible use is to emulate vim’s imap. For example, you can recreate the common jk to <esc> keybinding:
(general-imap "j"
(general-key-dispatch 'self-insert-command
"k" 'evil-normal-state))If you plan on using more than one of these with self-insert-command, you’ll need to use the :name keyword argument to prevent the newly created functions from clobbering each other.
Commands created in this way now support an optional timeout, meaning you could still insert jk (without C-q / quoted-insert) like with key-chord.el:
(general-imap "j"
(general-key-dispatch 'self-insert-command
:timeout 0.25
"k" 'evil-normal-state))If you are using general-key-dispatch with a timeout to mirror a prefix key’s bindings in insert state, it may also be convenient to use the :inherit-keymap keyword. This allows using all your prefix keybindings without the need to re-specify them all in the general-key-dispatch:
(general-nmap :prefix ","
:prefix-command 'my-prefix-map
"g" #'magit-status)
(general-imap ","
(general-key-dispatch #'self-insert-command
:timeout 0.25
:inherit-keymap my-prefix-map))If you bind more keys under your prefix later on in normal state, they will still be available when pressing the prefix in insert state without needing to re-evaluate the general-key-dispatch.
Like with general-simulate-keys used with a command name, the behavior of evil-repeat will depend on the command that ends up running. Having repeating work correctly requires handling a lot of edge cases, so please make an issue if you find any problems. Note that evil does not support repeating a count that comes before an operator currently, but repeating should work when the count follows the operator key (3cc vs c3c).
Choosing Definitions Based on Predicates
general-predicate-dispatch can be used to generate a menu-item that will behave differently based on the provided predicates. It takes a fallback definition as the first argument and then a list of predicates and alternate definitions (which can be commands, keymaps, etc.). Predicates are checked in order. If no predicate is matched and the fallback command is nil, then the mapping will be ignored (the keymap with the next highest precedence, if one exists, will be checked for the pressed key(s)).
(general-define-key "<right>"
(general-predicate-dispatch 'right-char
;; pred def ...
(eolp) 'beginning-of-line))The :docstring keyword can be specified to add a description to the menu-item.
Creating Extra Keybinding Functions
The primary purpose of this package is to provide a single function for key definitions that is simple and flexible. Most users probably won’t want to use this functionality (apart from general-evil-setup). However, if you would like more specific keybinding functions for certain prefixes, evil states, or keymaps, this package provides macros to generate these functions.
The general-create-definer macro can create functions for more succinctly defining keys. This is basically the same as naming a function with different defaults. For example, it can also be used to create a function that will always default to a certain prefix (like evil-leader does):
(general-create-definer my-leader1 :keymaps 'global :prefix "C-c")
;; bind "C-c o" to other-window
(my-leader1 "o" 'other-window)The user could also set general-default-prefix, general-default-state, or general-default-keymap to a different value within a function to achieve a similar effect.
As another example, one could make an extra vim definer using general-create-dual-vim-definer:
(general-create-dual-vim-definer nviemap '(normal visual insert emacs))As previously mentioned, how the newly created function creates keybindings can be altered by setting general-vim-definer-default. Unlike with general-create-definer, you can’t specify defaults for other keyword arguments with general-create-dual-vim-definer. If anyone would like to be able to do this to, for example, set a default prefix for the created function, feel free to make an issue.
Use-package Keyword
General also optionally provides a use-package keyword. :general is similar to :bind in that it implies :defer t and will create autoloads for the bound commands (though this is usually not necessary). The keyword is followed by one or more lists containing arguments for general-def; there is no difference in syntax:
(use-package org
:general
("C-c c" 'org-capture)
(:keymaps 'org-mode-map
"TAB" 'org-cycle)
;; uses `general-def' not `general-define-key', so this is fine
(org-mode-map
"TAB" 'org-cycle))The :general keyword also supports using any other key definer/wrapper by manually specifying it:
(use-package org
:general
(general-nmap "SPC c" 'org-capture))One annoyance you may encounter is that the default function for indentation will indent a list starting with a keyword like a function:
(:keymaps 'org-mode-map
"TAB" 'org-cycle)This is an annoyance you may have using other emacs packages as well and can be fixed by modifying lisp-indent-function (see this emacs stackexchange question and Fuco1’s modified lisp-indent-function in one of the answers there).
Use with Key-chord
General provides a simple function that will rewrite a string into a key-chord vector. This allows you to easily use general to create definitions for key-chord.el. The following are equivalent:
(key-chord-define evil-insert-state-map "jk" 'evil-normal-state)
(general-define-key :keymaps 'evil-insert-state-map
(general-chord "jk") 'evil-normal-state
(general-chord "kj") 'evil-normal-state)Note that the order of the keys does matter unlike with the default key-chord-define.
Extended Definition Syntax
General.el supports some extra per-definition keywords. It has “type” keywords that give general.el some extra information to use to create the definition (:prefix-command and :keymap) and other keywords that will alter or ignore the definition (:predicate and :ignore). There is also a system to allow users to support their own keywords. Note that anything done with external user functions can have side effects but cannot alter the definition directly. As an example, the which-key functionality described later in this section does not need to alter the definition, so it is implemented just as user-defined keyword would be.
Here are the keywords currently available by default:
“Type” specifiers:
:def- Implicit; for any definition that doesn’t fit under one of the below “types”:prefix-command- The same as:defbut will create a prefix command:prefix-mapThe corresponding prefix map (2nd arg todefine-prefix-command; global value never considered):prefix-nameThe prefix name (2nd arg todefine-prefix-command; global value never considered)
:keymap- For keymaps; if the keymap is not defined, will create an “autoloaded” keymap for:package:package- The package to load (also global)
:ignore- Do not create a keybinding for the key def pair
Which-key functionality (see below for more details):
:which-keyor:wk- The replacement text (or cons or function):major-mode- The major mode to add the text for (optional; also global):wk-match-keys- Whether to include the keys in the match cons (defaults to t; also global):wk-match-binding- Whether to include the binding in the match cons (defaults to t; also global):wk-full-keys- Whether the bound keys correspond to the full sequence to match (defaults totglobally)
Global keywords that can be overridden locally:
:predicate
The default value for a keyword is nil unless otherwise specified.
“Autoloaded” Keymaps
As the first example, an extended definition can be used to create an “autoload” for a keymap like use-package’s :bind-keymap keyword does:
(general-define-key
"C-c p" '(:keymap projectile-command-map :package projectile))Using this feature, a key can be bound to a keymap that does not exist yet and still work as expected. Projectile will be loaded when C-c p is used for the first time. This is done by using an intermediate function to load the package and rebind the keys.
:package can be specified locally within the extended definition or globally. When using the use-package :general keyword, it will automatically be specified.
:keymap must be specified in this case so that the unbound symbol can be distinguished as a keymap rather than a command. For other extended definitions, you can simply specify the definition as the first item in the list or explicitly use the :def keyword.
Which Key Integration
If you are not already familiar with which-key’s replacement system, please see the docstring for which-key-replacement-alist if you don’t understand any of the examples or information here.
There are several benefits to using general.el to add which-key replacements. The main benefit is that because the keys and definition are already specified, general.el can automatically assemble the match cons. This reuse of information saves a little space since it is not necessary to make an additional call to which-key-add-key-based-replacements with the key information. It is also useful since which-key does not currently provide any convenience function for creating a replacement that matches a binding (you have to manually add to which-key-replacement-alist).
Another related benefit of using :which-key instead of which-key-add-key-based-replacements directly even for keys that won’t be bound is that replacements will be added for all prefix combinations (i.e. when :non-normal-prefix and/or :global-prefix are also specified).
The argument supplied to :which-key or :wk is equivalent to the REPLACEMENT argument in which-key-add-key-based-replacements. It can be a full replacement cons of (KEY . BINDING) or just a string (which will be used as the BINDING and serve as the new description). Additionally it can be a function that will return a replacement cons (see the docstring for which-key-replacements-alist or the which-key README).
The :which-key keyword can be used with the :major-modes keyword (locally or globally) which can be compared to using which-key-add-major-mode-key-based-replacements. :major-modes can have the following values (see the examples below):
t- the major mode will be obtained from all keymaps by removing “-map”- the major mode name (when only one keymap is specified)
- a list of the following values:
t- same behavior as above but only for corresponding index in:keymaps- the major mode name for that index
nil(or no item at the index) - don’t match the major mode
:wk-match-keys, :wk-match-binding, and :wk-full-keys can be used to customize the match cons. Generally these will not need to be adjusted. The binding is only included in the match cons if one is available, and :wk-full-keys only needs to be specified as nil if you are binding keys in a prefix map.
Here are some examples:
(general-define-key :keymaps 'normal :prefix "SPC"
;; unbind SPC and give it a title for which-key (see echo area)
"" '(nil :which-key "my lieutenant general prefix")
;; bind nothing but give SPC f a description for which-key
"f" '(:ignore t :which-key "file prefix")
;; use a cons as a replacement
"g" '(:ignore t :wk ("g-key" . "git prefix"))
;; for a keymap, only the keys will be matched;
;; :no-match-binding is not necessary
"p" '(:keymap projectile-command-map :wk "projectile prefix")
...)
(general-define-key :keymaps 'help-map
;; allow keys before bound keys in match
;; since binding in a prefix map
:wk-full-keys nil
;; make a prefix-command and add description
"A" '(:prefix-command apropos-prefix-map :which-key "apropos"))
;; an equivalent of the above
(general-define-key :keymaps 'help-map
:wk-full-keys nil
:prefix "A"
:prefix-command 'apropos-prefix-map
;; make a prefix-command and add description
"" '(:ignore t :which-key "apropos"))
;; :major-modes
(general-define-key
:keymaps 'emacs-lisp-mode-map
:major-modes t
...)
(general-define-key
:keymaps '(no-follow-convention-mode-keymap1
org-mode-map)
:major-modes '(no-follow-convention-mode t)
...)User-defined Extended Definition Keywords
New keywords and functionality can be added by the user by adding a keyword to general-extended-def-keywords and creating a corresponding function named general-extended-def-:<keyword>. This function will be passed in state keymap key def kargs. state and keymap are the evil state (nil if none) and keymap the key (internal representation; kbd already used when necessary) is being bound in. Note that keymap will be the actual symbol for the keymap in case the name is needed. To get the actual keymap, using general--parse-keymap is recommended. def is the extended definition itself, and kargs is a list of all the keyword arguments given to the original general-define-key. This could, for example, be used to do something like add a keyword that would create autoloads for commands (I could also add this functionality directly if anyone wants it). For an example, see general-extended-def-:which-key.
User-defined Key Definers
In addition to being able to add new keywords for extended definitions, the user can also create their own key definers. These are potentially useful when you want to do something to rewrite a definition (e.g. like lispy-define-key does) as that is not possible with user-defined extended definition keywords.
This is also potentially useful even when rewriting the definitions is not necessary if some package already provides some key definer that does some additional work.
Alternate definers can be used by specifying the :definer keyword (globally or inside an extended definition):
(general-define-key :definer 'my
"key" #'def
"key2" '(def2 :definer 'my-other))The user-created function should be named general-<definer>-define-key. It will be passed state keymap key def orig-def kargs. These arguments are the same as for extended definition functions except for def and orig-def. def is the transformed definition, whereas orig-def is the original definition (an extended definition or the same as def). Since orig-def is not necessarily an extended definition, it may be useful to use general--getf (which uses general--extended-def-p; see general-lispy-define-key for an example). Since the keymap passed in is a symbol, general--parse-keymap may be useful as well. key-description will also be useful if the underlying definition function uses kbd (since key is the internal representation ready to be passed directly to define-key; note that key-description will work with both strings and vectors, including something like [remap kill-line])
Wrapping evil-define-minor-mode-key
If you want to use evil-define-minor-mode-key instead of evil-define-key*, you can use :definer 'minor-mode. This will simply repurpose :keymaps to specify minor mode names instead.
If you are wondering why you might want to use evil-define-minor-mode-key, see here.
Lispy Integration/ Wrapping lispy-define-key
To use lispy-define-key to make the definitions, :definer 'lispy can be specified. :lispy-plist can be specified globally or in an extended definition to set the last argument to lispy-define-key.
Worf Integration/ Wrapping worf-define-key
To use worf-define-key to make the definitions, :definer 'worf can be specified. :worf-plist can be specified globally or in an extended definition to set the last argument to worf-define-key.
Other Provided Definers
To use lpy-define-key to make the definitions, :definer 'lpy can be specified.
Non-keybinding-related Configuration Helpers
General.el also provides a few helper functions for other configuration purposes. They are intended to be slightly more convenient versions of functions provided by default. general-add-hook, general-remove-hook, general-advice-add, and general-advice-remove all act as drop-in replacements for their corresponding functions but allow lists for some of the arguments. Because I don’t like the difference in naming for the default advice functions, general-add-advice and general-remove-advice are also provided.
For example:
(general-add-hook my-lisp-mode-hooks
(list #'lispy-mode #'rainbow-delimiters-mode))
(general-add-advice (list #'git-gutter:next-hunk
#'git-gutter:previous-hunk)
:before #'evil-set-jump)The hook functions allow specifying lists for the hooks and functions, and the advice functions allow specifying lists for the symbols and functions.
FAQ
Why don’t some evil keybindings work (immediately)?
This is a known issue for evil. To work around this problem, you can use :definer ‘minor-mode. See here for more information.
