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).
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:
- annalist - Replaces general’s key recording/displaying utilities in a generic way (already on MELPA)
- once - Better deferred evaluation utilities (like those provided in Doom); the main use case is to simplify autoloading packages (replaces general helpers like
general-after-gui
,general-after-init
, andgeneral-with-package
) - satch.el - More convenient helpers for settings, hooks, and advice (replaces
general-setq
,general-add-hook
, andgeneral-advice-add
) - arcana - Miscellaneous key definitions helpers (to create commands/definitions not keybindings; replaces
general-key
,general-simulate-key
,general-key-dispatch
,general-predicate-dispatch
, etc.) - usul - Library/dsl for creating key definers (using context-sensitive local macros; basically setup.el but for keybindings)
- familiar - general-like key definer built on top of usul
- imp - evil support for familiar
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.
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.
- Breaking changes to syntax and extension system are necessary to improve the package, and I can’t make breaking changes in general.el without breaking people’s configurations.
- General.el does too many things. Many users think it only provides keybinding helpers or are overwhelmed by the size of this readme. It should be split into more focused packages for simplicity and clarity and so that users can use only the packages they need.
- The implementation is inflexible. Evil support, for example, is hardcoded rather than added in a separate package using the extension system. The new implementation will be built on top of a library that will do all the heavy lifting but be syntax-agnostic. The library could be used to build a definer with any syntax (e.g.
bind-key
syntax), so if I decided in the future that some syntax was preferable to general’s, a full rewrite would not be necessary. - General.el is not transparent. A large amount of the opened issues are questions caused by a misunderstanding normal keybindings work or how general translates to normal keybindings (e.g. questions about things that happen even if you don’t use general but just
define-key
orevil-define-key
). The rewrite will provide an “instructor” (leto.el) that will generate a readable expansion given a key definition form to show you the actual code that will end up running (e.g.define-key
). This will help new users learn what the key definer is actually doing and will help determine whether issues are actually bugs or not.
For a more detailed explanation, see issue 497.
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.
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.
: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.
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.
- Project Status
- About
- Dependency Versions
- Key Features
- Reading Recommendations
- Best Practices
- Basic Examples
- ~general-define-key~ Details
- Override Keymaps and Buffer Local Keybindings
- Displaying Keybindings
- Functions/Macros to Aid Key Definition
- Non-keybinding-related Configuration Helpers
- Integration with Other Packages
- Extended Definition Syntax
- User-defined Key Definers
- FAQ
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.
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.
- Provides a single function,
general-define-key
, that is usable for all key definition; wrappers are provided as well - Does not hide important details of key definition (unlike
evil-leader.el
); users should be familiar withdefine-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 (likeevil-define-key
,bind-map
,evil-leader.el
, etc.; unlikebind-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
andglobal-set-key
(no positional keymap argument)define-key
andevil-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 ofgeneral-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 withdefine-key
) - Allows using an arbitrary number of prefix keys or “leaders” of any length (but does not require prefix keys like) (unlike
evil-leader.el
) - Allows for automatically creating prefix commands (but does not require creating them like
bind-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
) (likeevil-define-key
) though it is slow compared toeval-after-load
and therefore not recommended - Allows displaying defined keys (like
bind-key.el
) - Provides integration with other packages such as
key-chord.el
andwhich-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 losingj
) - A helper to create a menu item to dispatch to different definitions based on predicates
- A method for creating “autoloaded” keymaps (like
- Provides other helpers for configuration (e.g. more convenient functions for hooks and advice)
- Is well tested (unlike
evil-leader.el
)
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.
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 like define-key
and evil-define-key
(general-emacs-define-key
, general-evil-define-key
, and general-def
). 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 mostly 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, 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 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 for commonly asked questions
To summarize, my recommended usage of general.el looks like this:
- Use
general-def
, other positional definers, and your own definers created withgeneral-create-definer
- Use
use-package
or a similar helper - Use
:general
for keybindings meant to load a package - Use
:general-config
or:config
for other keybindings - Do not use use the
:which-key
extended definition keyword unless you absolutely need to (see Which Key Integration for details) - Follow the other recommendations in Will general.el slow my initialization time?
From a stylistic perspective (completely personal preference) I:
- Explicitly use the command name with
:general
, e.g.:general (general-def <keymap> ...)
instead of:general (<keymap> ...)
. This allows individually evaling the forms or moving them elsewhere without having to change them. - Sharp quote commands (e.g.
#'execute-extended-command
) but not lambdas
(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)
(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)
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.
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
.
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
.
: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)
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 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" <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.
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.
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.
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)
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.
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-,")
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 ...)
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).
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
.
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
.
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.
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.
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
).
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.
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")
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.
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.
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.
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
.
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.
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).
: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.
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))
.
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
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
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)
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
.
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 tot
globally):wk-match-binding
- Whether to include the binding in the match cons (defaults tot
; also global):wk-full-keys
- Whether the bound keys correspond to the full sequence to match (defaults tot
; 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.
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.
NOTE: Which-key integration was added to general before which-key had 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" <original definition>)
, e.g. (general-def "<key>" '("<description>" . <definition>))
. The definition can be a command, prefix map, or whatever. Please see the which-key documentation for more details.
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 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)
...)
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.
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
.
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.
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.
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
.
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
.
To use lpy-define-key
to make the definitions, :definer 'lpy
can be specified.
There is an open issue where I talk about improving performance when rewriting general.el (and a “no overhead” experiment), but using general.el should not cause a major increase in startup time if you follow the guidelines listed here (see #180 and #502 for the original issues).
- Don’t use general.el’s deferred keybinding functionality
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)
- Bind keys in a prefix map instead of always using
:prefix
,:non-normal-prefix
, and/or:global-prefix
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)
- Defer your keybindings and configuration where possible
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.
- Set
general-emit-autoloads
tonil
(this may not significantly improve startup time)
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.
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.
This is a known issue for evil. To work around this problem, you can use :definer ‘minor-mode. See here for more information.