nkeymaps

2025-06-22

General-purpose keymap management à-la Emacs.

Upstream URL

Author

Atlas Engineer LLC

License

BSD 3-Clause
README
NKeymaps

This keymap library is inspired by Emacsy (keymap.scm), which in turn is inspired by Emacs.

The main types are:

  • A key structure that holds:
    a numeric code
    a hardware keycode;
    a string value
    a character (except for whitespaces);
    ???
    an ordered set of modifiers (to ensure equality on permutation).
  • A keyspec is a string representation of a key (e.g. "control-a"). Akeyspecs is the string concatenation of two or more =keyspec=s(e.g. "control-a control-b control-c").
  • A keymap contains a hash-table that maps keyspecs to either keymaps,function designators or nil (to unbind the keyspecs).

0.1Goals

  • Define keymaps with a prefix key (commonly known as prefix keymaps). Forinstance, my-mode-map can be defined as a prefix keymap that makes all of itsbindings accessible after pressing C-c.
  • List all bindings matching a given prefix.
  • List the bindings associated to a command.
  • Multiple inheritance.
  • Keycode support.
  • Validate keyspec at compile time.
  • Set multiple bindings in a single call to define-key.
  • Multiple keyschemes to make it easy to switch between, say, Emacs-style andVI-style bindings. This orthogonality to keymaps composes better than havingmultiple keymaps: changing keyscheme applies to the entire program, which iseasier than looping through all keymaps to change them.
  • Translate keyspecs as a fallback. For instance if shift-a is unbound, checkA.
  • Customizable behaviour with global parameters such as *print-shortcut*.
  • Merge multiple keymaps via compose.
  • Handle gracefully multiple arguments when sensible (e.g. multiple keymaps forlookup-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.

0.2Non-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 getsnil 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 thelocality of their keymaps.

0.3Example

(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)
   (nkeymaps:binding-keys 'cut parent-keymap)
   (nkeymaps:binding-keys 'copy my-keymap)
   (nkeymaps:binding-keys 'paste my-keymap)))

;; => NIL, CUT, COPY, NIL, ("C-c"), ("C-v")

See the package documentation for a usage guide and more examples.

0.4History

NKeymaps was originally developed for keymap management in Nyxt, so the "N" may stand for it, or "New", or whatever poetic meaning you may find behind it!

0.5Road-map

  • Lookup order (for instance parent-first or next-keymap-first) should becustomizable.
  • For now *translator* is a global, but ideally it would be part of thekeyscheme and keymap. But this would impact the lookup order, sincetranslations come after all the keymaps and their parents have been searchedfor.

0.6Change log

0.6.11.1.1

  • Remove NASDF as a dependency.
  • Minor fix relative to pretty-binding-keys.

0.6.21.1.0

  • New pretty-binding-keys function that prints keybindings using OS-dependentmodifier terminology.
  • Improve documentation.
  • Add NASDF as a dependency.
  • Add cl-str as a dependency.

0.6.31.0.0

  • Renamed scheme-name to keyscheme and scheme to keyscheme-map. It'smore consistent and intuitive. The previous naming was really confusing.
  • All warnings have now their own conditions, see the nkeymaps/conditionspackage.
  • define-keyscheme-map has a different syntax, it's now
      (define-keyscheme-map "NAME-PREFIX" (:import OPTIONAL-KEYSCHEME-MAP-TO-IMPORT)
        KEYSCHEME BINDINGS...)
    
  • The predefined keyscheme=s are now accessible from the =nkeymapspackage.
  • 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 andkeymap have a modifiers slot for the modifiers they accept.
  • Switched testing framework from Prove to Lisp-Unit2.
  • Removed the cl-str dependency.

Dependencies (6)

  • alexandria
  • cl-str
  • fset
  • lisp-unit2
  • trivial-package-local-nicknames
  • uiop

Dependents (0)

    • GitHub
    • Quicklisp