[[https://melpa.org/#/general][file:https://melpa.org/packages/general-badge.svg]] [[https://github.com/noctuid/general.el/actions?query=workflow%3Atest][https://github.com/noctuid/general.el/workflows/test/badge.svg]] [[https://codecov.io/gh/noctuid/general.el][https://codecov.io/gh/noctuid/general.el/branch/master/graph/badge.svg]]
[[https://github.com/noctuid/general.el][file:http://i.imgur.com/SXA66y7.png]]
A general is a leader. -- onioncheese
The joke from when I worked in Google was "deprecated means stable" -- romwell
General.el will eventually (maybe a decade from now) be superseded by a rewrite, or I will recommend a different package instead. However this does not mean I will "deprecate" general, only that I will not add new features and will recommend the rewrite to any newcomers. If you are already happy with general, stick with it. It has been stable for years and had very few new bug reports or feature requests. I will not remove the package from MELPA, just limit changes to bug fixes. All existing feature requests for general.el will either be implemented in the rewrite or have open issues on its tracker. If you need any new functionality, you should switch (or use both as you gradually transition, e.g. the rewrite for new keybindings or specific cases).
** Roadmap To rewrite general.el, I have split its functionality into separate packages that I will incrementally develop and add to MELPA roughly in this order:
The current status is that once.el is nearly ready to be on MELPA. If you are interested in using once.el, feel free to bump issues there to remind me to finish it/put it on MELPA. The other packages are very low priority to me, and I don't have plans to work on them for now.
** Making the Most of General
Eventually I will segment general, so you can optionally only load the keybinding helpers, but this won't really save much time when loading it.
** Why Rewrite
For a more detailed explanation, see [[https://github.com/noctuid/general.el/issues/497#issuecomment-1120235049][issue 497]].
** 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][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 :noexport:TOC:
[[#project-status][Project Status]]
[[#about][About]]
[[#dependency-versions][Dependency Versions]]
[[#key-features][Key Features]]
[[#reading-recommendations][Reading Recommendations]]
[[#best-practices][Best Practices]]
[[#basic-examples][Basic Examples]]
[[#general-define-key-details][~general-define-key~ Details]]
[[#override-keymaps-and-buffer-local-keybindings][Override Keymaps and Buffer Local Keybindings]]
[[#displaying-keybindings][Displaying Keybindings]]
[[#functionsmacros-to-aid-key-definition][Functions/Macros to Aid Key Definition]]
[[#non-keybinding-related-configuration-helpers][Non-keybinding-related Configuration Helpers]]
[[#integration-with-other-packages][Integration with Other Packages]]
[[#extended-definition-syntax][Extended Definition Syntax]]
[[#user-defined-key-definers][User-defined Key Definers]]
[[#faq][FAQ]]
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):
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~) though it is slow compared to ~eval-after-load~ and therefore not recommended
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):
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 [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Key-Bindings.html][emacs' key binding system]]. I recommend reading [[https://www.masteringemacs.org/article/mastering-key-bindings-emacs][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 [[https://github.com/noctuid/evil-guide][evil guide]]. The [[https://github.com/noctuid/evil-guide#keybindings-and-states][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.
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, it is recommended you use it with ~with-eval-after-load~ or use-package's =:config= keyword instead. This is because while the deferring mechanism works, it is much slower than using ~eval-after-load~. See [[#will-generalel-slow-my-initialization-time][Will general.el slow my initialization time?]] for more information on ensuring you are not unnecessarily slowing down Emacs initialization.
See also the rest of [[#faq][FAQ]] for commonly asked questions
To summarize, my recommended usage of general.el looks like this:
From a stylistic perspective (completely personal preference) I:
Explicitly use the command name with =:general=, e.g. ~:general (general-def
Sharp quote commands (e.g. ~#'execute-extended-command~) but not lambdas
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.
** 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-definition-syntax]["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 [[#keymapstate-aliases][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 [[#override-keymaps-and-buffer-local-keybindings][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][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 [[#creating-new-key-definers][~general-create-definer~]] and write the definition like this:
(my-normal-and-insert-define-key "/" 'swiper)
If you will be creating a definer and making a lot of keymaps it is recommended you use a prefix keymap instead of just the prefix keywords for performance reasons (see the example in [[#will-generalel-slow-my-initialization-time][Will general.el slow my initialization time?]]).
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"
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)
"
~
See [[http://endlessparentheses.com/define-context-aware-keys-in-emacs.html][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 [[#keymapstate-aliases][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 [[https://github.com/emacs-evil/evil-collection/blob/9fc1a19807dfcd0cc2b221832b6e6faad80a291d/evil-collection-util.el#L32][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 o" 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:
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-and-buffer-local-keybindings][Override Keymaps]]).
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 [[https://github.com/noctuid/evil-guide#what-overrides-evil][here]] with explanations on how to deal with these cases. To understand which evil keybindings override others, review the [[https://github.com/noctuid/evil-guide#keymap-precedence][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~.
** 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 [[https://www.emacswiki.org/emacs/Evil#toc14][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, [[https://github.com/justbur/emacs-which-key][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 =
(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 [[https://www.emacswiki.org/emacs/key-chord.el][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 "
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. [[https://github.com/jojojames/evil-collection][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
** 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.
(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 [[http://emacs.stackexchange.com/q/10230/5278][this emacs stackexchange question]] and Fuco1's modified ~lisp-indent-function~ in one of the answers there).
*** :general-config Keyword =:general-config= is the same as =:general= except it will be run after loading the package (after anything in =:config= is run) and will never generate any autoloads. You can use =:general= for keybindings meant to load the package and =:general-config= for keybindings only needed after the package is loaded. This will reduce Emacs initialization time if you make a lot of keybindings.
*** :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 [[#hooks-and-advice][~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-keyword][: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
;; ... )
(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~.
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][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):
"Type" specifiers:
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):
Evil command properties (see below for more details):
Global keywords that can be overridden locally:
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 NOTE: Which-key integration was added to general before which-key had [[https://github.com/justbur/emacs-which-key#keymap-based-replacement][keymap-based replacement]]. It is recommended that you never* use general's =:which-key= when it is possible to use keymap-based replacement instead. This is because keymap-based replacement is more performant and will give correct results in cases where a basic =:which-key= will not. The exception where ~which-key-replacement-alist~ is still very useful is when you need more advanced matching/replacement capabilities or want to replace what shows up for the key, not just the definition. Most users should never need =:which-key=, and any basic =:which-key "string"= (doesn't change the text shown for the key) can definitely be swapped to use keymap-based replacement.
To use keymap-based replacement, just bind your key to a cons cell in the form ~(cons "key description"
The rest of this section is related to general's =:which-key= keyword and should be ignored if you use keymap-based replacement.
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 [[https://github.com/justbur/emacs-which-key#automatic][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):
=: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 [[https://github.com/noctuid/evil-guide#command-properties][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-:
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-:
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)
"
(general-define-key
"
For more information, see the docstring of =general-extended-def-keywords=.
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-
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 [[https://github.com/noctuid/evil-guide#why-dont-keys-defined-with-evil-define-key-work-immediately][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.
General.el borrows a generic keybinding deferring mechanism from ~evil-define-key~. If a specified keymap does not exist when ~general-define-key~ is called, it will repeatedly check if that keymap now exists after loading something new until it does (e.g. see issue #180). It is much more efficient to use ~with-eval-after-load~, the =use-package= =:config=, the =:general-config= keyword, or any other similar functionality instead.
;; If you don't load evil (or any package that creates a keymap you are binding ;; keys in) immediately, do one of these (with-eval-after-load 'evil (general-def 'normal ";" #'evil-ex))
(use-package evil :general-config ('normal ";" #'evil-ex))
(use-package evil :config (general-def 'normal ";" #'evil-ex))
;; not this! (general-def 'normal ";" #'evil-ex)
This applies even if you are not using evil and only use =:prefix=, though it will not be nearly as much of an issue (unless you are binding in multiple keymaps). Where possible, you may have some improvements by using prefix keymaps so general.el does not have to concatenate the prefix to every key.
In addition to requiring concatenating each prefix to all keys, the prefix keywords also require making separate keybindings for every single listed keymap. Having 2+ prefix keywords will also double the number of keybindings required (e.g. if you specify =:global-prefix= and =:prefix=, it will create 1 keybinding for each in normal state). This means if you have 3 states and 2 or 3 prefix keywords, 6x the keybindings will be required.
Make prefix keybindings in a named prefix keymap when possible instead so that only a single ~define-key~ is required under the hood:
;; this will bind the prefixes to `my-prefix-map' (general-define-key :states '(emacs insert normal) :prefix-map 'my-prefix-map :global-prefix "C-c" :non-normal-prefix "M-SPC" :prefix "SPC")
(general-create-definer my-map :keymaps 'my-prefix-map)
;; this will make one keybinding that will result in all the following keys ;; being bound to 'foo: ;; - C-c f in all states ;; - SPC f in normal state ;; - M-SPC f in emacs and insert states (my-map "f" 'foo)
;; don't do this (6x the keybindings)! (general-create-definer my-map :states '(emacs insert normal) :prefix-map 'my-prefix-map :global-prefix "C-c" :non-normal-prefix "M-SPC" :prefix "SPC") (my-map "f" 'foo)
This applies regardless of whether you are using general.el but should be mentioned for completeness. Only use =:general= for keybindings that are meant to load a package (e.g. ~magit-status-here~). For keybindings that cannot be used or are not needed immediately (e.g. ~magit-stage~), use ~with-eval-after-load~, =:config=, or =:general-config= to defer making them until the package has loaded. If you have a lot of keybindings/packages, this will shave some time off initialization.
While this may not have a significant impact, this functionality should not be needed for most packages. If a package does not properly create autoloads for its commands already, you should make an issue asking the maintainer to add autoload cookies.
** 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 [[#mapping-under-non-prefix-keys][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, [[#automatic-key-unbinding][general can automatically unbind keys when necessary to prevent this error]].
** Why don't some evil keybindings work (immediately)? This is a [[https://github.com/emacs-evil/evil/issues/301][known issue for evil]]. To work around this problem, you can use [[#wrapping-evil-define-minor-mode-key][:definer 'minor-mode]]. See [[https://github.com/noctuid/evil-guide#why-dont-keys-defined-with-evil-define-key-work-immediately][here]] for more information.