branch: master commit 2fb700c07397304a074092e025a55f34142a5a7e Merge: 8b585bc 68bcaa8 Author: Oleh Krehel <ohwoeo...@gmail.com> Commit: Oleh Krehel <ohwoeo...@gmail.com>
Merge commit '68bcaa8d8df5518217a3833fd1bb400c8225fe02' from hydra --- packages/hydra/README.md | 66 +++++ packages/hydra/hydra-test.el | 571 ++++++++++++++++++++++++++++++++++-------- packages/hydra/hydra.el | 60 ++++- 3 files changed, 587 insertions(+), 110 deletions(-) diff --git a/packages/hydra/README.md b/packages/hydra/README.md index 0686965..7272098 100644 --- a/packages/hydra/README.md +++ b/packages/hydra/README.md @@ -171,3 +171,69 @@ Since version `0.6.0`, for any Hydra: - `digit-argment` can be called with <kbd>0</kbd>-<kbd>9</kbd>. - `negative-argument` can be called with <kbd>-</kbd> - `universal-argument` can be called with <kbd>C-u</kbd> + +## Hydras can have `:pre` and `:post` statements + +Since version `0.7.0`, you can specify code that will be called before each head, and +after the body. For example: + +```cl +(global-set-key + (kbd "C-z") + (defhydra hydra-vi + (:pre + (set-cursor-color "#40e0d0") + :post + (progn + (set-cursor-color "#ffffff") + (message + "Thank you, come again."))) + "vi" + ("l" forward-char) + ("h" backward-char) + ("j" next-line) + ("k" previous-line) + ("q" nil "quit"))) +``` + +## New Hydra color: amaranth + +Since version `0.8.0`, a new color - amaranth, in addition to the previous red and blue, is +available for the Hydra body. + +According to [Wikipedia](http://en.wikipedia.org/wiki/Amaranth): + +> 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: + +```cl +(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) + ("q" nil "quit"))) +``` + +The only way to exit it, is to press <kbd>q</kbd>. 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. diff --git a/packages/hydra/hydra-test.el b/packages/hydra/hydra-test.el index 07d0a74..eee851a 100644 --- a/packages/hydra/hydra-test.el +++ b/packages/hydra/hydra-test.el @@ -26,7 +26,7 @@ (require 'ert) -(ert-deftest defhydra-red-error () +(ert-deftest hydra-red-error () (should (equal (macroexpand @@ -46,39 +46,45 @@ The body can be accessed via `hydra-error/body'. Call the head: `first-error'." (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 - (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))) - t)) - (call-interactively (function first-error))) + (catch (quote hydra-disable) + (hydra-disable) + (condition-case err (prog1 t (call-interactively (function first-error))) + ((debug error) + (message "%S" err) + (sit-for 0.8) + nil)) + (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 '(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: \"h\": `first-error', @@ -89,39 +95,45 @@ The body can be accessed via `hydra-error/body'. Call the head: `next-error'." (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 - (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))) - t)) - (call-interactively (function next-error))) + (catch (quote hydra-disable) + (hydra-disable) + (condition-case err (prog1 t (call-interactively (function next-error))) + ((debug error) + (message "%S" err) + (sit-for 0.8) + nil)) + (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 '(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: \"h\": `first-error', @@ -132,39 +144,45 @@ The body can be accessed via `hydra-error/body'. Call the head: `previous-error'." (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 - (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))) - t)) - (call-interactively (function previous-error))) + (catch (quote hydra-disable) + (hydra-disable) + (condition-case err (prog1 t (call-interactively (function previous-error))) + ((debug error) + (message "%S" err) + (sit-for 0.8) + nil)) + (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 '(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") nil)) @@ -321,6 +339,359 @@ The body can be accessed via `toggle/body'." (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") + (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)))) + (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/backward-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: `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: + +\"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: `next-line'." + (interactive) + (set-cursor-color "#40e0d0") + (catch (quote hydra-disable) + (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: + +\"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: `previous-line'." + (interactive) + (set-cursor-color "#40e0d0") + (catch (quote hydra-disable) + (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: + +\"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: `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: + +\"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"))))))))) + (provide 'hydra-test) ;;; hydra-test.el ends here diff --git a/packages/hydra/hydra.el b/packages/hydra/hydra.el index 738b77c..6cbe705 100644 --- a/packages/hydra/hydra.el +++ b/packages/hydra/hydra.el @@ -5,7 +5,7 @@ ;; Author: Oleh Krehel <ohwoeo...@gmail.com> ;; Maintainer: Oleh Krehel <ohwoeo...@gmail.com> ;; URL: https://github.com/abo-abo/hydra -;; Version: 0.6.1 +;; Version: 0.8.0 ;; Keywords: bindings ;; Package-Requires: ((cl-lib "0.5")) @@ -89,6 +89,10 @@ '((t (:foreground "#758BC6" :bold t))) "Blue Hydra heads will vanquish the Hydra.") +(defface hydra-face-amaranth + '((t (:foreground "#E52B50" :bold t))) + "Amaranth Hydra can exit only through a blue head.") + ;;* Universal Argument (defvar hydra-base-map (let ((map (make-sparse-keymap))) @@ -116,7 +120,7 @@ (define-key map [kp-9] 'hydra--digit-argument) (define-key map [kp-subtract] 'hydra--negative-argument) map) - "Keymap that all Hydras inherit. See `universal-argument-map'.") + "Keymap that all Hydras inherit. See `universal-argument-map'.") (defvar hydra-curr-map (make-sparse-keymap) @@ -130,7 +134,7 @@ (if (eq arg '-) (list -4) '(4)))) - (hydra-set-transient-map hydra-curr-map)) + (hydra-set-transient-map hydra-curr-map t)) (defun hydra--digit-argument (arg) "Forward to (`digit-argument' ARG)." @@ -168,6 +172,7 @@ (cl-case (hydra--color h body-color) (blue 'hydra-face-blue) (red 'hydra-face-red) + (amaranth 'hydra-face-amaranth) (t (error "Unknown color for %S" h)))) (defun hydra--hint (docstring heads body-color) @@ -298,26 +303,57 @@ in turn can be either red or blue." 'red (or (plist-get (cddr body) :color) 'red))) + (body-pre (plist-get (cddr body) :pre)) + (body-post (plist-get (cddr body) :post)) (method (if (hydra--callablep body) body (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 (eq body-color 'amaranth) + (if (cl-some `(lambda (h) + (eq (hydra--color h ',body-color) 'blue)) + heads) + (define-key keymap [t] + `(lambda () + (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 ,hint)))) + (error "An amaranth Hydra must have at least one blue head in order to exit"))) `(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))))) - `((when hydra-is-helpful - (message ,hint)) - (setq hydra-last - (hydra-set-transient-map (setq hydra-curr-map ',keymap) t)) - (call-interactively #',(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)))))))))) heads names) ,@(unless (or (null body-key) (null method) @@ -339,10 +375,14 @@ in turn can be either red or blue." (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)))))) + (hydra-set-transient-map + ',keymap + t + ,@(if body-post `((lambda () ,body-post))))))))) (provide 'hydra)