[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master f972634 45/45: Merge commit '742d66a63e86ac740e610faa5abba
From: |
Oleh Krehel |
Subject: |
[elpa] master f972634 45/45: Merge commit '742d66a63e86ac740e610faa5abba97e7f8ad5c2' from hydra |
Date: |
Thu, 16 Apr 2015 12:45:58 +0000 |
branch: master
commit f9726342d0783bb3442acb69eb850650fc186bfb
Merge: 22139ae 742d66a
Author: Oleh Krehel <address@hidden>
Commit: Oleh Krehel <address@hidden>
Merge commit '742d66a63e86ac740e610faa5abba97e7f8ad5c2' from hydra
Conflicts:
packages/hydra/hydra-test.el
packages/hydra/hydra.el
---
packages/hydra/.travis.yml | 6 +-
packages/hydra/Makefile | 1 +
packages/hydra/README.md | 68 +-
packages/hydra/hydra-test.el | 1518 ++++++++++++++++++++++--------------------
packages/hydra/hydra.el | 783 +++++++++++------------
5 files changed, 1202 insertions(+), 1174 deletions(-)
diff --git a/packages/hydra/.travis.yml b/packages/hydra/.travis.yml
index 1f5dbc7..e97acdb 100644
--- a/packages/hydra/.travis.yml
+++ b/packages/hydra/.travis.yml
@@ -1,12 +1,14 @@
language: emacs-lisp
env:
matrix:
- - EMACS=emacs24
+ - emacs=emacs24
+ - emacs=emacs-snapshot
before_install:
- sudo add-apt-repository -y ppa:cassou/emacs
+ - sudo add-apt-repository -y ppa:ubuntu-elisp
- sudo apt-get update -qq
- - sudo apt-get install -qq $EMACS
+ - sudo apt-get install -qq $emacs
script:
- make test
diff --git a/packages/hydra/Makefile b/packages/hydra/Makefile
index 35709e1..43bcb5a 100644
--- a/packages/hydra/Makefile
+++ b/packages/hydra/Makefile
@@ -8,6 +8,7 @@ LOAD = -l lv.el -l hydra.el -l hydra-test.el
all: test
test:
+ @echo "Using $(shell which $(emacs))..."
$(emacs) -batch $(LOAD) -f ert-run-tests-batch-and-exit
compile:
diff --git a/packages/hydra/README.md b/packages/hydra/README.md
index 172524e..d2237d8 100644
--- a/packages/hydra/README.md
+++ b/packages/hydra/README.md
@@ -5,6 +5,8 @@ bindings with a common prefix - a Hydra.
![hydra](http://oremacs.com/download/Hydra.jpg)
+## Description for Poets
+
Once you summon the Hydra through the prefixed binding (the body + any one
head), all heads can be
called in succession with only a short extension.
@@ -13,6 +15,22 @@ Hercules, besides vanquishing the Hydra, will still serve
his original purpose,
command. This makes the Hydra very seamless, it's like a minor mode that
disables itself
auto-magically.
+## Description for Pragmatics
+
+Imagine that you have bound <kbd>C-c j</kbd> and <kbd>C-c k</kbd> in your
+config. You want to call <kbd>C-c j</kbd> and <kbd>C-c k</kbd> in some
+(arbitrary) sequence. Hydra allows you to:
+
+- Bind your functions in a way that pressing <kbd>C-c jjkk3j5k</kbd> is
+equivalent to pressing <kbd>C-c j C-c j C-c k C-c k M-3 C-c j M-5 C-c
+k</kbd>. Any key other than <kbd>j</kbd> or <kbd>k</kbd> exits this state.
+
+- Assign a custom hint to this group of functions, so that you know immediately
+after pressing <kbd>C-c</kbd> that you can follow up with <kbd>j</kbd> or
+<kbd>k</kbd>.
+
+If you want to quickly understand the concept, see [the video
demo](https://www.youtube.com/watch?v=_qZliI1BKzI).
+
<!-- markdown-toc start - Don't edit this section. Run M-x
markdown-toc/generate-toc again -->
**Table of Contents**
@@ -158,41 +176,21 @@ Here's what `hydra-zoom/body` looks like, if you're
interested:
The body can be accessed via `hydra-zoom/body'."
(interactive)
- (hydra-disable)
- (catch (quote hydra-disable)
- (when hydra-is-helpful (hydra-zoom/hint))
- (setq hydra-last
- (hydra-set-transient-map
- (setq hydra-curr-map
- (quote
- (keymap (7 . hydra-keyboard-quit)
- (108 . hydra-zoom/text-scale-decrease)
- (103 . hydra-zoom/text-scale-increase)
- (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-cleanup))))
- (setq prefix-arg current-prefix-arg)))
+ (hydra-default-pre)
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-zoom/hint))
+ (message
+ (eval hydra-zoom/hint))))
+ (hydra-set-transient-map
+ hydra-zoom/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil)
+ (setq prefix-arg
+ current-prefix-arg))
```
## `awesome-map` and `awesome-binding`
diff --git a/packages/hydra/hydra-test.el b/packages/hydra/hydra-test.el
index f876e36..15eabcf 100644
--- a/packages/hydra/hydra-test.el
+++ b/packages/hydra/hydra-test.el
@@ -26,6 +26,7 @@
(require 'ert)
(require 'hydra)
+(message "Emacs version: %s" emacs-version)
(ert-deftest hydra-red-error ()
(should
@@ -38,7 +39,61 @@
("k" previous-error "prev")
("SPC" hydra-repeat "rep" :bind nil)))
'(progn
- (defun hydra-error/first-error nil "Create a hydra with a \"M-g\" body
and the heads:
+ (set
+ (defvar hydra-error/keymap nil
+ "Keymap for hydra-error.")
+ (quote
+ (keymap
+ (32 . hydra-repeat)
+ (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))))
+ (set
+ (defvar hydra-error/heads nil
+ "Heads for hydra-error.")
+ (quote
+ (("h"
+ first-error
+ "first"
+ :exit nil)
+ ("j"
+ next-error
+ "next"
+ :exit nil)
+ ("k"
+ previous-error
+ "prev"
+ :exit nil)
+ ("SPC"
+ hydra-repeat
+ "rep"
+ :bind nil
+ :exit nil))))
+ (defun hydra-error/first-error nil
+ "Create a hydra with a \"M-g\" body and the heads:
\"h\": `first-error',
\"j\": `next-error',
@@ -48,50 +103,33 @@
The body can be accessed via `hydra-error/body'.
Call the head: `first-error'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (condition-case err (prog1 t (call-interactively (function
first-error)))
- ((quit 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 (7 . hydra-keyboard-quit)
- (32 . hydra-repeat)
- (107 . hydra-error/previous-error)
- (106 . hydra-error/next-error)
- (104 . hydra-error/first-error)
- (switch-frame .
hydra--handle-switch-frame)
- (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-cleanup))))))
- (defun hydra-error/next-error nil "Create a hydra with a \"M-g\" body
and the heads:
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore t))
+ (hydra-keyboard-quit))
+ (condition-case err
+ (progn
+ (setq this-command
+ (quote first-error))
+ (call-interactively
+ (function first-error)))
+ ((quit error)
+ (message "%S" err)
+ (unless hydra-lv (sit-for 0.8))))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-error/hint))
+ (message
+ (eval hydra-error/hint))))
+ (hydra-set-transient-map
+ hydra-error/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil))
+ (defun hydra-error/next-error nil
+ "Create a hydra with a \"M-g\" body and the heads:
\"h\": `first-error',
\"j\": `next-error',
@@ -101,50 +139,33 @@ Call the head: `first-error'."
The body can be accessed via `hydra-error/body'.
Call the head: `next-error'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (condition-case err (prog1 t (call-interactively (function
next-error)))
- ((quit 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 (7 . hydra-keyboard-quit)
- (32 . hydra-repeat)
- (107 . hydra-error/previous-error)
- (106 . hydra-error/next-error)
- (104 . hydra-error/first-error)
- (switch-frame .
hydra--handle-switch-frame)
- (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-cleanup))))))
- (defun hydra-error/previous-error nil "Create a hydra with a \"M-g\"
body and the heads:
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore t))
+ (hydra-keyboard-quit))
+ (condition-case err
+ (progn
+ (setq this-command
+ (quote next-error))
+ (call-interactively
+ (function next-error)))
+ ((quit error)
+ (message "%S" err)
+ (unless hydra-lv (sit-for 0.8))))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-error/hint))
+ (message
+ (eval hydra-error/hint))))
+ (hydra-set-transient-map
+ hydra-error/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil))
+ (defun hydra-error/previous-error nil
+ "Create a hydra with a \"M-g\" body and the heads:
\"h\": `first-error',
\"j\": `next-error',
@@ -154,68 +175,58 @@ Call the head: `next-error'."
The body can be accessed via `hydra-error/body'.
Call the head: `previous-error'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (condition-case err (prog1 t (call-interactively (function
previous-error)))
- ((quit 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 (7 . hydra-keyboard-quit)
- (32 . hydra-repeat)
- (107 . hydra-error/previous-error)
- (106 . hydra-error/next-error)
- (104 . hydra-error/first-error)
- (switch-frame .
hydra--handle-switch-frame)
- (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-cleanup))))))
- (unless (keymapp (lookup-key global-map (kbd "M-g")))
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore t))
+ (hydra-keyboard-quit))
+ (condition-case err
+ (progn
+ (setq this-command
+ (quote previous-error))
+ (call-interactively
+ (function previous-error)))
+ ((quit error)
+ (message "%S" err)
+ (unless hydra-lv (sit-for 0.8))))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-error/hint))
+ (message
+ (eval hydra-error/hint))))
+ (hydra-set-transient-map
+ hydra-error/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil))
+ (unless (keymapp
+ (lookup-key
+ global-map
+ (kbd "M-g")))
(define-key global-map (kbd "M-g")
nil))
(define-key global-map [134217831 104]
- (function hydra-error/first-error))
+ (function
+ hydra-error/first-error))
(define-key global-map [134217831 106]
- (function hydra-error/next-error))
+ (function
+ hydra-error/next-error))
(define-key global-map [134217831 107]
- (function hydra-error/previous-error))
- (defun hydra-error/hint nil
- (if hydra-lv (lv-message (format #("error: [h]: first, [j]: next, [k]:
prev, [SPC]: rep." 8 9 (face hydra-face-red)
- 20 21 (face hydra-face-red)
- 31 32 (face hydra-face-red)
- 42 45 (face hydra-face-red))))
- (message (format #("error: [h]: first, [j]: next, [k]: prev, [SPC]:
rep." 8 9 (face hydra-face-red)
- 20 21 (face hydra-face-red)
- 31 32 (face hydra-face-red)
- 42 45 (face hydra-face-red))))))
- (defun hydra-error/body nil "Create a hydra with a \"M-g\" body and the
heads:
+ (function
+ hydra-error/previous-error))
+ (set
+ (defvar hydra-error/hint nil
+ "Dynamic hint for hydra-error.")
+ (quote
+ (format
+ #("error: [h]: first, [j]: next, [k]: prev, [SPC]: rep."
+ 8 9 (face hydra-face-red)
+ 20 21 (face hydra-face-red)
+ 31 32 (face hydra-face-red)
+ 42 45 (face hydra-face-red)))))
+ (defun hydra-error/body nil
+ "Create a hydra with a \"M-g\" body and the heads:
\"h\": `first-error',
\"j\": `next-error',
@@ -223,45 +234,24 @@ Call the head: `previous-error'."
\"SPC\": `hydra-repeat'
The body can be accessed via `hydra-error/body'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (when hydra-is-helpful (hydra-error/hint))
- (setq hydra-last
- (hydra-set-transient-map
- (setq hydra-curr-map
- (quote (keymap (7 . hydra-keyboard-quit)
- (32 . hydra-repeat)
- (107 . hydra-error/previous-error)
- (106 . hydra-error/next-error)
- (104 . hydra-error/first-error)
- (switch-frame .
hydra--handle-switch-frame)
- (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-cleanup))))
- (setq prefix-arg current-prefix-arg)))))))
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore nil))
+ (hydra-keyboard-quit))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-error/hint))
+ (message
+ (eval hydra-error/hint))))
+ (hydra-set-transient-map
+ hydra-error/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil)
+ (setq prefix-arg
+ current-prefix-arg))))))
(ert-deftest hydra-blue-toggle ()
(should
@@ -274,7 +264,57 @@ The body can be accessed via `hydra-error/body'."
("a" abbrev-mode "abbrev")
("q" nil "cancel")))
'(progn
- (defun hydra-toggle/toggle-truncate-lines-and-exit nil "Create a hydra
with no body and the heads:
+ (set
+ (defvar hydra-toggle/keymap nil
+ "Keymap for hydra-toggle.")
+ (quote
+ (keymap
+ (113 . hydra-toggle/nil)
+ (97 . hydra-toggle/abbrev-mode-and-exit)
+ (102 . hydra-toggle/auto-fill-mode-and-exit)
+ (116 . hydra-toggle/toggle-truncate-lines-and-exit)
+ (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))))
+ (set
+ (defvar hydra-toggle/heads nil
+ "Heads for hydra-toggle.")
+ (quote
+ (("t"
+ toggle-truncate-lines
+ "truncate"
+ :exit t)
+ ("f"
+ auto-fill-mode
+ "fill"
+ :exit t)
+ ("a"
+ abbrev-mode
+ "abbrev"
+ :exit t)
+ ("q" nil "cancel" :exit t))))
+ (defun hydra-toggle/toggle-truncate-lines-and-exit nil
+ "Create a hydra with no body and the heads:
\"t\": `toggle-truncate-lines',
\"f\": `auto-fill-mode',
@@ -284,13 +324,17 @@ The body can be accessed via `hydra-error/body'."
The body can be accessed via `hydra-toggle/body'.
Call the head: `toggle-truncate-lines'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (hydra-cleanup)
- (catch (quote hydra-disable)
- (call-interactively (function toggle-truncate-lines))))
- (defun hydra-toggle/auto-fill-mode-and-exit nil "Create a hydra with no
body and the heads:
+ (interactive)
+ (hydra-default-pre)
+ (hydra-keyboard-quit)
+ (progn
+ (setq this-command
+ (quote toggle-truncate-lines))
+ (call-interactively
+ (function
+ toggle-truncate-lines))))
+ (defun hydra-toggle/auto-fill-mode-and-exit nil
+ "Create a hydra with no body and the heads:
\"t\": `toggle-truncate-lines',
\"f\": `auto-fill-mode',
@@ -300,13 +344,16 @@ Call the head: `toggle-truncate-lines'."
The body can be accessed via `hydra-toggle/body'.
Call the head: `auto-fill-mode'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (hydra-cleanup)
- (catch (quote hydra-disable)
- (call-interactively (function auto-fill-mode))))
- (defun hydra-toggle/abbrev-mode-and-exit nil "Create a hydra with no
body and the heads:
+ (interactive)
+ (hydra-default-pre)
+ (hydra-keyboard-quit)
+ (progn
+ (setq this-command
+ (quote auto-fill-mode))
+ (call-interactively
+ (function auto-fill-mode))))
+ (defun hydra-toggle/abbrev-mode-and-exit nil
+ "Create a hydra with no body and the heads:
\"t\": `toggle-truncate-lines',
\"f\": `auto-fill-mode',
@@ -316,13 +363,16 @@ Call the head: `auto-fill-mode'."
The body can be accessed via `hydra-toggle/body'.
Call the head: `abbrev-mode'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (hydra-cleanup)
- (catch (quote hydra-disable)
- (call-interactively (function abbrev-mode))))
- (defun hydra-toggle/nil nil "Create a hydra with no body and the heads:
+ (interactive)
+ (hydra-default-pre)
+ (hydra-keyboard-quit)
+ (progn
+ (setq this-command
+ (quote abbrev-mode))
+ (call-interactively
+ (function abbrev-mode))))
+ (defun hydra-toggle/nil nil
+ "Create a hydra with no body and the heads:
\"t\": `toggle-truncate-lines',
\"f\": `auto-fill-mode',
@@ -332,21 +382,21 @@ Call the head: `abbrev-mode'."
The body can be accessed via `hydra-toggle/body'.
Call the head: `nil'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (hydra-cleanup)
- (catch (quote hydra-disable)))
- (defun hydra-toggle/hint nil
- (if hydra-lv (lv-message (format #("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))))
- (message (format #("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:
+ (interactive)
+ (hydra-default-pre)
+ (hydra-keyboard-quit))
+ (set
+ (defvar hydra-toggle/hint nil
+ "Dynamic hint for hydra-toggle.")
+ (quote
+ (format
+ #("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',
@@ -354,45 +404,24 @@ Call the head: `nil'."
\"q\": `nil'
The body can be accessed via `hydra-toggle/body'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (when hydra-is-helpful (hydra-toggle/hint))
- (setq hydra-last
- (hydra-set-transient-map
- (setq hydra-curr-map
- (quote (keymap (7 . hydra-keyboard-quit)
- (113 . hydra-toggle/nil)
- (97 .
hydra-toggle/abbrev-mode-and-exit)
- (102 .
hydra-toggle/auto-fill-mode-and-exit)
- (116 .
hydra-toggle/toggle-truncate-lines-and-exit)
- (switch-frame .
hydra--handle-switch-frame)
- (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-cleanup))))
- (setq prefix-arg current-prefix-arg)))))))
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore nil))
+ (hydra-keyboard-quit))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-toggle/hint))
+ (message
+ (eval hydra-toggle/hint))))
+ (hydra-set-transient-map
+ hydra-toggle/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil)
+ (setq prefix-arg
+ current-prefix-arg))))))
(ert-deftest hydra-amaranth-vi ()
(should
@@ -409,27 +438,50 @@ The body can be accessed via `hydra-toggle/body'."
("k" previous-line)
("q" nil "quit")))
'(progn
- (defun hydra-vi/hydra-keyboard-quit-and-exit nil "Create a hydra with no
body and the heads:
+ (set
+ (defvar hydra-vi/keymap nil
+ "Keymap for hydra-vi.")
+ (quote
+ (keymap
+ (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))))
+ (set
+ (defvar hydra-vi/heads nil
+ "Heads for hydra-vi.")
+ (quote
+ (("j" next-line "" :exit nil)
+ ("k"
+ previous-line
+ ""
+ :exit nil)
+ ("q" nil "quit" :exit t))))
+ (defun hydra-vi/next-line nil
+ "Create a hydra with no body and the heads:
-\"\": `hydra-keyboard-quit',
-\"j\": `next-line',
-\"k\": `previous-line',
-\"q\": `nil'
-
-The body can be accessed via `hydra-vi/body'.
-
-Call the head: `hydra-keyboard-quit'."
- (interactive)
- (hydra-default-pre)
- (set-cursor-color "#e52b50")
- (hydra-disable)
- (hydra-cleanup)
- (catch (quote hydra-disable)
- (call-interactively (function hydra-keyboard-quit))
- (set-cursor-color "#ffffff")))
- (defun hydra-vi/next-line nil "Create a hydra with no body and the heads:
-
-\"\": `hydra-keyboard-quit',
\"j\": `next-line',
\"k\": `previous-line',
\"q\": `nil'
@@ -437,57 +489,34 @@ Call the head: `hydra-keyboard-quit'."
The body can be accessed via `hydra-vi/body'.
Call the head: `next-line'."
- (interactive)
- (hydra-default-pre)
- (set-cursor-color "#e52b50")
- (hydra-disable)
- (catch (quote hydra-disable)
- (condition-case err (prog1 t (call-interactively (function
next-line)))
- ((quit 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 (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)
- (7 .
hydra-vi/hydra-keyboard-quit-and-exit)
- (switch-frame .
hydra--handle-switch-frame)
- (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-cleanup))))))
- (defun hydra-vi/previous-line nil "Create a hydra with no body and the
heads:
-
-\"\": `hydra-keyboard-quit',
+ (interactive)
+ (hydra-default-pre)
+ (set-cursor-color "#e52b50")
+ (let ((hydra--ignore t))
+ (hydra-keyboard-quit))
+ (condition-case err
+ (progn
+ (setq this-command
+ (quote next-line))
+ (call-interactively
+ (function next-line)))
+ ((quit error)
+ (message "%S" err)
+ (unless hydra-lv (sit-for 0.8))))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-vi/hint))
+ (message (eval hydra-vi/hint))))
+ (hydra-set-transient-map
+ hydra-vi/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ (set-cursor-color "#ffffff"))
+ (quote warn)))
+ (defun hydra-vi/previous-line nil
+ "Create a hydra with no body and the heads:
+
\"j\": `next-line',
\"k\": `previous-line',
\"q\": `nil'
@@ -495,57 +524,34 @@ Call the head: `next-line'."
The body can be accessed via `hydra-vi/body'.
Call the head: `previous-line'."
- (interactive)
- (hydra-default-pre)
- (set-cursor-color "#e52b50")
- (hydra-disable)
- (catch (quote hydra-disable)
- (condition-case err (prog1 t (call-interactively (function
previous-line)))
- ((quit 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 (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)
- (7 .
hydra-vi/hydra-keyboard-quit-and-exit)
- (switch-frame .
hydra--handle-switch-frame)
- (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-cleanup))))))
- (defun hydra-vi/nil nil "Create a hydra with no body and the heads:
-
-\"\": `hydra-keyboard-quit',
+ (interactive)
+ (hydra-default-pre)
+ (set-cursor-color "#e52b50")
+ (let ((hydra--ignore t))
+ (hydra-keyboard-quit))
+ (condition-case err
+ (progn
+ (setq this-command
+ (quote previous-line))
+ (call-interactively
+ (function previous-line)))
+ ((quit error)
+ (message "%S" err)
+ (unless hydra-lv (sit-for 0.8))))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-vi/hint))
+ (message (eval hydra-vi/hint))))
+ (hydra-set-transient-map
+ hydra-vi/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ (set-cursor-color "#ffffff"))
+ (quote warn)))
+ (defun hydra-vi/nil nil
+ "Create a hydra with no body and the heads:
+
\"j\": `next-line',
\"k\": `previous-line',
\"q\": `nil'
@@ -553,72 +559,335 @@ Call the head: `previous-line'."
The body can be accessed via `hydra-vi/body'.
Call the head: `nil'."
- (interactive)
- (hydra-default-pre)
- (set-cursor-color "#e52b50")
- (hydra-disable)
- (hydra-cleanup)
- (catch (quote hydra-disable)
- (set-cursor-color "#ffffff")))
- (defun hydra-vi/hint nil
- (if hydra-lv (lv-message (format #("vi: j, k, [q]: quit." 4 5 (face
hydra-face-amaranth)
- 7 8 (face hydra-face-amaranth)
- 11 12 (face hydra-face-blue))))
- (message (format #("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:
-
-\"\": `hydra-keyboard-quit',
+ (interactive)
+ (hydra-default-pre)
+ (set-cursor-color "#e52b50")
+ (hydra-keyboard-quit))
+ (set
+ (defvar hydra-vi/hint nil
+ "Dynamic hint for hydra-vi.")
+ (quote
+ (format
+ #("vi: j, k, [q]: quit."
+ 4 5 (face hydra-face-amaranth)
+ 7 8 (face hydra-face-amaranth)
+ 11 12 (face hydra-face-teal)))))
+ (defun hydra-vi/body nil
+ "Create a hydra with no body and the heads:
+
\"j\": `next-line',
\"k\": `previous-line',
\"q\": `nil'
The body can be accessed via `hydra-vi/body'."
- (interactive)
- (hydra-default-pre)
- (set-cursor-color "#e52b50")
- (hydra-disable)
- (catch (quote hydra-disable)
- (when hydra-is-helpful (hydra-vi/hint))
- (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 (unless
hydra-lv (sit-for 0.8))
- (hydra-vi/hint)))
- (113 . hydra-vi/nil)
- (107 . hydra-vi/previous-line)
- (106 . hydra-vi/next-line)
- (7 .
hydra-vi/hydra-keyboard-quit-and-exit)
- (switch-frame .
hydra--handle-switch-frame)
- (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-cleanup))))
- (setq prefix-arg current-prefix-arg)))))))
+ (interactive)
+ (hydra-default-pre)
+ (set-cursor-color "#e52b50")
+ (let ((hydra--ignore nil))
+ (hydra-keyboard-quit))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-vi/hint))
+ (message (eval hydra-vi/hint))))
+ (hydra-set-transient-map
+ hydra-vi/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ (set-cursor-color "#ffffff"))
+ (quote warn))
+ (setq prefix-arg
+ current-prefix-arg))))))
+
+(ert-deftest hydra-zoom-duplicate-1 ()
+ (should
+ (equal
+ (macroexpand
+ '(defhydra hydra-zoom ()
+ "zoom"
+ ("r" (text-scale-set 0) "reset")
+ ("0" (text-scale-set 0) :bind nil :exit t)
+ ("1" (text-scale-set 0) nil :bind nil :exit t)))
+ '(progn
+ (set
+ (defvar hydra-zoom/keymap nil
+ "Keymap for hydra-zoom.")
+ (quote
+ (keymap
+ (114 . hydra-zoom/lambda-r)
+ (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-zoom/lambda-0-and-exit)
+ (48 . hydra-zoom/lambda-0-and-exit)
+ (45 . hydra--negative-argument)
+ (21 . hydra--universal-argument))))
+ (set
+ (defvar hydra-zoom/heads nil
+ "Heads for hydra-zoom.")
+ (quote
+ (("r"
+ (text-scale-set 0)
+ "reset"
+ :exit nil)
+ ("0"
+ (text-scale-set 0)
+ ""
+ :bind nil
+ :exit t)
+ ("1"
+ (text-scale-set 0)
+ nil
+ :bind nil
+ :exit t))))
+ (defun hydra-zoom/lambda-r nil
+ "Create a hydra with no body and the heads:
+
+\"r\": `(text-scale-set 0)',
+\"0\": `(text-scale-set 0)',
+\"1\": `(text-scale-set 0)'
+
+The body can be accessed via `hydra-zoom/body'.
+
+Call the head: `(text-scale-set 0)'."
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore t))
+ (hydra-keyboard-quit))
+ (condition-case err
+ (call-interactively
+ (function
+ (lambda nil
+ (interactive)
+ (text-scale-set 0))))
+ ((quit error)
+ (message "%S" err)
+ (unless hydra-lv (sit-for 0.8))))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-zoom/hint))
+ (message
+ (eval hydra-zoom/hint))))
+ (hydra-set-transient-map
+ hydra-zoom/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil))
+ (defun hydra-zoom/lambda-0-and-exit nil
+ "Create a hydra with no body and the heads:
+
+\"r\": `(text-scale-set 0)',
+\"0\": `(text-scale-set 0)',
+\"1\": `(text-scale-set 0)'
+
+The body can be accessed via `hydra-zoom/body'.
+
+Call the head: `(text-scale-set 0)'."
+ (interactive)
+ (hydra-default-pre)
+ (hydra-keyboard-quit)
+ (call-interactively
+ (function
+ (lambda nil
+ (interactive)
+ (text-scale-set 0)))))
+ (set
+ (defvar hydra-zoom/hint nil
+ "Dynamic hint for hydra-zoom.")
+ (quote
+ (format
+ #("zoom: [r 0]: reset."
+ 7 8 (face hydra-face-red)
+ 9 10 (face hydra-face-blue)))))
+ (defun hydra-zoom/body nil
+ "Create a hydra with no body and the heads:
+
+\"r\": `(text-scale-set 0)',
+\"0\": `(text-scale-set 0)',
+\"1\": `(text-scale-set 0)'
+
+The body can be accessed via `hydra-zoom/body'."
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore nil))
+ (hydra-keyboard-quit))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-zoom/hint))
+ (message
+ (eval hydra-zoom/hint))))
+ (hydra-set-transient-map
+ hydra-zoom/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil)
+ (setq prefix-arg
+ current-prefix-arg))))))
+
+(ert-deftest hydra-zoom-duplicate-2 ()
+ (should
+ (equal
+ (macroexpand
+ '(defhydra hydra-zoom ()
+ "zoom"
+ ("r" (text-scale-set 0) "reset")
+ ("0" (text-scale-set 0) :bind nil :exit t)
+ ("1" (text-scale-set 0) nil :bind nil)))
+ '(progn
+ (set
+ (defvar hydra-zoom/keymap nil
+ "Keymap for hydra-zoom.")
+ (quote
+ (keymap
+ (114 . hydra-zoom/lambda-r)
+ (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-zoom/lambda-r)
+ (48 . hydra-zoom/lambda-0-and-exit)
+ (45 . hydra--negative-argument)
+ (21 . hydra--universal-argument))))
+ (set
+ (defvar hydra-zoom/heads nil
+ "Heads for hydra-zoom.")
+ (quote
+ (("r"
+ (text-scale-set 0)
+ "reset"
+ :exit nil)
+ ("0"
+ (text-scale-set 0)
+ ""
+ :bind nil
+ :exit t)
+ ("1"
+ (text-scale-set 0)
+ nil
+ :bind nil
+ :exit nil))))
+ (defun hydra-zoom/lambda-r nil
+ "Create a hydra with no body and the heads:
+
+\"r\": `(text-scale-set 0)',
+\"0\": `(text-scale-set 0)',
+\"1\": `(text-scale-set 0)'
+
+The body can be accessed via `hydra-zoom/body'.
+
+Call the head: `(text-scale-set 0)'."
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore t))
+ (hydra-keyboard-quit))
+ (condition-case err
+ (call-interactively
+ (function
+ (lambda nil
+ (interactive)
+ (text-scale-set 0))))
+ ((quit error)
+ (message "%S" err)
+ (unless hydra-lv (sit-for 0.8))))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-zoom/hint))
+ (message
+ (eval hydra-zoom/hint))))
+ (hydra-set-transient-map
+ hydra-zoom/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil))
+ (defun hydra-zoom/lambda-0-and-exit nil
+ "Create a hydra with no body and the heads:
+
+\"r\": `(text-scale-set 0)',
+\"0\": `(text-scale-set 0)',
+\"1\": `(text-scale-set 0)'
+
+The body can be accessed via `hydra-zoom/body'.
+
+Call the head: `(text-scale-set 0)'."
+ (interactive)
+ (hydra-default-pre)
+ (hydra-keyboard-quit)
+ (call-interactively
+ (function
+ (lambda nil
+ (interactive)
+ (text-scale-set 0)))))
+ (set
+ (defvar hydra-zoom/hint nil
+ "Dynamic hint for hydra-zoom.")
+ (quote
+ (format
+ #("zoom: [r 0]: reset."
+ 7 8 (face hydra-face-red)
+ 9 10 (face hydra-face-blue)))))
+ (defun hydra-zoom/body nil
+ "Create a hydra with no body and the heads:
+
+\"r\": `(text-scale-set 0)',
+\"0\": `(text-scale-set 0)',
+\"1\": `(text-scale-set 0)'
+
+The body can be accessed via `hydra-zoom/body'."
+ (interactive)
+ (hydra-default-pre)
+ (let ((hydra--ignore nil))
+ (hydra-keyboard-quit))
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message
+ (eval hydra-zoom/hint))
+ (message
+ (eval hydra-zoom/hint))))
+ (hydra-set-transient-map
+ hydra-zoom/keymap
+ (lambda nil
+ (hydra-keyboard-quit)
+ nil)
+ nil)
+ (setq prefix-arg
+ current-prefix-arg))))))
(ert-deftest defhydradio ()
(should (equal
@@ -741,7 +1010,7 @@ _f_ auto-fill-mode: %`auto-fill-function
'(concat (format "%s abbrev-mode: %S
%s debug-on-error: %S
%s auto-fill-mode: %S
-" "{a}" abbrev-mode "{d}" debug-on-error "{f}" auto-fill-function) "[[q]]:
quit"))))
+" "{a}" abbrev-mode "{d}" debug-on-error "{f}" auto-fill-function) "[{q}]:
quit"))))
(ert-deftest hydra-format-2 ()
(should (equal
@@ -751,8 +1020,8 @@ _f_ auto-fill-mode: %`auto-fill-function
'bar
nil
"\n bar %s`foo\n"
- '(("a" (quote t) "" :cmd-name bar/lambda-a)
- ("q" nil "" :cmd-name bar/nil))))
+ '(("a" (quote t) "" :cmd-name bar/lambda-a :exit nil)
+ ("q" nil "" :cmd-name bar/nil :exit t))))
'(concat (format " bar %s\n" foo) "{a}, [q]"))))
(ert-deftest hydra-format-3 ()
@@ -784,7 +1053,7 @@ _f_ auto-fill-mode: %`auto-fill-function
(hydra--format
'hydra-toggle nil
"\n_n_ narrow-or-widen-dwim %(progn (message
\"checking\")(buffer-narrowed-p))asdf\n"
- '(("n" narrow-to-region nil) ("q" nil "cancel"))))
+ '(("n" narrow-to-region nil) ("q" nil "cancel" :exit t))))
'(concat (format "%s narrow-or-widen-dwim %Sasdf\n"
"{n}"
(progn
@@ -799,7 +1068,7 @@ _f_ auto-fill-mode: %`auto-fill-function
(hydra--format
'hydra-toggle nil
"\n_n_ narrow-or-widen-dwim %s(progn (message
\"checking\")(buffer-narrowed-p))asdf\n"
- '(("n" narrow-to-region nil) ("q" nil "cancel"))))
+ '(("n" narrow-to-region nil) ("q" nil "cancel" :exit t))))
'(concat (format "%s narrow-or-widen-dwim %sasdf\n"
"{n}"
(progn
@@ -807,36 +1076,6 @@ _f_ auto-fill-mode: %`auto-fill-function
(buffer-narrowed-p)))
"[[q]]: cancel"))))
-(ert-deftest hydra-compat-colors-1 ()
- (should (equal (hydra--head-color
- '("e" (message "Exiting now") "blue")
- '(nil nil :color blue))
- 'blue))
- (should (equal (hydra--head-color
- '("c" (message "Continuing") "red" :color red)
- '(nil nil :color blue))
- 'red))
- (should (equal (hydra--head-color
- '("e" (message "Exiting now") "blue")
- '(nil nil :exit t))
- 'blue))
- (should (equal (hydra--head-color
- '("j" next-line "" :exit t)
- '(nil nil))
- 'blue))
- (should (equal (hydra--head-color
- '("c" (message "Continuing") "red" :exit nil)
- '(nil nil :exit t))
- 'red))
- (equal (hydra--head-color
- '("a" abbrev-mode nil)
- '(nil nil :color teal))
- 'teal)
- (equal (hydra--head-color
- '("a" abbrev-mode :exit nil)
- '(nil nil :color teal))
- 'amaranth))
-
(ert-deftest hydra-compat-colors-2 ()
(should
(equal
@@ -897,256 +1136,6 @@ _f_ auto-fill-mode: %`auto-fill-function
("e" fun-e)
("f" fun-f))))))
-(ert-deftest hydra-zoom-duplicate-1 ()
- (should
- (equal
- (macroexpand
- '(defhydra hydra-zoom ()
- "zoom"
- ("r" (text-scale-set 0) "reset")
- ("0" (text-scale-set 0) :bind nil :exit t)
- ("1" (text-scale-set 0) nil :bind nil :exit t)))
- '(progn
- (defun hydra-zoom/lambda-r nil "Create a hydra with no body and the
heads:
-
-\"r\": `(text-scale-set 0)',
-\"0\": `(text-scale-set 0)',
-\"1\": `(text-scale-set 0)'
-
-The body can be accessed via `hydra-zoom/body'.
-
-Call the head: `(text-scale-set 0)'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (condition-case err (prog1 t (call-interactively (function
(lambda nil (interactive)
-
(text-scale-set 0)))))
- ((quit error)
- (message "%S" err)
- (unless hydra-lv (sit-for 0.8))
- nil))
- (when hydra-is-helpful (hydra-zoom/hint))
- (setq hydra-last
- (hydra-set-transient-map
- (setq hydra-curr-map
- (quote (keymap (7 . hydra-keyboard-quit)
- (114 . hydra-zoom/lambda-r)
- (switch-frame .
hydra--handle-switch-frame)
- (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-zoom/lambda-0-and-exit)
- (48 . hydra-zoom/lambda-0-and-exit)
- (45 . hydra--negative-argument)
- (21 . hydra--universal-argument))))
- t (lambda nil (hydra-cleanup))))))
- (defun hydra-zoom/lambda-0-and-exit nil "Create a hydra with no body and
the heads:
-
-\"r\": `(text-scale-set 0)',
-\"0\": `(text-scale-set 0)',
-\"1\": `(text-scale-set 0)'
-
-The body can be accessed via `hydra-zoom/body'.
-
-Call the head: `(text-scale-set 0)'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (hydra-cleanup)
- (catch (quote hydra-disable)
- (call-interactively (function (lambda nil (interactive)
- (text-scale-set 0))))))
- (defun hydra-zoom/hint nil
- (if hydra-lv (lv-message (format #("zoom: [r 0]: reset." 7 8 (face
hydra-face-red)
- 9 10 (face hydra-face-blue))))
- (message (format #("zoom: [r 0]: reset." 7 8 (face hydra-face-red)
- 9 10 (face hydra-face-blue))))))
- (defun hydra-zoom/body nil "Create a hydra with no body and the heads:
-
-\"r\": `(text-scale-set 0)',
-\"0\": `(text-scale-set 0)',
-\"1\": `(text-scale-set 0)'
-
-The body can be accessed via `hydra-zoom/body'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (when hydra-is-helpful (hydra-zoom/hint))
- (setq hydra-last
- (hydra-set-transient-map
- (setq hydra-curr-map
- (quote (keymap (7 . hydra-keyboard-quit)
- (114 . hydra-zoom/lambda-r)
- (switch-frame .
hydra--handle-switch-frame)
- (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-zoom/lambda-0-and-exit)
- (48 . hydra-zoom/lambda-0-and-exit)
- (45 . hydra--negative-argument)
- (21 . hydra--universal-argument))))
- t (lambda nil (hydra-cleanup))))
- (setq prefix-arg current-prefix-arg)))))))
-
-(ert-deftest hydra-zoom-duplicate-2 ()
- (should
- (equal
- (macroexpand
- '(defhydra hydra-zoom ()
- "zoom"
- ("r" (text-scale-set 0) "reset")
- ("0" (text-scale-set 0) :bind nil :exit t)
- ("1" (text-scale-set 0) nil :bind nil)))
- '(progn
- (defun hydra-zoom/lambda-r nil "Create a hydra with no body and the
heads:
-
-\"r\": `(text-scale-set 0)',
-\"0\": `(text-scale-set 0)',
-\"1\": `(text-scale-set 0)'
-
-The body can be accessed via `hydra-zoom/body'.
-
-Call the head: `(text-scale-set 0)'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (condition-case err (prog1 t (call-interactively (function
(lambda nil (interactive)
-
(text-scale-set 0)))))
- ((quit error)
- (message "%S" err)
- (unless hydra-lv (sit-for 0.8))
- nil))
- (when hydra-is-helpful (hydra-zoom/hint))
- (setq hydra-last
- (hydra-set-transient-map
- (setq hydra-curr-map
- (quote (keymap (7 . hydra-keyboard-quit)
- (114 . hydra-zoom/lambda-r)
- (switch-frame .
hydra--handle-switch-frame)
- (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-zoom/lambda-r)
- (48 . hydra-zoom/lambda-0-and-exit)
- (45 . hydra--negative-argument)
- (21 . hydra--universal-argument))))
- t (lambda nil (hydra-cleanup))))))
- (defun hydra-zoom/lambda-0-and-exit nil "Create a hydra with no body and
the heads:
-
-\"r\": `(text-scale-set 0)',
-\"0\": `(text-scale-set 0)',
-\"1\": `(text-scale-set 0)'
-
-The body can be accessed via `hydra-zoom/body'.
-
-Call the head: `(text-scale-set 0)'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (hydra-cleanup)
- (catch (quote hydra-disable)
- (call-interactively (function (lambda nil (interactive)
- (text-scale-set 0))))))
- (defun hydra-zoom/hint nil
- (if hydra-lv (lv-message (format #("zoom: [r 0]: reset." 7 8 (face
hydra-face-red)
- 9 10 (face hydra-face-blue))))
- (message (format #("zoom: [r 0]: reset." 7 8 (face hydra-face-red)
- 9 10 (face hydra-face-blue))))))
- (defun hydra-zoom/body nil "Create a hydra with no body and the heads:
-
-\"r\": `(text-scale-set 0)',
-\"0\": `(text-scale-set 0)',
-\"1\": `(text-scale-set 0)'
-
-The body can be accessed via `hydra-zoom/body'."
- (interactive)
- (hydra-default-pre)
- (hydra-disable)
- (catch (quote hydra-disable)
- (when hydra-is-helpful (hydra-zoom/hint))
- (setq hydra-last
- (hydra-set-transient-map
- (setq hydra-curr-map
- (quote (keymap (7 . hydra-keyboard-quit)
- (114 . hydra-zoom/lambda-r)
- (switch-frame .
hydra--handle-switch-frame)
- (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-zoom/lambda-r)
- (48 . hydra-zoom/lambda-0-and-exit)
- (45 . hydra--negative-argument)
- (21 . hydra--universal-argument))))
- t (lambda nil (hydra-cleanup))))
- (setq prefix-arg current-prefix-arg)))))))
-
(ert-deftest hydra--pad ()
(should (equal (hydra--pad '(a b c) 3)
'(a b c)))
@@ -1200,6 +1189,77 @@ _w_ Worf: % -8`hydra-tng/worf^^
_h_ Set phasers to
body-pre)
'(funcall (function foo)))))
+(defhydra hydra-simple-1 (global-map "C-c")
+ ("a" (insert "j"))
+ ("b" (insert "k"))
+ ("q" nil))
+
+(defhydra hydra-simple-2 (global-map "C-c" :color amaranth)
+ ("c" self-insert-command)
+ ("d" self-insert-command)
+ ("q" nil))
+
+(defhydra hydra-simple-3 (global-map "C-c")
+ ("g" goto-line)
+ ("1" find-file)
+ ("q" nil))
+
+(defmacro hydra-with (in &rest body)
+ `(let ((temp-buffer (generate-new-buffer " *temp*")))
+ (save-window-excursion
+ (unwind-protect
+ (progn
+ (switch-to-buffer temp-buffer)
+ (transient-mark-mode 1)
+ (insert ,in)
+ (goto-char (point-min))
+ (when (search-forward "~" nil t)
+ (backward-delete-char 1)
+ (set-mark (point)))
+ (goto-char (point-max))
+ (search-backward "|")
+ (delete-char 1)
+ (setq current-prefix-arg)
+ ,@body
+ (insert "|")
+ (when (region-active-p)
+ (exchange-point-and-mark)
+ (insert "~"))
+ (buffer-substring-no-properties
+ (point-min)
+ (point-max)))
+ (and (buffer-name temp-buffer)
+ (kill-buffer temp-buffer))))))
+
+(ert-deftest hydra-integration-1 ()
+ (should (string= (hydra-with "|"
+ (execute-kbd-macro
+ (kbd "C-c aabbaaqaabbaa")))
+ "jjkkjjaabbaa|"))
+ (should (string= (hydra-with "|"
+ (condition-case nil
+ (execute-kbd-macro
+ (kbd "C-c aabb C-g"))
+ (quit nil))
+ (execute-kbd-macro "aaqaabbaa"))
+ "jjkkaaqaabbaa|")))
+
+(ert-deftest hydra-integration-2 ()
+ (should (string= (hydra-with "|"
+ (execute-kbd-macro
+ (kbd "C-c c 1 c 2 d 4 c q")))
+ "ccddcccc|"))
+ (should (string= (hydra-with "|"
+ (execute-kbd-macro
+ (kbd "C-c c 1 c C-u d C-u 10 c q")))
+ "ccddddcccccccccc|")))
+
+(ert-deftest hydra-integration-3 ()
+ (should (string= (hydra-with "foo\nbar|"
+ (execute-kbd-macro
+ (kbd "C-c g 1 RET q")))
+ "|foo\nbar")))
+
(provide 'hydra-test)
;;; hydra-test.el ends here
diff --git a/packages/hydra/hydra.el b/packages/hydra/hydra.el
index 7195e36..27d48d5 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.12.1
+;; Version: 0.13.0
;; Keywords: bindings
;; Package-Requires: ((cl-lib "0.5"))
@@ -79,24 +79,89 @@
(require 'cl-lib)
(require 'lv)
-(defalias 'hydra-set-transient-map
- (if (fboundp 'set-transient-map)
- 'set-transient-map
- (lambda (map _keep-pred &optional on-exit)
- (with-no-warnings
- (set-temporary-overlay-map map (hydra--pred on-exit))))))
-
-(defun hydra--pred (on-exit)
- "Generate a predicate on whether to continue the Hydra state.
-Call ON-EXIT for clean-up.
-This is a compatibility code for Emacs older than 24.4."
- `(lambda ()
- (if (lookup-key hydra-curr-map (this-command-keys-vector))
- t
- (hydra-cleanup)
- ,(when on-exit
- `(funcall ,(hydra--make-callable on-exit)))
- nil)))
+(defvar hydra-curr-map nil
+ "The keymap of the current Hydra called.")
+
+(defvar hydra-curr-on-exit nil
+ "The on-exit predicate for the current Hydra.")
+
+(defvar hydra-curr-foreign-keys nil
+ "The current :foreign-keys behavior.")
+
+(defun hydra-set-transient-map (keymap on-exit &optional foreign-keys)
+ "Set KEYMAP to the highest priority.
+
+Call ON-EXIT when the KEYMAP is deactivated.
+
+FOREIGN-KEYS determines the deactivation behavior, when a command
+that isn't in KEYMAP is called:
+
+nil: deactivate KEYMAP and run the command.
+run: keep KEYMAP and run the command.
+warn: keep KEYMAP and issue a warning instead of running the command."
+ (setq hydra-curr-map keymap)
+ (setq hydra-curr-on-exit on-exit)
+ (setq hydra-curr-foreign-keys foreign-keys)
+ (add-hook 'pre-command-hook 'hydra--clearfun)
+ (internal-push-keymap keymap 'overriding-terminal-local-map))
+
+(defun hydra--clearfun ()
+ "Disable the current Hydra unless `this-command' is a head."
+ (when (or
+ (memq this-command '(handle-switch-frame keyboard-quit))
+ (null overriding-terminal-local-map)
+ (not (or (eq this-command
+ (lookup-key hydra-curr-map (this-single-command-keys)))
+ (cl-case hydra-curr-foreign-keys
+ (warn
+ (setq this-command 'hydra-amaranth-warn))
+ (run
+ t)
+ (t nil)))))
+ (hydra-disable)))
+
+(defvar hydra--ignore nil
+ "When non-nil, don't call `hydra-curr-on-exit'")
+
+(defun hydra-disable ()
+ "Disable the current Hydra."
+ (remove-hook 'pre-command-hook 'hydra--clearfun)
+ (dolist (frame (frame-list))
+ (with-selected-frame frame
+ (when overriding-terminal-local-map
+ (internal-pop-keymap hydra-curr-map 'overriding-terminal-local-map)
+ (unless hydra--ignore
+ (when hydra--input-method-function
+ (setq input-method-function hydra--input-method-function)
+ (setq hydra--input-method-function nil))
+ (when hydra-curr-on-exit
+ (let ((on-exit hydra-curr-on-exit))
+ (setq hydra-curr-on-exit nil)
+ (funcall on-exit))))))))
+
+(unless (fboundp 'internal-push-keymap)
+ (defun internal-push-keymap (keymap symbol)
+ (let ((map (symbol-value symbol)))
+ (unless (memq keymap map)
+ (unless (memq 'add-keymap-witness (symbol-value symbol))
+ (setq map (make-composed-keymap nil (symbol-value symbol)))
+ (push 'add-keymap-witness (cdr map))
+ (set symbol map))
+ (push keymap (cdr map))))))
+
+(unless (fboundp 'internal-pop-keymap)
+ (defun internal-pop-keymap (keymap symbol)
+ (let ((map (symbol-value symbol)))
+ (when (memq keymap map)
+ (setf (cdr map) (delq keymap (cdr map))))
+ (let ((tail (cddr map)))
+ (and (or (null tail) (keymapp tail))
+ (eq 'add-keymap-witness (nth 1 map))
+ (set symbol tail))))))
+
+(defun hydra-amaranth-warn ()
+ (interactive)
+ (message "An amaranth Hydra can only exit through a blue head"))
;;* Customize
(defgroup hydra nil
@@ -109,11 +174,6 @@ This is a compatibility code for Emacs older than 24.4."
: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.")
-
(defcustom hydra-lv t
"When non-nil, `lv-message' (not `message') will be used to display hints."
:type 'boolean)
@@ -128,27 +188,29 @@ When nil, you can specify your own at each location like
this: _ 5a_.")
(defface hydra-face-red
'((t (:foreground "#FF0000" :bold t)))
- "Red Hydra heads will persist indefinitely."
+ "Red Hydra heads don't exit the Hydra.
+Every other command exits the Hydra."
:group 'hydra)
(defface hydra-face-blue
'((t (:foreground "#0000FF" :bold t)))
- "Blue Hydra heads will vanquish the Hydra.")
+ "Blue Hydra heads exit the Hydra.
+Every other command exits as well.")
(defface hydra-face-amaranth
'((t (:foreground "#E52B50" :bold t)))
"Amaranth body has red heads and warns on intercepting non-heads.
-Vanquishable only through a blue head.")
+Exitable only through a blue head.")
(defface hydra-face-pink
'((t (:foreground "#FF6EB4" :bold t)))
- "Pink body has red heads and on intercepting non-heads calls them without
quitting.
-Vanquishable only through a blue head.")
+ "Pink body has red heads and runs intercepted non-heads.
+Exitable only through a blue head.")
(defface hydra-face-teal
'((t (:foreground "#367588" :bold t)))
"Teal body has blue heads an warns on intercepting non-heads.
-Vanquishable only through a blue head.")
+Exitable only through a blue head.")
;;* Fontification
(defun hydra-add-font-lock ()
@@ -188,20 +250,9 @@ Vanquishable only through a blue head.")
(define-key map [kp-8] 'hydra--digit-argument)
(define-key map [kp-9] 'hydra--digit-argument)
(define-key map [kp-subtract] 'hydra--negative-argument)
- (define-key map [switch-frame] 'hydra--handle-switch-frame)
map)
"Keymap that all Hydras inherit. See `universal-argument-map'.")
-(defvar hydra-curr-map
- (make-sparse-keymap)
- "Keymap of the current Hydra called.")
-
-(defun hydra--handle-switch-frame (evt)
- "Quit hydra and call old switch-frame event handler for EVT."
- (interactive "e")
- (hydra-keyboard-quit)
- (funcall (lookup-key (current-global-map) [switch-frame]) evt))
-
(defun hydra--universal-argument (arg)
"Forward to (`universal-argument' ARG)."
(interactive "P")
@@ -209,20 +260,34 @@ Vanquishable only through a blue head.")
(list (* 4 (car arg)))
(if (eq arg '-)
(list -4)
- '(4))))
- (hydra-set-transient-map hydra-curr-map t))
+ '(4)))))
(defun hydra--digit-argument (arg)
"Forward to (`digit-argument' ARG)."
(interactive "P")
- (let ((universal-argument-map hydra-curr-map))
- (digit-argument arg)))
+ (let* ((char (if (integerp last-command-event)
+ last-command-event
+ (get last-command-event 'ascii-character)))
+ (digit (- (logand char ?\177) ?0)))
+ (setq prefix-arg (cond ((integerp arg)
+ (+ (* arg 10)
+ (if (< arg 0)
+ (- digit)
+ digit)))
+ ((eq arg '-)
+ (if (zerop digit)
+ '-
+ (- digit)))
+ (t
+ digit)))))
(defun hydra--negative-argument (arg)
"Forward to (`negative-argument' ARG)."
(interactive "P")
- (let ((universal-argument-map hydra-curr-map))
- (negative-argument arg)))
+ (setq prefix-arg (cond ((integerp arg) (- arg))
+ ((eq arg '-) nil)
+ (t '-))))
+
;;* Repeat
(defvar hydra-repeat--prefix-arg nil
"Prefix arg to use with `hydra-repeat'.")
@@ -243,9 +308,6 @@ When ARG is non-nil, use that instead."
(funcall hydra-repeat--command))
;;* Misc internals
-(defvar hydra-last nil
- "The result of the last `hydra-set-transient-map' call.")
-
(defun hydra--callablep (x)
"Test if X is callable."
(or (functionp x)
@@ -278,72 +340,6 @@ one of the properties on the list."
Return DEFAULT if PROP is not in H."
(hydra-plist-get-default (cl-cdddr h) prop default))
-(defun hydra--aggregate-color (head-color body-color)
- "Return the resulting head color for HEAD-COLOR and BODY-COLOR."
- (cond ((eq head-color 'red)
- (cl-case body-color
- (red 'red)
- (blue 'red)
- (amaranth 'amaranth)
- (pink 'pink)
- (cyan 'amaranth)))
- ((eq head-color 'blue)
- (cl-case body-color
- (red 'blue)
- (blue 'blue)
- (amaranth 'teal)
- (pink 'blue)
- (cyan 'teal)))
- (t
- (error "Can't aggregate head %S to body %S"
- head-color body-color))))
-
-(defun hydra--head-color (h body)
- "Return the color of a Hydra head H with BODY."
- (let* ((exit (hydra--head-property h :exit 'default))
- (color (hydra--head-property h :color))
- (foreign-keys (hydra--body-foreign-keys body))
- (head-color
- (cond ((eq exit 'default)
- (cl-case color
- (blue 'blue)
- (red 'red)
- (t
- (unless (null color)
- (error "Use only :blue or :red for heads: %S" h)))))
- ((null exit)
- (if color
- (error "Don't mix :color and :exit - they are aliases:
%S" h)
- (cl-case foreign-keys
- (run 'pink)
- (warn 'amaranth)
- (t 'red))))
- ((eq exit t)
- (if color
- (error "Don't mix :color and :exit - they are aliases:
%S" h)
- 'blue))
- (t
- (error "Unknown :exit %S" exit)))))
- (cond ((null (cadr h))
- (when head-color
- (hydra--complain
- "Doubly specified blue head - nil cmd is already blue: %S" h))
- 'blue)
- ((null head-color)
- (hydra--body-color body))
- ((null foreign-keys)
- head-color)
- ((eq foreign-keys 'run)
- (if (eq head-color 'red)
- 'pink
- 'blue))
- ((eq foreign-keys 'warn)
- (if (memq head-color '(red amaranth))
- 'amaranth
- 'teal))
- (t
- (error "Unexpected %S %S" h body)))))
-
(defun hydra--body-foreign-keys (body)
"Return what BODY does with a non-head binding."
(or
@@ -353,28 +349,14 @@ Return DEFAULT if PROP is not in H."
((amaranth teal) 'warn)
(pink 'run)))))
-(defun hydra--body-color (body)
- "Return the color of BODY.
-BODY is the second argument to `defhydra'"
- (let ((color (plist-get (cddr body) :color))
- (exit (plist-get (cddr body) :exit))
- (foreign-keys (plist-get (cddr body) :foreign-keys)))
- (cond ((eq foreign-keys 'warn)
- (if exit 'teal 'amaranth))
- ((eq foreign-keys 'run) 'pink)
- (exit 'blue)
- (color color)
- (t 'red))))
-
-(defun hydra--face (h body)
- "Return the face for a Hydra head H with BODY."
- (cl-case (hydra--head-color h body)
- (blue 'hydra-face-blue)
- (red 'hydra-face-red)
- (amaranth 'hydra-face-amaranth)
- (pink 'hydra-face-pink)
- (teal 'hydra-face-teal)
- (t (error "Unknown color for %S" h))))
+(defun hydra--body-exit (body)
+ "Return the exit behavior of BODY."
+ (or
+ (plist-get (cddr body) :exit)
+ (let ((color (plist-get (cddr body) :color)))
+ (cl-case color
+ ((blue teal) t)
+ (t nil)))))
(defvar hydra--input-method-function nil
"Store overridden `input-method-function' here.")
@@ -386,58 +368,26 @@ BODY is the second argument to `defhydra'"
(setq hydra--input-method-function input-method-function)
(setq input-method-function nil))))
-(defun hydra-cleanup ()
- "Clean up after a Hydra."
- (when hydra--input-method-function
- (setq input-method-function hydra--input-method-function)
- (setq hydra--input-method-function nil))
- (when (window-live-p lv-wnd)
- (let ((buf (window-buffer lv-wnd)))
- (delete-window lv-wnd)
- (kill-buffer buf))))
-
-(defvar hydra-timer (timer-create)
+(defvar hydra-timeout-timer (timer-create)
"Timer for `hydra-timeout'.")
+(defvar hydra-message-timer (timer-create)
+ "Timer for the hint.")
+
(defun hydra-keyboard-quit ()
"Quitting function similar to `keyboard-quit'."
(interactive)
(hydra-disable)
- (hydra-cleanup)
- (cancel-timer hydra-timer)
- (unless hydra-lv
+ (cancel-timer hydra-timeout-timer)
+ (cancel-timer hydra-message-timer)
+ (if hydra-lv
+ (when (window-live-p lv-wnd)
+ (let ((buf (window-buffer lv-wnd)))
+ (delete-window lv-wnd)
+ (kill-buffer buf)))
(message ""))
nil)
-(defun hydra-disable ()
- "Disable the current Hydra."
- (cond
- ;; Emacs 25
- ((functionp hydra-last)
- (funcall hydra-last))
-
- ;; Emacs 24.3 or older
- ((< emacs-minor-version 4)
- (setq emulation-mode-map-alists
- (cl-remove-if
- (lambda (x)
- (and (consp x)
- (consp (car x))
- (equal (cdar x) hydra-curr-map)))
- emulation-mode-map-alists)))
-
- ;; Emacs 24.4.1
- (t
- (setq overriding-terminal-local-map nil))))
-
-(defun hydra--unalias-var (str prefix)
- "Return the symbol named STR if it's bound as a variable.
-Otherwise, add PREFIX to the symbol name."
- (let ((sym (intern-soft str)))
- (if (boundp sym)
- sym
- (intern (concat prefix "/" str)))))
-
(defun hydra--hint (body heads)
"Generate a hint for the echo area.
BODY, and HEADS are parameters to `defhydra'."
@@ -469,16 +419,36 @@ BODY, and HEADS are parameters to `defhydra'."
(defun hydra-fontify-head-default (head body)
"Produce a pretty string from HEAD and BODY.
HEAD's binding is returned as a string with a colored face."
- (propertize (car head) 'face (hydra--face head body)))
+ (let* ((foreign-keys (hydra--body-foreign-keys body))
+ (head-exit (hydra--head-property head :exit))
+ (head-color
+ (if head-exit
+ (if (eq foreign-keys 'warn)
+ 'teal
+ 'blue)
+ (cl-case foreign-keys
+ (warn 'amaranth)
+ (run 'pink)
+ (t 'red)))))
+ (when (and (null (cadr head))
+ (not (eq head-color 'blue)))
+ (hydra--complain "nil cmd can only be blue"))
+ (propertize (car head) 'face
+ (cl-case head-color
+ (blue 'hydra-face-blue)
+ (red 'hydra-face-red)
+ (amaranth 'hydra-face-amaranth)
+ (pink 'hydra-face-pink)
+ (teal 'hydra-face-teal)
+ (t (error "Unknown color for %S" head))))))
(defun hydra-fontify-head-greyscale (head body)
"Produce a pretty string from HEAD and BODY.
HEAD's binding is returned as a string wrapped with [] or {}."
- (let ((color (hydra--head-color head body)))
- (format
- (if (eq color 'blue)
- "[%s]"
- "{%s}") (car head))))
+ (format
+ (if (hydra--head-property head :exit)
+ "[%s]"
+ "{%s}") (car head)))
(defun hydra-fontify-head (head body)
"Produce a pretty string from HEAD and BODY."
@@ -497,7 +467,7 @@ The expressions can be auto-expanded according to NAME."
offset)
(while (setq start
(string-match
- "\\(?:%\\(
?-?[0-9]*s?\\)\\(`[a-z-A-Z/0-9]+\\|(\\)\\)\\|\\(?:_\\(
?-?[0-9]*\\)\\([a-z-A-Z~.,;:0-9/|?<>={}]+\\)_\\)"
+ "\\(?:%\\(
?-?[0-9]*s?\\)\\(`[a-z-A-Z/0-9]+\\|(\\)\\)\\|\\(?:_\\(
?-?[0-9]*\\)\\([[:alnum:]-~.,;:/|?<>={}*+#]+\\)_\\)"
docstring start))
(cond ((eq ?_ (aref (match-string 0 docstring) 0))
(let* ((key (match-string 4 docstring))
@@ -537,15 +507,6 @@ The expressions can be auto-expanded according to NAME."
,rest)
`(format ,(concat docstring ": " rest ".")))))
-(defun hydra--message (name body docstring heads)
- "Generate code to display the hint in the preferred echo area.
-Set `hydra-lv' to choose the echo area.
-NAME, BODY, DOCSTRING, and HEADS are parameters of `defhydra'."
- (let ((format-expr (hydra--format name body docstring heads)))
- `(if hydra-lv
- (lv-message ,format-expr)
- (message ,format-expr))))
-
(defun hydra--complain (format-string &rest args)
"Forward to (`message' FORMAT-STRING ARGS) unless `hydra-verbose' is nil."
(when hydra-verbose
@@ -567,149 +528,89 @@ HEADS is a list of heads."
heads ",\n")
(format "The body can be accessed via `%S'." body-name)))
+(defun hydra--call-interactively (cmd name)
+ "Generate a `call-interactively' statement for CMD.
+Set `this-command' to NAME."
+ (if (and (symbolp name)
+ (not (memq name '(nil body))))
+ `(progn
+ (setq this-command ',name)
+ (call-interactively #',cmd))
+ `(call-interactively #',cmd)))
+
(defun hydra--make-defun (name body doc head
- keymap body-pre body-post &optional other-post)
+ keymap body-pre body-before-exit
+ &optional body-after-exit)
"Make a defun wrapper, using NAME, BODY, DOC, HEAD, and KEYMAP.
NAME and BODY are the arguments to `defhydra'.
DOC was generated with `hydra--doc'.
HEAD is one of the HEADS passed to `defhydra'.
-BODY-PRE and BODY-POST are pre-processed in `defhydra'.
-OTHER-POST is an optional extension to the :post key of BODY."
+BODY-PRE is added to the start of the wrapper.
+BODY-BEFORE-EXIT will be called before the hydra quits.
+BODY-AFTER-EXIT is added to the end of the wrapper."
(let ((name (hydra--head-name head name body))
(cmd (when (car head)
(hydra--make-callable
(cadr head))))
- (color (when (car head)
- (hydra--head-color head body)))
(doc (if (car head)
(format "%s\n\nCall the head: `%S'." doc (cadr head))
doc))
(hint (intern (format "%S/hint" name)))
- (body-color (hydra--body-color body))
- (body-timeout (plist-get body :timeout)))
+ (body-foreign-keys (hydra--body-foreign-keys body))
+ (body-timeout (plist-get body :timeout))
+ (body-idle (plist-get body :idle)))
`(defun ,name ()
,doc
(interactive)
(hydra-default-pre)
,@(when body-pre (list body-pre))
- (hydra-disable)
- ,@(when (memq color '(blue teal)) '((hydra-cleanup)))
- (catch 'hydra-disable
- ,@(delq nil
- (if (memq color '(blue teal))
- `(,(when cmd `(call-interactively #',cmd))
- ,body-post)
- `(,(when cmd
- `(condition-case err
- (prog1 t
- (call-interactively #',cmd))
- ((quit error)
- (message "%S" err)
- (unless hydra-lv
- (sit-for 0.8))
- nil)))
- (when hydra-is-helpful
- (,hint))
- (setq hydra-last
- (hydra-set-transient-map
- (setq hydra-curr-map ',keymap)
- t
- ,(if (and
- (not (memq body-color
- '(amaranth pink teal)))
- body-post)
- `(lambda () (hydra-cleanup) ,body-post)
- `(lambda () (hydra-cleanup)))))
- ,(or other-post
- (when body-timeout
- (list 'hydra-timeout
- body-timeout
- (when body-post
- (hydra--make-callable
body-post))))))))))))
-
-(defun hydra-pink-fallback ()
- "On intercepting a non-head, try to run it."
- (let ((keys (this-command-keys))
- kb)
- (when (equal keys [backspace])
- (setq keys ""))
- (setq kb (key-binding keys))
- (if kb
- (if (commandp kb)
- (condition-case err
- (call-interactively kb)
- ((quit error)
- (message "%S" err)
- (unless hydra-lv
- (sit-for 0.8))))
- (message "Pink Hydra can't currently handle prefixes, continuing"))
- (message "Pink Hydra could not resolve: %S" keys))))
-
-(defun hydra--modify-keymap (keymap def)
- "In KEYMAP, add DEF to each sub-keymap."
- (cl-labels
- ((recur (map)
- (if (atom map)
- map
- (if (eq (car map) 'keymap)
- (cons 'keymap
- (cons
- def
- (recur (cdr map))))
- (cons
- (recur (car map))
- (recur (cdr map)))))))
- (recur keymap)))
+ ,@(if (hydra--head-property head :exit)
+ `((hydra-keyboard-quit)
+ ,@(if body-after-exit
+ `((unwind-protect
+ ,(when cmd
+ (hydra--call-interactively cmd (cadr head)))
+ ,body-after-exit))
+ (when cmd
+ `(,(hydra--call-interactively cmd (cadr head))))))
+ (delq
+ nil
+ `((let ((hydra--ignore ,(not (eq (cadr head) 'body))))
+ (hydra-keyboard-quit))
+ ,(when cmd
+ `(condition-case err
+ ,(hydra--call-interactively cmd (cadr head))
+ ((quit error)
+ (message "%S" err)
+ (unless hydra-lv
+ (sit-for 0.8)))))
+ ,(if (and body-idle (eq (cadr head) 'body))
+ `(hydra-idle-message ,body-idle ,hint)
+ `(when hydra-is-helpful
+ (if hydra-lv
+ (lv-message (eval ,hint))
+ (message (eval ,hint)))))
+ (hydra-set-transient-map
+ ,keymap
+ (lambda () (hydra-keyboard-quit) ,body-before-exit)
+ ,(when body-foreign-keys
+ (list 'quote body-foreign-keys)))
+ ,body-after-exit
+ ,(when body-timeout
+ `(hydra-timeout ,body-timeout))))))))
(defmacro hydra--make-funcall (sym)
- "Transform SYM into a `funcall' that calls it."
+ "Transform SYM into a `funcall' to call it."
`(when (and ,sym (symbolp ,sym))
(setq ,sym `(funcall #',,sym))))
-(defun hydra--handle-nonhead (keymap name body heads)
- "Setup KEYMAP for intercepting non-head bindings.
-NAME, BODY and HEADS are parameters to `defhydra'."
- (let ((body-color (hydra--body-color body))
- (body-post (plist-get (cddr body) :post)))
- (if body-post
- (hydra--make-funcall body-post)
- (when hydra-keyboard-quit
- (define-key keymap hydra-keyboard-quit #'hydra-keyboard-quit)))
- (when (memq body-color '(amaranth pink teal))
- (if (cl-some (lambda (h)
- (memq (hydra--head-color h body) '(blue teal)))
- heads)
- (progn
- (setcdr
- keymap
- (cdr
- (hydra--modify-keymap
- keymap
- (cons t
- `(lambda ()
- (interactive)
- ,(cond
- ((memq body-color '(amaranth teal))
- '(message "An amaranth Hydra can only exit through
a blue head"))
- (t
- '(hydra-pink-fallback)))
- (hydra-set-transient-map hydra-curr-map t)
- (when hydra-is-helpful
- (unless hydra-lv
- (sit-for 0.8))
- (,(intern (format "%S/hint" name))))))))))
- (unless (eq body-color 'teal)
- (error
- "An %S Hydra must have at least one blue head in order to exit"
- body-color))))))
-
(defun hydra--head-name (h name body)
"Return the symbol for head H of hydra with NAME and BODY."
(let ((str (format "%S/%s" name
(if (symbolp (cadr h))
(cadr h)
(concat "lambda-" (car h))))))
- (when (and (memq (hydra--head-color h body) '(blue teal))
+ (when (and (hydra--head-property h :exit)
(not (memq (cadr h) '(body nil))))
(setq str (concat str "-and-exit")))
(intern str)))
@@ -717,15 +618,15 @@ NAME, BODY and HEADS are parameters to `defhydra'."
(defun hydra--delete-duplicates (heads)
"Return HEADS without entries that have the same CMD part.
In duplicate HEADS, :cmd-name is modified to whatever they duplicate."
- (let ((ali '(((hydra-repeat . red) . hydra-repeat)))
+ (let ((ali '(((hydra-repeat . nil) . hydra-repeat)))
res entry)
(dolist (h heads)
(if (setq entry (assoc (cons (cadr h)
- (hydra--head-color h '(nil nil)))
+ (hydra--head-property h :exit))
ali))
(setf (cl-cdddr h) (plist-put (cl-cdddr h) :cmd-name (cdr entry)))
(push (cons (cons (cadr h)
- (hydra--head-color h '(nil nil)))
+ (hydra--head-property h :exit))
(plist-get (cl-cdddr h) :cmd-name))
ali)
(push h res)))
@@ -824,20 +725,36 @@ NAMES should be defined by `defhydradio' or similar."
(dolist (n names)
(set n (aref (get n 'range) 0))))
+(defun hydra-idle-message (secs hint)
+ "In SECS seconds display HINT."
+ (cancel-timer hydra-message-timer)
+ (setq hydra-message-timer (timer-create))
+ (timer-set-time hydra-message-timer
+ (timer-relative-time (current-time) secs))
+ (timer-set-function
+ hydra-message-timer
+ (lambda ()
+ (when hydra-is-helpful
+ (if hydra-lv
+ (lv-message (eval hint))
+ (message (eval hint))))
+ (cancel-timer hydra-message-timer)))
+ (timer-activate hydra-message-timer))
+
(defun hydra-timeout (secs &optional function)
"In SECS seconds call FUNCTION, then function `hydra-keyboard-quit'.
Cancel the previous `hydra-timeout'."
- (cancel-timer hydra-timer)
- (setq hydra-timer (timer-create))
- (timer-set-time hydra-timer
+ (cancel-timer hydra-timeout-timer)
+ (setq hydra-timeout-timer (timer-create))
+ (timer-set-time hydra-timeout-timer
(timer-relative-time (current-time) secs))
(timer-set-function
- hydra-timer
+ hydra-timeout-timer
`(lambda ()
,(when function
`(funcall ,function))
(hydra-keyboard-quit)))
- (timer-activate hydra-timer))
+ (timer-activate hydra-timeout-timer))
;;* Macros
;;;###autoload
@@ -864,7 +781,7 @@ 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 (both are strings passed to `kbd'), and will set
the transient map so that all following heads can be called
-though KEY only. BODY-KEY can be an empty string.
+though KEY only. BODY-KEY can be an empty string.
CMD is a callable expression: either an interactive function
name, or an interactive lambda, or a single sexp (it will be
@@ -900,94 +817,144 @@ result of `defhydra'."
(setq docstring "hydra")))
(when (keywordp (car body))
(setq body (cons nil (cons nil body))))
- (let* ((keymap (copy-keymap hydra-base-map))
- (body-name (intern (format "%S/body" name)))
- (body-key (cadr body))
- (body-plist (cddr body))
- (body-map (or (car body)
- (plist-get body-plist :bind)))
- (body-pre (plist-get body-plist :pre))
- (body-body-pre (plist-get body-plist :body-pre))
- (body-post (plist-get body-plist :post)))
- (hydra--make-funcall body-post)
- (when body-post
- (setq heads (cons (list hydra-keyboard-quit #'hydra-keyboard-quit nil
:exit t)
- heads)))
- (dolist (h heads)
- (let ((len (length h)))
- (cond ((< len 2)
- (error "Each head should have at least two items: %S" h))
- ((= len 2)
- (setcdr (cdr h)
- (list
- (hydra-plist-get-default body-plist :hint "")))
- (setcdr (nthcdr 2 h)
- (list :cmd-name (hydra--head-name h name body))))
- (t
- (let ((hint (cl-caddr h)))
- (unless (or (null hint)
- (stringp hint))
- (setcdr (cdr h) (cons
- (hydra-plist-get-default body-plist :hint
"")
- (cddr h))))
- (setcdr (cddr h)
- `(:cmd-name
- ,(hydra--head-name h name body)
- ,@(cl-cdddr h))))))))
- (let ((doc (hydra--doc body-key body-name heads))
- (heads-nodup (hydra--delete-duplicates heads)))
- (mapc
- (lambda (x)
- (define-key keymap (kbd (car x))
- (plist-get (cl-cdddr x) :cmd-name)))
- heads)
- (hydra--make-funcall body-pre)
- (hydra--make-funcall body-body-pre)
- (hydra--handle-nonhead keymap name body heads)
- `(progn
- ;; create defuns
- ,@(mapcar
- (lambda (head)
- (hydra--make-defun name body doc head keymap
- body-pre body-post))
- heads-nodup)
- ;; free up keymap prefix
- ,@(unless (or (null body-key)
- (null body-map)
- (hydra--callablep body-map))
- `((unless (keymapp (lookup-key ,body-map (kbd ,body-key)))
- (define-key ,body-map (kbd ,body-key) nil))))
- ;; bind keys
- ,@(delq nil
- (mapcar
- (lambda (head)
- (let ((name (hydra--head-property head :cmd-name)))
- (when (and (cadr head)
- (not (eq (cadr head) 'hydra-keyboard-quit))
- (or body-key body-map))
- (let ((bind (hydra--head-property head :bind body-map))
- (final-key
- (if body-key
- (vconcat (kbd body-key) (kbd (car head)))
- (kbd (car head)))))
- (cond ((null bind) nil)
- ((hydra--callablep bind)
- `(funcall ,bind ,final-key (function ,name)))
- ((and (symbolp bind)
- (if (boundp bind)
- (keymapp (symbol-value bind))
- t))
- `(define-key ,bind ,final-key (function
,name)))
- (t
- (error "Invalid :bind property `%S' for head
%S" bind head)))))))
- heads))
- (defun ,(intern (format "%S/hint" name)) ()
- ,(hydra--message name body docstring heads))
- ,(hydra--make-defun
- name body doc '(nil body)
- keymap
- (or body-body-pre body-pre) body-post
- '(setq prefix-arg current-prefix-arg))))))
+ (condition-case err
+ (let* ((keymap (copy-keymap hydra-base-map))
+ (keymap-name (intern (format "%S/keymap" name)))
+ (body-name (intern (format "%S/body" name)))
+ (body-key (cadr body))
+ (body-plist (cddr body))
+ (body-map (or (car body)
+ (plist-get body-plist :bind)))
+ (body-pre (plist-get body-plist :pre))
+ (body-body-pre (plist-get body-plist :body-pre))
+ (body-before-exit (or (plist-get body-plist :post)
+ (plist-get body-plist :before-exit)))
+ (body-after-exit (plist-get body-plist :after-exit))
+ (body-inherit (plist-get body-plist :inherit))
+ (body-foreign-keys (hydra--body-foreign-keys body))
+ (body-exit (hydra--body-exit body)))
+ (dolist (base body-inherit)
+ (setq heads (append heads (copy-sequence (eval base)))))
+ (dolist (h heads)
+ (let ((len (length h)))
+ (cond ((< len 2)
+ (error "Each head should have at least two items: %S" h))
+ ((= len 2)
+ (setcdr (cdr h)
+ (list
+ (hydra-plist-get-default body-plist :hint "")))
+ (setcdr (nthcdr 2 h) (list :exit body-exit)))
+ (t
+ (let ((hint (cl-caddr h)))
+ (unless (or (null hint)
+ (stringp hint))
+ (setcdr (cdr h) (cons
+ (hydra-plist-get-default body-plist
:hint "")
+ (cddr h)))))
+ (let ((hint-and-plist (cddr h)))
+ (if (null (cdr hint-and-plist))
+ (setcdr hint-and-plist (list :exit body-exit))
+ (let* ((plist (cl-cdddr h))
+ (h-color (plist-get plist :color)))
+ (if h-color
+ (progn
+ (plist-put plist :exit
+ (cl-case h-color
+ ((blue teal) t)
+ (t nil)))
+ (cl-remf (cl-cdddr h) :color))
+ (let ((h-exit (hydra-plist-get-default plist :exit
'default)))
+ (plist-put plist :exit
+ (if (eq h-exit 'default)
+ body-exit
+ h-exit))))))))))
+ (plist-put (cl-cdddr h) :cmd-name (hydra--head-name h name body))
+ (when (null (cadr h)) (plist-put (cl-cdddr h) :exit t)))
+ (let ((doc (hydra--doc body-key body-name heads))
+ (heads-nodup (hydra--delete-duplicates heads)))
+ (mapc
+ (lambda (x)
+ (define-key keymap (kbd (car x))
+ (plist-get (cl-cdddr x) :cmd-name)))
+ heads)
+ (hydra--make-funcall body-pre)
+ (hydra--make-funcall body-body-pre)
+ (hydra--make-funcall body-before-exit)
+ (hydra--make-funcall body-after-exit)
+ (when (memq body-foreign-keys '(run warn))
+ (unless (cl-some
+ (lambda (h)
+ (hydra--head-property h :exit))
+ heads)
+ (error
+ "An %S Hydra must have at least one blue head in order to exit"
+ body-foreign-keys)))
+ `(progn
+ ;; create keymap
+ (set (defvar ,keymap-name
+ nil
+ ,(format "Keymap for %S." name))
+ ',keymap)
+ ;; declare heads
+ (set (defvar ,(intern (format "%S/heads" name))
+ nil
+ ,(format "Heads for %S." name))
+ ',(mapcar (lambda (h)
+ (let ((j (copy-sequence h)))
+ (cl-remf (cl-cdddr j) :cmd-name)
+ j))
+ heads))
+ ;; create defuns
+ ,@(mapcar
+ (lambda (head)
+ (hydra--make-defun name body doc head keymap-name
+ body-pre
+ body-before-exit
+ body-after-exit))
+ heads-nodup)
+ ;; free up keymap prefix
+ ,@(unless (or (null body-key)
+ (null body-map)
+ (hydra--callablep body-map))
+ `((unless (keymapp (lookup-key ,body-map (kbd
,body-key)))
+ (define-key ,body-map (kbd ,body-key) nil))))
+ ;; bind keys
+ ,@(delq nil
+ (mapcar
+ (lambda (head)
+ (let ((name (hydra--head-property head :cmd-name)))
+ (when (and (cadr head)
+ (or body-key body-map))
+ (let ((bind (hydra--head-property head :bind
body-map))
+ (final-key
+ (if body-key
+ (vconcat (kbd body-key) (kbd (car
head)))
+ (kbd (car head)))))
+ (cond ((null bind) nil)
+ ((hydra--callablep bind)
+ `(funcall ,bind ,final-key (function
,name)))
+ ((and (symbolp bind)
+ (if (boundp bind)
+ (keymapp (symbol-value bind))
+ t))
+ `(define-key ,bind ,final-key (function
,name)))
+ (t
+ (error "Invalid :bind property `%S' for
head %S" bind head)))))))
+ heads))
+ (set
+ (defvar ,(intern (format "%S/hint" name)) nil
+ ,(format "Dynamic hint for %S." name))
+ ',(hydra--format name body docstring heads))
+ ,(hydra--make-defun
+ name body doc '(nil body)
+ keymap-name
+ (or body-body-pre body-pre) body-before-exit
+ '(setq prefix-arg current-prefix-arg)))))
+ (error
+ (if debug-on-error
+ (signal (car err) (cdr err))
+ (message "Error in defhydra %S: %s" name (cdr err)))
+ nil)))
(defmacro defhydradio (name _body &rest heads)
"Create radios with prefix NAME.
- [elpa] master 3d7d8c7 20/45: Add basic error handling, (continued)
- [elpa] master 3d7d8c7 20/45: Add basic error handling, Oleh Krehel, 2015/04/16
- [elpa] master 684f8a2 39/45: Add integration test for red hydras temporarily exiting, Oleh Krehel, 2015/04/16
- [elpa] master d3d435d 25/45: Finalize head inheritance, Oleh Krehel, 2015/04/16
- [elpa] master b2c9ea6 36/45: README.md: Update intro, Oleh Krehel, 2015/04/16
- [elpa] master d678cc0 34/45: Work around `overriding-terminal-local-map' being terminal-local, Oleh Krehel, 2015/04/16
- [elpa] master d6e00ed 33/45: README.md: Add video demo link, Oleh Krehel, 2015/04/16
- [elpa] master 97c9b9b 32/45: Move `this-command' setter, Oleh Krehel, 2015/04/16
- [elpa] master 60ce256 41/45: Add an idle message timeout option, Oleh Krehel, 2015/04/16
- [elpa] master 99b2aea 35/45: hydra.el (hydra--format): Match alnum for the "_..._" syntax, Oleh Krehel, 2015/04/16
- [elpa] master f01c87e 38/45: Fix red heads not exiting temporarily, Oleh Krehel, 2015/04/16
- [elpa] master f972634 45/45: Merge commit '742d66a63e86ac740e610faa5abba97e7f8ad5c2' from hydra,
Oleh Krehel <=
- [elpa] master aeaabd3 42/45: hydra.el (defhydra): Re-throw a caught error when debug-on-error, Oleh Krehel, 2015/04/16
- [elpa] master 742d66a 44/45: hydra.el: Bump version, Oleh Krehel, 2015/04/16
- [elpa] master 09b63b5 43/45: Don't re-activate key chords too early, Oleh Krehel, 2015/04/16
- [elpa] master cab5a73 37/45: README.md: Update `hydra-zoom/body', Oleh Krehel, 2015/04/16
- [elpa] master 00aef59 40/45: hydra.el (hydra--format): Fix "s-t" issue, Oleh Krehel, 2015/04/16