Releases: abo-abo/hydra
hydra 0.15.0
hydra 0.13.0
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
standard set-transient-map
to my own hydra-set-transient-map
.
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)
Binding 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 lookup-key
and this-command-keys
and try to call the result. That method was quite
fragile:
- 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
input-decode-map
.
The new method solves the mentioned issues by not using t
and instead running
this function in the pre-command-hook
:
(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 set-transient-map
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.
Fixes
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
calls hydra-zoom/lambda-r
.
Don't default hydra-repeast--prefix-arg
to 1
See #61 for more info.
Allow hydra-repeat
to take a numeric arg
For 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.
New Features
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 %s(foo)
forms.
:bind property can now 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 :bind
for
each head. This is especially useful to set :bind
to nil for a few heads that
you don't want to bind outside.
Previously, :bind
could be either a lambda or nil. Now a keymap is also accepted.
Integration tests
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.
And (kbd "C-c aabbaaqaabbaa")
represents the key sequence that you can normally press by hand.
Finally, "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 *Messages*
buffer.
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))
Here, hydra-zoom-child
inherits the heads of hydra-zoom
and hydra-arrows
.
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.
hydra 0.12.0
Fixes
- Handling of heads with duplicate
cmd
was improved. - Don't bind
nil
in outside keymaps. - Work-around
golden-ratio-mode
inlv-window
. - C-g (
hydra-keyboard-quit
) should run:post
. - Bind
[switch-frame]
tohydra-keyboard-quit
. :post
is called for:timeout
.
New Features
hydra-key-format-spec
is a new defcustom for the keys format in the docstring. It's"%s"
by
default, but you can set it to e.g."%-4s"
if you like.- The key regex was extended to include most common key binding characters.
hydra-repeat
is a hydra-specificrepeat
function. It behaves as you would expectrepeat
to behave.- New body option -
:timeout
. Use e.g.:timeout 2.0
to set the timer. After the first head is called,
a timer is started to disable the hydra. Each new head call resets this timer, so the hydra won't disappear as long as you keep typing. - Lines are truncated in
lv-message
. This is useful for large docstring not to become misaligned when the window
becomes too small.
Allow for a %s(test)
spec in docstring
The spec that's used for e.g. (test)
is %S
. So if (test)
returns a string, it will be quoted.
This may not be desired, hence the new feature.
Example:
(defhydra hydra-marked-items (dired-mode-map "")
"
Number of marked items: %(length (dired-get-marked-files))
Directory size: %s(shell-command-to-string \"du -hs\")
"
("m" dired-mark "mark"))
Emulate org-mode export dispatch with hydra-ox
You can also look at that code to see how nested hydras work. Several other examples were added to
hydra-examples.el.
The pink/amaranth override is set recursively
This fixes the issue in this hydra:
(defhydra hydra-test (:color amaranth)
"foo"
("fo" (message "yay"))
("q" nil))
Before, pressing e.g. fp would not issue a warning, since f started its own
keymap. This is now fixed.
An option to specify the hint for all heads in body
When you write a large docstring, you usually pass nil
as the hint for most heads.
Now you can omit it, if you set :hint nil
in body.
Example:
(defhydra hydra-org-template (:color blue :hint nil)
"
_c_enter _q_uote _L_aTeX:
_l_atex _e_xample _i_ndex:
_a_scii _v_erse _I_NCLUDE:
_s_rc ^ ^ _H_TML:
_h_tml ^ ^ _A_SCII:
"
("s" (hot-expand "<s"))
("e" (hot-expand "<e"))
("q" (hot-expand "<q"))
("v" (hot-expand "<v"))
("c" (hot-expand "<c"))
("l" (hot-expand "<l"))
("h" (hot-expand "<h"))
("a" (hot-expand "<a"))
("L" (hot-expand "<L"))
("i" (hot-expand "<i"))
("I" (hot-expand "<I"))
("H" (hot-expand "<H"))
("A" (hot-expand "<A"))
("<" self-insert-command "ins")
("o" nil "quit"))
hydra 0.11.0
Fixes
- Emacs 24.3 (and maybe earlier versions) compatibility added
- (hydra-cleanup): Should not delete buffer first when window is dedicated.
- (lv-message): Should not deactivate mark.
- quit signals are intercepted, so that proper clean-up is done.
- no defuns with the same name will be generated in case some heads' cmd parts coincide
New Features
Pink body color
When body color is pink, and hydra intercepts a binding which isn't a head, instead of quitting the
hydra and executing the binding (default red behavior), it will execute the binding and not quit
the hydra.
So now, pink and amaranth are body colors that are a flavor of the default red that, instead of
quitting when a non-head binding is intercepted, respectively run the binding or issue a warning.
Teal body color
Teal color behaves similarly to amaranth (issues a warning when intercepting foreign bindings),
but the default behavior for each head is to quit.
The following evaluates to true:
(equal
(macroexpand
'(defhydra hydra-test (:color amaranth)
("a" fun-a)
("b" fun-b :color blue)
("c" fun-c :color blue)
("d" fun-d :color blue)
("e" fun-e :color blue)
("f" fun-f :color blue)))
(macroexpand
'(defhydra hydra-test (:color teal)
("a" fun-a :color red)
("b" fun-b)
("c" fun-c)
("d" fun-d)
("e" fun-e)
("f" fun-f))))
So if you want to write less, use teal color in this case.
Alternative color-less syntax
The color syntax is working quite well, since I was able to keep an almost full backward-compatibility:
once you pinpoint a color it will have that behavior, all the new behaviors will be released under a new color.
Some might prefer an alternative set of switches rather than colors. New compat switches are:
- ":exit nil" for ":color red"; You don't have to specify either
option in the body, since they are the default behavior that was
available on Hydra release
- ":exit t" for ":color blue"
- ":foreign-keys warn" for ":color amaranth"
- ":foreign-keys warn :exit t" for ":color teal"
- ":foreign-keys run" for ":color pink"
The property :exit
can be inherited and overridden in the same way as :color
red or blue.
The following evaluates to true:
(equal
(macroexpand
'(defhydra hydra-test ()
("a" fun-a)
("b" fun-b :color blue)
("c" fun-c :color blue)
("d" fun-d :color blue)
("e" fun-e :color blue)
("f" fun-f :color blue)))
(macroexpand
'(defhydra hydra-test (:color blue)
("a" fun-a :color red)
("b" fun-b)
("c" fun-c)
("d" fun-d)
("e" fun-e)
("f" fun-f))))
The following two are exactly the same as the two above:
(equal
(macroexpand
'(defhydra hydra-test ()
("a" fun-a)
("b" fun-b :exit t)
("c" fun-c :exit t)
("d" fun-d :exit t)
("e" fun-e :exit t)
("f" fun-f :exit t)))
(macroexpand
'(defhydra hydra-test (:exit t)
("a" fun-a :exit nil)
("b" fun-b)
("c" fun-c)
("d" fun-d)
("e" fun-e)
("f" fun-f))))
Ruby-style string interpolation in docstrings
Here's an example:
(defhydra hydra-marked-items (dired-mode-map "")
"
Number of marked items: %(length (dired-get-marked-files))
Auto-revert mode is: %`auto-revert-mode
"
("m" dired-mark "mark"))
To enable the behavior, the docstring has to start with a newline (which will be stripped). This
makes sense, since you usually want to align extensive docstrings, like I did this one. The escape
syntax for variables is such that you can auto-complete them with company-mode
. Both variables
and sexps can use format
style width specifiers. See the documentation of format
for more
details.
Examples 7-9 added to hydra-examples.el
These examples explain Ruby-style interpolation.
Updated faces
I've updated each face to match their color by default. Remember, that you can customize each
face interactively with M-x customize-group
hydra
.
Alternative grey-scale or custom faces
If you don't want colors for some reason, you can customize hydra-fontify-head-function
to your
taste.
As an example:
(setq hydra-fontify-head-function
#'hydra-fontify-head-greyscale)
This function is already defined in hydra.el, but you could define a similar one on your own:
(defun hydra-fontify-head-greyscale (head body)
"Produce a pretty string from HEAD and BODY.
HEAD's binding is returned as a string wrapped with [] or {}."
(let ((color (hydra--head-color head body)))
(format
(if (eq color 'blue)
"[%s]"
"{%s}") (car head))))
New switch :body-pre
This sexp or function will be prepended to the prefix/body function.
Here's an example of use:
(defvar hydra-vi/init-pos nil)
(defhydra hydra-vi (:body-pre (setq hydra-vi/init-pos (point))
:color pink)
"vi"
;; arrows
("h" backward-char)
("j" next-line)
("k" previous-line)
("l" forward-char)
;; exit points
("q" (goto-char hydra-vi/init-pos) "ins" :exit t)
("C-n" (forward-line 1) nil :exit t)
("C-p" (forward-line -1) nil :exit t))
Here, upon entering the hydra, the point position will be saved. And upon quitting, it will be
restored.
hydra 0.10.0
New features
Define Hydra heads that don't show up in the hint at all
This can be done by setting the head's hint explicitly to nil
, instead of the usual string. For
instance, if you always tend to bind the arrows to hjkl, there's no point to show a hint
for them.
Use a dedicated window for Hydra hints
Since version 0.10.0
, setting hydra-lv
to t
(the default setting) will make it use a dedicated
window right above the Echo Area for hints. This has the advantage that you can immediately see any
message
output from the functions that you call, since Hydra no longer uses message
to display
the hint. You can still have the old behavior by setting hydra-lv
to nil
.
Allow duplicate functions in heads
Duplicate functions will be concatenated in the hint.
Example:
(defhydra hydra-zoom (global-map "<f2>")
"zoom"
("g" text-scale-increase "in")
("l" text-scale-decrease "out")
("0" (text-scale-set 0) "reset")
("1" (text-scale-set 0) :bind nil)
("2" (text-scale-set 0) :bind nil :color blue))
Here, the hint will be: "zoom: [g]: in, [l]: out, [0 1 2]: reset.", and "2" will be colored blue.
Add option to font-lock defhydra
If you want to nicely font-lock your defhydra
statements, just add this to your config:
(require 'hydra)
(hydra-add-font-lock)
Additionally, defhydra
is now to be indented as a defun
, so it will be indented like this:
(defhydra hydra-goto-line (global-map "M-g"
:pre (linum-mode 1)
:post (linum-mode -1)
:color blue)
("g" goto-line "line")
("c" goto-char "char"))
Note that the indentation of the body argument is as if it was data and not code, i.e. the proper
one.
Incompatible changes
The macro hydra-create
, as well as the variables that were supposed to be used with it
(hydra-example-text-scale
, hydra-example-move-window-splitter
, hydra-example-goto-error
,
hydra-example-windmove
) were removed. All the functionality is still there in hydra-examples.el
with the better defhydra
macro.
hydra 0.9.0
Fixes
- amaranth heads will not call :post, since they're not quitting
New features
Keyboard quit
hydra-keyboard-quit
set to "C-g"
means that it's possible to quit an amaranth Hydra
with C-g. You can customize this variable.
:pre and :post refinement
:post
and :pre
keys can be either a single sexp or a function name. The function
doesn't need to be interactive.
Support for local Hydra heads via :bind property
Example:
(defhydra hydra-next-error (global-map "C-x")
"next-error"
("`" next-error "next")
("j" next-error "next" :bind nil)
("k" previous-error "previous" :bind nil))
What it does:
- binds C-x ` to
next-error
. - does not bind C-x j and C-x k
- you can still do C-x `jjkk
Thanks, @ffevotte.
Support for :bind property in Hydra body
Both body and heads recognize :bind
property in their plist.
It can be either nil or a lambda of global-set-key
format.
Example:
(defhydra hydra-goto (global-map "M-g"
:bind
(lambda (key cmd)
(bind-key key cmd)))
("g" goto-line "goto-line" :bind global-set-key)
("c" goto-char "goto-char"))
Here, global-set-key
will be used to bind goto-line
to M-g g. And
bind-key
will be used to bind goto-char
to M-g c. Note that since
bind-key
is a macro, it was necessary to wrap it in a lambda.
Since this commit, it's not possible to pass a lambda instead of the whole BODY arg, as
was advertised before. Just put it on :bind
now.
hydra/body
will pass the initial current-prefix-arg
along
Example:
(global-set-key
(kbd "C-z")
(defhydra hydra-vi ()
"vi"
("l" forward-char)
("q" nil "quit")))
Now, C-u C-z l will result in (forward-char 4)
. All the other l
will normally call (forward-char 1)
, unless an additional prefix is given. The previous
behavior allowed only for C-z C-u l to get (forward-char 4)
.
Allow a sexp as head's CMD parameter
Example:
(defhydra hydra-launcher (:color blue)
"Launch"
("h" man "man")
("r" (browse-url "http://www.reddit.com/r/emacs/") "reddit")
("w" (browse-url "http://www.emacswiki.org/") "emacswiki")
("s" shell "shell")
("q" nil "cancel"))
(global-set-key (kbd "C-c r") 'hydra-launcher/body)
Here, r and w heads are using this feature.
Here's what will be generated, if you're interested:
(defun hydra-launcher/lambda-w nil
"Create a hydra with no body and the heads:
\"h\": `man',
\"r\": `(browse-url \"http://www.reddit.com/r/emacs/\")',
\"w\": `(browse-url \"http://www.emacswiki.org/\")',
\"s\": `shell',
\"q\": `nil'
The body can be accessed via `hydra-launcher/body'.
Call the head: `(browse-url \"http://www.emacswiki.org/\")'."
(interactive)
(hydra-disable)
(catch (quote hydra-disable)
(call-interactively
(function
(lambda nil
(interactive)
(browse-url "http://www.emacswiki.org/"))))))
Obsolete declarations
hydra-create
and all old examples in hydra-examples.el are now obsolete.
You can still use them for a short while, but they will be removed soon.
You should take the time to switch from hydra-create
to defhydra
. All the old examples
are provided in the new style in hydra-examples.el. However, they will not be evaluated
through (require 'hydra-examples)
unless you (setq hydra-examples-verbatim t)
beforehand. This is because I have no idea what kind of bindings will work for you, you
should decide yourself. But I am providing you with a template. The number of examples
has also grown to six.
hydra 0.8.0
This release introduces a new color: amaranth
, in addition to the previously available
red
and blue
.
According to Wikipedia:
The word amaranth comes from the Greek word amaranton, meaning "unwilting" (from the
verb marainesthai, meaning "wilt"). The word was applied to amaranth because it did not
soon fade and so symbolized immortality.
Hydras with amaranth body are impossible to quit with any binding except a blue head.
A check for at least one blue head exists in defhydra
, so that you don't get stuck by accident.
Here's an example of an amaranth Hydra:
(global-set-key
(kbd "C-z")
(defhydra hydra-vi
(:pre
(set-cursor-color "#40e0d0")
:post
(set-cursor-color "#ffffff")
:color amaranth)
"vi"
("l" forward-char)
("h" backward-char)
("j" next-line)
("k" previous-line)
("e" move-end-of-line :color blue)
("q" nil "quit")))
The only way to exit it, is to press either q or e. No other methods will work.
You can use an amaranth Hydra instead of a red one, if for you the
cost of being able to exit only though certain bindings is less than the cost of
accidentally exiting a red Hydra by pressing the wrong prefix.
Note that it does not make sense to define a singe amaranth head, so this color can only
be assigned to the body. An amaranth body will always have some amaranth heads and some
blue heads (otherwise, it's impossible to exit), no reds.
hydra 0.7.0
This release adds :pre
and :post
statements to Hydra body, and fixes the issue with commands that want input, such as goto-line
.
See the accompanying blog post for more information.
hydra 0.6.1
New features
This release adds the digit-argument
and universal-argument
machinery to Hydras. Now, each Hydra will automatically bind: 0 - 9, - and C-u. Note that digits can be used plainly, i.e. not as M-2 or C-2, but as 2.
This also means that you should try not to use the above keys as prefixes for Hydra heads.
Fixes
The heads calling hydra-disable
were finally fixed for Emacs 24.4.1, thanks @bcarell.
This was hard to do, because three different Emacs versions needed three different approaches to do this.
Just a reminder:
(global-set-key
(kbd "C-c C-v")
(defhydra toggle (:color blue)
"toggle"
("a" abbrev-mode "abbrev")
("d" toggle-debug-on-error "debug")
("f" auto-fill-mode "fill")
("t" toggle-truncate-lines "truncate")
("w" whitespace-mode "whitespace")
("q" nil "cancel")))
Here, all heads will call hydra-disable
since they are all blue
(because the body :color
is blue
).
Even if the body :color
was red
, q would still call hydra-disable
, because q has nil
instead of a function name.
hydra 0.5.0
This release introduces colorful Hydra heads. Now, each head is:
- either red - calling this head will not vanquish the Hydra. This is the default one, and
is exactly the same as the previous behavior - or blue - calling this head will vanquish the Hydra.
You can mix-and-match the colors, if you like.
Here's an example of a pure-blue Hydra:
(global-set-key
(kbd "C-c C-v")
(defhydra toggle (:color blue)
"toggle"
("a" abbrev-mode "abbrev")
("d" toggle-debug-on-error "debug")
("f" auto-fill-mode "fill")
("t" toggle-truncate-lines "truncate")
("w" whitespace-mode "whitespace")
("q" nil "cancel")))
Here's an almost all-red Hydra, with one blue head:
(defhydra hydra-error (global-map "M-g")
"goto-error"
("h" first-error "first")
("j" next-error "next")
("k" previous-error "prev")
("v" recenter-top-bottom "recenter")
("q" nil "quit"))
The q became blue, because its function is nil
.
Or, for example, you could make the first head also blue:
(defhydra hydra-error (global-map "M-g")
"goto-error"
("h" first-error "first" :color blue)
("j" next-error "next")
("k" previous-error "prev")
("v" recenter-top-bottom "recenter")
("q" nil "quit"))
The advantages of a pure-blue Hydra over a plain nested keymap:
-
You get a hint straight away, e.g. after C-c C-v
toggle: [a]: abbrev, [d]: debug, [f]: fill, [t]: truncate, [w]: whitespace, [q]: cancel.
-
You can still cancel C-c C-v with any other command.