General-purpose keymap management à-la Emacs.

Upstream URL



Atlas Engineer LLC


BSD 3-Clause

This keymap library is inspired by 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 ofmodifiers (the order is to avoid inequality on permutation) and possibly morein 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 aspace-separated string representation of a sequence of =key=s.
  • A keymap contains a hash-table where the keys are a keyspec and the valuesare either a keymap or anything else. As a special case, a nil valueunbinds the key.


  • Support prefix keys to other keymaps. For instance, prefixing my-mode-mapwith 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 inEmacs.)
  • 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-styleand VI-style bindings. This orthogonality to keymaps composes better thanhaving multiple keymaps: changing keyscheme applies to the entire program, whichis easier 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*.
  • The compose function can merge multiple keymaps together.
  • Support multiple arguments when that makes sense (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.


  • 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.


  (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)
     (nkeymaps:lookup-key "C-x" parent-keymap)
     (nkeymaps:lookup-key "C-x" my-keymap)
     (nkeymaps:lookup-key "C-c" my-keymap)))

  ;; => NIL, CUT, COPY

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


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!


  • 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 thekeyscheme and keymap. But this would impact the lookup order, sincetranslations come after all the keymaps and their parents have been searched for.

0.6Change log

  • 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
        (define-keyscheme-map "NAME-PREFIX" (:import OPTIONAL-KEYSCHEME-MAP-TO-IMPORT)
  • 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 andkeymap have a modifiers slot for the modifiers they accept.
  • Switched testing framework from Prove to Lisp-Unit2.
  • Removed the cl-str dependency.

Dependencies (5)

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

Dependents (1)

  • GitHub
  • Quicklisp