csand / general.el

More convenient key definitions in emacs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

https://melpa.org/packages/general-badge.svg

http://i.imgur.com/SXA66y7.png

A general is a leader. – onioncheese

Recent Major Changes

2018-01-21 general-default-... variables are obsolete

general-default-prefix, general-default-non-normal-prefix, general-default-global-prefix, general-default-states, and general-default-keymaps still work. However, they will eventually be removed, so please switch to using general-create-definer if you want to use a definer with different defaults.

2018-01-20 general-create-vim-definer and general-create-dual-vim-definer have been removed

general-create-definer should now be used instead as it is now capable of the same functionality (general-evil-setup now uses it). Additionally, general-vim-definer-default is obsolete and will be removed eventually. The second argument to general-evil-setup is no longer used and will also be removed eventually. The vim definers will now always set the default :states (and never the default :keymaps) because of the change below.

2018-01-20 :states 'normal is now the same as :keymaps 'normal

:keymaps 'global :states 'normal will now bind in evil-normal-state-keymap as opposed to the normal state auxiliary keymap of (current-global-map) (see Note for Evil Users). It is not recommended to bind in a state and (current-global-map). If you want to prevent certain keys from being overridden, please use evil intercept keymaps instead.

If you update general, please make sure that you are also using a recent version of evil.

2018-01-20: general-simulate-keys is now obsolete

Please switch to general-key or general-simulate-key. Note that keyword arguments have replaced the positional arguments of general-simulate-keys. general-simulate-keys will likely be removed sometime in the future.

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 with define-key and other definers (e.g. evil-define-key(*) for evil users) before using this package
  • Uses a syntax similar to setq for key definitions (like evil-define-key, bind-map, evil-leader.el, etc.; unlike bind-key)
  • Provides tight (and optional) integration with evil (unlike bind-key)
  • general-def can act as a drop-in replacement for the following definers (see the documentation below for a minor caveat) (unique):
    • general-define-key and global-set-key (no positional keymap argument)
    • define-key and evil-global-set-key (positional argument for keymap)
    • evil-define-key (positional argument for state and keymap)
  • With the :definer keyword, general-define-key can be extended to use any key definition function (e.g. evil-define-minor-mode-key, lispy-define-key, etc.) (unique)
  • With “extended” definitions, user-created keywords can be added globally (in general-define-key) and locally (in an “extended” definition plist) to extend the behavior of general-define-key (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 with define-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-key does)
  • 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) (like evil-define-key)
  • Allows displaying defined keys (like bind-key.el)
  • Provides integration with other packages such as key-chord.el and which-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 jk in insert mode without losing j)
    • A helper to create a menu item to dispatch to different definitions based on predicates
  • Provides other helpers for configuration (e.g. more convenient functions for hooks and advice)
  • Is well tested (unlike evil-leader.el)

Basic Setup

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 j and k in normal state globally
(general-define-key
 :keymaps 'normal
 "j" 'evil-next-visual-line
 "k" 'evil-previous-visual-line)

;; bind gj and gk
(general-define-key
 :prefix "g"
 :keymaps 'normal
 "j" 'evil-next-line
 "k" 'evil-previous-line)

;; named prefix key
(setq my-leader1 ",")
(general-define-key
 :prefix my-leader1
 :keymaps 'normal
 "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"). This option is mainly provided to make it easy to transition to general-define-key or general-def from other key definers with search and replace and therefore only applies to general-define-key (and wrappers). kbd will always be called on string keys for other helpers such as general-key, general-key-dispatch, and general-translate-key.

:prefix, :states, and :keymaps are the most basic keyword arguments. 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 here). When the keymap is global, the key will be bound in (current-global-map) (or the corresponding evil global map if :states is specified).

:states and :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. 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 and/or :prefix-map. All prefix keys will then be bound to the prefix command or prefix keymap in the correct keymaps. If :prefix-command is specified, define-prefix-command will be used with prefix-map and prefix-name passed in as additional arguments to define-prefix-command. If only :prefix-map is specified, a prefix keymap alone will be created with a menu item/prompt corresponding to :prefix-name. Note that existing prefix commands/keymaps will not be redefined, so reevaluating a general.el form that uses :prefix-command or :prefix-map will not clear the previously created keymap.

(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, general-define-key will act as a wrapper around evil-define-key*. evil-define-key* now directly supports the symbol global for the keymap argument, so the following are equivalent:

(general-define-key
 ;; (default)
 ;; :keymaps 'global
 :states '(normal visual)
 ...)
(general-define-key
 :keymaps '(normal visual)
 ...)

Note that this previously was not the case and (general-define-key :states 'normal ...) would bind in the normal state auxiliary map for (current-global-map). Since auxiliary maps have a higher precedence than evil global and override keymaps, this was previously mentioned as one possible way of preventing certain keybindings from being overridden. However, this is not a reliable method. Keys bound in auxiliary maps can override keys bound in other auxiliary maps, for example, and keys bound in evil local or minor-mode keymaps will always override keys bound in regular auxiliary maps. If you need this functionality, please use evil intercept keymaps instead (see Override Keymaps).

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 ...)

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 Definers

general-evil-setup can be used to generate key definition functions that are named similarly to vim’s. Currently, the following functions will be created:

  • general-imap
  • general-emap
  • general-nmap
  • general-vmap
  • general-omap
  • general-mmap
  • general-rmap
  • general-iemap
  • general-nvmap
  • general-otomap
  • general-itomap
  • general-tomap

These are wrappers around general-def created with general-create-definer that set the default :states. You can see the help text for each for a more specific description. general-evil-setup can be called with a non-nil argument (i.e. (general-evil-setup t)) to create non-prefixed aliases for these definers (e.g. nmap).

Here is an example using general-nmap:

(general-evil-setup)
;; define in evil-normal-state-map
(general-nmap ...)
;; define in the normal state auxiliary map for org-mode-map
(general-nmap org-mode-map ...)
;; or
(general-nmap :keymaps 'org-mode-map ...)

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-key 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. Also, 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-key "C-c"))

Note that when a named prefix keymap/command exists (e.g. help-command), you should prefer to bind directly to that. If the key you want to simulate is always bound to the same keymap, you can still bind directly to it even if it is unnamed:

(general-nmap "SPC" (key-binding (kbd "C-c")))

General provides another macro called general-key that is usually preferable for simulating a key that corresponds to a single definition. If you actually need to simulate a key that is bound to a keymap, which-key may not work correctly with general-key, but for keys corresponding to commands, general-key should be used instead of general-simulate-key. Unlike general-simulate-key, which creates a function, general-key expands to an extended menu item like general-predicate-dispatch. This is a simpler and more direct approach that has the advantage of showing the docstring for the exact command with C-h k. If the key to act as is unbound, key lookup can continue (like if :predicate returns nil), so having a fallback keybinding is possible unlike with general-simulate-key.

general-key may be useful when you want to have a key act as another without having to bind it to the exact command in every relevant keymap:

(general-nmap "RET" (general-key "C-c C-c"))

general-simulate-key and general-key also support keyword arguments (:state and :keymap) to control the context the keys are simulated in. For example:

(general-nmap "j" (general-simulate-key "C-n" :state 'emacs))
;; `general-key' supports :state only`
(general-nmap "j" (general-key "C-n" :state 'emacs))

The advantage of general-simulate-key over general-key is that it can be used to simulate a key sequence corresponding to multiple commands or a command followed by a key sequence. The key argument can be replaced by a list of a command and keys (e.g. (general-simulate-key ('evil-delete "iw"))). See the next section for a reasonable use case for this feature. When a command is specified, general will used the remapped version of it if it exists (e.g. if [remap evil-delete] 'lispyville-delete is in an active keymap, lispyville-delete will be used instead of evil-delete). To use the exact command instead, :remap nil can be specified

general-simulate-key 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, docstring, and which-key description can be replaced with keyword arguments:

(general-nmap "SPC" (general-simulate-key "C-c"
                      :state 'emacs
                      :name general-SPC-simulates-C-c
                      :docstring "Simulate C-c in emacs state with SPC."
                      :which-key "Simulate C-c"))

Make sure that you don’t bind a key to simulate itself (e.g. (general-emap "C-n" (general-simulate-key "C-n" :state 'emacs))) as this will cause an infinite loop.

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 when operator state is not used (e.g. you want to bind something like ctb or jk in insert state). I’ve come up with a more general workaround called general-key-dispatch. Consider the following example:

(general-nmap "c" (general-key-dispatch 'evil-change
                    "ow" 'toggle-word-wrap
                    "tb" 'some-command
                    "c" 'evil-change-whole-line
                    ;; alternatively if there was no linewise version:
                    "c" (general-simulate-key ('evil-change "c"))))
;; `evil-change' is not bound in `evil-visual-state-map' by default but
;; inherited from `evil-normal-state-map'
;; if you don't want "c" to be affected in visual state, you should add this
(general-vmap "c" 'evil-change)

general-key-dispatch is a function-creating macro. In this example, the command created will wait for user input and try to match one of the specified key sequences (e.g. ow). If a key sequence is matched, the corresponding command will be executed. Otherwise it will fall back to simulating the fallback command followed by the unmatched keys (using the same mechanism as general-simulate-key). For example, ow is bound, so cow would run toggle-word-wrap. On the other hand, b is not mapped, so cb would 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 ('evil-change "c")) to keep its behavior. c is not actually bound in visual state by default, so to keep c working the same in visual state, you should explicitly bind it to evil-change.

Like with general-simulate-key, general will first check to see if the command to be executed has been remapped (e.g. if [remap evil-delete] 'lispyville-delete is in an active keymap, lispyville-delete will be used instead of evil-delete). To use the exact command instead, :remap nil can be specified.

Another thing to note is that you can’t bind a key in the general-key-dispatch section to simulate the base key (i.e. the key you bind to the resulting command, in this case c). For this example, you couldn’t bind w to (general-simulate-key "ciw"). While this wouldn’t cause an infinite loop, it wouldn’t work either, so you would have to use the command name instead (e.g (general-simulate-key ('evil-change "iw"))).

Also, if you use a count in the middle (e.g. c2tb and 2 is not explicitly bound), the fallback command will be run immediately. If anyone cares about this, feel free to make an issue. I could potentially add an option to allow changing the count in the middle without immediately falling back to the default command.

Another possible use case of general-key-dispatch 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))

Commands created in this way 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 some prefix keymap in insert state, it may also convenient to use the :inherit-keymap keyword. This allows using prefix keybindings without the need to re-specify them 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 the need to re-evaluate the general-key-dispatch.

By default, general-key-dispatch will prevent name clashes by appending a unique number to name of the created command (e.g. general-dispatch-self-insert-command-G402). If you would like to reference the created command by name, you can name it yourself using the :name keyword argument (e.g. :name general-insert-prefix-dispatch).

Like with general-simulate-key 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.

Key “Translation”

general-translate-key allows binding a key to the definition of another key in the same keymap (comparable to how vim’s keybindings work). Its arguments are the states (which can be nil for non-evil keymaps) and keymaps (both symbols or lists of symbols like for general-define-key) to bind/look up the key(s) in followed optionally by keyword arguments (currently only :destructive) and key/replacement pairs.

evil-collection-translate-key allows binding a key to the definition of another key in the same keymap (comparable to how vim’s keybindings work). Its arguments are the states and keymaps to bind/look up the key(s) in followed optionally by keyword arguments (currently only :destructive) and key/replacement pairs. states can be nil for non-evil keymaps, and both states and keymaps can be a single symbol or a list of symbols.

This can be particularly useful, for example, when you want make key swaps/cycles en masse. This use case is similar to one for general-simulate-key (i.e. make a key act as another key that has a consistent meaning but different commands for different modes without having to individually bind the key to the exact definition in each mode’s keymap). However, general-simulate-key is not always suitable for this purpose. It can be used to, for example, make j in normal state act as C-n in emacs state (to use the default “down” navigation key for all modes without needing to individually make keybindings for every mode), but it cannot be used to swap/cycle keys within a single keymap, as this would cause an infinite loop of simulating the other key(s).

An example use case of general-translate-key is for non-QWERTY users who want to retain the hjkl keyboard positions for movement in dired, mu4e, etc. When using a package that already creates hjkl keybindings for the desired mode(s) (e.g. evil-collection), it is easily possible to make these cycles in a single statement:

;; single invocation example
(general-translate-key nil 'evil-normal-state-keymap
  "n" "j"
  "e" "k"
  ...)
;; cycling keys en masse
(dolist (keymap keymaps-with-hjkl-keybindings)
  (general-translate-key 'normal keymap
    ;; colemak hnei is qwerty hjkl
    "n" "j"
    "e" "k"
    "i" "l"
    ;; add back nei
    "j" "e"
    "k" "n"
    "l" "i"))

By default, the first invocation of general-translate-key will make a backup of the keymap. Each subsequent invocation will look up keys in the backup instead of the original. This means that a call to general-translate-key will always have the same behavior even if evaluated multiple times. When :destructive t is specified, keys are looked up in the keymap as it is currently. This means that a call to general-translate-key that swapped two keys would continue to swap/unswap them with each call. Therefore when :destructive t is used, all cycles/swaps must be done within a single call to general-translate-key. To make a comparison to vim keybindings, :destructive t is comparable to vim’s map, :destructive nil is comparable to vim’s noremap (where the “original” keybindings are those that existed in the keymap when the general-translate-key was first used), and generally you will want to use noremap (:destructive nil).

Note that general state and keymap aliases (as well as local and global) and general-implicit-kbd are supported by general-translate-key:

;; normal -> evil-normal-state-keymap
(general-translate-key nil 'normal
  ;; kbd not necessary by default
  "C-p" "C-n")

Keys are bound using general-define-key, so they are viewable with general-describe-keybindings.

general-swap-key is provided as a wrapper around general-translate-key that allows swapping keys:

(general-swap-key nil 'normal
  ";" ":"
  "a" "A")
;; equivalent to
(general-translate-key nil 'normal
  ";" ":"
  ":" ";"
  "a" "A"
  "A" "a")

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 definitions (e.g. :prefix-command and :keymap) and other keywords that will alter or ignore definitions (e.g. :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 (to do that, a custom definer should be created instead). 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”
  • :keymap - For keymaps; if the keymap is not defined, will create an “autoloaded” keymap for :package
    • :package - The package to load (also global)
  • :prefix-command or :prefix-map - These are the same as :def and :keymap respectively but will create a prefix command and/or keymap (these behave the same as the global keyword arguments except for any key as opposed to just :prefix)
    • :prefix-name The keymap menu name/prompt (global value never considered)
  • :ignore - Do not create a keybinding for the key def pair

Which-key functionality (see below for more details):

  • :which-key or :wk - The replacement text (or cons or function)
    • :major-modes - Major modes to match (optional; also global)
    • :wk-match-keys - Whether to include the keys in the match cons (defaults to t globally)
    • :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 to t; also global)

Evil command properties (see below for more details):

  • :properties - The list of properties to add to the command (also global)
  • :repeat - The repeat property to set for the command (also global)
  • :jump - The jump property to set for the command (also global)

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)
 ...)

Evil Command Properties

The :properties, :repeat, and :jump keywords can be used to add evil command properties:

(general-define-key
 :keymaps 'normal
 :prefix "SPC"
 "gj" '(git-gutter:next-hunk :properties (:repeat t :jump t))
 "gk" '(git-gutter:previous-hunk :repeat t :jump t))

;; they also work globally
(general-define-key
 :keymaps 'normal
 :prefix "SPC"
 :properties '(:repeat t :jump t)
 ;; or
 :repeat t
 :jump t
 "gj" 'git-gutter:next-hunk
 "gk" 'git-gutter:previous-hunk)

If you would like for more keywords to be added that correspond to specific properties (like :repeat), feel free to make an issue or pull request. For more information on command properties see evil’s documentation and here.

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 that the key (internal representation; kbd already used if necessary) is being bound in. Note that keymap will be the symbol for the keymap in case it is needed. To get the actual keymap, using general--get-keymap is recommended. def is the extended definition itself, and kargs is the plist of all the keyword arguments given to the original general-define-key. For examples, see general-extended-def-:which-key and general-extended-def-:properties.

Note that the keywords in general-extended-def-keywords and their helper keywords can all be specified both globally and locally. Since globally specifying keywords may not always make sense, it is up to the general-extended-def-:<keyword> function to decide how to handle things. When a keyword can be specified both globally and locally, general--getf may be useful.

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--get-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 repurpose :keymaps to specify minor mode names instead of keymap names:

(general-define-key
 :definer 'minor-mode
 :states 'normal
 :keymaps 'org-src-mode
 "RET" 'org-edit-src-exit)

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/macros for other configuration purposes. They are intended to be slightly more convenient versions of functions/macros provided by default.

Settings

general-setq is a stripped-down customize-set-variable that can act as a drop-in replacement for setq. Some variables defined with defcustom specify a custom setter with :set that must be used for changes to take effect (e.g. auto-revert-interval). setq cannot be used to set such variables correctly if the corresponding package has already been loaded, but general-setq will correctly use the custom setter when necessary. The benefit of general-setq over customize-set-variable is that it can be used to set multiple variables at once. It does not do everything customize-set-variable does (e.g. it cannot be used interactively, does not attempt to load variable dependencies, and does not allow the user to specify comments). From some basic testing, it is 10x to 100x faster because of this, but the speed difference should not be noticeable if you aren’t setting thousands of variables during emacs initialization.

Here’s an example using variables that have a custom setter:

(general-setq auto-revert-interval 10
              evil-want-Y-yank-to-eol t
              evil-search-module 'evil-search)

Note that setq will work as expected as long it is used before the corresponding package is loaded, but with customize-set-variable or general-setq, you do not need to worry about whether or not the package has been loaded. If you decide to use general-setq, I’d recommend aliasing it to something shorter like gsetq.

Hooks and Advice

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. The hook functions allow specifying lists for the hooks and functions, and the advice functions allow specifying lists for the symbols and functions. Because I don’t like the difference in naming for the default advice functions, general-add-advice and general-remove-advice are also provided as aliases.

For example:

(general-add-hook my-lisp-mode-hooks
                  (list #'lispy-mode #'rainbow-delimiters-mode))
;; note that setting the :jump command property is recommended instead of this
(general-add-advice (list #'git-gutter:next-hunk
                          #'git-gutter:previous-hunk)
                    :before #'evil-set-jump)

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.

About

More convenient key definitions in emacs

License:GNU General Public License v3.0


Languages

Language:Emacs Lisp 99.8%Language:Makefile 0.2%