Context aware hydra
30 Jun 2015In this post, I'll demonstrate a snippet that came up this week in a conversation over John Kitchin's post. While the following snippet isn't inherently useful, it might help a creative user to understand Hydra a bit more and accomplish more advanced things.
This hydra demonstrates that the hint and even the key bindings aren't set in stone: you can modify them depending on the current state of your Emacs, in this case the current line number.
(defhydra hydra-vi (:hint nil)
"vi"
("j" next-line)
("k" previous-line)
("n" next-line)
("p" previous-line))
(setq hydra-vi/hint
'(if (evenp (line-number-at-pos))
(prog1 (eval
(hydra--format nil '(nil nil :hint nil)
"\neven: _j_ _k_\n" hydra-vi/heads))
(define-key hydra-vi/keymap "n" nil)
(define-key hydra-vi/keymap "p" nil)
(define-key hydra-vi/keymap "j" 'hydra-vi/next-line)
(define-key hydra-vi/keymap "k" 'hydra-vi/previous-line))
(prog1 (eval
(hydra--format nil '(nil nil :hint nil)
"\nodd: _n_ _p_\n" hydra-vi/heads))
(define-key hydra-vi/keymap "j" nil)
(define-key hydra-vi/keymap "k" nil)
(define-key hydra-vi/keymap "n" 'hydra-vi/next-line)
(define-key hydra-vi/keymap "p" 'hydra-vi/previous-line))))
The first statement is one of the most elementary defhydra
calls. The only extra thing is that it sets the :hint
to nil.
The defhydra
statement generates a bunch of function and variable
definitions. You can examine them closely by evaluating:
(macroexpand
'(defhydra hydra-vi (:hint nil)
"vi"
("j" next-line)
("k" previous-line)
("n" next-line)
("p" previous-line)))
Just paste that code into *scratch*
, and press C-j. If
you want pretty output (153 lines of code instead of 26), turn on
lispy-mode and press E
instead of C-j.
Anyway, among these defined variables is hydra-vi/hint
which is
evaluated each time to display the hint. So now we can just redefine
hydra-vi/hint
to make it so that on even lines n calls
next-line
, while on odd lines it's j, with the
appropriate changes in the doc. The change in bindings, modifying
hydra-vi/keymap
- also one of the defined variables, needs to be a
side-effect, since hydra-vi/hint
is expected to evaluate to a
string.
Just to give you some idea of how it could be used: you can have a
context-aware "open" command that, for instance, delegates to
open-in-pdf-tools
or open-in-firefox
or open-in-emacs
when it
detects that the point is on a link. And of course all these commands
would have their own key binding that works only if the command makes
sense.
This approach is described on
the wiki,
in case you read this post much later and want to see an up-to-date
code, or even update it yourself. In case something cool comes out of
this snippet, I can try to implement a more palatable API for
defhydra
, most likely an option to supply a function name in the
docstring
argument position.