branch: master commit 806e04b315283a6d3fdffb7ff45eec78a3ca6dbe Author: Oleh Krehel <ohwoeo...@gmail.com> Commit: Oleh Krehel <ohwoeo...@gmail.com>
Don't clutter Echo Area * lv.el (lv-wnd): New variable. (lv-window): New defun to get a window similar in properties to Echo Area. (lv-message): New defun, a replacement for `message', that writes to `lv-window'. * hydra.el (hydra-lv): New defcustom. If nil, use the Echo Area, otherwise, use LV. (hydra-disable): Add optional arg KILL-LV. (hydra--message): New defun to dispatch on `hydra-lv'. (hydra--make-defun): Prematurely disable with LV only for blue heads, since regenerating LV window would cause screen tearing. No need for timeouts between `message' when using LV. HINT argument is now a function symbol that returns a string, instead of a plain string. (defhydra): Generate a new defun with name `NAME/hint'. * Makefile: Load lv. * hydra-test.el: Update all tests. * README.md: Update. --- Makefile | 2 +- README.md | 11 + hydra-test.el | 625 ++++++++++++++++++++++++++++----------------------------- hydra.el | 50 ++++-- lv.el | 72 +++++++ 5 files changed, 429 insertions(+), 331 deletions(-) diff --git a/Makefile b/Makefile index b2d473d..4b6451f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ EMACS = emacs # EMACS = emacs-24.3 -LOAD = -l hydra.el -l hydra-test.el +LOAD = -l lv.el -l hydra.el -l hydra-test.el .PHONY: all test clean diff --git a/README.md b/README.md index c7c1dff..960684a 100644 --- a/README.md +++ b/README.md @@ -266,3 +266,14 @@ to a head. This sexp will be wrapped in an interactive lambda. Here's an example ("q" nil "cancel")) (global-set-key (kbd "C-c r") 'hydra-launcher/body) ``` + +## Define Hydra heads that don't show up in the hint at all + +This can be done by setting the head's hint explicitly to `nil`, instead of the usual string. + +## Use a dedicated window for Hydra hints + +Since version `0.10.0`, setting `hydra-lv` to `t` (the default setting) will make it use a dedicated +window right above the Echo Area for hints. This has the advantage that you can immediately see +any `message` output from the functions that you call, since Hydra no longer uses `message` to display +the hint. You can still have the old behavior by setting `hydra-lv` to `nil`. diff --git a/hydra-test.el b/hydra-test.el index 028f510..8e1df9a 100644 --- a/hydra-test.el +++ b/hydra-test.el @@ -46,46 +46,44 @@ The body can be accessed via `hydra-error/body'. Call the head: `first-error'." (interactive) - (hydra-disable) + (hydra-disable nil) (catch (quote hydra-disable) (condition-case err (prog1 t (call-interactively (function first-error))) - (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)))) + (error (message "%S" err) + (unless hydra-lv (sit-for 0.8)) + nil)) + (when hydra-is-helpful (hydra-error/hint)) (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)))) + (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 (lambda nil (hydra-disable t)))))) (defun hydra-error/next-error nil "Create a hydra with a \"M-g\" body and the heads: \"h\": `first-error', @@ -96,46 +94,44 @@ The body can be accessed via `hydra-error/body'. Call the head: `next-error'." (interactive) - (hydra-disable) + (hydra-disable nil) (catch (quote hydra-disable) (condition-case err (prog1 t (call-interactively (function next-error))) - (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)))) + (error (message "%S" err) + (unless hydra-lv (sit-for 0.8)) + nil)) + (when hydra-is-helpful (hydra-error/hint)) (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)))) + (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 (lambda nil (hydra-disable t)))))) (defun hydra-error/previous-error nil "Create a hydra with a \"M-g\" body and the heads: \"h\": `first-error', @@ -146,46 +142,44 @@ The body can be accessed via `hydra-error/body'. Call the head: `previous-error'." (interactive) - (hydra-disable) + (hydra-disable nil) (catch (quote hydra-disable) (condition-case err (prog1 t (call-interactively (function previous-error))) - (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)))) + (error (message "%S" err) + (unless hydra-lv (sit-for 0.8)) + nil)) + (when hydra-is-helpful (hydra-error/hint)) (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)))) + (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 (lambda nil (hydra-disable t)))))) (unless (keymapp (lookup-key global-map (kbd "M-g"))) (define-key global-map (kbd "M-g") nil)) @@ -195,6 +189,10 @@ Call the head: `previous-error'." (function hydra-error/next-error)) (define-key global-map [134217831 107] (function hydra-error/previous-error)) + (defun hydra-error/hint nil + (hydra--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)))) (defun hydra-error/body nil "Create a hydra with a \"M-g\" body and the heads: \"h\": `first-error', @@ -203,155 +201,157 @@ Call the head: `previous-error'." The body can be accessed via `hydra-error/body'." (interactive) - (hydra-disable) + (hydra-disable nil) (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)))) + (when hydra-is-helpful (hydra-error/hint)) (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)) + (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 (lambda nil (hydra-disable t)))) (setq prefix-arg current-prefix-arg))))))) (ert-deftest hydra-blue-toggle () (should (equal (macroexpand - '(defhydra toggle (:color blue) + '(defhydra hydra-toggle (:color blue) "toggle" ("t" toggle-truncate-lines "truncate") ("f" auto-fill-mode "fill") ("a" abbrev-mode "abbrev") ("q" nil "cancel"))) '(progn - (defun toggle/toggle-truncate-lines nil "Create a hydra with no body and the heads: + (defun hydra-toggle/toggle-truncate-lines nil "Create a hydra with no body and the heads: \"t\": `toggle-truncate-lines', \"f\": `auto-fill-mode', \"a\": `abbrev-mode', \"q\": `nil' -The body can be accessed via `toggle/body'. +The body can be accessed via `hydra-toggle/body'. Call the head: `toggle-truncate-lines'." (interactive) - (hydra-disable) + (hydra-disable t) (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: + (defun hydra-toggle/auto-fill-mode nil "Create a hydra with no body and the heads: \"t\": `toggle-truncate-lines', \"f\": `auto-fill-mode', \"a\": `abbrev-mode', \"q\": `nil' -The body can be accessed via `toggle/body'. +The body can be accessed via `hydra-toggle/body'. Call the head: `auto-fill-mode'." (interactive) - (hydra-disable) + (hydra-disable t) (catch (quote hydra-disable) (call-interactively (function auto-fill-mode)))) - (defun toggle/abbrev-mode nil "Create a hydra with no body and the heads: + (defun hydra-toggle/abbrev-mode nil "Create a hydra with no body and the heads: \"t\": `toggle-truncate-lines', \"f\": `auto-fill-mode', \"a\": `abbrev-mode', \"q\": `nil' -The body can be accessed via `toggle/body'. +The body can be accessed via `hydra-toggle/body'. Call the head: `abbrev-mode'." (interactive) - (hydra-disable) + (hydra-disable t) (catch (quote hydra-disable) (call-interactively (function abbrev-mode)))) - (defun toggle/nil nil "Create a hydra with no body and the heads: + (defun hydra-toggle/nil nil "Create a hydra with no body and the heads: \"t\": `toggle-truncate-lines', \"f\": `auto-fill-mode', \"a\": `abbrev-mode', \"q\": `nil' -The body can be accessed via `toggle/body'. +The body can be accessed via `hydra-toggle/body'. Call the head: `nil'." (interactive) - (hydra-disable) + (hydra-disable t) (catch (quote hydra-disable))) - (defun toggle/body nil "Create a hydra with no body and the heads: + (defun hydra-toggle/hint nil + (hydra--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)))) + (defun hydra-toggle/body nil "Create a hydra with no body and the heads: \"t\": `toggle-truncate-lines', \"f\": `auto-fill-mode', \"a\": `abbrev-mode', \"q\": `nil' -The body can be accessed via `toggle/body'." +The body can be accessed via `hydra-toggle/body'." (interactive) - (hydra-disable) + (hydra-disable nil) (catch (quote hydra-disable) - (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)))) + (when hydra-is-helpful (hydra-toggle/hint)) (setq hydra-last (hydra-set-transient-map (setq hydra-curr-map - (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) - (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)) + (quote + (keymap (113 . hydra-toggle/nil) + (97 . hydra-toggle/abbrev-mode) + (102 . hydra-toggle/auto-fill-mode) + (116 . hydra-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 (lambda nil (hydra-disable t)))) (setq prefix-arg current-prefix-arg))))))) (ert-deftest hydra-amaranth-vi () @@ -381,56 +381,52 @@ The body can be accessed via `hydra-vi/body'. Call the head: `next-line'." (interactive) (set-cursor-color "#e52b50") - (hydra-disable) + (hydra-disable nil) (catch (quote hydra-disable) (condition-case err (prog1 t (call-interactively (function next-line))) - (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)))) + (error (message "%S" err) + (unless hydra-lv (sit-for 0.8)) + nil)) + (when hydra-is-helpful (hydra-vi/hint)) (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)))) + (quote + (keymap (7 lambda nil (interactive) + (hydra-disable t) + (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 (unless hydra-lv (sit-for 0.8)) + (hydra-vi/hint))) + (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 (lambda nil (hydra-disable t)))))) (defun hydra-vi/previous-line nil "Create a hydra with no body and the heads: \"j\": `next-line', @@ -442,56 +438,52 @@ The body can be accessed via `hydra-vi/body'. Call the head: `previous-line'." (interactive) (set-cursor-color "#e52b50") - (hydra-disable) + (hydra-disable nil) (catch (quote hydra-disable) (condition-case err (prog1 t (call-interactively (function previous-line))) - (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)))) + (error (message "%S" err) + (unless hydra-lv (sit-for 0.8)) + nil)) + (when hydra-is-helpful (hydra-vi/hint)) (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)))) + (quote + (keymap (7 lambda nil (interactive) + (hydra-disable t) + (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 (unless hydra-lv (sit-for 0.8)) + (hydra-vi/hint))) + (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 (lambda nil (hydra-disable t)))))) (defun hydra-vi/nil nil "Create a hydra with no body and the heads: \"j\": `next-line', @@ -503,9 +495,13 @@ The body can be accessed via `hydra-vi/body'. Call the head: `nil'." (interactive) (set-cursor-color "#e52b50") - (hydra-disable) + (hydra-disable t) (catch (quote hydra-disable) (set-cursor-color "#ffffff"))) + (defun hydra-vi/hint nil + (hydra--message #("vi: j, k, [q]: quit." 4 5 (face hydra-face-amaranth) + 7 8 (face hydra-face-amaranth) + 11 12 (face hydra-face-blue)))) (defun hydra-vi/body nil "Create a hydra with no body and the heads: \"j\": `next-line', @@ -515,51 +511,48 @@ Call the head: `nil'." The body can be accessed via `hydra-vi/body'." (interactive) (set-cursor-color "#e52b50") - (hydra-disable) + (hydra-disable nil) (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)))) + (when hydra-is-helpful (hydra-vi/hint)) (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)) + (quote + (keymap (7 lambda nil (interactive) + (hydra-disable t) + (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 (unless hydra-lv (sit-for 0.8)) + (hydra-vi/hint))) + (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 (lambda nil (hydra-disable t)))) (setq prefix-arg current-prefix-arg)))))))) (provide 'hydra-test) diff --git a/hydra.el b/hydra.el index 4aca1a2..40aae23 100644 --- a/hydra.el +++ b/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.9.0 +;; Version: 0.10.0 ;; Keywords: bindings ;; Package-Requires: ((cl-lib "0.5")) @@ -77,6 +77,7 @@ ;;; Code: ;;* Requires (require 'cl-lib) +(require 'lv) (defalias 'hydra-set-transient-map (if (fboundp 'set-transient-map) @@ -99,6 +100,10 @@ It's the only other way to quit it besides though a blue head. It's possible to set this to nil.") +(defcustom hydra-lv t + "When non-nil, `lv-message' will be used to display hints instead of `message'." + :type 'boolean) + (defface hydra-face-red '((t (:foreground "#7F0055" :bold t))) "Red Hydra heads will persist indefinitely." @@ -251,8 +256,9 @@ It's intended for the echo area, when a Hydra is active." (nreverse (mapcar #'cdr alist)) ", ")))) -(defun hydra-disable () - "Disable the current Hydra." +(defun hydra-disable (&optional kill-lv) + "Disable the current Hydra. +When KILL-LV is non-nil, kill LV window and buffer." (cond ;; Emacs 25 ((functionp hydra-last) @@ -268,7 +274,17 @@ It's intended for the echo area, when a Hydra is active." (consp (caar emulation-mode-map-alists)) (equal (cl-cdaar emulation-mode-map-alists) ',keymap)) (setq emulation-mode-map-alists - (cdr emulation-mode-map-alists)))))) + (cdr emulation-mode-map-alists))))) + (when (and kill-lv (window-live-p lv-wnd)) + (kill-buffer (window-buffer lv-wnd)) + (delete-window lv-wnd))) + +(defun hydra--message (format-str &rest args) + "Forward to (`message' FORMAT-STR ARGS). +Or to `lv-message' if `hydra-lv' is non-nil." + (if hydra-lv + (apply #'lv-message format-str args) + (apply #'message format-str args))) (defun hydra--doc (body-key body-name heads) "Generate a part of Hydra docstring. @@ -295,7 +311,7 @@ BODY-COLOR, BODY-PRE, BODY-POST, and OTHER-POST are used as well." ,doc (interactive) ,@(when body-pre (list body-pre)) - (hydra-disable) + (hydra-disable ,(eq color 'blue)) (catch 'hydra-disable ,@(delq nil (if (eq color 'blue) @@ -307,16 +323,18 @@ BODY-COLOR, BODY-PRE, BODY-POST, and OTHER-POST are used as well." (call-interactively #',cmd)) (error (message "%S" err) - (sit-for 0.8) + (unless hydra-lv + (sit-for 0.8)) nil))) (when hydra-is-helpful - (message ,hint)) + (,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))))) + ,(if (and (not (eq body-color 'amaranth)) body-post) + `(lambda () (hydra-disable t) ,body-post) + (lambda () (hydra-disable t))))) ,other-post)))))) ;;* Macros @@ -373,6 +391,7 @@ except a blue head can stop the Hydra state. (concat "lambda-" (car x))))))) heads)) (body-name (intern (format "%S/body" name))) + (hint-name (intern (format "%S/hint" name))) (body-key (unless (hydra--callablep body) (cadr body))) (body-color (if (hydra--callablep body) @@ -407,14 +426,15 @@ except a blue head can stop the Hydra state. (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))))) + (unless hydra-lv + (sit-for 0.8)) + (,hint-name))))) (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) + (hydra-disable t) ,body-post)))) `(progn ,@(cl-mapcar @@ -422,7 +442,7 @@ except a blue head can stop the Hydra state. (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 + hint-name keymap body-color body-pre body-post)) heads names) ,@(unless (or (null body-key) @@ -457,7 +477,9 @@ except a blue head can stop the Hydra state. (t (error "Invalid :bind property %S" head)))))) heads names)) - ,(hydra--make-defun body-name nil nil doc hint keymap + (defun ,hint-name () + (hydra--message ,hint)) + ,(hydra--make-defun body-name nil nil doc hint-name keymap body-color body-pre body-post '(setq prefix-arg current-prefix-arg))))) diff --git a/lv.el b/lv.el new file mode 100644 index 0000000..c7cbdac --- /dev/null +++ b/lv.el @@ -0,0 +1,72 @@ +;;; lv.el --- Other echo area + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Oleh Krehel + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; This package provides `lv-message' intended to be used in place of +;; `message' when semi-permanent hints are needed, in order to not +;; interfere with Echo Area. +;; +;; "Я тихо-тихо пiдглядаю, +;; І тiшуся собi, як бачу то, +;; Шо страшить i не пiдпускає, +;; А iншi п’ють тебе, як воду пiсок." +;; -- Андрій Кузьменко, L.V. + +;;; Code: + +(defvar lv-wnd nil + "Holds the current LV window.") + +(defun lv-window () + "Ensure that LV window is live and return it." + (if (window-live-p lv-wnd) + lv-wnd + (let ((ori (selected-window)) + buf) + (prog1 (setq lv-wnd + (select-window + (split-window + (frame-root-window) -1 'below))) + (if (setq buf (get-buffer "*LV*")) + (switch-to-buffer buf) + (switch-to-buffer "*LV*") + (setq truncate-lines nil) + (setq mode-line-format nil) + (setq cursor-type nil) + (set-window-dedicated-p lv-wnd t)) + (select-window ori))))) + +(defun lv-message (format-string &rest args) + "Set LV window contents to (`format' FORMAT-STRING ARGS)." + (let ((ori (selected-window)) + (str (apply #'format format-string args))) + (select-window (lv-window)) + (unless (string= (buffer-string) str) + (delete-region (point-min) (point-max)) + (insert str) + (fit-window-to-buffer nil nil 2)) + (goto-char (point-min)) + (select-window ori))) + +(provide 'lv) + +;;; lv.el ends here