- Show, don’t tell
- Upstreams
- About =bind=
- Comparison with other key binders
- Package configurator support
- FAQ
Key bindings live in keymaps and keymaps are active depending on active major mode and minor modes.
You have define-key
function in Emacs, which binds a command to a key sequence in a map.
(define-key my-window-map "d" #'delete-window)
This is how you do it with bind
.
(bind my-window-map "d" #'delete-window)
First I have to define my-window-map
.
(bind (setq my-window-map (make-sparse-keymap)) "d" #'delete-window)
If you want to bind many commands to keys,
(bind my-window-map
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
If you want to bind those bindings in multiple maps,
(bind (my-window-map my-other-window-map)
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
I will be using those keymaps in multiple places.
(defun my-window-keymaps ()
(list my-window-map my-other-window-map))
(bind (my-window-keymaps)
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
I want to bind my-open-map
I previously defined.
(bind my-window-map
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right
"o" my-open-map)
Jeez, I forgot that I was going to do it in my-leader-map
.
(bind (my-window-map
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right
"d" #'delete-window)
(my-leader-map
"o" my-open-map))
I want to prefix each window command with w
key.
(bind my-window-map
(bind-prefix "w"
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right))
Never mind, I will just bind my-window-map
to w
in my-leader-map
.
(bind (my-window-map
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
(my-leader-map
"o" my-open-map
"w" my-window-map))
Shoot, something went wrong let me undo changes.
(bind-undo (my-window-map
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
(my-leader-map
"o" my-open-map
"w" my-window-map))
I just want to unbind keys.
(bind-undo my-window-map "h" "j" "k" "l")
Hey, I want to make use of repeat-mode
, when enabled, for my window keys.
(bind my-window-map
(bind-repeat
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right))
Let’s bind some keys for vertico package.
(bind vertico-map
"M-j" #'vertico-next
"M-k" #'vertico-previous
"M-J" #'vertico-next-group
"M-K" #'vertico-previous-group
"M->" #'vertico-scroll-up
"M-<" #'vertico-scroll-down
"C->" #'vertico-last
"C-<" #'vertico-first)
Hmm, can I prefix those with a modifier key?
(bind vertico-map
(bind-prefix "M-"
"j" #'vertico-next
"k" #'vertico-previous
"J" #'vertico-next-group
"K" #'vertico-previous-group
">" #'vertico-scroll-up
"<" #'vertico-scroll-down)
(bind-prefix "C-"
">" #'vertico-last
"<" #'vertico-first))
Good! Let’s autoload vertico when a command is called in my-leader-map
that is not yet loaded (and not autoloaded by package).
(bind my-leader-map
(bind-autoload :vertico
"r" #'vertico-repeat))
I’ve gone mad. I want to put window movement commands under key m
and layout commands under l
.
(bind my-window-map
(bind-prefix "m"
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
"d" #'delete-window
"D" #'delete-other-windows
(bind-prefix "l"
"b" #'split-window-below
"r" #'split-window-right))
Hmm, it would be good if I could also repeat them and just autoload layout commands.
(bind my-window-map
(bind-repeat
(bind-prefix "m"
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
"d" #'delete-window
"D" #'delete-other-windows
(bind-autoload :my-package
(bind-prefix "l"
"b" #'split-window-below
"r" #'split-window-right))))
Let’s bind my-leader-map
to global map at the end.
(bind (my-window-map
(bind-repeat
(bind-prefix "m"
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
"d" #'delete-window
"D" #'delete-other-windows
(bind-autoload :my-package
(bind-prefix "l"
"b" #'split-window-below
"r" #'split-window-right))))
(my-leader-map
"o" my-open-map
"w" my-window-map)
((current-global-map)
"SPC" my-leader-map))
This is all good. I get that they are just functions evaluating arbitrary code that take bindings and return bindings in the end. But they are a bit verbose and I don’t usually evaluate them myself for debugging. Is there a concise way?
(bind (my-window-map
(:repeat
(:prefix "m"
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
"d" #'delete-window
"D" #'delete-other-windows
(:autoload :my-package
(:prefix "l"
"b" #'split-window-below
"r" #'split-window-right))))
(my-leader-map
"o" my-open-map
"w" my-window-map)
((current-global-map)
"SPC" my-leader-map))
Can I provide a help string like in define-key
?
(bind my-window-map
"d" (cons "Delete current window" #'delete-window))
Great! So it is define-key
like at the end. Now I want to bind a command in c-mode
locally.
(add-hook 'c-mode-hook
(lambda ()
(bind (bind-local-map)
"k" #'my-command)))
Hmm, (bind-local-map)
is a function and seems to be returning a keymap just like how local-set-key
does. Is there a global counterpart, just to complement each other?
(bind (bind-global-map) "SPC" my-leader-map)
(bind (:global-map) "SPC" my-leader-map) ; same
Can I still remap a command just like in define-key
?
(bind help-map [remap define-function] #'my-define-function)
All is good, but sometimes I do mistakes and lose my previous bindings. Is there a way I can safely bind
keys?
;; create a save of current definitions
(setq save
(bind-save (bind-global-map)
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right))
;; bind new definitions
(bind (bind-global-map)
"h" #'windmove-left
"j" #'windmove-down
"k" #'windmove-up
"l" #'windmove-right)
;; restore old definitions
(bind-restore save)
Available on Melpa.
Syntax is (bind FORM)
or (bind (FORM)...)
so (FORM)
is
repeatable.
FORM
’s first element can be a keymap, list of keymaps, a function
returning keymap (setq
) or keymaps (a user function). It is
quoted, if it is a keymap or a list of keymaps.
FORM
’s rest elements must be bindings. A binding is in the form
of KEY DEF
where KEY
and DEF
has the same specs as in
define-key
, in the case of bind
.
Here are some gists about bind
.
- Every key binding in Emacs lives in a key map. Instead of providing different functions for specific cases,
bind
suggests one function. - Putting multiple
bind
forms in onebind
call is same as calling each one on its own. - There are processing functions like
bind-prefix
,bind-autoload
etc. which takes bindings and acts on them and returns bindings, possibly modified. Those can be nested as however wanted.bind
carries information, metadata, at upper levels to lower levels and then processing function propagates backwards. - Processing functions are just any other arbitrary functions you can evaluate arbitrary code.
All atoms in bind
form can instead typed without package prefix (without bind-
or bind--
) in keyword form. For example, bind-prefix
can be written as :prefix
and bind-global-map
as :global-map
etc.
You lose the ability of evaluating them without expanding the macro but this is not needed most of the time except debugging.
Simplest processing function, prefixes each key with given prefix. Understands modifier prefixes.
Support for repeat-mode
. Puts repeat-map
property to definitions in bindings for bind :main
property in metadata, unless a target map is given. Note that definitions also need to be in that target map for repeat-mode
to work.
Make sure repeat-mode
is enabled.
Autoload definitions in bindings. If first argument to function is a symbol, then autoload that feature. Otherwise try to retrieve :main-file
prop from metadata.
bind
doesn’t provide that prop but package configurators usually have that info which they can provide it in their bind
support.
Return a save of current definitions instead of binding them in bind
FORM. This way, you can safely use bind
and restore in case the result is unwanted.
Restores a save returned from bind-save
.
unbind
sounds nice with bind
instead of bind-undo
. It is not called that way because package conventions but no one is limiting you.
At the end of everything, bind--definer
is called with KEYMAP
, KEY
and DEF
(arguments to define-key
). You can lexically change that variable and call bind
in your own function for custom behaviors.
bind--metadata
is a lexical plist that carries information populated by upper bind calls to use from lower bind calls (nesting wise) so that information isn’t repeated.
bind
provides following properties.
Calling top level bind macro when bind
is not used.
For example, its value will be nil
if bind
is used and 'bind-undo
if bind-undo
is used.
Processor functions take different actions depending on different engines. For example bind-repeat
my remove repeat property from definitions instead of putting them when bind-undo
is used.
Contains the main keymap for current bind
forms.
Following is the logic for resolving bind main, in order,
BIND-FIRST
is the first element of bind FORM
.
- If
BIND-FIRST
is a keymap thenBIND-FIRST
- If
BIND-FIRST
a function call then- If
BIND-FIRST
is a call to'bind-safe
function (a symbol that has'bind-safe
prop), then first of it is output - Otherwise first argument to function call (like to
setq
).
- If
- Otherwise first element of
BIND-FIRST
.
Only put ‘bind-safe to a function if function doesn’t mutate data.
See bind-autoload
for a use case.
All a processor function must do is taking bindings and returning them, possibly modified. While doing so it can provide other utilities through bindings.
Users are encouraged to define their own function which they can then evaluate arbitrary code.
User is encouraged to make use of bind-keyp
, bind-foreach-key-def
, bind-flatten1-key-of-bindings
and bind-with-metadata
utility functions for their custom behavior. See default processing functions’ definitions for examples.
bind-flatten1-key-of-bindings
is especially useful because processing functions shouldn’t assume bindings will be in (KEY DEF)+
but ((KEY DEF)|((KEY DEF)+))+
form due to inner processing functions returning bindings in a list.
You should also put indentation to its synonym for proper indentation. For example, bind-prefix
defines,
(put :prefix 'lisp-indent-function 1)
See a processing function I use here.
In the extensions/ directory, you can grab your particular configuration. These won’t be installed by default so you have to manually install them or through your package manager if it has a support for this.
See (bind-setup-integrate keyword)
which will add KEYWORD to setup
. Please read commentary for what it brings.
See (bind-use-package-integrate keyword &optional anchor after test)
which will add KEYWORD to use-package
. Please read commentary for what it brings.
I’ve used evil-mode
before but I am not sure of its requirements or why it needs a special treatment. I suppose a smart function/macro returning maps based on synonyms should be enough. I am open to talk about it.