eta a micro-framework for customising key-bindings built on top of
bind-key. It provides two macros for standardising and associating
intent with key-bindings: a standard version eta-bind
and
multi-dispatch version for standardisation of functionality across
major modes: eta-modal-init
and eta-modal
.
Install from Melpa:
quepa
(use-package eta :ensure t)
Let’s see these macros in action:
;; First argument is a [vector]
;; [*] means cannot be overriden
;; [] means global but can be overriden by other modes
;; [<keymap>] means bind for given mode only
;; Subsequent arguments are tabular of the form
;; - ACTION (a tag that states what the action is)
;; - BINDINGS (a set of key bindings bound to the intent)
;; - IMPLEMENTATION (the actual command that performs the action)
;; ACTION BINDINGS IMPLEMENTATION
(eta-bind [*]
::no-action () 'e/no-action ;; can be nothing
::which-key ("C-x C-h" "C-x h") 'which-key-show-major-mode ;; allow multi
::main-menu ("C-p" "M-p" "M-x" "ESC p" "ESC x") 'counsel-M-x
::quit ("ESC q" "M-q") 'save-buffers-kill-terminal
::help-key ("ESC <f1>") 'helpful-key
::eval-elisp ("ESC l") 'eval-last-sexp)
It’s easier to think of this as using multimethods for major
modes. eta-modal-init
is akin to defmulti
while eta-modal
is akin
to defmethod
. This allows actiovs that will be consistent across
major modes.
For example the default bindings in Eclipse are <F8>
for build and
<F6>
for debug. Build
and Debug
are actions that can be reassigned
key-bindings. Whether one is writing java
, c++
or any other
language, these actions are the same although the underlying
implementation might be very different.
This type of standardisation can now be applied to one’s Emacs setup.
;; ACTION BINDINGS INTERACTIVE PARAMS
(eta-modal-init []
::mode-menu ("<f8>") ()
::eval-cursor ("C-e") ("P")
::eval-cursor-alt ("C-x p" "C-x C-p") ("P")
::eval-file ("C-x x" "C-x C-x") ())
;;... then later on ...
;; elisp mode
(eta-modal [::lisp lisp-interaction-mode]
::eval-cursor 'eval-last-sexp
::eval-file 'e/eval-buffer
::eval-cursor-alt 'pp-macroexpand-last-sexp)
;; org mode
(eta-modal [::org org-mode]
::eval-cursor 'org-ctrl-c-ctrl-c
::eval-cursor-alt 'e/org-babel-tangle-block
::mode-connect 'org-babel-tangle
::eval-file nil)
;; elisp mode
(eta-modal [::clojure cider-mode]
::eval-cursor 'cider-eval-last-sexp
::eval-file 'cider-eval-buffer
::eval-cursor-alt 'cider-macroexpand-last-sexp)
;; ... and so on ...
eta
provides a indirection layer between the bindings and the
implementation so that the intent of the user can be stated.
BINDINGS -> [ACTION or INTENT] -> IMPLEMENTATION
This leads to better organisation a cleaner binding definition and gives the user a bit more control over where the bindings are placed. It makes migrating away from older libraries much less painful.
Another features is the ability to have multiple keybindings for a given action. This is extremely useful in a lot of situations:
- allow access to a commonly used feature at various hand positions
- when minor modes override one binding, the feature is still available via another set of shortcuts.
These are current libraries that attempt to solve the same problem:
- Bind Key is the go to binding library shipped as part of
use-package
.eta
is essentially offerring a more conventient interface forbind-key
). - Major Mode Hydra is a multimethod for only the one action. In this
case, a user can define a
major-mode-hydra
key that brings up the hydra menu for the mode.- This library is backed by
pretty-hydra
.pretty-hydra
andeta
are used extensively in etude for modal menus (effectively emulatinig what Major Mode Hydra provides).
- This library is backed by
- General does something similar.
- Rigpa This project is quite recent, very interesting and offers a framework for the user to build around as opposed to eta’s whatever goes approach.