130

GitHub - noctuid/general.el: More convenient key definitions in emacs

 6 years ago
source link: https://github.com/noctuid/general.el
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

General User Manual

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

A general is a leader. – onioncheese

Recent Breaking 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.

Table of Contents

About

general.el provides a more convenient method for binding keys in emacs (for both evil and non-evil users). Like use-package, which provides a convenient, unified interface for managing packages, general.el is intended to provide a convenient, unified interface for key definitions. While this package does implement some completely new functionality (such as the ability to make vim-style keybindings under non-prefix keys with an optional timeout), its primary purpose is to build on existing functionality to make key definition more clear and concise. general-define-key is user-extensible and supports defining multiple keys in multiple keymaps at once, implicitly wrapping key strings with (kbd ...), using named prefix key sequences (like the leader key in vim), and much more.

One advantage of using general-define-key (or a wrapper for it) even in cases where its extra functionality isn’t necessary and doesn’t significantly improve brevity is that all keybindings are recorded and can be displayed later with general-describe-keybindings.

This manual explains the most relevant parts of every general.el feature. All user-facing functions, macros, and variables also have docstrings (e.g. accessible with C-h f or C-h v), so please consult these for further details.

Dependency Versions

Please use MELPA and not MELPA stable for installing optional dependencies (e.g. evil and use-package). General may rely on functionality not in released versions for these packages, so if you are having trouble, please try updating them.

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

Reading Recommendations

Before using general.el, you should first be familiar with define-key, global-set-key, and emacs’ key binding system. I recommend reading Mastering Key Bindings in Emacs if you are new to emacs. Also see define-key’s help text for information on valid keys and definitions.

If you are also using evil, you should first be familiar with how evil-define-key, evil-define-minor-mode-key, etc. work. If you are a new evil user, I’d recommend looking at my evil guide. The Keybindings and States section in particular may be useful

A large number of issues opened on this repository are not specifically related to general.el (e.g. user usage or syntax errors that would also occur without general-define-key). While I don’t mind generic questions about keybinding issues, you may save yourself some time if you first determine whether or not an issue is related to general-define-key by, if possible, testing with an equivalent define-key, evil-define-key, etc. statement.

See also the FAQ for commonly asked questions.

Usage Recommendations and Documentation Clarifications

To facilitate extensibility and easy creation of wrappers, general-define-key uses keyword arguments to specify everything besides the key definitions, including for the :states and :keymaps. Since users will most often specify one or both of these keyword arguments, general-define-key is often less concise than define-key or evil-define-key. It is for this reason that it is recommended that general-define-key not be used directly. general.el provides wrappers around general-define-key that take positional arguments, and it is recommended that you use these instead of general-define-key. general-create-definer can also be used to create a new definer with certain default settings (e.g. prefix settings). For clarity and consistency, examples in the documentation usually use general-define-key unless the example is explicitly for a wrapper. However, ~general-def~ is recommended over general-define-key as it is more flexible and concise. Positional arguments are optional but not required, so general-def can basically act as a drop-in replacement for many key definers (including general-define-key, define-key, and evil-define-key). Note that general-create-definer and the :general keyword argument for use-package use general-def. I personally only use general-def.

Since it is more common for commands to not be sharp quoted in key definitions, this package’s examples use single quotes for commands. I personally prefer to always properly sharp quote functions, so commands in the actual non-example code are always sharp quoted.

Although general-define-key will automatically defer keybindings until the specified keymaps exist, you can still use it with with-eval-after-load or use-package’s :config keyword whenever it makes sense to. If you have a lot of keybindings, this could potentially shave some fraction of a second off of your startup time.

Basic Examples

General Examples

(require 'general)

;; * Global Keybindings
;; `general-define-key' acts like `global-set-key' when :keymaps is not
;; specified (because ":keymaps 'global" is the default)
;; kbd is not necessary and arbitrary amount of key def pairs are allowed
(general-define-key
 "M-x" 'amx                             ; or 'smex
 "C-s" 'counsel-grep-or-swiper)

;; * Mode Keybindings
;; `general-define-key' is comparable to `define-key' when :keymaps is specified
(general-define-key
 ;; NOTE: keymaps specified with :keymaps must be quoted
 :keymaps 'org-mode-map
 "C-c C-q" 'counsel-org-tag
 ;; ...
 )
;; `general-def' can be used instead for `define-key'-like syntax
(general-def org-mode-map
  "C-c C-q" 'counsel-org-tag
  ;; ...
  )

;; * Prefix Keybindings
;; :prefix can be used to prevent redundant specification of prefix keys
(general-define-key
 :prefix "C-c"
 ;; bind "C-c a" to 'org-agenda
 "a" 'org-agenda
 "b" 'counsel-bookmark
 "c" 'org-capture)

;; for frequently used prefix keys, the user can create a custom definer with a
;; default :prefix
;; using a variable is not necessary, but it may be useful if you want to
;; experiment with different prefix keys and aren't using `general-create-definer'
(defconst my-leader "C-c")

(general-create-definer my-leader-def
  ;; :prefix my-leader
  ;; or without a variable
  :prefix "C-c")

;; ** Global Keybindings
(my-leader-def
  "a" 'org-agenda
  "b" 'counsel-bookmark
  "c" 'org-capture)

;; ** Mode Keybindings
(my-leader-def
  :keymaps 'clojure-mode-map
  ;; bind "C-c C-l"
  "C-l" 'cider-load-file
  "C-z" 'cider-switch-to-repl-buffer)
;; `general-create-definer' creates wrappers around `general-def', so
;; `define-key'-like syntax is also supported
(my-leader-def clojure-mode-map
  "C-l" 'cider-load-file
  "C-z" 'cider-switch-to-repl-buffer)

;; * Settings
;; change `auto-revert-interval' after autorevert has been loaded (`setq' will
;; not work)
(general-setq auto-revert-interval 10)

Evil Examples

(require 'general)

;; * Global Keybindings
;; `general-define-key' acts like `evil-define-key' when :states is specified
(general-define-key
 :states 'motion
 ;; swap ; and :
 ";" 'evil-ex
 ":" 'evil-repeat-find-char)
;; same as
(general-define-key
 :states 'motion
 ";" 'evil-ex
 ":" 'evil-repeat-find-char)
;; `general-def' can be used instead for `evil-global-set-key'-like syntax
(general-def 'motion
  ";" 'evil-ex
  ":" 'evil-repeat-find-char)

;; alternative using `general-translate-key'
;; swap ; and : in `evil-motion-state-map'
(general-swap-key nil 'motion
  ";" ":")

;; * Mode Keybindings
(general-define-key
 :states 'normal
 :keymaps 'emacs-lisp-mode-map
 ;; or xref equivalent
 "K" 'elisp-slime-nav-describe-elisp-thing-at-point)
;; `general-def' can be used instead for `evil-define-key'-like syntax
(general-def 'normal emacs-lisp-mode-map
  "K" 'elisp-slime-nav-describe-elisp-thing-at-point)

;; * Prefix Keybindings
;; :prefix can be used to prevent redundant specification of prefix keys
;; again, variables are not necessary and likely not useful if you are only
;; using a definer created with `general-create-definer' for the prefixes
;; (defconst my-leader "SPC")
;; (defconst my-local-leader "SPC m")

(general-create-definer my-leader-def
  ;; :prefix my-leader
  :prefix "SPC")

(general-create-definer my-local-leader-def
  ;; :prefix my-local-leader
  :prefix "SPC m")

;; ** Global Keybindings
(my-leader-def
  :keymaps 'normal
  ;; bind "SPC a"
  "a" 'org-agenda
  "b" 'counsel-bookmark
  "c" 'org-capture)
;; `general-create-definer' creates wrappers around `general-def', so
;; `evil-global-set-key'-like syntax is also supported
(my-leader-def 'normal
  "a" 'org-agenda
  "b" 'counsel-bookmark
  "c" 'org-capture)

;; to prevent your leader keybindings from ever being overridden (e.g. an evil
;; package may bind "SPC"), use :keymaps 'override
(my-leader-def
  :states 'normal
  :keymaps 'override
  "a" 'org-agenda)
;; or
(my-leader-def 'normal 'override
  "a" 'org-agenda)

;; ** Mode Keybindings
(my-local-leader-def
  :states 'normal
  :keymaps 'org-mode-map
  "y" 'org-store-link
  "p" 'org-insert-link
  ;; ...
  )
;; `general-create-definer' creates wrappers around `general-def', so
;; `evil-define-key'-like syntax is also supported
(my-local-leader-def 'normal org-mode-map
  "y" 'org-store-link
  "p" 'org-insert-link
  ;; ...
  )

;; * Settings
;; change evil's search module after evil has been loaded (`setq' will not work)
(general-setq evil-search-module 'evil-search)

Vim-like definitions:

(general-evil-setup)
;; * Global Keybindings
;; all keywords arguments are still supported
;; these are just wrappers around `general-def' that set a default :states
(general-nmap
  :prefix "SPC"
  "p" 'helm-mini)

;; bind in motion state (inherited by the normal, visual, and operator states)
(general-mmap
  ";" 'evil-ex
  ":" 'evil-repeat-find-char)

;; alternatively, for shorter names
(general-evil-setup t)
(mmap
  ";" 'evil-ex
  ":" 'evil-repeat-find-char)

;; * Mode Keybindings
(general-nmap
  :keymaps 'emacs-lisp-mode-map
  "K" 'elisp-slime-nav-describe-elisp-thing-at-point)
;; same as
(general-nmap emacs-lisp-mode-map
  "K" 'elisp-slime-nav-describe-elisp-thing-at-point)

Switching Completely to General

It is possible to gradually switch to using general by using it only for new configuration and slowly converting old configuration if desired. If you would like to quickly convert all keybindings in your init file to use general so that they show up with general-describe-keybindings, you can potentially use regexp replace. For example, you could use M-< C-M-% \(global-set-key\|define-key\|evil-global-set-key\|evil-define-key\) RET general-def RET !. The evil equivalent would be :%s/\(global-set-key\|define-key\|evil-global-set-key\|evil-define-key\)/general-def/g.

There are two caveats. The old key definers all require using kbd. This means that you will either have to remove every kbd in these key definers (e.g. :%s/(kbd ?\(.*?\))/\1/gc; you should likely confirm whether each kbd should be removed) or set general-implicit-kbd to nil for the old configuration. Furthermore, general-def can only correctly replace definer statements where the first specified key is a string or vector. It will not work correctly to replace a definer that uses a variable or function for the first key (e.g. (global-set-key my-key 'command) cannot be replaced directly with general-def). To use general for definitions like this, you must either use the actual equivalent definer that general-def ends up using (general-define-key, general-emacs-define-key, or evil-define-key) or explicitly separate the positional arguments from the first key with a bogus keyword argument (e.g. (general-def :start-maps my-key 'command)).

If you decide to do this, please make sure that your configuration is backed up, and test this out to make sure that there are no errors before permanently changing your configuration.

general-define-key Details

This package provides one main function, general-define-key, for key definitions for both evil and non-evil users. It is recommended you use the provided wrappers around it or create your own with general-create-definer, but first you should understand the keyword arguments provided by general-define-key.

Definitions

The only positional arguments for general-define-key are any number of key/definition pairs. General supports all key and definition types supported by define-key (see its help text) as well as its own “extended definitions”. Here are a few examples of definitions that aren’t standard =”string key” ‘command= pairs:

;; vector keys, including [t] and [remap] are supported
(general-define-key
 :keymaps 'org-capture-mode-map
 [remap evil-save-and-close]          'org-capture-finalize
 [remap evil-save-modified-and-close] 'org-capture-finalize
 [remap evil-quit]                    'org-capture-kill)

(general-define-key
 :states 'normal
 :keymaps 'org-capture-mode-map
 ;; keyboard macro definition
 "RET" "C-c C-c"
 ;; general.el extended definition
 "SPC k" '(org-capture-kill :which-key "abort capture"))

kbd will automatically be called on every string key. general-implicit-kbd can be set to nil if you want to manually use (kbd "key"). 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.

Keyword Arguments

: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 =’global=. Each keymap can either be a quoted keymap, quoted keymap alias, =’global=, or =’local=. This is the biggest contrast between general-define-key and other definers such as define-key, where the keymap is passed in directly. Note that the provided wrappers such as general-def do not require quoting keymaps. When the keymap is =’local=, the key will be bound only in the current buffer (see here for more details). When the keymap is =’global=, the key will be bound in (current-global-map) (or the corresponding evil global map if :states is specified; see Note for Evil Users for more information).

: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 redundancy.

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
 :keymaps '(normal insert emacs)
 :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
 :keymaps '(normal insert emacs)
 :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 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...>)

If you just want to create the prefix keymap and bind keys directly in it without immediately binding a prefix key to the prefix keymap, simply don’t specify :keymaps or :prefix:

;; bind "/" directly in the newly created my-prefix-map
(general-define-key :prefix-map 'my-prefix-map "/" 'swiper)

There is also a :predicate keyword for giving a condition under which a map should be active.

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. In other words, :predicate is only useful if a fallback keybinding already exists in a different, lower precedence 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.

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)
;; or (emacs 25+)
(setf (alist-get 'help general-keymap-aliases) 'help-map)
;; or (emacs 25+)
(setf (alist-get '(h help) general-keymap-aliases) 'help-map)

;; now
(general-define-key :keymaps 'help ...)
;; is the same as
(general-define-key :keymaps 'help-map ...)

Note that earlier entries in the alist take precedence.

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.

general-define-key Wrappers

Positional Argument Wrappers

When 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 and another macro that can basically act is a drop-in replacement for both of those and more. They are general-emacs-define-key, general-evil-define-key, and general-def respectively. 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 all 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-visible-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))

Similarly, the following are all 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 evil-global-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 (see keymap aliases).

The 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' when no "positional" arguments
(general-def
  "key" 'def
  ...)
;; example equivalents
(general-define-key "key" 'def)
(global-set-key (kbd "key") 'def)

;; use `general-emacs-define-key' when one "positional" argument
(general-def org-mode-map
  "key" 'def
  ...)
;; example equivalent
(define-key org-mode-map (kbd "key") 'def)
;; act like `evil-global-set-key'
(general-def 'normal
  "key" 'def
  ...)
;; example equivalents
(evil-global-set-key 'normal (kbd "key") 'def)
(evil-define-key 'normal 'global (kbd "key") 'def)

;; use `general-evil-define-key' when two "positional" arguments
(general-def 'normal org-mode-map
  "key" 'def
  ...)
;; example equivalent
(evil-define-key 'normal org-mode-map (kbd "key") 'def)

Note that all leading quoted and unquoted symbols and lists are considered to be positional arguments. This means that if you want to use a variable or function for a key that could be a positional argument, you should either use the definer general-def would end up using (general-define-key, general-emacs-define-key, or evil-define-key) or explicitly separate the positional arguments from the first key with a bogus keyword argument:

(general-def
  :start-maps t
  some-key 'some-command)

Mass Key Unbinding Wrapper

general-unbind acts as general-def, but the positional arguments should all be keys (instead of pairs of keys and definitions) that should be unbound:

(general-unbind 'insert
  "C-v"
  "C-k"
  "C-y"
  "C-e")
;; equivalent to
(general-def 'insert
  "C-v" nil
  "C-k" nil
  "C-y" nil
  "C-e" nil)

This wrapper can also be used, for example, if you want to disable certain commands or keys from working in certain modes by using with :with keyword argument (example use case taken from evil-collection):

(general-unbind 'normal Info-mode-map
  :with 'ignore
  [remap evil-append]
  [remap evil-append-line]
  [remap evil-insert]
  [remap evil-insert-line])
;; equivalent to
(general-def 'normal Info-mode-map
  [remap evil-append] 'ignore
  [remap evil-append-line] 'ignore
  [remap evil-insert] 'ignore
  [remap evil-insert-line] 'ignore)

The reason that this functionality is implemented as a wrapper and not as a keyword argument for general-define-key is that cl-defun cannot correctly parse keyword arguments when the keyword is in an odd position (e.g. ("a" :keyword 'arg) instead of (:keyword 'arg "a")). For example, if this functionality was implemented with an :unbind keyword, the :general use-package keyword and any definer created with general-create-definer would not work if the user specified an odd number of keys to unbind (because the default keyword arguments would be at the end of the arglist, in the wrong positions). As I’d rather not re-implement keyword argument parsing just for this use case, this functionality is provided as a macro. This macro will correctly handle any positioning for keyword arguments.

Creating New Key Definers

The general-create-definer macro can create definers that wrap general-def but with certain default settings. For example, it can be used to create a definer that will default to a certain prefix (like evil-leader does):

;; basic example
(general-create-definer my-leader-def
  :prefix "C-c")
;; bind "C-c o" to `other-window'
(my-leader-def "o" 'other-window)

;; more complex example
(general-create-definer tyrant-def
  :states '(normal insert emacs)
  :prefix "SPC"
  :non-normal-prefix "M-SPC"
  :prefix-command 'tyrant-prefix-command
  :prefix-map 'tyrant-prefix-map)
;; globally bind "SPC /" in normal state and "M-SPC /" in the insert/emacs
;; states to `swiper'
(tyrant-def "/" 'swiper)

;; for org-mode, bind "SPC o" in normal state and "M-SPC /" in the insert/emacs
;; states to `counsel-org-goto'
(tyrant-def org-mode-map "o" 'counsel-org-goto)
;; same as
(tyrant-def :keymaps 'org-mode-map "o" 'counsel-org-goto)

It takes an optional :wrapping keyword argument that can be specified to use another definer instead of general-def:

(general-create-definer my-prefix-def
  :wrapping general-emacs-define-key
  :prefix "M-,")

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 "key" 'def ...)
;; define in the normal state auxiliary map for org-mode-map
(general-nmap org-mode-map "key" 'def ...)
;; same as
(general-nmap :keymaps 'org-mode-map "key" '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).

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-mode-map will take precedence over keys bound in any other minor mode keymaps. By default, general.el will automatically enable general-override-mode when binding a key in general-override-mode-map. If you would prefer to enable it manually (e.g. you wish to toggle it at some point), you can set general-override-auto-enable to nil.

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 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, affecting other buffers). When :states is specified with :keymaps 'local, evil-local-set-key will be used instead.

Note that binding directly in general-override-mode-map (i.e. no :states specified) is only useful for non-evil keybindings. Evil keybindings already override almost all normal emacs keybindings using the same method used here (i.e. evil keymaps are in emulation-mode-map-alists). The main exceptions where evil keybindings will be overridden by non-evil keybindings are noted here with explanations on how to deal with these cases. To understand which evil keybindings override others, review the precedence for evil keymaps. If you want a global evil keybinding to not be overridden by any other evil keymaps (e.g. overriding keymaps created in evil-integration.el or auxiliary keymaps created by some evil package), you can use intercept keymaps. 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:

;; keybindings that should not be overriden
(general-define-key
 :states 'normal
 :keymaps 'override
 :prefix "SPC"
 "f" 'find-file)

;; the above has precedence over the following (excerpt from evil-collection)
;; "SPC f" will still work as `find-file'
(evil-define-key 'normal transmission-mode-map
  (kbd "SPC") 'scroll-up-command)

Note that by default, evil keybindings made with :keymaps 'override will override even those made with :keymaps 'local.

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.

Functions/Macros to Aid Key Definition

Disclaimer

Key simulation (for general-simulate-key and general-key-dispatch but not for general-key) can result in duplicate keys being recorded for keyboard macros and evil repeating. To work around this issue, general.el will discard these duplicate keys during macro playback (i.e. executing-kbd-macro is non-nil). So far, this seems to be a reliable method for getting macros and repeating to work correctly with key simulation. However, it is hard (and maybe impossible) to test some of these cases automatically since it involves simulating keys that in turn simulate keys, and, for example, I haven’t found a way to correctly simulate recording a macro in these cases. Therefore, if you find any issues with macro playback or evil repeating when using general-simulate-key or general-key-dispatch, please make an issue.

Simulating Keypresses

General provides two macros called general-key and general-simulate-key that can be used to simulate key sequences. In some cases, they can be used similarly to keyboard macros, but they have some advantages. Unlike with a keyboard macro, prefix arguments will work for the command that ends up running. Also, the key simulated does not have to correspond to the full key sequence for a command. See here for information on an alternative method of doing some of the things these key simulation helpers can do using key-translation-map. I personally prefer general’s helpers as they are simple and more powerful.

Note that when a named prefix keymap/command exists (e.g. help-command), you should generally prefer to bind directly to that. However, this is not possible for a key like C-c whose definition varies depending on the buffer. Therefore, you need to use either general-key or general-simulate-key:

(general-nmap "SPC" (general-simulate-key "C-c"))
;; or
(general-nmap "SPC" (general-key "C-c"))

Although both will work correctly, which-key does not currently show all available keys when general-key is used, so I would currently recommend using general-simulate-key instead for an example like this.

On the other hand, general-key should be preferred for simulating a key that corresponds to a single command. Unlike general-simulate-key, which creates/returns a function, general-key expands to an extended menu item like general-predicate-dispatch. Using an extended menu item is a simpler and more direct approach as emacs will dynamically look up and act as the specified key. This 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 with general-key but not with general-simulate-key.

Another downside of general-simulate-key is that any commands/functions called just afterwards will actually be run before the keys are simulated. This won’t affect the most common use cases, but it makes setting up and tearing down a context more difficult (e.g. simulating a key in a specific evil state requires using post-command-hook for general-simulate-key but not for general-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"))
;; a keyboard macro works, but C-h k will not show the command docstring
(general-nmap "RET" "C-c C-c")

general-simulate-key and general-key also support keyword arguments to control the context the keys are simulated in (both support :state; general-simulate-key supports :keymap for now but I don’t know how useful it is; please make an issue if you think it would be useful to add :keymap to general-key). 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))

general-key also supports custom setup and teardown before key lookup. Here’s a similar example to the previous one:

(general-nmap "j" (general-key "C-n"
                    :setup (evil-local-mode -1)
                    :teardown (evil-local-mode)))

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"))). For example, the following is possible with general-simulate-key but not with general-key or a keyboard macro:

(general-nmap "s" (general-simulate-key ('evil-ex "s/")))

See the next section for another reasonable use case for this feature.

When a command is specified for general-simulate-key, 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 there is input lag, a timeout will not work well (this is also true for packages like key-chord.el). One example is vterm (even though there is not normally visible input lag). In vterm, the real amount of time you would have to wait after pressing “j” before pressing “k” is longer than 0.25 seconds. It is also likely that the next character you type will be input instead (e.g. “jo” would result in “oo”). There’s not much that can be done about the first problem. You can try lowering the timeout in a problematic mode. However, if the input lag is inconsistently present (e.g. caused by some minor mode) and/or severe, this probably won’t help much. You can at least address the second problem by explicitly specifying the character you want to insert:

(defun my-insert-j ()
  (interactive)
  (insert "j"))

(general-imap "j" (general-key-dispatch 'my-insert-j
                    :timeout 0.25
                    "k" 'evil-normal-state))

If input lag is an issue, :timeout can still be used as a visual enhancement. For example, you can bind SPC SPC to end a sentence if you don’t normally need to type two spaces anywhere else. This works without :timeout but is visually confusing since spaces are never be inserted until the next keypress. :timeout can be used to enhance such a keybinding:

(defun my-insert-space ()
  (interactive)
  (insert " "))

(defun my-sentence-end ()
  (interactive)
  (insert ".  "))

(general-def 'insert text-mode-map
  "SPC" (general-key-dispatch 'my-insert-space
          :timeout 0.1
          "SPC" 'my-sentence-end))

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 extended 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, and :destructive nil is comparable to vim’s noremap (where the “original” keybindings are those that existed in the keymap when general-translate-key was first used).

You’ll almost always want to use the default behavior (especially in your init file). The limitation of :destructive nil is that you can’t translate a key to another key that was defined after the first evil-collection-translate-key, so :destructive t may be useful for interactive experimentation.

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

Automatic Key Unbinding

To automatically prevent Key sequence starts with a non-prefix key errors without the need to explicitly unbind non-prefix keys, you can add (general-auto-unbind-keys) to your configuration file. This will advise define-key to unbind any bound subsequence of the KEY. Currently, this will only have an effect for general.el key definers. The advice can later be removed with (general-auto-unbind-keys t).

The reason that advice is used is because general-define-key does not always define keys in the same manner. Because customer definers are supported with :definer, general-define-key does not have the necessary information to handle every case itself.

As a final note, if you, for example, bind s to a command using general-define-key and then later bind s <key> to something, s will still show up in general-describe-keybindings even though it’s no longer bound. Since this is preventable by simply removing the initial unused keybinding, I likely will not try to add a workaround to fix this.

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. The reason you might want to use it instead of setq is that setq cannot correctly set all variables. 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). If the corresponding package has already been loaded, using setq will generally not work to set these variables. On the other hand, general-setq will correctly use the custom setter when necessary. One 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 really 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.

One major difference from customize-set-variable that you should be aware of is that general-setq falls back to using set instead of set-default. This means that, like setq, it will alter the local value of buffer-local variables instead of the default value.

general-setq-default and general-setq-local also exist but do not attempt to call custom setters. The reason for this is that I have never seen any custom setters for variables that make sense to set both globally and locally (custom setters I’ve seen just use set-default). setq-default is useful when you want to globally change the default for a buffer-local variable. setq-local is useful when you want to make a non-buffer-local variable buffer-local and then change its local value (setq already preferentially alters the buffer-local value of a variable if there is one). For now, the general.el equivalents are just aliases, but in the future, they will likely record user settings to be displayed in a table later.

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)

general-add-hook and general-add-advice can add “transient” functions to hooks or as advice. These transient functions will remove themselves from the hook or as advice after they run once (inspired by Doom Emacs). Additionally, they can remove themselves after the first time they return non-nil or after any arbitrary condition is met. For an example of this, see the implementation of general-after-gui.

Miscellaneous

general-after-init can be used to run code after initialization (e.g. (general-after-init (do-something) (do-something-else))). It just adds to after-init-hook or runs the code immediately if initialization has happened already.

general-after-gui and general-after-tty can be used to run some code once after the first graphical or terminal frame is created. Here is an example use case:

(use-package clipetty
  :ensure t
  :init
  ;; only need to load if create a terminal frame
  ;; `global-clipetty-mode' will not cause issues if enabled for a server with
  ;; both graphical and terminal frames
  (general-after-tty
    (global-clipetty-mode)))

These both use general-add-hook to create “transient” hooks.

Integration with Other Packages

Use-package Keywords

:general Keyword

General also optionally provides a use-package keyword. :general is similar to :bind in that it implies :defer t whenever there are bound commands that can be autoloaded (e.g. it will not imply :defer t if the only bound command is to a lambda, for example). Whenever autoloadable commands are bound, and the option general-use-package-emit-autoloads is non-nil, use-package will create autoloads for them (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).

:no-autoload Keyword

If generating autoloads for commands is not desirable, it can be disabled globally (with the general-use-package-emit-autoloads option), on a per-binding basis, or on a per-form basis. To skip generating autoloads for a command, use the extended command definition and set the :no-autoload option to non-nil. This can be particularly handy when binding to functions defined in the same use-package block, otherwise the byte-compiler complains about multiple definitions of the same function:

(use-package org
  :general
  (:states 'normal
   "SPC oa" '(my-org-agenda :no-autoload t))
  :preface
  (defun my-org-agenda ()
    (interactive)
    (let ((org-agenda-tag-filter-preset '("-drill")))
      (call-interactively #'org-agenda))))

The keyword can also be used at the global level, instructing general to skip autoloads for all the keybindings in a form:

:general
(:states 'normal
 :no-autoload t
 "SPC oa" #'my-org-agenda
 "SPC oc" #'my-org-capture)

If you wish to disable emitting autoloads with the general-use-package-emit-autoloads variable in a byte-compiled configuration, make sure it is set during macro-expansion time before the use-package declarations, with something like (eval-and-compile (setq general-use-package-emit-autoloads nil)).

Hook Keywords

General provides two alternatives to :hook that use general-add-hook called :ghook and :gfhook. Both take any number of arguments of symbols or lists. List arguments work the same for both; they correspond to a list of arguments for ~general-add-hook~. The primary difference between the two is that symbol arguments to :ghook are hooks, but they are functions for :gfhook (hence the f). Furthermore, :ghook usually implies :defer t, and :gfhook never implies :defer t. :ghook should be used when the general-add-hook is meant to trigger the loading of the package. :gfhook should be used when the general-add-hook is meant to trigger some function in response to the package’s mode being enabled (or toggled in the case of a minor mode). More simply put, :ghook is suited towards enabling minor modes, and :gfhook is suited towards performing setup once some mode has loaded. The use case for each is further explained below.

:ghook Keyword

:ghook is intended to be used to add a package’s minor mode enabling function to a user-specified hook, so that when hook is run, the package will be loaded and the mode enabled. This means that :ghook will usually imply :defer t. While it does not always imply :defer t, it will add any non-lambda functions to :commands (this is the same behavior as :hook). Though this is usually unnecessary (the commands probably already have autoloads), it will in turn imply :defer t.

Symbols specified with :ghook correspond to hooks, and the function to add to each hook is inferred from the package’s name (i.e. -mode is automatically added to the package name unless the package’s name already ends in -mode). For example, these are all the same:

(use-package rainbow-delimiters
  :ghook 'prog-mode-hook)

(use-package rainbow-delimiters
  ;; `general-add-hook' arglist: HOOKS FUNCTIONS &optional APPEND LOCAL
  ;; a missing FUNCTIONS argument will be replaced with inferred minor mode
  :ghook ('prog-mode-hook))

(use-package rainbow-delimiters
  ;; a null or non-symbol placeholder for FUNCTIONS will be replaced with
  ;; inferred minor mode command; this may be useful if you want to keep the
  ;; inferred command but also want to set the APPEND and/or LOCAL arguments
  ;; afterwards, e.g. ('prog-mode-hook nil t)
  :ghook ('prog-mode-hook nil))

(use-package rainbow-delimiters
  ;; the full arglist for `general-add-hook' can be specified
  ;; this is necessary if inference is not possible (see below for an example)
  :ghook ('prog-mode-hook #'rainbow-delimiters-mode))

(use-package
  ;; :commands implies :defer t
  :commands rainbow-delimiters-mode
  :init (general-add-hook 'prog-mode-hook #'rainbow-delimiters-mode))

If you are already familiar with :hook, you should note that there are quite a few syntactic differences between :ghook and :hook. Firstly, quoting the hooks and functions is required. Like :general uses the same syntax as general-def(ine-key) (unlike :bind), :ghook uses the same syntax as (general-)add-hook for both clarity and convenience. For example, the user may want to use a helper function/macro to generate the function(s) to add to the hook (see the :gfhook section for a specific example). The user may also want to specify a variable containing a list of hooks instead of an actual hook name:

(defconst my-lisp-mode-hooks
  '(lisp-mode-hook
    emacs-lisp-mode-hook
    clojure-mode-hook
    scheme-mode-hook
    ;; ...
    ))

(use-package lispy
  :ghook my-lisp-mode-hooks)

;; same as
(use-package lispy
  :ghook (my-lisp-mode-hooks))

;; same as
(use-package lispy
  ;;  `general-add-hook' can take a list of hooks for the HOOK argument
  :ghook ('(lisp-mode-hook
            emacs-lisp-mode-hook
            clojure-mode-hook
            scheme-mode-hook
            ;; ...
            )))

Furthermore, :ghook will not automatically add -hook to specified hook symbols (i.e. you must specify prog-mode-hook; prog-mode is not sufficient). This design decision is intended to help prevent confusion since :gfhook also exists, and its symbols correspond to functions (not hooks) that could also end in -mode (and could potentially not be sharp quoted). I don’t think the loss in conciseness is major, and hopefully this will help always make it immediately clear whether symbols correspond to functions or hooks.

Lastly, :hook only takes one argument, whereas :ghook can take an arbitrary number of arguments (just like :general):

(use-package lispy
  ;; any number of symbols (or lists) is allowed
  :ghook
  'lisp-mode-hook
  'emacs-lisp-mode-hook
  'clojure-mode-hook
  'scheme-mode-hook)

Note that if the function name cannot be inferred from the package name (i.e. the package name or the package name with -mode appended is not correct), you need to specify a full general-add-hook arglist:

(use-package yasnippet
  :ghook ('(text-mode-hook prog-mode-hook) #'yas-minor-mode))

:gfhook Keyword

:gfhook is intended to be used to specify functions to add to the package’s mode hook. The hook is inferred from the package’s name (by appending either -mode-hook or just -hook if the package’s name ends in -mode). If the hook cannot be inferred from the package name, then the full arglist must be specified just as with :ghook. Unlike :ghook, :gfhook never adds functions to :commands and therefore never implies :defer t. This is because the functions specified are ones that should be run when turning on (or toggling) the mode(s) the package provides. The specified functions are external to the package, could be called elsewhere, and therefore should not trigger the package to load. The following all have the same effect:

(use-package org
  ;; for a major-mode package, you might use :mode to imply :defer t (or just
  ;; use :defer t; or just `use-package-always-defer' which I personally prefer)
  :gfhook
  #'visual-line-mode
  #'my-org-setup
  ;; ...
  )

(use-package org
  :init
  (general-add-hook 'org-mode-hook #'visual-line-mode)
  (general-add-hook 'org-mode-hook #'my-org-setup))

;; this is also valid but less concise
(use-package org
  ;; specify null or non-symbol placeholder for HOOKS to use inferred hook
  :gfhook (nil (list #'visual-line-mode #'my-org-setup)))

(use-package org
  :init
  (general-add-hook 'org-mode-hook (list #'visual-line-mode #'my-org-setup)))

Like with :ghook, :gfhook still requires quoting, so you can use variables and function/macro calls to generate the function to add to the hook:

(defmacro disable (mode)
  `(lambda () (,mode -1)))

(use-package proced
  ;; must be in a `general-add-hook' argument list, so that it itself is not
  ;; considered one
  :gfhook (nil (disable visual-line-mode)))

Although you could use :gfhook to enable minor modes for some major mode (e.g. enable flyspell inside (use-package org)), it is probably more logical/organized to group these hooks along with their minor modes’ use-package declarations (e.g. using :ghook). :gfhook is more suited for setup functions. Expanding on the proced example:

(defun my-proced-setup ()
  (visual-line-mode -1)
  ;; not global; has to be run in buffer
  (proced-toggle-auto-update t))

(use-package proced
  :gfhook #'my-proced-setup)

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

The system that allows for the default keywords can also be extended by the user to support more keywords that can either directly alter the definition or just be used for side effects (like :which-key). An extended definition keyword can have any number of helper keywords (and can also be used as a helper keyword itself, e.g. :keymap). See User-defined Extended Definition Keywords for more information on creating new keywords.

Here are the keywords available by default (helper keywords are subitems; specific examples are given later):

  • :def - Implicit; this is paired with the actual definition (helper keyword; does not trigger any special behavior by itself)

“Type” specifiers:

  • :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 and/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

Note that every bindable definition must have :def, but general allows for shorthand where :def can be omitted or a “type” specifier can be used instead:

;; shorthand
'(swiper :wk "swipe")
;; rewritten to
'(:def swiper :wk "swipe")

;; shorthand
'(:keymap some-keymap)
;; rewritten to
'(:def some-keymap :keymap some-keymap)
;; same as
'(:def some-keymap :keymap t)

;; shorthand
'(:prefix-command my-prefix-cmd :prefix-map my-prefix-map)
;; rewritten to
'(:def my-prefix-cmd :prefix-command my-prefix-cmd :prefix-map my-prefix-map)

After the shorthand expansion, the type keywords are handled exactly the same as any other extended definition keyword.

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)
    • :keymap - When non-nil, general will not try to match a keymap symbol as if it was a command

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.

:keymap is the primary keyword that triggers this check. It can also be used as a helper keyword (e.g. for which-key). If the keymap already exists, general will not try to create an autoloaded keymap, and :package is not required.

:package is a helper keyword that can be specified locally within the extended definition or globally. When using the use-package :general keyword, it will automatically be specified.

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). However, see which-key’s which-key-enable-extended-define-key which provides another method for automatically creating replacements and binding keys simultaneously.

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). Finally, which-key allows for a special replacement of t to prevent a key from being shown in the which-key popup at all.

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
 :prefix "SPC"
 :keymaps 'normal
 ;; 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"))
 ;; toggle lispy; use a function as a replacement to show if currently on
 "l" '(lispy-mode :wk my-lispy-which-key-display)
 ;; for a keymap, only the keys will be matched;
 ;; :no-match-binding is not necessary
 "p" '(:keymap projectile-command-map :wk "projectile prefix")
 ;; don't display this keybinding at all
 "z" '(hidden-command :wk t)
 ...)

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

Note that the default for commands without a repeat property are treated the same as commands with :repeat t, so the above repeat configuration isn’t explicitly necessary in this case.

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

Whenever this keyword is specified, general calls the corresponding function with the arguments state keymap key edef kargs. Generally, you can ignore at least some of these arguments. 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. edef is the extended definition itself, and kargs is the plist of all the keyword arguments given to the original general-define-key.

Extended definition functions can optionally alter the definitions. Keywords that have this behavior must be added to either general-rewrite-def-keywords or general-rewrite-def-after-keywords instead of to general-extended-def-keywords. The difference between the two is that the former will alter the definition before the functions for the keywords in general-extended-def-keywords are called. Functions that alter the definition should return a new extended definition plist with the :def entry updated. For a simple example of a function that does not alter the definition, see general-extended-def-:properties. For a simple example of a function that does alter the definition, see general-extended-def-:predicate.

Extended definition keywords may use any number of helper keywords. These do not need to be added to any variables but should be distinct from any other keywords.

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 to get the local value or the global value if there is no local one (e.g. (general--getf edef kargs :predicate)). If it does not make sense for your keyword to be specified globally, you can add it to general-extended-def-global-ignore-keywords. This will prevent your function from being called unless the keyword is specified locally.

Although general--get-keymap and general--getf are marked internal, they will continue to exist and keep their current functionality; they are intended to be used as helpers for extended definitions.

You can rely on edef being a valid extended definition plist with a :def keyword. Even if the user only specifies a keyword globally and does not explicitly write definitions as plists or explicitly specify :def, general will automatically rewrite definitions to be valid plists. Consider the following example:

(general-define-key
 :predicate '(eobp)
 "<right>" 'beginning-of-buffer)
;; call `general-extended-def-:predicate' with this as an edef argument:
'(:def beginning-of-buffer)

(general-define-key
 "<right>" '(beginning-of-buffer :predicate (eobp)))
;; call `general-extended-def-:predicate' with this as an edef argument:
'(:def beginning-of-buffer :predicate (eobp))

For more information, see the docstring of general-extended-def-keywords.

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 generally useful when you want to use some package-specific key definer that has some additional functionality (e.g. lispy-define-key).

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 in its final form (though the definer may also alter it before binding it). On the other hand, orig-def is the original definition but always as an extended definition plist (e.g. =’(:def command)= if the user only specified =’command=).

Like extended definitions, custom definers can have any number of helper keyword arguments specified locally in an extended definition or globally in the arguments to general-define-key. In cases where a keyword can be both global and local, general--getf is a useful helper function. Since the keymap passed in is a symbol, general--get-keymap may be useful as well for transforming it to the keymap value. 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]).

See general-lispy-define-key for a basic example.

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.

How do I prevent Key sequence starts with non-prefix key errors?

By default, emacs does not support binding a key sequence where a subsequence of the key is already bound in the same keymap (e.g. you cannot bind C-a a to a command in a keymap where C-a is already bound to a command).

If you want to be able to bind both key sequences and fall back to the shorter key’s command after a timeout or unmatched keypress, see general-key-dispatch.

Otherwise, you should unbind the non-prefix key. For example:

(general-define-key
 :keymaps 'normal
 :prefix "s"
 ;; prefix keys are prepended to other keys, so "" refers to the prefix itself
 "" nil
 "a" #'def
 ;; ...
 )

If you would rather force key definitions to always be made regardless of whether a subsequence of the key is already bound, general can automatically unbind keys when necessary to prevent this error.

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 Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK