Next: Introduction, Previous: (dir), Up: (dir) [Contents][Index]
This is the nkeymaps Reference Manual, version 1.0.0, generated automatically by Declt version 4.0 beta 2 "William Riker" on Mon Aug 15 05:27:58 2022 GMT+0.
Next: Systems, Previous: The nkeymaps Reference Manual, Up: The nkeymaps Reference Manual [Contents][Index]
#+TITLE: NKeymaps #+SUBTITLE: General-purpose keymap management This keymap library is inspired by [[https://savannah.nongnu.org/projects/emacsy][Emacsy]] (=keymap.scm=), which in turn is inspired by Emacs. The main types are: - A =key= structure has a hardware =code=, a symbolic =value=, an ordered set of modifiers (the order is to avoid inequality on permutation) and possibly more in the future. Whitespaces are the only reserved characters. Anything else can be stored in the =key= value. - A =keyspec= is a string representation of a =key=. A =keyspec= is a space-separated string representation of a sequence of =key=s. - A =keymap= contains a hash-table where the keys are a =keyspec= and the values are either a keymap or anything else. As a special case, a =nil= value unbinds the key. ** Goals - Support prefix keys to other keymaps. For instance, prefixing my-mode-map with =C-c= makes all bindings for my-mode accessible after pressing =C-c=. - List all bindings matching a given prefix. (Also known as =which-key= in Emacs.) - List the bindings associated to a command. - Support multiple inheritance. - Support keycode. - Validate keyspec at compile time. - ~define-key~ can set multiple bindings in a single call. - Support multiple keyschemes to make it easy to switch between, say, Emacs-style and VI-style bindings. This orthogonality to keymaps composes better than having multiple keymaps: changing keyscheme applies to the entire program, which is easier than looping through all keymaps to change them. - Translate keyspecs as a fallback. For instance if =shift-a= is unbound, check =A=. - Customizable behaviour with global parameters such as ~*print-shortcut*~. - The ~compose~ function can merge multiple keymaps together. - Support multiple arguments when that makes sense (e.g. multiple keymaps for ~lookup-key~). - Key remapping à-la Emacs. - Typed keymaps, i.e. keymaps where bound values can only be of a given type. This is convenient to catch typos, for instance when binding ='FOO= instead of =#'FOO=. ** Non-goals - Customizable modifiers: the input system must decide how to map =meta=, =control=, etc. - Dead key support: the input system must handle it. - Fallback function when binding is not found: the caller of =lookup-key= gets =nil= when unbound, thus it can decide of a default behaviour. - Provide a self-insert function: the caller must handle it. - Global or local bindings: it's up to the calling application to manage the locality of their keymaps. ** Example #+begin_src lisp (let* ((parent-keymap (nkeymaps:make-keymap "parent-keymap")) (my-keymap (nkeymaps:make-keymap "my-keymap" parent-keymap))) (nkeymaps:define-key parent-keymap "C-c" 'copy "C-v" 'paste) (nkeymaps:define-key my-keymap "C-x" 'cut) (values (nkeymaps:lookup-key "C-x" parent-keymap) (nkeymaps:lookup-key "C-x" my-keymap) (nkeymaps:lookup-key "C-c" my-keymap))) ;; => NIL, CUT, COPY #+end_src See the [[file:package.lisp][package]] documentation for a usage guide and more examples. ** History NKeymaps was originally developed for keymap management in [[https://nyxt.atlas.engineer][Nyxt]], so the "N" may stand for it, or "New", or whatever poetic meaning you may find behind it! ** Road-map - [ ] Lookup order (for instance parent-first or next-keymap-first) should be customizable. - [ ] For now =*translator*= is a global, but ideally it would be part of the keyscheme and keymap. But this would impact the lookup order, since translations come after all the keymaps and their parents have been searched for. ** Change log *** 1.0.0 - Renamed =scheme-name= to =keyscheme= and =scheme= to =keyscheme-map=. It's more consistent and intuitive. The previous naming was really confusing. - All warnings have now their own conditions, see the =nkeymaps/conditions= package. - =define-keyscheme-map= has a different syntax, it's now #+begin_src lisp (define-keyscheme-map "NAME-PREFIX" (:import OPTIONAL-KEYSCHEME-MAP-TO-IMPORT) KEYSCHEME BINDINGS...) #+end_src - The predefined =keyscheme=s are now accessible from the =nkeymaps= package. - New =default= =keyscheme= which is the new parent of other keyschemes (including =cua=), instead of =cua=. - =*modifier-list*= is no longer exported. Instead, both =keyscheme= and =keymap= have a =modifiers= slot for the modifiers they accept. - Switched testing framework from =Prove= to =Lisp-Unit2=. - Removed the =cl-str= dependency.
Next: Files, Previous: Introduction, Up: The nkeymaps Reference Manual [Contents][Index]
The main system appears first, followed by any subsystem dependency.
General-purpose keymap management à-la Emacs.
Atlas Engineer LLC
BSD 3-Clause
1.0.0
Next: Packages, Previous: Systems, Up: The nkeymaps Reference Manual [Contents][Index]
Files are sorted by type and then listed depth-first from the systems components trees.
Next: nkeymaps/types.lisp, Previous: Lisp, Up: Lisp [Contents][Index]
nkeymaps (system).
Next: nkeymaps/conditions.lisp, Previous: nkeymaps/nkeymaps.asd, Up: Lisp [Contents][Index]
nkeymaps (system).
list-of (type).
Next: nkeymaps/package.lisp, Previous: nkeymaps/types.lisp, Up: Lisp [Contents][Index]
types.lisp (file).
nkeymaps (system).
Next: nkeymaps/keymap.lisp, Previous: nkeymaps/conditions.lisp, Up: Lisp [Contents][Index]
conditions.lisp (file).
nkeymaps (system).
Next: nkeymaps/modifiers.lisp, Previous: nkeymaps/package.lisp, Up: Lisp [Contents][Index]
package.lisp (file).
nkeymaps (system).
Next: nkeymaps/translators.lisp, Previous: nkeymaps/keymap.lisp, Up: Lisp [Contents][Index]
keymap.lisp (file).
nkeymaps (system).
Next: nkeymaps/keyscheme-map.lisp, Previous: nkeymaps/modifiers.lisp, Up: Lisp [Contents][Index]
modifiers.lisp (file).
nkeymaps (system).
Next: nkeymaps/keyschemes.lisp, Previous: nkeymaps/translators.lisp, Up: Lisp [Contents][Index]
translators.lisp (file).
nkeymaps (system).
Previous: nkeymaps/keyscheme-map.lisp, Up: Lisp [Contents][Index]
keyscheme-map.lisp (file).
nkeymaps (system).
Next: Definitions, Previous: Files, Up: The nkeymaps Reference Manual [Contents][Index]
Packages are listed by definition order.
Next: nkeymaps/keyscheme, Previous: Packages, Up: Packages [Contents][Index]
Package listing conditions.
common-lisp.
Next: nkeymaps/modifier, Previous: nkeymaps/conditions, Up: Packages [Contents][Index]
Package holding the list of well-known keyschemes.
We use a dedicated package so that keyschemes can easily be listed and completed.
common-lisp.
Next: nkeymaps/translator, Previous: nkeymaps/keyscheme, Up: Packages [Contents][Index]
Package holding the list of predefined modifiers.
We use a dedicated package so that modifiers can easily be listed and completed.
See ‘nkeymaps:define-modifier’.
common-lisp.
Next: nkeymaps, Previous: nkeymaps/modifier, Up: Packages [Contents][Index]
Package holding the list of predefined translators.
We use a dedicated package so that modifiers can easily be listed and completed.
See ‘nkeymaps:*translator*’.
Next: nkeymaps/types, Previous: nkeymaps/translator, Up: Packages [Contents][Index]
The workflow goes as follows:
- Make a keymap with ‘nkeymaps:make-keymap’.
- Define a binding on it with ‘nkeymaps:define-key’.
- Lookup this binding with ‘nkeymaps:lookup-key’.
Example:
(let* ((parent-keymap (nkeymaps:make-keymap "parent-keymap"))
(my-keymap (nkeymaps:make-keymap "my-keymap" parent-keymap)))
(nkeymaps:define-key parent-keymap
"C-c" ’copy
"C-v" ’paste)
(nkeymaps:define-key my-keymap
"C-x" ’cut)
(values
(nkeymaps:lookup-key "C-x" parent-keymap)
(nkeymaps:lookup-key "C-x" my-keymap)
(nkeymaps:lookup-key "C-c" my-keymap)))
;; => NIL, CUT, COPY
Another workflow is to use ‘nkeymaps:keyscheme’s which allow to compose
different binding styles.
Example:
(nkeymaps:define-keyscheme-map "test" ()
nkeymaps:cua ’("C-c" copy
"C-v" paste)
nkeymaps:emacs ’("C-x" cut))
The default keyschemes can be listed from the ‘nkeymaps/keyscheme:’ package
exported symbols.
New keyschemes can be created with ‘nkeymaps:make-keyscheme’.
Keys can be created with ‘nkeymaps:make-key’, which gives you more fine-tuning
compared to the "keyspecs" above:
(nkeymaps:make-key :code 38 :value "a" :modifiers ’("C"))
You can also specify key codes from the keyspec directly. For instance,
"C-#10" corresponds to keycode 10 with the ‘nkeymaps:+control+’.
The reverse-action of ‘nkeymaps:lookup-key’ is ‘nkeymaps:binding-keys’.
Keymaps can be composed with ‘nkeymaps:compose’.
New modifiers can be defined with ‘nkeymaps:define-modifier’.
(nkeymaps:define-modifier :string "duper" :shortcut "D")
Some globals can be tweaked to customize the library to your needs:
- ‘nkeymaps:*translator*’: The function to infer the right binding when
the exact binding hits nothing.
- ‘nkeymaps:*print-shortcut*’: Print modifiers using their short form instead of the
full name, e.g. "C" instead of "control".
Next: nkeymaps/core, Previous: nkeymaps, Up: Packages [Contents][Index]
Package for types.
It’s useful to have a separate package because some types may need to generate
functions for the ‘satisfies’ type condition.
common-lisp.
list-of (type).
Previous: nkeymaps/types, Up: Packages [Contents][Index]
See the ‘nkeymaps’ package documentation.
Next: Indexes, Previous: Packages, Up: The nkeymaps Reference Manual [Contents][Index]
Definitions are sorted by export status, category, package, and then by lexicographic order.
Next: Internals, Previous: Definitions, Up: Definitions [Contents][Index]
Next: Compiler macros, Previous: Public Interface, Up: Public Interface [Contents][Index]
Whether to print the short form of the modifiers.
Key translator to use in ‘keymap’ objects.
When no binding is found, call this function to
generate new bindings to lookup. The function takes a list of ‘key’ objects and
returns a list of list of keys.
Ths parameter can be let-bound around ‘lookup-key’ calls.
Next: Ordinary functions, Previous: Special variables, Up: Public Interface [Contents][Index]
We need a compiler macro to check that bindings are valid at compile time.
This is because most Common Lisp implementations are not capable of checking
types that use ‘satisfies’ for non-top-level symbols.
We can verify this with:
(compile nil (lambda () (nkeymaps::define-key keymap "C-x C-f" ’open-file)))
See the ‘define-key’ compiler-macro for why we need one here too.
Next: Generic functions, Previous: Compiler macros, Up: Public Interface [Contents][Index]
Return the list of ‘keyspec’s bound to BINDING in KEYMAP.
The result is ordered by priority of keymaps, that is, keymaps hits from
beginning to end of the keymap list.
Duplicates are removed.
Shadowed bindings are removed.
A a second value, return an alist of (keyspec keymap) for all the ‘keyspec’s
bound to BINDING in KEYMAP.
Comparison against BINDING is done with TEST.
For instance, to list all keymaps that have a binding, call
(mapcar #’second (nth-value 1 (binding-keys ...)))
Return a new keymap that’s the composition of all given KEYMAPS.
KEYMAPS are composed by order of precedence, first keymap being the one with
highest precedence.
Bind KEYS to BOUND-VALUE in KEYMAP.
Return KEYMAP.
KEYSPECS is either a ‘keyspecs-type’, or a list of arguments passed to invocations
of ‘make-key’s, or (:REMAP OTHER-VALUE &OPTIONAL OTHER-KEYMAP).
BOUND-VALUE can be anything. If NIL, the binding is removed.
With (:REMAP OTHER-VALUE &OPTIONAL OTHER-KEYMAP), define-key maps the binding of
OTHER-VALUE in OTHER-KEYMAP (default to KEYMAP) to BOUND-VALUE.
In other words, it remaps OTHER-VALUE to VALUE.
Examples:
(define-key foo-map "C-x C-f" ’open-file)
(define-key foo-map
"C-x C-f" ’open-file
"C-h k" ’describe-key)
"C-M-1 x" on a QWERTY:
(define-key foo-map ’((:code 10 :modifiers ("C" "M") (:value "x"))) ’open-file)
or the shorter:
(define-key foo-map "C-M-#1" ’open-file)
Remapping keys:
(define-key foo-map ’(:remap foo-a) ’foo-value)
(define-key foo-map ‘(:remap foo-a ,bar-map) ’new-value)
Return a keyscheme-map, a hash table with ‘keyscheme’s as key and ‘keymap’s
holding BINDINGS as value.
The keymap names are prefixed with NAME-PREFIX and suffixed with "-map".
OPTIONS is list of keyword arguments.
For now the only supported option is IMPORT.
When given a ‘keyscheme-map’ to IMPORT, it is used as initial values for the new
keyscheme-map. The content is copied. Further alteration to the imported
keyscheme-map won’t reflect on this newly define keyscheme-map.
This is a macro like ‘define-key’ so that it can type-check the BINDINGS
keyspecs at compile-time.
Example:
(define-keyscheme-map "my-mode" ’()
nkeymaps/keyscheme:cua (list
"C-c" ’copy
"C-v" ’paste)
nkeymaps/keyscheme:emacs ’("M-w" copy
"M-y" paste))
‘nkeymaps/keyscheme:cua’ and ‘nkeymaps/keyscheme:emacs’ are pre-defined keyschemes.
To define a new keyscheme, see ‘make-keyscheme’.
‘nkeymaps/keyscheme:cua’ is a parent of ‘nkeymaps/keyscheme:emacs’; thus, in the
above example, the Emacs keymap will have the CUA keymap as parent.
The keyscheme-map keymaps are named "my-mode-cua-map" and
"my-mode-emacs-map".
Return a new modifier.
It is registered globally and can be used from any new keymap, unless the keymap
filters out the modifier in its ‘modifiers’ slot.
Return keymap corresponding to KEYSCHEME in KEYSCHEME-MAP.
If no keymap is found, try with KEYSCHEME’s ‘parents’.
For instance, if KEYSCHEME has a ‘nkeymaps/keyscheme:cua’ keymap and no
‘nkeymaps/keyscheme:emacs’ keymap, this function returns the
‘nkeymaps/keyscheme:cua’ keymap when NAME is ‘nkeymaps/keyscheme:emacs’.
Return nil if nothing is found.
Two keys are equal if the have the same modifiers, status and key code.
If codes don’t match, the values are compared instead. This way, code-matching
keys match before the value which is usually what the users want when they
specify a key-code binding.
Return a hash-table of (KEYSPEC BOUND-VALUE) from KEYMAP.
Parent bindings are not listed; see ‘keymap-with-parents->map’ instead.
This is convenient if the caller wants to list all the bindings.
When multiple keymaps are provided, return the union of the ‘fset:map’ of each arguments.
Keymaps are ordered by precedence, highest precedence comes first.
List bindings in KEYMAP and all its parents. See ‘keymap->map’.
Return the keyspecs (a list of ‘keyspec’) for KEYS. See ‘key->keyspec’ for the details.
Return the value bound to KEYS-OR-KEYSPECS in KEYMAP-OR-KEYMAPS.
As a second value, return the matching keymap.
As a third value, return the possibly translated KEYS.
Return NIL if no value is found.
The successive keymaps from KEYMAP-OR-KEYMAPS (if a list) are looked up one
after the other.
If no binding is found, the direct parents are looked up in the same order.
And so on if the binding is still not found.
Return new ‘key’.
Modifiers can be either a ‘modifier’ type or a string that will be looked up in
‘*modifier-list*’.
Return a new ‘keyscheme’ object.
The scheme name inherits from the optional PARENTS, ordered by priority.
Example:
(defvar emacs (make-keyscheme "emacs" cua))
In the above, we define a new scheme name called ‘emacs‘ which inherits from the existing keyscheme ‘cua‘.
Return a new scheme associating KEYSCHEME to KEYMAP.
With MORE-KEYSCHEME+KEYMAP-PAIRS, include those names and keymaps as well. This is
useful in complement to ‘define-keyscheme-map’ to make a scheme with pre-existing
keymaps.
With control, keys without control except for the first key: ’C-x C-c’ -> ’C-x c’.
With shift, keys without shift: ’shift-a’ -> ’a’.
With control and shift, keys without control except for the first key and without shift everywhere: ’C-shift-C C-shift-f’ -> ’C-C f.
With control and shift, keys without control except for the first key and without shift everywhere: ’C-shift-C C-shift-f’ -> ’C-c F.
With shift, keys without shift and with their key value case reversed: ’shift-a shift-B’ -> ’A b’.
Next: Standalone methods, Previous: Ordinary functions, Up: Public Interface [Contents][Index]
Type of the bound-value.
The type is enforced in ‘define-keyscheme-map’ at macro-expansion time.
Type should allow ‘keymap’s, so it should probably be in the form
(or keymap NON-KEYMAP-BOUND-TYPE).
Type of the bound-value.
The type is enforced in ‘define-key’ at macro-expansion time.
Type should allow ‘keymap’s, so it should probably be in the form
(or keymap NON-KEYMAP-BOUND-TYPE).
Next: Conditions, Previous: Generic functions, Up: Public Interface [Contents][Index]
Needed to use the KEY structure as keys in Fset maps.
Would we use the default comparison function, case-sensitivity would be lost on
key values because ‘fset:equal?‘ folds case.
fset.
Modifier sets need this comparison function to be ordered, so that ("C" "M") is the same as ("M" "C").
fset.
Next: Structures, Previous: Standalone methods, Up: Public Interface [Contents][Index]
Warning raised when trying to derive a key from an illegal keyspec.
warning.
(quote "illegal keyspec")
:message
(quote (alexandria:required-argument (quote nkeymaps/conditions::error-condition)))
:error-condition
Condition raised when we didn’t get a modifier as expected.
error.
(quote "")
:message
Warning raised when keymap has cycles.
This is possible if a bound value is a keymap that occured before.
Warning raised when a keyspec contains multiple occurences of the same modifiers.
warning.
(quote "duplicate modifiers")
:message
(quote (alexandria:required-argument (quote nkeymaps/conditions::modifiers)))
:modifiers
error.
error.
(quote (alexandria:required-argument (quote nkeymaps/conditions::keyspec)))
:keyspec
error.
(quote (alexandria:required-argument (quote nkeymaps/conditions::keyspec)))
:keyspec
error.
Warning raised overriding an existing binding.
warning.
(quote "key was bound to")
:message
(quote (alexandria:required-argument (quote nkeymaps/conditions::existing-binding-value)))
:existing-binding-value
Next: Classes, Previous: Conditions, Up: Public Interface [Contents][Index]
structure-object.
integer
0
string
""
fset:wb-set
(fset:set)
nkeymaps/core::key-status-type
:pressed
Next: Types, Previous: Structures, Up: Public Interface [Contents][Index]
Name of the keymap.
Used for documentation purposes, e.g. referring to a keymap by a well known name.
string
"anonymous"
:name
name.
Hash table of which the keys are key-chords and the values are a symbol or a keymap.
fset:wb-map
(fset:empty-map)
:entries
Type of the bound-value.
The type is enforced in ‘define-key’ at macro-expansion time.
Type should allow ‘keymap’s, so it should probably be in the form
(or keymap NON-KEYMAP-BOUND-TYPE).
nkeymaps/core::*default-bound-type*
:bound-type
List of parent keymaps.
Parents are ordered by priority, the first parent has highest priority.
(nkeymaps/types:list-of nkeymaps/core:keymap)
:parents
Accepted modifiers for this ‘keymap’.
fset:wb-set
(fset:convert (quote fset:set) nkeymaps/core::*modifier-list*)
:modifiers
The list of parents. When a scheme is defined, the
keymap parents are automatically set to the keymaps corresponding to the given
keyschemes. See ‘define-keyscheme-map’.
(nkeymaps/types:list-of nkeymaps/core:keyscheme)
(quote nil)
:parents
Type of the bound-value.
The type is enforced in ‘define-keyscheme-map’ at macro-expansion time.
Type should allow ‘keymap’s, so it should probably be in the form
(or keymap NON-KEYMAP-BOUND-TYPE).
nkeymaps/core::*default-bound-type*
:bound-type
Accepted modifiers for this ‘keyscheme’.
fset:wb-set
(fset:convert (quote fset:set) nkeymaps/core::*modifier-list*)
:modifiers
Previous: Classes, Up: Public Interface [Contents][Index]
A ‘hash-table’ mapping ‘keyscheme’s to ‘keymap’s.
The empty list of a proper list of TYPE elements. Unlike ‘(cons TYPE *)’, it checks all the elements. ‘(cons TYPE *)’ does not accept the empty list.
Previous: Public Interface, Up: Definitions [Contents][Index]
Next: Ordinary functions, Previous: Internals, Up: Internals [Contents][Index]
Default value for the ‘bound-type’ slot of ‘keymap’.
Do not change this, instead create new ‘scheme-name’s or subclass ‘keymap’.
List of known modifiers.
‘make-key’ and ‘define-key’ raise an error when setting a modifier that is not
in this list.
Next: Generic functions, Previous: Special variables, Up: Internals [Contents][Index]
Recursively bind the KEYS to keymaps starting from KEYMAP.
The last key is bound to BOUND-VALUE.
If BOUND-VALUE is nil, the key is unbound.
If KEYS has modifiers that are not allowed in KEYMAP, do nothing.
Return KEYMAP.
Return a the list of ‘keyspec’s bound to BINDING in KEYMAP. The list is sorted alphabetically to ensure reproducible results. Comparison against BINDING is done with TEST.
Return the keyspec of KEY.
If the key has a code, return it prefixed with ’#’.
For now the status is not encoded in the keyspec, this may change in the future.
Return a map of (KEYSPEC SYM) from KEYMAP.
Flatten the KEYMAPS into a list.
It traverses KEYMAPS first, then traverses the each keymap ‘parent’ layer after layer.
Example:
- keymap1 has parents (k1a k1b)
- k1a has parents (k1ap)
- keymap2 has parents (k2a)
Return (keymap1 keymap2 k1a k1b k2a k1ap).
Return the list of keymap and all its parents.
Parse STRING and return a new ‘key’.
The specifier is expected to be in the form
MOFIFIERS-CODE/VALUE
MODIFIERS are hyphen-separated modifiers as per ‘*modifier-list*’.
CODE/VALUE is either a code that starts with ’#’ or a key symbol.
Note that ’-’ or ’#’ as a last character is supported, e.g. ’control–’ and ’control-#’ are valid.
Parse SPEC and return corresponding list of keys.
Whether KEY’s modifiers are allowed in KEYMAP.
Return non-nil if LIST contains only elements of the given TYPE.
Internal function, see ‘lookup-key’ for the user-facing function.
VISITED is used to detect cycles.
As a second value, return the matching keymap.
Return bound value or keymap for KEYS. Return nil when KEYS is not found in KEYMAP. VISITED is used to detect cycles.
Return the ‘modifier’ corresponding to STRING-OR-MODIFIER.
Return the list of ‘modifier’s corresponding to STRINGS-OR-MODIFIERS.
Adapted from ‘serapeum:string-join’.
Return the input with reversed case if it has only one character.
Return the successive translations of
- ‘translate-remove-shift’,
- ‘translate-remove-shift-toggle-case’,
- ‘translate-remove-but-first-control’,
- ‘translate-remove-shift-but-first-control’,
- ‘translate-remove-shift-but-first-control-toggle-case’.
We first remove shift before toggle the case because we want ’s-A’ to match an ’A’ binding before matching ’a’.
Next: Structures, Previous: Ordinary functions, Up: Internals [Contents][Index]
Next: Types, Previous: Generic functions, Up: Internals [Contents][Index]
Previous: Definitions, Up: The nkeymaps Reference Manual [Contents][Index]
Jump to: | %
(
B C D E F G K L M N P Q S T |
---|
Jump to: | %
(
B C D E F G K L M N P Q S T |
---|
Next: Data types, Previous: Functions, Up: Indexes [Contents][Index]
Jump to: | *
+
B C D E K M N P S V |
---|
Jump to: | *
+
B C D E K M N P S V |
---|
Jump to: | B C D E F K L M N O P S T |
---|
Jump to: | B C D E F K L M N O P S T |
---|