Hydra 0.13.0 is out13 Apr 2015
A lot of the changes for this release aren't very user-visible, although they
are important in improving the usability. The main change is the move from the
set-transient-map to my own
This change has allowed to remove the part where the amaranth and pink hydras intercept a binding which doesn't belong to them and then try to forward it back to Emacs. It was done in this way, which might be interesting for people who write Elisp:
(define-key hydra-keymap t 'hydra-intercept)
t means that when the keymap is active, any binding which doesn't
belong to the keymap will be interpreted as
t. Then, I would use
this-command-keys and try to call the result. That method was quite
- It didn't work for prefix keys while a pink hydra was active.
- It didn't work for some keys in the terminal because of
The new method solves the mentioned issues by not using
t and instead running
this function in the
(defun hydra--clearfun () "Disable the current Hydra unless `this-command' is a head." (if (memq this-command '(handle-switch-frame keyboard-quit)) (hydra-disable) (unless (eq this-command (lookup-key hydra-curr-map (this-single-command-keys))) (unless (cl-case hydra-curr-foreign-keys (warn (setq this-command 'hydra-amaranth-warn)) (run t) (t nil)) (hydra-disable)))))
This approach is actually very similar to what the built-in
does from Emacs 24.4 onward. Of course, changing the a large cog in the Hydra
mechanism can lead to some new bugs, or even old bugs to re-surface. So I really
appreciate the help from @jhonnyseven in testing the new code.
As always, if you find something very broken, you can roll back to the GNU ELPA version and raise an issue.
single command red/blue issue
Fix the uniqueness issue, when a single command is assigned to both a red and a blue head.
Here's an example:
(defhydra hydra-zoom (global-map "<f2>") "zoom" ("g" text-scale-increase "in") ("l" text-scale-decrease "out") ("r" (text-scale-set 0) "reset") ("0" (text-scale-set 0) :bind nil :exit t) ("1" (text-scale-set 0) nil :bind nil :exit t))
Here, three heads are assigned
(text-scale-set 0), however their behavior is different:
- r doesn't exit and has a string hint.
- 0 exits and has an empty hint (so only the key is in the docstring).
- 1 exits and has a nil hint (will not be displayed in the docstring).
The latter two call
hydra-zoom/lambda-0-and-exit, while r
hydra-repeast--prefix-arg to 1
See #61 for more info.
hydra-repeat to take a numeric arg
hydra-vi, it's now possible to do this 4j.2...
The line will be forwarded:
- 4 times by 4j
- 4 times by .
- 2 times by 2.
- 2 times by .
See #92 for more info.
Key chord will be disabled for the duration of a hydra
This means that hydras have become much more easy to use with key chords. For instance, if dj key chord calls a hydra or is part of the hydra, you won't call the jj key chord by accident with djj.
See #97 for more info.
Variable as a string docstring spec
You can now use this form in your hydras:
(defvar foo "a b c") (defhydra bar () " bar %s`foo " ("a" 't) ("q" nil))
Previously, it would only work for
:bind property can also be a keymap
If you remember, you can set
:bind in the body to define in which way the
heads should be bound outside the Hydra. You also assign/override
each head. This is especially useful to set
:bind to nil for a few heads that
you don't want to bind outside.
:bind could be either a lambda or nil. Now a keymap is also accepted.
In addition to the abundant macro-expansion tests, integration tests are now
also running, both for
emacs24 and for
emacs-snapshot. This means that hydra
should be a lot more stable now.
Here's an example test:
(defhydra hydra-simple-1 (global-map "C-c") ("a" (insert "j")) ("b" (insert "k")) ("q" nil)) (ert-deftest hydra-integration-1 () (should (string= (hydra-with "|" (execute-kbd-macro (kbd "C-c aabbaaqaabbaa"))) "jjkkjjaabbaa|")) (should (string= (hydra-with "|" (condition-case nil (execute-kbd-macro (kbd "C-c aabb C-g")) (quit nil)) (execute-kbd-macro "aaqaabbaa")) "jjkkaaqaabbaa|")))
In the tests above,
hydra-simple is a defined and bound hydra.
represents the buffer text (empty), where
| is the point position.
(kbd "C-c aabbaaqaabbaa") represents the key sequence that you can normally press by hand.
"jjkkjjaabbaa|" is what the buffer and the point position should look like afterwards.
If you find a hydra bug, it would be really cool to submit a new integration test to make sure that
this bug doesn't happen in the future.
Basic error handling
I really like the use-package feature where it catches load-time errors and issues a message instead of bringing up the debugger. This is really useful, since it's hard to fix the bug with a mostly broken Emacs, in the case when the error happened early in the load process. So the same behavior now happens with
defhydra. In case of an error,
defhydra will be equivalent to a no-op, and the error message will be written to the
Use a variable instead of a function for the hint
This leads up to the yet unresolved #86, which asks for heads to be activated conditionally.
For now, you can modify the docstring on your own if you wish.
Here's some code from the expansion of
hydra-zoom to explain what I mean:
(set (defvar hydra-zoom/hint nil "Dynamic hint for hydra-zoom.") '(format #("zoom: [g]: in, [l]: out." 7 8 (face hydra-face-red) 16 17 (face hydra-face-red)))) ;; in head body: (when hydra-is-helpful (if hydra-lv (lv-message (eval hydra-zoom/hint)) (message (eval hydra-zoom/hint))))
Eventually, I'll add some automatic stuff to fix #86. But for now, you can
experiment with modifying e.g.
hydra-zoom/hint inside heads, if you want.
Multiple inheritance for Hydra heads
Each hydra, e.g.
hydra-foo will now declare its own heads as a variable
hydra-foo/heads. It's possible to inherit them like this:
(defhydra hydra-zoom (global-map "<f2>") "zoom" ("g" text-scale-increase "in") ("s" text-scale-decrease "out")) (defhydra hydra-arrows () ("h" backward-char "left") ("j" next-line "down") ("k" previous-line "up") ("l" forward-char "right")) (defhydra hydra-zoom-child (:inherit (hydra-zoom/heads hydra-arrows/heads) :color amaranth) "zoom" ("q" nil))
hydra-zoom-child inherits the heads of
It adds one more head q to quit. Also, it changes the color to
amaranth, which means that it's only possible to exit it with q or
C-g. This hydra's parents remain at their original (red) color.
See #57 for more info.
A big thanks to all who contributed towards this release and to the wiki. If you have an idea that would be cool in Hydra, do raise an issue. And if you written some cool code that uses the already available Hydra features, please share it on the wiki. It's also a good way to protect your code against regressions (although the best one would be an integration test).
Finally, check out the new version of pandoc-mode which is one of the coolest and most elaborate uses of Hydra yet.