1 ;;; hydra.el --- Make bindings that stick around
3 ;; Copyright (C) 2015 Free Software Foundation, Inc.
5 ;; Author: Oleh Krehel <ohwoeowho@gmail.com>
6 ;; Maintainer: Oleh Krehel <ohwoeowho@gmail.com>
7 ;; URL: https://github.com/abo-abo/hydra
10 ;; Package-Requires: ((cl-lib "0.5"))
12 ;; This file is part of GNU Emacs.
14 ;; GNU Emacs is free software: you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation, either version 3 of the License, or
17 ;; (at your option) any later version.
19 ;; GNU Emacs is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
29 ;; This package can be used to tie related commands into a family of
30 ;; short bindings with a common prefix - a Hydra.
32 ;; Once you summon the Hydra (through the prefixed binding), all the
33 ;; heads can be called in succession with only a short extension.
34 ;; The Hydra is vanquished once Hercules, any binding that isn't the
35 ;; Hydra's head, arrives. Note that Hercules, besides vanquishing the
36 ;; Hydra, will still serve his orignal purpose, calling his proper
37 ;; command. This makes the Hydra very seamless, it's like a minor
38 ;; mode that disables itself automagically.
40 ;; Here's an example Hydra, bound in the global map (you can use any
41 ;; keymap in place of `global-map'):
43 ;; (defhydra hydra-zoom (global-map "<f2>")
45 ;; ("g" text-scale-increase "in")
46 ;; ("l" text-scale-decrease "out"))
48 ;; It allows to start a command chain either like this:
49 ;; "<f2> gg4ll5g", or "<f2> lgllg".
51 ;; Here's another approach, when you just want a "callable keymap":
53 ;; (defhydra hydra-toggle (:color blue)
55 ;; ("a" abbrev-mode "abbrev")
56 ;; ("d" toggle-debug-on-error "debug")
57 ;; ("f" auto-fill-mode "fill")
58 ;; ("t" toggle-truncate-lines "truncate")
59 ;; ("w" whitespace-mode "whitespace")
60 ;; ("q" nil "cancel"))
62 ;; This binds nothing so far, but if you follow up with:
64 ;; (global-set-key (kbd "C-c C-v") 'hydra-toggle/body)
66 ;; you will have bound "C-c C-v a", "C-c C-v d" etc.
68 ;; Knowing that `defhydra' defines e.g. `hydra-toggle/body' command,
69 ;; you can nest Hydras if you wish, with `hydra-toggle/body' possibly
70 ;; becoming a blue head of another Hydra.
72 ;; Initially, Hydra shipped with a simplified `hydra-create' macro, to
73 ;; which you could hook up the examples from hydra-examples.el. It's
74 ;; better to take the examples simply as templates and use `defhydra'
75 ;; instead of `hydra-create', since it's more flexible.
82 (defalias 'hydra-set-transient-map
83 (if (fboundp 'set-transient-map)
85 (lambda (map keep-pred &optional on-exit)
86 (set-temporary-overlay-map map (hydra--pred on-exit)))))
88 (defun hydra--pred (on-exit)
89 "Generate a predicate on whether to continue the Hydra state.
90 Call ON-EXIT for clean-up.
91 This is a compatibility code for Emacs older than 24.4."
93 (if (lookup-key hydra-curr-map (this-command-keys-vector))
97 `(funcall ,(hydra--make-callable on-exit)))
102 "Make bindings that stick around."
106 (defcustom hydra-is-helpful t
107 "When t, display a hint with possible bindings in the echo area."
111 (defcustom hydra-keyboard-quit "
\a"
112 "This binding will quit an amaranth Hydra.
113 It's the only other way to quit it besides though a blue head.
114 It's possible to set this to nil.")
116 (defcustom hydra-lv t
117 "When non-nil, `lv-message' (not `message') will be used to display hints."
120 (defface hydra-face-red
121 '((t (:foreground "#FF0000" :bold t)))
122 "Red Hydra heads will persist indefinitely."
125 (defface hydra-face-blue
126 '((t (:foreground "#0000FF" :bold t)))
127 "Blue Hydra heads will vanquish the Hydra.")
129 (defface hydra-face-amaranth
130 '((t (:foreground "#E52B50" :bold t)))
131 "Amaranth body has red heads and warns on intercepting non-heads.
132 Vanquishable only through a blue head.")
134 (defface hydra-face-pink
135 '((t (:foreground "#FF6EB4" :bold t)))
136 "Pink body has red heads and on intercepting non-heads calls them without quitting.
137 Vanquishable only through a blue head.")
139 (defface hydra-face-teal
140 '((t (:foreground "#367588" :bold t)))
141 "Teal body has blue heads an warns on intercepting non-heads.
142 Vanquishable only through a blue head.")
145 (defun hydra-add-font-lock ()
146 "Fontify `defhydra' statements."
147 (font-lock-add-keywords
149 '(("(\\(defhydra\\)\\_> +\\(.*?\\)\\_>"
150 (1 font-lock-keyword-face)
151 (2 font-lock-type-face))
152 ("(\\(defhydradio\\)\\_> +\\(.*?\\)\\_>"
153 (1 font-lock-keyword-face)
154 (2 font-lock-type-face)))))
156 ;;* Universal Argument
157 (defvar hydra-base-map
158 (let ((map (make-sparse-keymap)))
159 (define-key map [?\C-u] 'hydra--universal-argument)
160 (define-key map [?-] 'hydra--negative-argument)
161 (define-key map [?0] 'hydra--digit-argument)
162 (define-key map [?1] 'hydra--digit-argument)
163 (define-key map [?2] 'hydra--digit-argument)
164 (define-key map [?3] 'hydra--digit-argument)
165 (define-key map [?4] 'hydra--digit-argument)
166 (define-key map [?5] 'hydra--digit-argument)
167 (define-key map [?6] 'hydra--digit-argument)
168 (define-key map [?7] 'hydra--digit-argument)
169 (define-key map [?8] 'hydra--digit-argument)
170 (define-key map [?9] 'hydra--digit-argument)
171 (define-key map [kp-0] 'hydra--digit-argument)
172 (define-key map [kp-1] 'hydra--digit-argument)
173 (define-key map [kp-2] 'hydra--digit-argument)
174 (define-key map [kp-3] 'hydra--digit-argument)
175 (define-key map [kp-4] 'hydra--digit-argument)
176 (define-key map [kp-5] 'hydra--digit-argument)
177 (define-key map [kp-6] 'hydra--digit-argument)
178 (define-key map [kp-7] 'hydra--digit-argument)
179 (define-key map [kp-8] 'hydra--digit-argument)
180 (define-key map [kp-9] 'hydra--digit-argument)
181 (define-key map [kp-subtract] 'hydra--negative-argument)
183 "Keymap that all Hydras inherit. See `universal-argument-map'.")
185 (defvar hydra-curr-map
187 "Keymap of the current Hydra called.")
189 (defun hydra--universal-argument (arg)
190 "Forward to (`universal-argument' ARG)."
192 (setq prefix-arg (if (consp arg)
193 (list (* 4 (car arg)))
197 (hydra-set-transient-map hydra-curr-map t))
199 (defun hydra--digit-argument (arg)
200 "Forward to (`digit-argument' ARG)."
202 (let ((universal-argument-map hydra-curr-map))
203 (digit-argument arg)))
205 (defun hydra--negative-argument (arg)
206 "Forward to (`negative-argument' ARG)."
208 (let ((universal-argument-map hydra-curr-map))
209 (negative-argument arg)))
212 (defvar hydra-last nil
213 "The result of the last `hydra-set-transient-map' call.")
215 (defun hydra--callablep (x)
216 "Test if X is callable."
219 (memq (car x) '(function quote)))))
221 (defun hydra--make-callable (x)
222 "Generate a callable symbol from X.
223 If X is a function symbol or a lambda, return it. Otherwise, it
224 should be a single statement. Wrap it in an interactive lambda."
225 (if (or (symbolp x) (functionp x))
231 (defun hydra--head-property (h prop &optional default)
232 "Return for Hydra head H the value of property PROP.
233 Return DEFAULT if PROP is not in H."
234 (let ((plist (if (or (stringp (cl-caddr h))
239 (plist-get plist prop)
242 (defun hydra--head-color (h body)
243 "Return the color of a Hydra head H with BODY."
244 (let ((color (hydra--head-property h :color))
245 (exit (or (plist-get (cddr body) :exit)
246 (hydra--head-property h :exit 'default)))
247 (nonheads (plist-get (cddr body) :nonheads)))
248 (cond ((null (cadr h))
259 (hydra--body-color body))
263 (defun hydra--body-color (body)
264 "Return the color of BODY.
265 BODY is the second argument to `defhydra'"
266 (let ((color (plist-get (cddr body) :color))
267 (exit (plist-get (cddr body) :exit))
268 (nonheads (plist-get (cddr body) :nonheads)))
269 (cond ((eq nonheads 'warn)
270 (if exit 'teal 'amaranth))
271 ((eq nonheads 'run) 'pink)
276 (defun hydra--face (h body)
277 "Return the face for a Hydra head H with BODY."
278 (cl-case (hydra--head-color h body)
279 (blue 'hydra-face-blue)
280 (red 'hydra-face-red)
281 (amaranth 'hydra-face-amaranth)
282 (pink 'hydra-face-pink)
283 (teal 'hydra-face-teal)
284 (t (error "Unknown color for %S" h))))
286 (defun hydra-cleanup ()
287 "Clean up after a Hydra."
288 (when (window-live-p lv-wnd)
289 (let ((buf (window-buffer lv-wnd)))
290 (delete-window lv-wnd)
293 (defun hydra-disable ()
294 "Disable the current Hydra."
297 ((functionp hydra-last)
298 (funcall hydra-last))
300 ;; Emacs 24.3 or older
301 ((< emacs-minor-version 4)
302 (setq emulation-mode-map-alists
307 (equal (cdar x) hydra-curr-map)))
308 emulation-mode-map-alists)))
312 (setq overriding-terminal-local-map nil))))
314 (defun hydra--unalias-var (str prefix)
315 "Return the symbol named STR if it's bound as a variable.
316 Otherwise, add PREFIX to the symbol name."
317 (let ((sym (intern-soft str)))
320 (intern (concat prefix "/" str)))))
322 (defun hydra--hint (name body docstring heads)
323 "Generate a hint for the echo area.
324 NAME, BODY, DOCSTRING and HEADS are parameters to `defhydra'."
327 (let ((val (assoc (cadr h) alist))
328 (pstr (hydra-fontify-head h body)))
329 (unless (and (> (length h) 2)
333 (concat (cadr val) " " pstr))
337 (and (stringp (cl-caddr h)) (cl-caddr h))))
343 (concat "[%s]: " (cdr x))
346 (nreverse (mapcar #'cdr alist))
349 (defvar hydra-fontify-head-function nil
350 "Possible replacement for `hydra-fontify-head-default'.")
352 (defun hydra-fontify-head-default (head body)
353 "Produce a pretty string from HEAD and BODY.
354 HEAD's binding is returned as a string with a colored face."
355 (propertize (car head) 'face (hydra--face head body)))
357 (defun hydra-fontify-head-greyscale (head body)
358 "Produce a pretty string from HEAD and BODY.
359 HEAD's binding is returned as a string wrapped with [] or {}."
360 (let ((color (hydra--head-color head body)))
364 "{%s}") (car head))))
366 (defun hydra-fontify-head (head body)
367 "Produce a pretty string from HEAD and BODY."
368 (funcall (or hydra-fontify-head-function 'hydra-fontify-head-default)
371 (defun hydra--format (name body docstring heads)
372 "Generate a `format' statement from STR.
373 \"%`...\" expressions are extracted into \"%S\".
374 NAME, BODY, DOCSTRING and HEADS are parameters of `defhydra'.
375 The expressions can be auto-expanded according to NAME."
376 (setq docstring (replace-regexp-in-string "\\^" "" docstring))
377 (let ((rest (hydra--hint name body docstring heads))
378 (body-color (hydra--body-color body))
379 (prefix (symbol-name name))
384 "\\(?:%\\( ?-?[0-9]*\\)`\\([a-z-A-Z/0-9]+\\)\\)\\|\\(?:_\\([a-z-~A-Z]+\\)_\\)"
386 (if (eq ?_ (aref (match-string 0 docstring) 0))
387 (let* ((key (match-string 3 docstring))
388 (head (assoc key heads)))
391 (push (hydra-fontify-head head body) varlist)
392 (setq docstring (replace-match "% 3s" nil nil docstring)))
393 (error "Unrecognized key: _%s_" key)))
394 (push (hydra--unalias-var (match-string 2 docstring) prefix) varlist)
395 (setq docstring (replace-match (concat "%" (match-string 1 docstring) "S") nil nil docstring 0))))
396 (if (eq ?\n (aref docstring 0))
397 `(concat (format ,docstring ,@(nreverse varlist))
399 `(format ,(concat docstring ": " rest ".")))))
401 (defun hydra--message (name body docstring heads)
402 "Generate code to display the hint in the preferred echo area.
403 Set `hydra-lv' to choose the echo area.
404 NAME, BODY, DOCSTRING, and HEADS are parameters of `defhydra'."
405 (let ((format-expr (hydra--format name body docstring heads)))
407 (lv-message ,format-expr)
408 (message ,format-expr))))
410 (defun hydra--doc (body-key body-name heads)
411 "Generate a part of Hydra docstring.
412 BODY-KEY is the body key binding.
413 BODY-NAME is the symbol that identifies the Hydra.
414 HEADS is a list of heads."
416 "Create a hydra with %s body and the heads:\n\n%s\n\n%s"
418 (format "a \"%s\"" body-key)
422 (format "\"%s\": `%S'" (car x) (cadr x)))
424 (format "The body can be accessed via `%S'." body-name)))
426 (defun hydra--make-defun (name cmd color
428 body-color body-pre body-post &optional other-post)
429 "Make a defun wrapper, using NAME, CMD, COLOR, DOC, HINT, and KEYMAP.
430 BODY-COLOR, BODY-PRE, BODY-POST, and OTHER-POST are used as well."
434 ,@(when body-pre (list body-pre))
436 ,@(when (memq color '(blue teal)) '((hydra-cleanup)))
437 (catch 'hydra-disable
439 (if (memq color '(blue teal))
440 `(,(when cmd `(call-interactively #',cmd))
445 (call-interactively #',cmd))
451 (when hydra-is-helpful
454 (hydra-set-transient-map
455 (setq hydra-curr-map ',keymap)
457 ,(if (and (not (memq body-color '(amaranth pink teal))) body-post)
458 `(lambda () (hydra-cleanup) ,body-post)
459 `(lambda () (hydra-cleanup)))))
462 (defun hydra-pink-fallback ()
463 "On intercepting a non-head, try to run it."
464 (let ((keys (this-command-keys))
466 (when (equal keys [backspace])
468 (setq kb (key-binding keys))
472 (call-interactively kb)
477 (message "Pink Hydra can't currently handle prefixes, continuing"))
478 (message "Pink Hydra could not resolve: %S" keys))))
480 (defun hydra--handle-nonhead (keymap name body heads)
481 "Setup KEYMAP for intercepting non-head bindings.
482 NAME, BODY and HEADS are parameters to `defhydra'."
483 (let ((body-color (hydra--body-color body))
484 (body-post (plist-get (cddr body) :post)))
485 (when (and body-post (symbolp body-post))
486 (setq body-post `(funcall #',body-post)))
487 (when (memq body-color '(amaranth pink teal))
488 (if (cl-some `(lambda (h)
489 (eq (hydra--head-color h body) 'blue))
492 (define-key keymap [t]
496 ((eq body-color 'amaranth)
497 '(message "An amaranth Hydra can only exit through a blue head"))
498 ((eq body-color 'teal)
499 '(message "A teal Hydra can only exit through a blue head"))
501 '(hydra-pink-fallback)))
502 (hydra-set-transient-map hydra-curr-map t)
503 (when hydra-is-helpful
506 (,(intern (format "%S/hint" name)))))))
508 "An %S Hydra must have at least one blue head in order to exit"
510 (when hydra-keyboard-quit
511 (define-key keymap hydra-keyboard-quit
521 (defmacro defhydra (name body &optional docstring &rest heads)
522 "Create a Hydra - a family of functions with prefix NAME.
524 NAME should be a symbol, it will be the prefix of all functions
529 (BODY-MAP BODY-KEY &rest PLIST)
531 DOCSTRING will be displayed in the echo area to identify the
534 Functions are created on basis of HEADS, each of which has the
537 (KEY CMD &optional HINT &rest PLIST)
539 BODY-MAP is a keymap; `global-map' is used quite often. Each
540 function generated from HEADS will be bound in BODY-MAP to
541 BODY-KEY + KEY (both are strings passed to `kbd'), and will set
542 the transient map so that all following heads can be called
545 CMD is a callable expression: either an interactive function
546 name, or an interactive lambda, or a single sexp (it will be
547 wrapped in an interactive lambda).
549 HINT is a short string that identifies its head. It will be
550 printed beside KEY in the echo erea if `hydra-is-helpful' is not
551 nil. If you don't even want the KEY to be printed, set HINT
554 The heads inherit their PLIST from the body and are allowed to
555 override each key. The keys recognized are :color and :bind.
558 - red (default): this head will continue the Hydra state.
559 - blue: this head will stop the Hydra state.
560 - amaranth (applies to body only): similar to red, but no binding
561 except a blue head can stop the Hydra state.
564 - nil: this head will not be bound in BODY-MAP.
565 - a lambda taking KEY and CMD used to bind a head
567 It is possible to omit both BODY-MAP and BODY-KEY if you don't
568 want to bind anything. In that case, typically you will bind the
569 generated NAME/body command. This command is also the return
570 result of `defhydra'."
571 (declare (indent defun))
572 (unless (stringp docstring)
573 (setq heads (cons docstring heads))
574 (setq docstring "hydra"))
575 (when (keywordp (car body))
576 (setq body (cons nil (cons nil body))))
577 (let* ((keymap (copy-keymap hydra-base-map))
580 (define-key keymap (kbd (car x))
581 (intern (format "%S/%s" name
582 (if (symbolp (cadr x))
584 (concat "lambda-" (car x)))))))
586 (body-name (intern (format "%S/body" name)))
587 (hint-name (intern (format "%S/hint" name)))
588 (body-key (unless (hydra--callablep body)
590 (body-color (hydra--body-color body))
591 (body-pre (plist-get (cddr body) :pre))
592 (body-post (plist-get (cddr body) :post))
593 (method (or (plist-get body :bind)
595 (doc (hydra--doc body-key body-name heads)))
596 (when (and body-pre (symbolp body-pre))
597 (setq body-pre `(funcall #',body-pre)))
598 (when (and body-post (symbolp body-post))
599 (setq body-post `(funcall #',body-post)))
600 (hydra--handle-nonhead keymap name body heads)
605 name (hydra--make-callable
606 (cadr head)) (hydra--head-color head body)
607 (format "%s\n\nCall the head: `%S'." doc (cadr head))
609 body-color body-pre body-post))
611 ,@(unless (or (null body-key)
613 (hydra--callablep method))
614 `((unless (keymapp (lookup-key ,method (kbd ,body-key)))
615 (define-key ,method (kbd ,body-key) nil))))
619 (when (or body-key method)
620 (let ((bind (hydra--head-property head :bind 'default))
623 (vconcat (kbd body-key) (kbd (car head)))
625 (cond ((null bind) nil)
629 (if (hydra--callablep method)
634 (list 'function name)))
636 ((hydra--callablep bind)
637 `(funcall (function ,bind)
642 (error "Invalid :bind property %S" head))))))
645 ,(hydra--message name body docstring heads))
646 ,(hydra--make-defun body-name nil nil doc hint-name keymap
647 body-color body-pre body-post
648 '(setq prefix-arg current-prefix-arg)))))
650 (defmacro defhydradio (name body &rest heads)
651 "Create radios with prefix NAME.
652 BODY specifies the options; there are none currently.
653 HEADS have the format:
655 (TOGGLE-NAME &optional VALUE DOC)
657 TOGGLE-NAME will be used along with NAME to generate a variable
658 name and a function that cycles it with the same name. VALUE
659 should be an array. The first element of VALUE will be used to
660 inialize the variable.
661 VALUE defaults to [nil t].
662 DOC defaults to TOGGLE-NAME split and capitalized."
663 (declare (indent defun))
667 (hydra--radio name h))
669 (defun ,(intern (format "%S/reset-radios" name)) ()
672 (let ((full-name (intern (format "%S/%S" name (car h))))
674 `(setq ,full-name ,(hydra--quote-maybe
675 (and (cadr h) (aref (cadr h) 0))))))
678 (defun hydra--radio (parent head)
679 "Generate a hydradio with PARENT from HEAD."
680 (let* ((name (car head))
681 (full-name (intern (format "%S/%S" parent name)))
682 (val (or (cadr head) [nil t]))
683 (doc (or (cl-caddr head)
684 (mapconcat #'capitalize
685 (split-string (symbol-name name) "-")
687 `((defvar ,full-name ,(hydra--quote-maybe (aref val 0)) ,doc)
688 (put ',full-name 'range ,val)
690 (hydra--cycle-radio ',full-name)))))
692 (defun hydra--quote-maybe (x)
693 "Quote X if it's a symbol."
701 (defun hydra--cycle-radio (sym)
702 "Set SYM to the next value in its range."
703 (let* ((val (symbol-value sym))
704 (range (get sym 'range))
709 (if (equal (aref range i) val)
712 (error "Val not in range for %S" sym)))
722 ;;; outline-regexp: ";;\\*+"
725 ;;; hydra.el ends here