#+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 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").  A
  =keyspecs= 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=).

** Goals

- Define keymaps with a prefix key (commonly known as prefix keymaps).  For
  instance, my-mode-map can be defined as a prefix keymap that makes all of its
  bindings 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 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*~.
- Merge multiple keymaps via ~compose~.
- Handle gracefully multiple arguments when sensible (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)
   (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")
#+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-browser.com][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.1.1
- Remove =NASDF= as a dependency.
- Minor fix relative to =pretty-binding-keys=.

*** 1.1.0
- New =pretty-binding-keys= function that prints keybindings using OS-dependent
  modifier terminology.
- Improve documentation.
- Add =NASDF= as a dependency.
- Add =cl-str= as a dependency.

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