summaryrefslogtreecommitdiff
path: root/README.md
blob: 4c2852c7a6ebb22229de68701ebe1cc7f1a2c7f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
This is a package for GNU Emacs that can be used to tie related commands into a family of short
bindings with a common prefix - a Hydra.

## 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.

The Hydra is vanquished once Hercules, any binding that isn't the Hydra's head, arrives.  Note that
Hercules, besides vanquishing the Hydra, will still serve his original purpose, calling his proper
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**

- [Sample Hydras](#sample-hydras)
    - [The one with the least amount of code](#the-one-with-the-least-amount-of-code)
    - [The impressive-looking one](#the-impressive-looking-one)
- [Community wiki](#community-wiki)
- [The Rules of Hydra-tics](#the-rules-of-hydra-tics)
    - [`hydra-awesome`](#hydra-awesome)
    - [`awesome-map` and `awesome-binding`](#awesome-map-and-awesome-binding)
    - [`awesome-plist`](#awesome-plist)
        - [`:pre` and `:post`](#pre-and-post)
        - [`:exit`](#exit)
        - [`:foreign-keys`](#foreign-keys)
        - [`:color`](#color)
        - [`:timeout`](#timeout)
        - [`:hint`](#hint)
        - [`:bind`](#bind)
    - [`awesome-docstring`](#awesome-docstring)
    - [`awesome-head-1`](#awesome-head-1)
        - [`head-binding`](#head-binding)
        - [`head-command`](#head-command)
        - [`head-hint`](#head-hint)
        - [`head-plist`](#head-plist)

<!-- markdown-toc end -->

# Sample Hydras

## The one with the least amount of code

```cl
(defhydra hydra-zoom (global-map "<f2>")
  "zoom"
  ("g" text-scale-increase "in")
  ("l" text-scale-decrease "out"))
```

With this simple code, you can:

- Start zooming in with <kbd>&lt;f2&gt; g</kbd>.
- Continue to zoom in with <kbd>g</kbd>.
- Or zoom out with <kbd>l</kbd>.
- Zoom in five times at once with <kbd>5g</kbd>.
- Stop zooming with *any* key that isn't <kbd>g</kbd> or <kbd>l</kbd>.

For any Hydra:

- `digit-argument` can be called with <kbd>0</kbd>-<kbd>9</kbd>.
- `negative-argument` can be called with <kbd>-</kbd>.
- `universal-argument` can be called with <kbd>C-u</kbd>.

## The impressive-looking one

The code is large but very simple:

```cl
(defhydra hydra-buffer-menu (:color pink
                             :hint nil)
  "
^Mark^             ^Unmark^           ^Actions^          ^Search
^^^^^^^^-----------------------------------------------------------------
_m_: mark          _u_: unmark        _x_: execute       _R_: re-isearch
_s_: save          _U_: unmark up     _b_: bury          _I_: isearch
_d_: delete        ^ ^                _g_: refresh       _O_: multi-occur
_D_: delete up     ^ ^                _T_: files only: % -28`Buffer-menu-files-only
_~_: modified
"
  ("m" Buffer-menu-mark)
  ("u" Buffer-menu-unmark)
  ("U" Buffer-menu-backup-unmark)
  ("d" Buffer-menu-delete)
  ("D" Buffer-menu-delete-backwards)
  ("s" Buffer-menu-save)
  ("~" Buffer-menu-not-modified)
  ("x" Buffer-menu-execute)
  ("b" Buffer-menu-bury)
  ("g" revert-buffer)
  ("T" Buffer-menu-toggle-files-only)
  ("O" Buffer-menu-multi-occur :color blue)
  ("I" Buffer-menu-isearch-buffers :color blue)
  ("R" Buffer-menu-isearch-buffers-regexp :color blue)
  ("c" nil "cancel")
  ("v" Buffer-menu-select "select" :color blue)
  ("o" Buffer-menu-other-window "other-window" :color blue)
  ("q" quit-window "quit" :color blue))

(define-key Buffer-menu-mode-map "." 'hydra-buffer-menu/body)
```

Looking at the code, you can see `hydra-buffer-menu` as sort of a namespace construct that wraps
each function that it's given in code that shows that hint and makes it easy to call the related
functions. One additional function is created and returned as the result of `defhydra` -
`hydra-buffer-menu/body`.  This function does nothing except setting up the hint and the keymap, and
is usually the entry point to complex hydras.

To write your own hydras, you can:

- Either modify an existing hydra to do what you want to do.
- Or read [the rules](#the-rules-of-hydra-tics),
  [the examples](https://github.com/abo-abo/hydra/blob/master/hydra-examples.el),
  the docstrings and comments in the source.

# Community wiki

You can find some user created hydras and more documentation in the project's
[community wiki](https://github.com/abo-abo/hydra/wiki/). Feel free to add your
own or edit the existing ones.

# The Rules of Hydra-tics

Each hydra (take `awesome` as a prefix to make it more specific) looks like this:

```
(defhydra hydra-awesome (awesome-map awesome-binding awesome-plist)
  awesome-docstring
  awesome-head-1
  awesome-head-2
  awesome-head-3
  ...)
```

## `hydra-awesome`

Each hydra needs a name, and this one is named `hydra-awesome`. You can name your hydras as you wish,
but I prefer to start each one with `hydra-`, because it acts as an additional namespace layer, for example:
`hydra-zoom`, `hydra-helm`, `hydra-apropos` etc.

If you name your hydra `hydra-awesome`, the return result of `defhydra` will be `hydra-awesome/body`.

Here's what `hydra-zoom/body` looks like, if you're interested:

```cl
(defun hydra-zoom/body nil
  "Create a hydra with a \"<f2>\" body and the heads:

\"g\":    `text-scale-increase',
\"l\":    `text-scale-decrease'

The body can be accessed via `hydra-zoom/body'."
  (interactive)
  (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`

This can be any keymap, for instance, `global-map` or `isearch-mode-map`.

For this example:

```cl
(defhydra hydra-zoom (global-map "<f2>")
  "zoom"
  ("g" text-scale-increase "in")
  ("l" text-scale-decrease "out"))
```

- `awesome-map` is `global-map`
- `awesome-binding` is `"<f2>"`

And here's the relevant generated code:

```cl
(unless (keymapp (lookup-key global-map (kbd "<f2>")))
  (define-key global-map (kbd "<f2>") nil))
(define-key global-map [f2 103]
  (function hydra-zoom/text-scale-increase))
(define-key global-map [f2 108]
  (function hydra-zoom/text-scale-decrease))
```

As you see, `"<f2>"` is used as a prefix for <kbd>g</kbd> (char value 103) and <kbd>l</kbd>
(char value 108).

If you don't want to use a map right now, you can skip it like this:

```cl
(defhydra hydra-zoom (nil nil)
  "zoom"
  ("g" text-scale-increase "in")
  ("l" text-scale-decrease "out"))
```

Or even simpler:

```cl
(defhydra hydra-zoom ()
  "zoom"
  ("g" text-scale-increase "in")
  ("l" text-scale-decrease "out"))
```

But then you would have to bind `hydra-zoom/text-scale-increase` and
`hydra-zoom/text-scale-decrease` yourself.

## `awesome-plist`

You can read up on what a plist is in
[the Elisp manual](https://www.gnu.org/software/emacs/manual/html_node/elisp/Property-Lists.html).

You can use `awesome-plist` to modify the behavior of each head in some way.
Below is a list of each key.

### `:pre` and `:post`

You can specify code that will be called before each head, and after the body. For example:

```cl
(defhydra hydra-vi (:pre (set-cursor-color "#40e0d0")
                    :post (progn
                            (set-cursor-color "#ffffff")
                            (message
                             "Thank you, come again.")))
  "vi"
  ("l" forward-char)
  ("h" backward-char)
  ("j" next-line)
  ("k" previous-line)
  ("q" nil "quit"))
```

Thanks to `:pre`, each time any head is called, the cursor color is changed.
And when the hydra quits, the cursor color will be made black again with `:post`.

### `:exit`

The `:exit` key is inherited by every head (they can override it) and influences what will happen
after executing head's command:

- `:exit nil` (the default) means that the hydra state will continue - you'll still see the hint and be able to use short bindings.
- `:exit t` means that the hydra state will stop.

### `:foreign-keys`

The `:foreign-keys` key belongs to the body and decides what to do when a key is pressed that doesn't
belong to any head:

- `:foreign-keys nil` (the default) means that the hydra state will stop and the foreign key will
do whatever it was supposed to do if there was no hydra state.
- `:foreign-keys warn` will not stop the hydra state, but instead will issue a warning without
running the foreign key.
- `:foreign-keys run` will not stop the hydra state, and try to run the foreign key.

### `:color`

The `:color` key is a shortcut. It aggregates `:exit` and `:foreign-keys` key in the following way:

    | color    | toggle                     |
    |----------+----------------------------|
    | red      |                            |
    | blue     | :exit t                    |
    | amaranth | :foreign-keys warn         |
    | teal     | :foreign-keys warn :exit t |
    | pink     | :foreign-keys run          |

It's also a trick to make you instantly aware of the current hydra keys that you're about to press:
the keys will be highlighted with the appropriate color.

### `:timeout`

The `:timeout` key starts a timer for the corresponding amount of seconds that disables the hydra.
Calling any head will refresh the timer.

### `:hint`

The `:hint` key will be inherited by each head. Each head is allowed to override it, of course.
One value that makes sense is `:hint nil`. See below for an explanation of head hint.

### `:bind`

The `:bind` key provides a lambda to be used to bind each head.  This is quite advanced and rarely
used, you're not likely to need it.  But if you would like to bind your heads with e.g. `bind-key`
instead of `define-key` you can use this option.

The `:bind` key can be overridden by each head. This is useful if you want to have a few heads that
are not bound outside the hydra.

## `awesome-docstring`

This can be a simple string used to build the final hydra hint.  However, if you start it with a
newline, the key-highlighting and Ruby-style string interpolation becomes enabled, as you can see in
`hydra-buffer-menu` above.

To highlight a key, just wrap it in underscores. Note that the key must belong to one of the heads.
The key will be highlighted with the color that is appropriate to the behavior of the key, i.e.  if
the key will make the hydra exit, the color will be blue.

To insert an empty character, use `^`. The only use of this is to have your code aligned as
nicely as the result.

To insert a dynamic Elisp variable, use `%`&#96; followed by the variable. Each time the variable
changes due to a head, the docstring will be updated. `format`-style width specifiers can be used.

To insert a dynamic Elisp expression, use e.g. `%(length (dired-get-marked-files))`.  If a head will
change the amount of marked files, for example, it will be appropriately updated.

If the result of the Elisp expression is a string and you don't want to quote it, use this form:
`%s(shell-command-to-string "du -hs")`.

## `awesome-head-1`

Each head looks like this:

```cl
(head-binding head-command head-hint head-plist)
```

For the head `("g" text-scale-increase "in")`:

- `head-binding` is `"g"`.
- `head-command` is `text-scale-increase`.
- `head-hint` is `"in"`.
- `head-plist` is `nil`.

### `head-binding`

The `head-binding` is a string that can be passed to `kbd`.

### `head-command`

The `head-command` can be:

- command name, like `text-scale-increase`.
- a lambda, like

        ("g" (lambda ()
               (interactive)
               (let ((current-prefix-arg 4))
                 (call-interactively #'magit-status)))
             "git")

- nil, which exits the hydra.
- a single sexp, which will be wrapped in an interactive lambda.

Here's an example of the last option:

```cl
(defhydra hydra-launcher (:color blue)
   "Launch"
   ("h" man "man")
   ("r" (browse-url "http://www.reddit.com/r/emacs/") "reddit")
   ("w" (browse-url "http://www.emacswiki.org/") "emacswiki")
   ("s" shell "shell")
   ("q" nil "cancel"))
(global-set-key (kbd "C-c r") 'hydra-launcher/body)
```

### `head-hint`

In case of a large body docstring, you usually don't want the head hint to show up, since
you've already documented it the the body docstring.
You can set the head hint to `nil` to do this.

Example:

```cl
(defhydra hydra-zoom (global-map "<f2>")
  "
Press _g_ to zoom in.
"
  ("g" text-scale-increase nil)
  ("l" text-scale-decrease "out"))
```

### `head-plist`

Here's a list of body keys that can be overridden in each head:

- `:exit`
- `:color`
- `:bind`