emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 11b130e 17/17: Merge commit '0c08964462812942db51d177e6ea9


From: Oleh Krehel
Subject: [elpa] master 11b130e 17/17: Merge commit '0c08964462812942db51d177e6ea922b26019e65' from hydra
Date: Tue, 10 Feb 2015 07:13:29 +0000

branch: master
commit 11b130e1353349b0ac612811e598eb3c93f75365
Merge: 61bddf2 0c08964
Author: Oleh Krehel <address@hidden>
Commit: Oleh Krehel <address@hidden>

    Merge commit '0c08964462812942db51d177e6ea922b26019e65' from hydra
---
 packages/hydra/README.md         |   59 +++-
 packages/hydra/hydra-examples.el |  173 ++++++++-
 packages/hydra/hydra-test.el     |  758 ++++++++++++++++----------------------
 packages/hydra/hydra.el          |  243 ++++++++----
 4 files changed, 679 insertions(+), 554 deletions(-)

diff --git a/packages/hydra/README.md b/packages/hydra/README.md
index 7272098..c7c1dff 100644
--- a/packages/hydra/README.md
+++ b/packages/hydra/README.md
@@ -16,29 +16,42 @@ Hydra, will still serve his orignal purpose, calling his 
proper
 command.  This makes the Hydra very seamless, it's like a minor mode
 that disables itself auto-magically.
 
-## Simplified usage
-
-Here's how to quickly bind the examples bundled with Hydra:
+## Sample global Hydras
+### Zoom
 
 ```cl
-(require 'hydra-examples)
-(hydra-create "C-M-y" hydra-example-move-window-splitter)
-(hydra-create "M-g" hydra-example-goto-error)
-(hydra-create "<f2>" hydra-example-text-scale)
+(defhydra hydra-zoom (global-map "<f2>")
+  "zoom"
+  ("g" text-scale-increase "in")
+  ("l" text-scale-decrease "out"))
 ```
 
-## Using Hydra for global bindings
+### Goto-error
 
-But it's much better to just take the examples as a template and write
-down everything explicitly:
+```cl
+(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"))
+```
+
+### Splitter
 
 ```cl
-(defhydra hydra-zoom (global-map "<f2>")
-  "zoom"
-  ("g" text-scale-increase "in")
-  ("l" text-scale-decrease "out"))
+(require 'hydra-examples)
+(defhydra hydra-splitter (global-map "C-M-s")
+  "splitter"
+  ("h" hydra-move-splitter-left)
+  ("j" hydra-move-splitter-down)
+  ("k" hydra-move-splitter-up)
+  ("l" hydra-move-splitter-right))
 ```
 
+## Using the functions generated by `defhydra`
+
 With the example above, you can e.g.:
 
 ```cl
@@ -234,6 +247,22 @@ use an amaranth Hydra instead of a red one, if for you the 
cost of being able to
 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
+Note that it does not make sense to define a single 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.
+
+## Generate simple lambdas in-place:
+
+Since version `0.9.0` it's possible to pass a single sexp instead of a 
function name or a lambda
+to a head. This sexp will be wrapped in an interactive lambda. Here's an 
example:
+
+```cl
+(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)
+```
diff --git a/packages/hydra/hydra-examples.el b/packages/hydra/hydra-examples.el
index 3dfc836..5167c50 100644
--- a/packages/hydra/hydra-examples.el
+++ b/packages/hydra/hydra-examples.el
@@ -21,26 +21,147 @@
 
 ;;; Commentary:
 ;;
-;; These are the sample Hydras that you can use.
+;; These are the sample Hydras.
 ;;
-;; Note that the better way to create Hydras is with `defhydra':
-;;
-;; (defhydra hydra-zoom (global-map "<f2>")
-;;   "zoom"
-;;   ("g" text-scale-increase "in")
-;;   ("l" text-scale-decrease "out"))
-;;
-;; This way, you have more options, and everything is in one place.
+;; If you want to use them plainly, set `hydra-examples-verbatim' to t
+;; before requiring this file. But it's probably better to only look
+;; at them and use them as templates for building your own.
 
 ;;; Code:
 
 (require 'hydra)
 
-(defvar hydra-example-text-scale
-  '(("g" text-scale-increase "zoom in")
-    ("l" text-scale-decrease "zoom out"))
-  "A two-headed hydra for text scale manipulation.")
+;;* Examples
+;;** Example 1: text scale
+(when (bound-and-true-p hydra-examples-verbatim)
+  (defhydra hydra-zoom (global-map "<f2>")
+    "zoom"
+    ("g" text-scale-increase "in")
+    ("l" text-scale-decrease "out")))
+
+;; This example generates three commands:
+;;
+;;     `hydra-zoom/text-scale-increase'
+;;     `hydra-zoom/text-scale-decrease'
+;;     `hydra-zoom/body'
+;;
+;; In addition, two of them are bound like this:
+;;
+;;     (global-set-key (kbd "<f2> g") 'hydra-zoom/text-scale-increase)
+;;     (global-set-key (kbd "<f2> l") 'hydra-zoom/text-scale-decrease)
+;;
+;; Note that you can substitute `global-map' with e.g. `emacs-lisp-mode-map' 
if you need.
+;; The functions generated will be the same, except the binding code will 
change to:
+;;
+;;     (define-key emacs-lisp-mode-map [f2 103]
+;;       (function hydra-zoom/text-scale-increase))
+;;     (define-key emacs-lisp-mode-map [f2 108]
+;;       (function hydra-zoom/text-scale-decrease))
+
+;;** Example 2: move window splitter
+(when (bound-and-true-p hydra-examples-verbatim)
+  (defhydra hydra-splitter (global-map "C-M-s")
+    "splitter"
+    ("h" hydra-move-splitter-left)
+    ("j" hydra-move-splitter-down)
+    ("k" hydra-move-splitter-up)
+    ("l" hydra-move-splitter-right)))
 
+;;** Example 3: jump to error
+(when (bound-and-true-p hydra-examples-verbatim)
+  (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")))
+
+;; This example introduces only one new thing: since the command
+;; passed to the "q" head is nil, it will quit the Hydra without doing
+;; anything. Heads that quit the Hydra instead of continuing are
+;; referred to as having blue :color. All the other heads have red
+;; :color, unless other is specified.
+
+;;** Example 4: toggle rarely used modes
+(when (bound-and-true-p hydra-examples-verbatim)
+  (global-set-key
+   (kbd "C-c C-v")
+   (defhydra hydra-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"))))
+
+;; Note that in this case, `defhydra' returns the `hydra-toggle/body'
+;; symbol, which is then passed to `global-set-key'.
+;;
+;; Another new thing is that both the keymap and the body prefix are
+;; skipped.  This means that `defhydra' will bind nothing - that's why
+;; `global-set-key' is necessary.
+;;
+;; One more new thing is that you can assign a :color to the body. All
+;; heads will inherit this color. The code above is very much equivalent to:
+;;
+;;     (global-set-key (kbd "C-c C-v a") 'abbrev-mode)
+;;     (global-set-key (kbd "C-c C-v d") 'toggle-debug-on-error)
+;;
+;; The differences are:
+;;
+;; * You get a hint immediately after "C-c C-v"
+;; * You can cancel and call a command immediately, e.g. "C-c C-v C-n"
+;;   is equivalent to "C-n" with Hydra approach, while it will error
+;;   that "C-c C-v C-n" isn't bound with the usual approach.
+
+;;** Example 5: mini-vi
+(defun hydra-vi/pre ()
+  (set-cursor-color "#e52b50"))
+
+(defun hydra-vi/post ()
+  (set-cursor-color "#ffffff"))
+
+(when (bound-and-true-p hydra-examples-verbatim)
+  (global-set-key
+   (kbd "C-z")
+   (defhydra hydra-vi (:pre hydra-vi/pre :post hydra-vi/post :color amaranth)
+     "vi"
+     ("l" forward-char)
+     ("h" backward-char)
+     ("j" next-line)
+     ("k" previous-line)
+     ("m" set-mark-command "mark")
+     ("a" move-beginning-of-line "beg")
+     ("e" move-end-of-line "end")
+     ("d" delete-region "del" :color blue)
+     ("y" kill-ring-save "yank" :color blue)
+     ("q" nil "quit"))))
+
+;; This example introduces :color amaranth. It's similar to red,
+;; except while you can quit red with any binding which isn't a Hydra
+;; head, you can quit amaranth only with a blue head. So you can quit
+;; this mode only with "d", "y", "q" or "C-g".
+;;
+;; Another novelty are the :pre and :post handlers. :pre will be
+;; called before each command, while :post will be called when the
+;; Hydra quits. In this case, they're used to override the cursor
+;; color while Hydra is active.
+
+;;** Example 6: selective global bind
+(when (bound-and-true-p hydra-examples-verbatim)
+  (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)))
+
+;; This example will bind "C-x `" in `global-map', but it will not
+;; bind "C-x j" and "C-x k".
+;; You can still "C-x `jjk" though.
+
+;;* Windmove helpers
 (require 'windmove)
 
 (defun hydra-move-splitter-left (arg)
@@ -75,6 +196,17 @@
       (shrink-window arg)
     (enlarge-window arg)))
 
+;;* Obsoletes
+(defvar hydra-example-text-scale
+  '(("g" text-scale-increase "zoom in")
+    ("l" text-scale-decrease "zoom out"))
+  "A two-headed hydra for text scale manipulation.")
+(make-obsolete-variable
+ 'hydra-example-text-scale
+ "Don't use `hydra-example-text-scale', just write your own
+`defhydra' using hydra-examples.el as a template"
+ "0.9.0")
+
 (defvar hydra-example-move-window-splitter
   '(("h" hydra-move-splitter-left)
     ("j" hydra-move-splitter-down)
@@ -82,6 +214,11 @@
     ("l" hydra-move-splitter-right))
   "A four-headed hydra for the window splitter manipulation.
 Works best if you have not more than 4 windows.")
+(make-obsolete-variable
+ 'hydra-example-move-window-splitter
+ "Don't use `hydra-example-move-window-splitter', just write your own
+`defhydra' using hydra-examples.el as a template"
+ "0.9.0")
 
 (defvar hydra-example-goto-error
   '(("h" first-error "first")
@@ -89,6 +226,11 @@ Works best if you have not more than 4 windows.")
     ("k" previous-error "prev"))
   "A three-headed hydra for jumping between \"errors\".
 Useful for e.g. `occur', `rgrep' and the like.")
+(make-obsolete-variable
+ 'hydra-example-goto-error
+ "Don't use `hydra-example-goto-error', just write your own
+`defhydra' using hydra-examples.el as a template"
+ "0.9.0")
 
 (defvar hydra-example-windmove
   '(("h" windmove-left)
@@ -96,6 +238,11 @@ Useful for e.g. `occur', `rgrep' and the like.")
     ("k" windmove-up)
     ("l" windmove-right))
   "A four-headed hydra for `windmove'.")
+(make-obsolete-variable
+ 'hydra-example-windmove
+ "Don't use `hydra-example-windmove', just write your own
+`defhydra' using hydra-examples.el as a template"
+ "0.9.0")
 
 (provide 'hydra-examples)
 ;;; hydra-examples.el ends here
diff --git a/packages/hydra/hydra-test.el b/packages/hydra/hydra-test.el
index eee851a..f2311ab 100644
--- a/packages/hydra/hydra-test.el
+++ b/packages/hydra/hydra-test.el
@@ -46,8 +46,8 @@ The body can be accessed via `hydra-error/body'.
 
 Call the head: `first-error'."
              (interactive)
+             (hydra-disable)
              (catch (quote hydra-disable)
-               (hydra-disable)
                (condition-case err (prog1 t (call-interactively (function 
first-error)))
                  ((debug error)
                   (message "%S" err)
@@ -58,32 +58,33 @@ Call the head: `first-error'."
                                                  31 32 (face hydra-face-red))))
                (setq hydra-last
                      (hydra-set-transient-map
-                      (setq hydra-curr-map '(keymap (107 . 
hydra-error/previous-error)
-                                             (106 . hydra-error/next-error)
-                                             (104 . hydra-error/first-error)
-                                             (kp-subtract . 
hydra--negative-argument)
-                                             (kp-9 . hydra--digit-argument)
-                                             (kp-8 . hydra--digit-argument)
-                                             (kp-7 . hydra--digit-argument)
-                                             (kp-6 . hydra--digit-argument)
-                                             (kp-5 . hydra--digit-argument)
-                                             (kp-4 . hydra--digit-argument)
-                                             (kp-3 . hydra--digit-argument)
-                                             (kp-2 . hydra--digit-argument)
-                                             (kp-1 . hydra--digit-argument)
-                                             (kp-0 . hydra--digit-argument)
-                                             (57 . hydra--digit-argument)
-                                             (56 . hydra--digit-argument)
-                                             (55 . hydra--digit-argument)
-                                             (54 . hydra--digit-argument)
-                                             (53 . hydra--digit-argument)
-                                             (52 . hydra--digit-argument)
-                                             (51 . hydra--digit-argument)
-                                             (50 . hydra--digit-argument)
-                                             (49 . hydra--digit-argument)
-                                             (48 . hydra--digit-argument)
-                                             (45 . hydra--negative-argument)
-                                             (21 . hydra--universal-argument)))
+                      (setq hydra-curr-map
+                            (quote (keymap (107 . hydra-error/previous-error)
+                                           (106 . hydra-error/next-error)
+                                           (104 . hydra-error/first-error)
+                                           (kp-subtract . 
hydra--negative-argument)
+                                           (kp-9 . hydra--digit-argument)
+                                           (kp-8 . hydra--digit-argument)
+                                           (kp-7 . hydra--digit-argument)
+                                           (kp-6 . hydra--digit-argument)
+                                           (kp-5 . hydra--digit-argument)
+                                           (kp-4 . hydra--digit-argument)
+                                           (kp-3 . hydra--digit-argument)
+                                           (kp-2 . hydra--digit-argument)
+                                           (kp-1 . hydra--digit-argument)
+                                           (kp-0 . hydra--digit-argument)
+                                           (57 . hydra--digit-argument)
+                                           (56 . hydra--digit-argument)
+                                           (55 . hydra--digit-argument)
+                                           (54 . hydra--digit-argument)
+                                           (53 . hydra--digit-argument)
+                                           (52 . hydra--digit-argument)
+                                           (51 . hydra--digit-argument)
+                                           (50 . hydra--digit-argument)
+                                           (49 . hydra--digit-argument)
+                                           (48 . hydra--digit-argument)
+                                           (45 . hydra--negative-argument)
+                                           (21 . hydra--universal-argument))))
                       t))))
       (defun hydra-error/next-error nil "Create a hydra with a \"M-g\" body 
and the heads:
 
@@ -95,8 +96,8 @@ The body can be accessed via `hydra-error/body'.
 
 Call the head: `next-error'."
              (interactive)
+             (hydra-disable)
              (catch (quote hydra-disable)
-               (hydra-disable)
                (condition-case err (prog1 t (call-interactively (function 
next-error)))
                  ((debug error)
                   (message "%S" err)
@@ -107,32 +108,33 @@ Call the head: `next-error'."
                                                  31 32 (face hydra-face-red))))
                (setq hydra-last
                      (hydra-set-transient-map
-                      (setq hydra-curr-map '(keymap (107 . 
hydra-error/previous-error)
-                                             (106 . hydra-error/next-error)
-                                             (104 . hydra-error/first-error)
-                                             (kp-subtract . 
hydra--negative-argument)
-                                             (kp-9 . hydra--digit-argument)
-                                             (kp-8 . hydra--digit-argument)
-                                             (kp-7 . hydra--digit-argument)
-                                             (kp-6 . hydra--digit-argument)
-                                             (kp-5 . hydra--digit-argument)
-                                             (kp-4 . hydra--digit-argument)
-                                             (kp-3 . hydra--digit-argument)
-                                             (kp-2 . hydra--digit-argument)
-                                             (kp-1 . hydra--digit-argument)
-                                             (kp-0 . hydra--digit-argument)
-                                             (57 . hydra--digit-argument)
-                                             (56 . hydra--digit-argument)
-                                             (55 . hydra--digit-argument)
-                                             (54 . hydra--digit-argument)
-                                             (53 . hydra--digit-argument)
-                                             (52 . hydra--digit-argument)
-                                             (51 . hydra--digit-argument)
-                                             (50 . hydra--digit-argument)
-                                             (49 . hydra--digit-argument)
-                                             (48 . hydra--digit-argument)
-                                             (45 . hydra--negative-argument)
-                                             (21 . hydra--universal-argument)))
+                      (setq hydra-curr-map
+                            (quote (keymap (107 . hydra-error/previous-error)
+                                           (106 . hydra-error/next-error)
+                                           (104 . hydra-error/first-error)
+                                           (kp-subtract . 
hydra--negative-argument)
+                                           (kp-9 . hydra--digit-argument)
+                                           (kp-8 . hydra--digit-argument)
+                                           (kp-7 . hydra--digit-argument)
+                                           (kp-6 . hydra--digit-argument)
+                                           (kp-5 . hydra--digit-argument)
+                                           (kp-4 . hydra--digit-argument)
+                                           (kp-3 . hydra--digit-argument)
+                                           (kp-2 . hydra--digit-argument)
+                                           (kp-1 . hydra--digit-argument)
+                                           (kp-0 . hydra--digit-argument)
+                                           (57 . hydra--digit-argument)
+                                           (56 . hydra--digit-argument)
+                                           (55 . hydra--digit-argument)
+                                           (54 . hydra--digit-argument)
+                                           (53 . hydra--digit-argument)
+                                           (52 . hydra--digit-argument)
+                                           (51 . hydra--digit-argument)
+                                           (50 . hydra--digit-argument)
+                                           (49 . hydra--digit-argument)
+                                           (48 . hydra--digit-argument)
+                                           (45 . hydra--negative-argument)
+                                           (21 . hydra--universal-argument))))
                       t))))
       (defun hydra-error/previous-error nil "Create a hydra with a \"M-g\" 
body and the heads:
 
@@ -144,8 +146,8 @@ The body can be accessed via `hydra-error/body'.
 
 Call the head: `previous-error'."
              (interactive)
+             (hydra-disable)
              (catch (quote hydra-disable)
-               (hydra-disable)
                (condition-case err (prog1 t (call-interactively (function 
previous-error)))
                  ((debug error)
                   (message "%S" err)
@@ -156,32 +158,33 @@ Call the head: `previous-error'."
                                                  31 32 (face hydra-face-red))))
                (setq hydra-last
                      (hydra-set-transient-map
-                      (setq hydra-curr-map '(keymap (107 . 
hydra-error/previous-error)
-                                             (106 . hydra-error/next-error)
-                                             (104 . hydra-error/first-error)
-                                             (kp-subtract . 
hydra--negative-argument)
-                                             (kp-9 . hydra--digit-argument)
-                                             (kp-8 . hydra--digit-argument)
-                                             (kp-7 . hydra--digit-argument)
-                                             (kp-6 . hydra--digit-argument)
-                                             (kp-5 . hydra--digit-argument)
-                                             (kp-4 . hydra--digit-argument)
-                                             (kp-3 . hydra--digit-argument)
-                                             (kp-2 . hydra--digit-argument)
-                                             (kp-1 . hydra--digit-argument)
-                                             (kp-0 . hydra--digit-argument)
-                                             (57 . hydra--digit-argument)
-                                             (56 . hydra--digit-argument)
-                                             (55 . hydra--digit-argument)
-                                             (54 . hydra--digit-argument)
-                                             (53 . hydra--digit-argument)
-                                             (52 . hydra--digit-argument)
-                                             (51 . hydra--digit-argument)
-                                             (50 . hydra--digit-argument)
-                                             (49 . hydra--digit-argument)
-                                             (48 . hydra--digit-argument)
-                                             (45 . hydra--negative-argument)
-                                             (21 . hydra--universal-argument)))
+                      (setq hydra-curr-map
+                            (quote (keymap (107 . hydra-error/previous-error)
+                                           (106 . hydra-error/next-error)
+                                           (104 . hydra-error/first-error)
+                                           (kp-subtract . 
hydra--negative-argument)
+                                           (kp-9 . hydra--digit-argument)
+                                           (kp-8 . hydra--digit-argument)
+                                           (kp-7 . hydra--digit-argument)
+                                           (kp-6 . hydra--digit-argument)
+                                           (kp-5 . hydra--digit-argument)
+                                           (kp-4 . hydra--digit-argument)
+                                           (kp-3 . hydra--digit-argument)
+                                           (kp-2 . hydra--digit-argument)
+                                           (kp-1 . hydra--digit-argument)
+                                           (kp-0 . hydra--digit-argument)
+                                           (57 . hydra--digit-argument)
+                                           (56 . hydra--digit-argument)
+                                           (55 . hydra--digit-argument)
+                                           (54 . hydra--digit-argument)
+                                           (53 . hydra--digit-argument)
+                                           (52 . hydra--digit-argument)
+                                           (51 . hydra--digit-argument)
+                                           (50 . hydra--digit-argument)
+                                           (49 . hydra--digit-argument)
+                                           (48 . hydra--digit-argument)
+                                           (45 . hydra--negative-argument)
+                                           (21 . hydra--universal-argument))))
                       t))))
       (unless (keymapp (lookup-key global-map (kbd "M-g")))
         (define-key global-map (kbd "M-g")
@@ -200,38 +203,42 @@ Call the head: `previous-error'."
 
 The body can be accessed via `hydra-error/body'."
              (interactive)
-             (when hydra-is-helpful (message #("error: [h]: first, [j]: next, 
[k]: prev." 8 9 (face hydra-face-red)
-                                               20 21 (face hydra-face-red)
-                                               31 32 (face hydra-face-red))))
-             (setq hydra-last
-                   (hydra-set-transient-map
-                    '(keymap (107 . hydra-error/previous-error)
-                      (106 . hydra-error/next-error)
-                      (104 . hydra-error/first-error)
-                      (kp-subtract . hydra--negative-argument)
-                      (kp-9 . hydra--digit-argument)
-                      (kp-8 . hydra--digit-argument)
-                      (kp-7 . hydra--digit-argument)
-                      (kp-6 . hydra--digit-argument)
-                      (kp-5 . hydra--digit-argument)
-                      (kp-4 . hydra--digit-argument)
-                      (kp-3 . hydra--digit-argument)
-                      (kp-2 . hydra--digit-argument)
-                      (kp-1 . hydra--digit-argument)
-                      (kp-0 . hydra--digit-argument)
-                      (57 . hydra--digit-argument)
-                      (56 . hydra--digit-argument)
-                      (55 . hydra--digit-argument)
-                      (54 . hydra--digit-argument)
-                      (53 . hydra--digit-argument)
-                      (52 . hydra--digit-argument)
-                      (51 . hydra--digit-argument)
-                      (50 . hydra--digit-argument)
-                      (49 . hydra--digit-argument)
-                      (48 . hydra--digit-argument)
-                      (45 . hydra--negative-argument)
-                      (21 . hydra--universal-argument))
-                    t)))))))
+             (hydra-disable)
+             (catch (quote hydra-disable)
+               (when hydra-is-helpful (message #("error: [h]: first, [j]: 
next, [k]: prev." 8 9 (face hydra-face-red)
+                                                 20 21 (face hydra-face-red)
+                                                 31 32 (face hydra-face-red))))
+               (setq hydra-last
+                     (hydra-set-transient-map
+                      (setq hydra-curr-map
+                            (quote (keymap (107 . hydra-error/previous-error)
+                                           (106 . hydra-error/next-error)
+                                           (104 . hydra-error/first-error)
+                                           (kp-subtract . 
hydra--negative-argument)
+                                           (kp-9 . hydra--digit-argument)
+                                           (kp-8 . hydra--digit-argument)
+                                           (kp-7 . hydra--digit-argument)
+                                           (kp-6 . hydra--digit-argument)
+                                           (kp-5 . hydra--digit-argument)
+                                           (kp-4 . hydra--digit-argument)
+                                           (kp-3 . hydra--digit-argument)
+                                           (kp-2 . hydra--digit-argument)
+                                           (kp-1 . hydra--digit-argument)
+                                           (kp-0 . hydra--digit-argument)
+                                           (57 . hydra--digit-argument)
+                                           (56 . hydra--digit-argument)
+                                           (55 . hydra--digit-argument)
+                                           (54 . hydra--digit-argument)
+                                           (53 . hydra--digit-argument)
+                                           (52 . hydra--digit-argument)
+                                           (51 . hydra--digit-argument)
+                                           (50 . hydra--digit-argument)
+                                           (49 . hydra--digit-argument)
+                                           (48 . hydra--digit-argument)
+                                           (45 . hydra--negative-argument)
+                                           (21 . hydra--universal-argument))))
+                      t))
+               (setq prefix-arg current-prefix-arg)))))))
 
 (ert-deftest hydra-blue-toggle ()
   (should
@@ -256,7 +263,8 @@ The body can be accessed via `toggle/body'.
 Call the head: `toggle-truncate-lines'."
              (interactive)
              (hydra-disable)
-             (call-interactively (function toggle-truncate-lines)))
+             (catch (quote hydra-disable)
+               (call-interactively (function toggle-truncate-lines))))
       (defun toggle/auto-fill-mode nil "Create a hydra with no body and the 
heads:
 
 \"t\":    `toggle-truncate-lines',
@@ -269,7 +277,8 @@ The body can be accessed via `toggle/body'.
 Call the head: `auto-fill-mode'."
              (interactive)
              (hydra-disable)
-             (call-interactively (function auto-fill-mode)))
+             (catch (quote hydra-disable)
+               (call-interactively (function auto-fill-mode))))
       (defun toggle/abbrev-mode nil "Create a hydra with no body and the heads:
 
 \"t\":    `toggle-truncate-lines',
@@ -282,7 +291,8 @@ The body can be accessed via `toggle/body'.
 Call the head: `abbrev-mode'."
              (interactive)
              (hydra-disable)
-             (call-interactively (function abbrev-mode)))
+             (catch (quote hydra-disable)
+               (call-interactively (function abbrev-mode))))
       (defun toggle/nil nil "Create a hydra with no body and the heads:
 
 \"t\":    `toggle-truncate-lines',
@@ -294,7 +304,8 @@ The body can be accessed via `toggle/body'.
 
 Call the head: `nil'."
              (interactive)
-             (hydra-disable))
+             (hydra-disable)
+             (catch (quote hydra-disable)))
       (defun toggle/body nil "Create a hydra with no body and the heads:
 
 \"t\":    `toggle-truncate-lines',
@@ -304,100 +315,19 @@ Call the head: `nil'."
 
 The body can be accessed via `toggle/body'."
              (interactive)
-             (when hydra-is-helpful (message #("toggle: [t]: truncate, [f]: 
fill, [a]: abbrev, [q]: cancel." 9 10 (face hydra-face-blue)
-                                               24 25 (face hydra-face-blue)
-                                               35 36 (face hydra-face-blue)
-                                               48 49 (face hydra-face-blue))))
-             (setq hydra-last
-                   (hydra-set-transient-map
-                    '(keymap (113 . toggle/nil)
-                      (97 . toggle/abbrev-mode)
-                      (102 . toggle/auto-fill-mode)
-                      (116 . toggle/toggle-truncate-lines)
-                      (kp-subtract . hydra--negative-argument)
-                      (kp-9 . hydra--digit-argument)
-                      (kp-8 . hydra--digit-argument)
-                      (kp-7 . hydra--digit-argument)
-                      (kp-6 . hydra--digit-argument)
-                      (kp-5 . hydra--digit-argument)
-                      (kp-4 . hydra--digit-argument)
-                      (kp-3 . hydra--digit-argument)
-                      (kp-2 . hydra--digit-argument)
-                      (kp-1 . hydra--digit-argument)
-                      (kp-0 . hydra--digit-argument)
-                      (57 . hydra--digit-argument)
-                      (56 . hydra--digit-argument)
-                      (55 . hydra--digit-argument)
-                      (54 . hydra--digit-argument)
-                      (53 . hydra--digit-argument)
-                      (52 . hydra--digit-argument)
-                      (51 . hydra--digit-argument)
-                      (50 . hydra--digit-argument)
-                      (49 . hydra--digit-argument)
-                      (48 . hydra--digit-argument)
-                      (45 . hydra--negative-argument)
-                      (21 . hydra--universal-argument))
-                    t)))))))
-
-(ert-deftest hydra-amaranth-vi ()
-  (should
-   (equal
-    (macroexpand
-     '(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)
-       ("q" nil "quit")))
-    '(progn
-      (defun hydra-vi/forward-char nil "Create a hydra with no body and the 
heads:
-
-\"l\":    `forward-char',
-\"h\":    `backward-char',
-\"j\":    `next-line',
-\"k\":    `previous-line',
-\"q\":    `nil'
-
-The body can be accessed via `hydra-vi/body'.
-
-Call the head: `forward-char'."
-             (interactive)
-             (set-cursor-color "#40e0d0")
+             (hydra-disable)
              (catch (quote hydra-disable)
-               (hydra-disable)
-               (condition-case err (prog1 t (call-interactively (function 
forward-char)))
-                 ((debug error)
-                  (message "%S" err)
-                  (sit-for 0.8)
-                  nil))
-               (when hydra-is-helpful (message #("vi: l, h, j, k, [q]: quit." 
4 5 (face hydra-face-amaranth)
-                                                 7 8 (face hydra-face-amaranth)
-                                                 10 11 (face 
hydra-face-amaranth)
-                                                 13 14 (face 
hydra-face-amaranth)
-                                                 17 18 (face 
hydra-face-blue))))
+               (when hydra-is-helpful (message #("toggle: [t]: truncate, [f]: 
fill, [a]: abbrev, [q]: cancel." 9 10 (face hydra-face-blue)
+                                                 24 25 (face hydra-face-blue)
+                                                 35 36 (face hydra-face-blue)
+                                                 48 49 (face 
hydra-face-blue))))
                (setq hydra-last
                      (hydra-set-transient-map
                       (setq hydra-curr-map
-                            (quote (keymap (t lambda nil (interactive)
-                                              (message "An amaranth Hydra can 
only exit through a blue head")
-                                              (hydra-set-transient-map 
hydra-curr-map t)
-                                              (when hydra-is-helpful (sit-for 
0.8)
-                                                    (message #("vi: l, h, j, 
k, [q]: quit." 4 5 (face hydra-face-amaranth)
-                                                               7 8 (face 
hydra-face-amaranth)
-                                                               10 11 (face 
hydra-face-amaranth)
-                                                               13 14 (face 
hydra-face-amaranth)
-                                                               17 18 (face 
hydra-face-blue)))))
-                                           (113 . hydra-vi/nil)
-                                           (107 . hydra-vi/previous-line)
-                                           (106 . hydra-vi/next-line)
-                                           (104 . hydra-vi/backward-char)
-                                           (108 . hydra-vi/forward-char)
+                            (quote (keymap (113 . toggle/nil)
+                                           (97 . toggle/abbrev-mode)
+                                           (102 . toggle/auto-fill-mode)
+                                           (116 . toggle/toggle-truncate-lines)
                                            (kp-subtract . 
hydra--negative-argument)
                                            (kp-9 . hydra--digit-argument)
                                            (kp-8 . hydra--digit-argument)
@@ -421,77 +351,27 @@ Call the head: `forward-char'."
                                            (48 . hydra--digit-argument)
                                            (45 . hydra--negative-argument)
                                            (21 . hydra--universal-argument))))
-                      t (lambda nil (set-cursor-color "#ffffff"))))))
-      (defun hydra-vi/backward-char nil "Create a hydra with no body and the 
heads:
+                      t))
+               (setq prefix-arg current-prefix-arg)))))))
 
-\"l\":    `forward-char',
-\"h\":    `backward-char',
-\"j\":    `next-line',
-\"k\":    `previous-line',
-\"q\":    `nil'
-
-The body can be accessed via `hydra-vi/body'.
-
-Call the head: `backward-char'."
-             (interactive)
-             (set-cursor-color "#40e0d0")
-             (catch (quote hydra-disable)
-               (hydra-disable)
-               (condition-case err (prog1 t (call-interactively (function 
backward-char)))
-                 ((debug error)
-                  (message "%S" err)
-                  (sit-for 0.8)
-                  nil))
-               (when hydra-is-helpful (message #("vi: l, h, j, k, [q]: quit." 
4 5 (face hydra-face-amaranth)
-                                                 7 8 (face hydra-face-amaranth)
-                                                 10 11 (face 
hydra-face-amaranth)
-                                                 13 14 (face 
hydra-face-amaranth)
-                                                 17 18 (face 
hydra-face-blue))))
-               (setq hydra-last
-                     (hydra-set-transient-map
-                      (setq hydra-curr-map
-                            (quote (keymap (t lambda nil (interactive)
-                                              (message "An amaranth Hydra can 
only exit through a blue head")
-                                              (hydra-set-transient-map 
hydra-curr-map t)
-                                              (when hydra-is-helpful (sit-for 
0.8)
-                                                    (message #("vi: l, h, j, 
k, [q]: quit." 4 5 (face hydra-face-amaranth)
-                                                               7 8 (face 
hydra-face-amaranth)
-                                                               10 11 (face 
hydra-face-amaranth)
-                                                               13 14 (face 
hydra-face-amaranth)
-                                                               17 18 (face 
hydra-face-blue)))))
-                                           (113 . hydra-vi/nil)
-                                           (107 . hydra-vi/previous-line)
-                                           (106 . hydra-vi/next-line)
-                                           (104 . hydra-vi/backward-char)
-                                           (108 . hydra-vi/forward-char)
-                                           (kp-subtract . 
hydra--negative-argument)
-                                           (kp-9 . hydra--digit-argument)
-                                           (kp-8 . hydra--digit-argument)
-                                           (kp-7 . hydra--digit-argument)
-                                           (kp-6 . hydra--digit-argument)
-                                           (kp-5 . hydra--digit-argument)
-                                           (kp-4 . hydra--digit-argument)
-                                           (kp-3 . hydra--digit-argument)
-                                           (kp-2 . hydra--digit-argument)
-                                           (kp-1 . hydra--digit-argument)
-                                           (kp-0 . hydra--digit-argument)
-                                           (57 . hydra--digit-argument)
-                                           (56 . hydra--digit-argument)
-                                           (55 . hydra--digit-argument)
-                                           (54 . hydra--digit-argument)
-                                           (53 . hydra--digit-argument)
-                                           (52 . hydra--digit-argument)
-                                           (51 . hydra--digit-argument)
-                                           (50 . hydra--digit-argument)
-                                           (49 . hydra--digit-argument)
-                                           (48 . hydra--digit-argument)
-                                           (45 . hydra--negative-argument)
-                                           (21 . hydra--universal-argument))))
-                      t (lambda nil (set-cursor-color "#ffffff"))))))
-      (defun hydra-vi/next-line nil "Create a hydra with no body and the heads:
+(ert-deftest hydra-amaranth-vi ()
+  (unless (version< emacs-version "24.4")
+    (should
+     (equal
+      (macroexpand
+       '(defhydra hydra-vi
+         (:pre
+          (set-cursor-color "#e52b50")
+          :post
+          (set-cursor-color "#ffffff")
+          :color amaranth)
+         "vi"
+         ("j" next-line)
+         ("k" previous-line)
+         ("q" nil "quit")))
+      '(progn
+        (defun hydra-vi/next-line nil "Create a hydra with no body and the 
heads:
 
-\"l\":    `forward-char',
-\"h\":    `backward-char',
 \"j\":    `next-line',
 \"k\":    `previous-line',
 \"q\":    `nil'
@@ -499,65 +379,60 @@ Call the head: `backward-char'."
 The body can be accessed via `hydra-vi/body'.
 
 Call the head: `next-line'."
-             (interactive)
-             (set-cursor-color "#40e0d0")
-             (catch (quote hydra-disable)
+               (interactive)
+               (set-cursor-color "#e52b50")
                (hydra-disable)
-               (condition-case err (prog1 t (call-interactively (function 
next-line)))
-                 ((debug error)
-                  (message "%S" err)
-                  (sit-for 0.8)
-                  nil))
-               (when hydra-is-helpful (message #("vi: l, h, j, k, [q]: quit." 
4 5 (face hydra-face-amaranth)
-                                                 7 8 (face hydra-face-amaranth)
-                                                 10 11 (face 
hydra-face-amaranth)
-                                                 13 14 (face 
hydra-face-amaranth)
-                                                 17 18 (face 
hydra-face-blue))))
-               (setq hydra-last
-                     (hydra-set-transient-map
-                      (setq hydra-curr-map
-                            (quote (keymap (t lambda nil (interactive)
-                                              (message "An amaranth Hydra can 
only exit through a blue head")
-                                              (hydra-set-transient-map 
hydra-curr-map t)
-                                              (when hydra-is-helpful (sit-for 
0.8)
-                                                    (message #("vi: l, h, j, 
k, [q]: quit." 4 5 (face hydra-face-amaranth)
-                                                               7 8 (face 
hydra-face-amaranth)
-                                                               10 11 (face 
hydra-face-amaranth)
-                                                               13 14 (face 
hydra-face-amaranth)
-                                                               17 18 (face 
hydra-face-blue)))))
-                                           (113 . hydra-vi/nil)
-                                           (107 . hydra-vi/previous-line)
-                                           (106 . hydra-vi/next-line)
-                                           (104 . hydra-vi/backward-char)
-                                           (108 . hydra-vi/forward-char)
-                                           (kp-subtract . 
hydra--negative-argument)
-                                           (kp-9 . hydra--digit-argument)
-                                           (kp-8 . hydra--digit-argument)
-                                           (kp-7 . hydra--digit-argument)
-                                           (kp-6 . hydra--digit-argument)
-                                           (kp-5 . hydra--digit-argument)
-                                           (kp-4 . hydra--digit-argument)
-                                           (kp-3 . hydra--digit-argument)
-                                           (kp-2 . hydra--digit-argument)
-                                           (kp-1 . hydra--digit-argument)
-                                           (kp-0 . hydra--digit-argument)
-                                           (57 . hydra--digit-argument)
-                                           (56 . hydra--digit-argument)
-                                           (55 . hydra--digit-argument)
-                                           (54 . hydra--digit-argument)
-                                           (53 . hydra--digit-argument)
-                                           (52 . hydra--digit-argument)
-                                           (51 . hydra--digit-argument)
-                                           (50 . hydra--digit-argument)
-                                           (49 . hydra--digit-argument)
-                                           (48 . hydra--digit-argument)
-                                           (45 . hydra--negative-argument)
-                                           (21 . hydra--universal-argument))))
-                      t (lambda nil (set-cursor-color "#ffffff"))))))
-      (defun hydra-vi/previous-line nil "Create a hydra with no body and the 
heads:
+               (catch (quote hydra-disable)
+                 (condition-case err (prog1 t (call-interactively (function 
next-line)))
+                   ((debug error)
+                    (message "%S" err)
+                    (sit-for 0.8)
+                    nil))
+                 (when hydra-is-helpful (message #("vi: j, k, [q]: quit." 4 5 
(face hydra-face-amaranth)
+                                                   7 8 (face 
hydra-face-amaranth)
+                                                   11 12 (face 
hydra-face-blue))))
+                 (setq hydra-last
+                       (hydra-set-transient-map
+                        (setq hydra-curr-map
+                              (quote (keymap (7 lambda nil (interactive)
+                                                (hydra-disable)
+                                                (set-cursor-color "#ffffff"))
+                                             (t lambda nil (interactive)
+                                                (message "An amaranth Hydra 
can only exit through a blue head")
+                                                (hydra-set-transient-map 
hydra-curr-map t)
+                                                (when hydra-is-helpful 
(sit-for 0.8)
+                                                      (message #("vi: j, k, 
[q]: quit." 4 5 (face hydra-face-amaranth)
+                                                                 7 8 (face 
hydra-face-amaranth)
+                                                                 11 12 (face 
hydra-face-blue)))))
+                                             (113 . hydra-vi/nil)
+                                             (107 . hydra-vi/previous-line)
+                                             (106 . hydra-vi/next-line)
+                                             (kp-subtract . 
hydra--negative-argument)
+                                             (kp-9 . hydra--digit-argument)
+                                             (kp-8 . hydra--digit-argument)
+                                             (kp-7 . hydra--digit-argument)
+                                             (kp-6 . hydra--digit-argument)
+                                             (kp-5 . hydra--digit-argument)
+                                             (kp-4 . hydra--digit-argument)
+                                             (kp-3 . hydra--digit-argument)
+                                             (kp-2 . hydra--digit-argument)
+                                             (kp-1 . hydra--digit-argument)
+                                             (kp-0 . hydra--digit-argument)
+                                             (57 . hydra--digit-argument)
+                                             (56 . hydra--digit-argument)
+                                             (55 . hydra--digit-argument)
+                                             (54 . hydra--digit-argument)
+                                             (53 . hydra--digit-argument)
+                                             (52 . hydra--digit-argument)
+                                             (51 . hydra--digit-argument)
+                                             (50 . hydra--digit-argument)
+                                             (49 . hydra--digit-argument)
+                                             (48 . hydra--digit-argument)
+                                             (45 . hydra--negative-argument)
+                                             (21 . 
hydra--universal-argument))))
+                        t))))
+        (defun hydra-vi/previous-line nil "Create a hydra with no body and the 
heads:
 
-\"l\":    `forward-char',
-\"h\":    `backward-char',
 \"j\":    `next-line',
 \"k\":    `previous-line',
 \"q\":    `nil'
@@ -565,65 +440,60 @@ Call the head: `next-line'."
 The body can be accessed via `hydra-vi/body'.
 
 Call the head: `previous-line'."
-             (interactive)
-             (set-cursor-color "#40e0d0")
-             (catch (quote hydra-disable)
+               (interactive)
+               (set-cursor-color "#e52b50")
                (hydra-disable)
-               (condition-case err (prog1 t (call-interactively (function 
previous-line)))
-                 ((debug error)
-                  (message "%S" err)
-                  (sit-for 0.8)
-                  nil))
-               (when hydra-is-helpful (message #("vi: l, h, j, k, [q]: quit." 
4 5 (face hydra-face-amaranth)
-                                                 7 8 (face hydra-face-amaranth)
-                                                 10 11 (face 
hydra-face-amaranth)
-                                                 13 14 (face 
hydra-face-amaranth)
-                                                 17 18 (face 
hydra-face-blue))))
-               (setq hydra-last
-                     (hydra-set-transient-map
-                      (setq hydra-curr-map
-                            (quote (keymap (t lambda nil (interactive)
-                                              (message "An amaranth Hydra can 
only exit through a blue head")
-                                              (hydra-set-transient-map 
hydra-curr-map t)
-                                              (when hydra-is-helpful (sit-for 
0.8)
-                                                    (message #("vi: l, h, j, 
k, [q]: quit." 4 5 (face hydra-face-amaranth)
-                                                               7 8 (face 
hydra-face-amaranth)
-                                                               10 11 (face 
hydra-face-amaranth)
-                                                               13 14 (face 
hydra-face-amaranth)
-                                                               17 18 (face 
hydra-face-blue)))))
-                                           (113 . hydra-vi/nil)
-                                           (107 . hydra-vi/previous-line)
-                                           (106 . hydra-vi/next-line)
-                                           (104 . hydra-vi/backward-char)
-                                           (108 . hydra-vi/forward-char)
-                                           (kp-subtract . 
hydra--negative-argument)
-                                           (kp-9 . hydra--digit-argument)
-                                           (kp-8 . hydra--digit-argument)
-                                           (kp-7 . hydra--digit-argument)
-                                           (kp-6 . hydra--digit-argument)
-                                           (kp-5 . hydra--digit-argument)
-                                           (kp-4 . hydra--digit-argument)
-                                           (kp-3 . hydra--digit-argument)
-                                           (kp-2 . hydra--digit-argument)
-                                           (kp-1 . hydra--digit-argument)
-                                           (kp-0 . hydra--digit-argument)
-                                           (57 . hydra--digit-argument)
-                                           (56 . hydra--digit-argument)
-                                           (55 . hydra--digit-argument)
-                                           (54 . hydra--digit-argument)
-                                           (53 . hydra--digit-argument)
-                                           (52 . hydra--digit-argument)
-                                           (51 . hydra--digit-argument)
-                                           (50 . hydra--digit-argument)
-                                           (49 . hydra--digit-argument)
-                                           (48 . hydra--digit-argument)
-                                           (45 . hydra--negative-argument)
-                                           (21 . hydra--universal-argument))))
-                      t (lambda nil (set-cursor-color "#ffffff"))))))
-      (defun hydra-vi/nil nil "Create a hydra with no body and the heads:
+               (catch (quote hydra-disable)
+                 (condition-case err (prog1 t (call-interactively (function 
previous-line)))
+                   ((debug error)
+                    (message "%S" err)
+                    (sit-for 0.8)
+                    nil))
+                 (when hydra-is-helpful (message #("vi: j, k, [q]: quit." 4 5 
(face hydra-face-amaranth)
+                                                   7 8 (face 
hydra-face-amaranth)
+                                                   11 12 (face 
hydra-face-blue))))
+                 (setq hydra-last
+                       (hydra-set-transient-map
+                        (setq hydra-curr-map
+                              (quote (keymap (7 lambda nil (interactive)
+                                                (hydra-disable)
+                                                (set-cursor-color "#ffffff"))
+                                             (t lambda nil (interactive)
+                                                (message "An amaranth Hydra 
can only exit through a blue head")
+                                                (hydra-set-transient-map 
hydra-curr-map t)
+                                                (when hydra-is-helpful 
(sit-for 0.8)
+                                                      (message #("vi: j, k, 
[q]: quit." 4 5 (face hydra-face-amaranth)
+                                                                 7 8 (face 
hydra-face-amaranth)
+                                                                 11 12 (face 
hydra-face-blue)))))
+                                             (113 . hydra-vi/nil)
+                                             (107 . hydra-vi/previous-line)
+                                             (106 . hydra-vi/next-line)
+                                             (kp-subtract . 
hydra--negative-argument)
+                                             (kp-9 . hydra--digit-argument)
+                                             (kp-8 . hydra--digit-argument)
+                                             (kp-7 . hydra--digit-argument)
+                                             (kp-6 . hydra--digit-argument)
+                                             (kp-5 . hydra--digit-argument)
+                                             (kp-4 . hydra--digit-argument)
+                                             (kp-3 . hydra--digit-argument)
+                                             (kp-2 . hydra--digit-argument)
+                                             (kp-1 . hydra--digit-argument)
+                                             (kp-0 . hydra--digit-argument)
+                                             (57 . hydra--digit-argument)
+                                             (56 . hydra--digit-argument)
+                                             (55 . hydra--digit-argument)
+                                             (54 . hydra--digit-argument)
+                                             (53 . hydra--digit-argument)
+                                             (52 . hydra--digit-argument)
+                                             (51 . hydra--digit-argument)
+                                             (50 . hydra--digit-argument)
+                                             (49 . hydra--digit-argument)
+                                             (48 . hydra--digit-argument)
+                                             (45 . hydra--negative-argument)
+                                             (21 . 
hydra--universal-argument))))
+                        t))))
+        (defun hydra-vi/nil nil "Create a hydra with no body and the heads:
 
-\"l\":    `forward-char',
-\"h\":    `backward-char',
 \"j\":    `next-line',
 \"k\":    `previous-line',
 \"q\":    `nil'
@@ -631,66 +501,66 @@ Call the head: `previous-line'."
 The body can be accessed via `hydra-vi/body'.
 
 Call the head: `nil'."
-             (interactive)
-             (set-cursor-color "#40e0d0")
-             (hydra-disable)
-             (set-cursor-color "#ffffff"))
-      (defun hydra-vi/body nil "Create a hydra with no body and the heads:
+               (interactive)
+               (set-cursor-color "#e52b50")
+               (hydra-disable)
+               (catch (quote hydra-disable)
+                 (set-cursor-color "#ffffff")))
+        (defun hydra-vi/body nil "Create a hydra with no body and the heads:
 
-\"l\":    `forward-char',
-\"h\":    `backward-char',
 \"j\":    `next-line',
 \"k\":    `previous-line',
 \"q\":    `nil'
 
 The body can be accessed via `hydra-vi/body'."
-             (interactive)
-             (set-cursor-color "#40e0d0")
-             (when hydra-is-helpful (message #("vi: l, h, j, k, [q]: quit." 4 
5 (face hydra-face-amaranth)
-                                               7 8 (face hydra-face-amaranth)
-                                               10 11 (face hydra-face-amaranth)
-                                               13 14 (face hydra-face-amaranth)
-                                               17 18 (face hydra-face-blue))))
-             (setq hydra-last
-                   (hydra-set-transient-map
-                    (quote (keymap (t lambda nil (interactive)
-                                      (message "An amaranth Hydra can only 
exit through a blue head")
-                                      (hydra-set-transient-map hydra-curr-map 
t)
-                                      (when hydra-is-helpful (sit-for 0.8)
-                                            (message #("vi: l, h, j, k, [q]: 
quit." 4 5 (face hydra-face-amaranth)
-                                                       7 8 (face 
hydra-face-amaranth)
-                                                       10 11 (face 
hydra-face-amaranth)
-                                                       13 14 (face 
hydra-face-amaranth)
-                                                       17 18 (face 
hydra-face-blue)))))
-                                   (113 . hydra-vi/nil)
-                                   (107 . hydra-vi/previous-line)
-                                   (106 . hydra-vi/next-line)
-                                   (104 . hydra-vi/backward-char)
-                                   (108 . hydra-vi/forward-char)
-                                   (kp-subtract . hydra--negative-argument)
-                                   (kp-9 . hydra--digit-argument)
-                                   (kp-8 . hydra--digit-argument)
-                                   (kp-7 . hydra--digit-argument)
-                                   (kp-6 . hydra--digit-argument)
-                                   (kp-5 . hydra--digit-argument)
-                                   (kp-4 . hydra--digit-argument)
-                                   (kp-3 . hydra--digit-argument)
-                                   (kp-2 . hydra--digit-argument)
-                                   (kp-1 . hydra--digit-argument)
-                                   (kp-0 . hydra--digit-argument)
-                                   (57 . hydra--digit-argument)
-                                   (56 . hydra--digit-argument)
-                                   (55 . hydra--digit-argument)
-                                   (54 . hydra--digit-argument)
-                                   (53 . hydra--digit-argument)
-                                   (52 . hydra--digit-argument)
-                                   (51 . hydra--digit-argument)
-                                   (50 . hydra--digit-argument)
-                                   (49 . hydra--digit-argument)
-                                   (48 . hydra--digit-argument)
-                                   (45 . hydra--negative-argument)
-                                   (21 . hydra--universal-argument)))
-                    t (lambda nil (set-cursor-color "#ffffff")))))))))
+               (interactive)
+               (set-cursor-color "#e52b50")
+               (hydra-disable)
+               (catch (quote hydra-disable)
+                 (when hydra-is-helpful (message #("vi: j, k, [q]: quit." 4 5 
(face hydra-face-amaranth)
+                                                   7 8 (face 
hydra-face-amaranth)
+                                                   11 12 (face 
hydra-face-blue))))
+                 (setq hydra-last
+                       (hydra-set-transient-map
+                        (setq hydra-curr-map
+                              (quote (keymap (7 lambda nil (interactive)
+                                                (hydra-disable)
+                                                (set-cursor-color "#ffffff"))
+                                             (t lambda nil (interactive)
+                                                (message "An amaranth Hydra 
can only exit through a blue head")
+                                                (hydra-set-transient-map 
hydra-curr-map t)
+                                                (when hydra-is-helpful 
(sit-for 0.8)
+                                                      (message #("vi: j, k, 
[q]: quit." 4 5 (face hydra-face-amaranth)
+                                                                 7 8 (face 
hydra-face-amaranth)
+                                                                 11 12 (face 
hydra-face-blue)))))
+                                             (113 . hydra-vi/nil)
+                                             (107 . hydra-vi/previous-line)
+                                             (106 . hydra-vi/next-line)
+                                             (kp-subtract . 
hydra--negative-argument)
+                                             (kp-9 . hydra--digit-argument)
+                                             (kp-8 . hydra--digit-argument)
+                                             (kp-7 . hydra--digit-argument)
+                                             (kp-6 . hydra--digit-argument)
+                                             (kp-5 . hydra--digit-argument)
+                                             (kp-4 . hydra--digit-argument)
+                                             (kp-3 . hydra--digit-argument)
+                                             (kp-2 . hydra--digit-argument)
+                                             (kp-1 . hydra--digit-argument)
+                                             (kp-0 . hydra--digit-argument)
+                                             (57 . hydra--digit-argument)
+                                             (56 . hydra--digit-argument)
+                                             (55 . hydra--digit-argument)
+                                             (54 . hydra--digit-argument)
+                                             (53 . hydra--digit-argument)
+                                             (52 . hydra--digit-argument)
+                                             (51 . hydra--digit-argument)
+                                             (50 . hydra--digit-argument)
+                                             (49 . hydra--digit-argument)
+                                             (48 . hydra--digit-argument)
+                                             (45 . hydra--negative-argument)
+                                             (21 . 
hydra--universal-argument))))
+                        t))
+                 (setq prefix-arg current-prefix-arg))))))))
 
 (provide 'hydra-test)
 
diff --git a/packages/hydra/hydra.el b/packages/hydra/hydra.el
index 6cbe705..2770fbc 100644
--- a/packages/hydra/hydra.el
+++ b/packages/hydra/hydra.el
@@ -5,7 +5,7 @@
 ;; Author: Oleh Krehel <address@hidden>
 ;; Maintainer: Oleh Krehel <address@hidden>
 ;; URL: https://github.com/abo-abo/hydra
-;; Version: 0.8.0
+;; Version: 0.9.0
 ;; Keywords: bindings
 ;; Package-Requires: ((cl-lib "0.5"))
 
@@ -37,28 +37,42 @@
 ;; command.  This makes the Hydra very seamless, it's like a minor
 ;; mode that disables itself automagically.
 ;;
-;; Here's how to use the examples bundled with Hydra:
+;; Here's an example Hydra, bound in the global map (you can use any
+;; keymap in place of `global-map'):
 ;;
-;;    (require 'hydra-examples)
-;;    (hydra-create "C-M-y" hydra-example-move-window-splitter)
-;;    (hydra-create "M-g" hydra-example-goto-error)
+;;     (defhydra hydra-zoom (global-map "<f2>")
+;;       "zoom"
+;;       ("g" text-scale-increase "in")
+;;       ("l" text-scale-decrease "out"))
 ;;
-;; You can expand the examples in-place, it still looks elegant:
+;; It allows to start a command chain either like this:
+;; "<f2> gg4ll5g", or "<f2> lgllg".
 ;;
-;;     (hydra-create "<f2>"
-;;       '(("g" text-scale-increase "zoom in")
-;;         ("l" text-scale-decrease "zoom out")))
+;; Here's another approach, when you just want a "callable keymap":
 ;;
-;; The third element of each list is the optional doc string that will
-;; be displayed in the echo area when `hydra-is-helpful' is t.
+;;     (defhydra hydra-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"))
 ;;
-;; It's better to take the examples simply as templates and use
-;; `defhydra' instead of `hydra-create', since it's more flexible.
+;; This binds nothing so far, but if you follow up with:
 ;;
-;;     (defhydra hydra-zoom (global-map "<f2>")
-;;       "zoom"
-;;       ("g" text-scale-increase "in")
-;;       ("l" text-scale-decrease "out"))
+;;     (global-set-key (kbd "C-c C-v") 'hydra-toggle/body)
+;;
+;; you will have bound "C-c C-v a", "C-c C-v d" etc.
+;;
+;; Knowing that `defhydra' defines e.g. `hydra-toggle/body' command,
+;; you can nest Hydras if you wish, with `hydra-toggle/body' possibly
+;; becoming a blue head of another Hydra.
+;;
+;; Initially, Hydra shipped with a simplified `hydra-create' macro, to
+;; which you could hook up the examples from hydra-examples.el.  It's
+;; better to take the examples simply as templates and use `defhydra'
+;; instead of `hydra-create', since it's more flexible.
 
 ;;; Code:
 ;;* Requires
@@ -80,6 +94,11 @@
   :type 'boolean
   :group 'hydra)
 
+(defcustom hydra-keyboard-quit ""
+  "This binding will quit an amaranth Hydra.
+It's the only other way to quit it besides though a blue head.
+It's possible to set this to nil.")
+
 (defface hydra-face-red
     '((t (:foreground "#7F0055" :bold t)))
   "Red Hydra heads will persist indefinitely."
@@ -158,14 +177,31 @@
       (and (consp x)
            (memq (car x) '(function quote)))))
 
+(defun hydra--make-callable (x)
+  "Generate a callable symbol from X.
+If X is a function symbol or a lambda, return it.  Otherwise, it
+should be a single statement. Wrap it in an interactive lambda."
+  (if (or (symbolp x) (functionp x))
+      x
+    `(lambda ()
+       (interactive)
+       ,x)))
+
+(defun hydra--head-property (h prop &optional default)
+  "Return for Hydra head H the value of property PROP.
+Return DEFAULT if PROP is not in H."
+  (let ((plist (if (stringp (cl-caddr h))
+                   (cl-cdddr h)
+                 (cddr h))))
+    (if (memq prop h)
+        (plist-get plist prop)
+      default)))
+
 (defun hydra--color (h body-color)
   "Return the color of a Hydra head H with BODY-COLOR."
   (if (null (cadr h))
       'blue
-    (let ((plist (if (stringp (cl-caddr h))
-                     (cl-cdddr h)
-                   (cddr h))))
-      (or (plist-get plist :color) body-color))))
+    (or (hydra--head-property h :color) body-color)))
 
 (defun hydra--face (h body-color)
   "Return the face for a Hydra head H with BODY-COLOR."
@@ -226,6 +262,39 @@ HEADS is a list of heads."
     heads ",\n")
    (format "The body can be accessed via `%S'." body-name)))
 
+(defun hydra--make-defun (name cmd color
+                          doc hint keymap
+                          body-color body-pre body-post &optional other-post)
+  "Make a defun wrapper, using NAME, CMD, COLOR, DOC, HINT, and KEYMAP.
+BODY-COLOR, BODY-PRE, BODY-POST, and OTHER-POST are used as well."
+  `(defun ,name ()
+     ,doc
+     (interactive)
+     ,@(when body-pre (list body-pre))
+     (hydra-disable)
+     (catch 'hydra-disable
+       ,@(delq nil
+               (if (eq color 'blue)
+                   `(,(when cmd `(call-interactively #',cmd))
+                      ,body-post)
+                 `(,(when cmd
+                          `(condition-case err
+                               (prog1 t
+                                 (call-interactively #',cmd))
+                             ((debug error)
+                              (message "%S" err)
+                              (sit-for 0.8)
+                              nil)))
+                    (when hydra-is-helpful
+                      (message ,hint))
+                    (setq hydra-last
+                          (hydra-set-transient-map
+                           (setq hydra-curr-map ',keymap)
+                           t
+                           ,@(if (and (not (eq body-color 'amaranth)) 
body-post)
+                                 `((lambda () ,body-post)))))
+                    ,other-post))))))
+
 ;;* Macros
 ;;** hydra-create
 ;;;###autoload
@@ -244,7 +313,8 @@ It defaults to `global-set-key'.
 When `(keymapp METHOD)`, it becomes:
 
     (lambda (key command) (define-key METHOD key command))"
-  (declare (indent 1))
+  (declare (indent 1)
+           (obsolete defhydra "0.8.0"))
   `(defhydra ,(intern
                (concat
                 "hydra-" (replace-regexp-in-string " " "_" body)))
@@ -260,28 +330,41 @@ When `(keymapp METHOD)`, it becomes:
 ;;** defhydra
 ;;;###autoload
 (defmacro defhydra (name body &optional docstring &rest heads)
-  "Create a hydra named NAME with a prefix BODY.
+  "Create a Hydra - a family of functions with prefix NAME.
 
 NAME should be a symbol, it will be the prefix of all functions
 defined here.
 
-BODY should be either:
+BODY has the format:
 
-    (BODY-MAP &optional BODY-KEY &rest PLIST)
-or:
+    (BODY-MAP BODY-KEY &rest PLIST)
 
-    (lambda (KEY CMD) ...)
+DOCSTRING will be displayed in the echo area to identify the
+Hydra.
 
-BODY-MAP should be a keymap; `global-map' is acceptable here.
-BODY-KEY should be a string processable by `kbd'.
+Functions are created on basis of HEADS, each of which has the
+format:
 
-DOCSTRING will be displayed in the echo area to identify the
-hydra.
+    (KEY CMD &optional HINT &rest PLIST)
+
+BODY-MAP is a keymap; `global-map' is used quite often.  Each
+function generated from HEADS will be bound in BODY-MAP to
+BODY-KEY + KEY, and will set the transient map so that all
+following heads can be called though KEY only.
 
-HEADS is a list of (KEY CMD &optional HINT &rest PLIST).
+The heads inherit their PLIST from the body and are allowed to
+override each key.  The keys recognized are :color and :bind.
+:color can be:
 
-PLIST in both cases recognizes only the :color key so far, which
-in turn can be either red or blue."
+- red (default): this head will continue the Hydra state.
+- blue: this head will stop the Hydra state.
+- amaranth (applies to body only): similar to red, but no binding
+except a blue head can stop the Hydra state.
+
+:bind can be:
+- nil: this head will not be bound in BODY-MAP.
+- a lambda taking KEY and CMD used to bind a head"
+  (declare (indent 2))
   (unless (stringp docstring)
     (setq heads (cons docstring heads))
     (setq docstring "hydra"))
@@ -305,14 +388,17 @@ in turn can be either red or blue."
                            'red)))
          (body-pre (plist-get (cddr body) :pre))
          (body-post (plist-get (cddr body) :post))
-         (method (if (hydra--callablep body)
-                     body
-                   (car body)))
+         (method (or (plist-get body :bind)
+                     (car body)))
          (hint (hydra--hint docstring heads body-color))
          (doc (hydra--doc body-key body-name heads)))
     (when (and (or body-pre body-post)
                (version< emacs-version "24.4"))
       (error "At least Emacs 24.4 is needed for :pre and :post"))
+    (when (and body-pre (symbolp body-pre))
+      (setq body-pre `(funcall #',body-pre)))
+    (when (and body-post (symbolp body-post))
+      (setq body-post `(funcall #',body-post)))
     (when (eq body-color 'amaranth)
       (if (cl-some `(lambda (h)
                       (eq (hydra--color h ',body-color) 'blue))
@@ -325,35 +411,21 @@ in turn can be either red or blue."
                (when hydra-is-helpful
                  (sit-for 0.8)
                  (message ,hint))))
-        (error "An amaranth Hydra must have at least one blue head in order to 
exit")))
+        (error "An amaranth Hydra must have at least one blue head in order to 
exit"))
+      (when hydra-keyboard-quit
+        (define-key keymap hydra-keyboard-quit
+          `(lambda ()
+             (interactive)
+             (hydra-disable)
+             ,body-post))))
     `(progn
        ,@(cl-mapcar
           (lambda (head name)
-            `(defun ,name ()
-               ,(format "%s\n\nCall the head: `%S'." doc (cadr head))
-               (interactive)
-               ,@(if body-pre (list body-pre))
-               ,@(if (eq (hydra--color head body-color) 'blue)
-                     `((hydra-disable)
-                       ,@(unless (null (cadr head))
-                                 `((call-interactively #',(cadr head))))
-                       ,@(if body-post (list body-post)))
-                     `((catch 'hydra-disable
-                         (hydra-disable)
-                         (condition-case err
-                             (prog1 t
-                               (call-interactively #',(cadr head)))
-                           ((debug error)
-                            (message "%S" err)
-                            (sit-for 0.8)
-                            nil))
-                         (when hydra-is-helpful
-                           (message ,hint))
-                         (setq hydra-last
-                               (hydra-set-transient-map
-                                (setq hydra-curr-map ',keymap)
-                                t
-                                ,@(if body-post `((lambda () 
,body-post))))))))))
+            (hydra--make-defun
+             name (hydra--make-callable (cadr head)) (hydra--color head 
body-color)
+             (format "%s\n\nCall the head: `%S'." doc (cadr head))
+             hint keymap
+             body-color body-pre body-post))
           heads names)
        ,@(unless (or (null body-key)
                      (null method)
@@ -363,26 +435,33 @@ in turn can be either red or blue."
        ,@(delq nil
                (cl-mapcar
                 (lambda (head name)
-                  (unless (or (null body-key) (null method))
-                    (list
-                     (if (hydra--callablep method)
-                         'funcall
-                       'define-key)
-                     method
-                     (vconcat (kbd body-key) (kbd (car head)))
-                     (list 'function name))))
+                  (when (or body-key method)
+                    (let ((bind (hydra--head-property head :bind 'default))
+                          (final-key (if body-key
+                                         (vconcat (kbd body-key) (kbd (car 
head)))
+                                       (kbd (car head)))))
+                      (cond ((null bind) nil)
+
+                            ((eq bind 'default)
+                             (list
+                              (if (hydra--callablep method)
+                                  'funcall
+                                'define-key)
+                              method
+                              final-key
+                              (list 'function name)))
+
+                            ((hydra--callablep bind)
+                             `(funcall (function ,bind)
+                                       ,final-key
+                                       (function ,name)))
+
+                            (t
+                             (error "Invalid :bind property %S" head))))))
                 heads names))
-       (defun ,body-name ()
-         ,doc
-         (interactive)
-         ,@(if body-pre (list body-pre))
-         (when hydra-is-helpful
-           (message ,hint))
-         (setq hydra-last
-               (hydra-set-transient-map
-                ',keymap
-                t
-                ,@(if body-post `((lambda () ,body-post)))))))))
+       ,(hydra--make-defun body-name nil nil doc hint keymap
+                           body-color body-pre body-post
+                           '(setq prefix-arg current-prefix-arg)))))
 
 (provide 'hydra)
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]