{"id":13646634,"url":"https://github.com/abo-abo/hydra","last_synced_at":"2025-05-14T08:05:51.765Z","repository":{"id":26086727,"uuid":"29530717","full_name":"abo-abo/hydra","owner":"abo-abo","description":"make Emacs bindings that stick around","archived":false,"fork":false,"pushed_at":"2025-03-16T12:55:28.000Z","size":576,"stargazers_count":1876,"open_issues_count":91,"forks_count":114,"subscribers_count":33,"default_branch":"master","last_synced_at":"2025-04-13T06:21:18.103Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abo-abo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"liberapay":"abo-abo","patreon":"abo_abo"}},"created_at":"2015-01-20T13:21:51.000Z","updated_at":"2025-04-10T19:22:22.000Z","dependencies_parsed_at":"2025-04-13T04:02:29.207Z","dependency_job_id":"86c790d9-ffb9-4df4-a1e7-3ddbc425e087","html_url":"https://github.com/abo-abo/hydra","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abo-abo%2Fhydra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abo-abo%2Fhydra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abo-abo%2Fhydra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abo-abo%2Fhydra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abo-abo","download_url":"https://codeload.github.com/abo-abo/hydra/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254101615,"owners_count":22014909,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T01:03:01.490Z","updated_at":"2025-05-14T08:05:46.756Z","avatar_url":"https://github.com/abo-abo.png","language":"Emacs Lisp","funding_links":["https://liberapay.com/abo-abo","https://patreon.com/abo_abo"],"categories":["emacs","Emacs Lisp"],"sub_categories":[],"readme":"# Hydra\n\n[![Build Status](https://travis-ci.org/abo-abo/hydra.svg?branch=master)](https://travis-ci.org/abo-abo/hydra)\n[![GNU ELPA](https://elpa.gnu.org/packages/hydra.svg)](https://elpa.gnu.org/packages/hydra.html)\n[![MELPA](https://melpa.org/packages/hydra-badge.svg)](https://melpa.org/#/hydra)\n[![MELPA Stable](https://stable.melpa.org/packages/hydra-badge.svg)](https://stable.melpa.org/#/hydra)\n\nThis is a package for GNU Emacs that can be used to tie related commands into a family of short\nbindings with a common prefix - a Hydra.\n\n![hydra](http://oremacs.com/download/Hydra.jpg)\n\n## Description for Poets\n\nOnce you summon the Hydra through the prefixed binding (the body + any one head), all heads can be\ncalled in succession with only a short extension.\n\nThe Hydra is vanquished once Hercules, any binding that isn't the Hydra's head, arrives.  Note that\nHercules, besides vanquishing the Hydra, will still serve his original purpose, calling his proper\ncommand.  This makes the Hydra very seamless, it's like a minor mode that disables itself\nauto-magically.\n\n## Description for Pragmatics\n\nImagine that you have bound \u003ckbd\u003eC-c j\u003c/kbd\u003e and \u003ckbd\u003eC-c k\u003c/kbd\u003e in your\nconfig.  You want to call \u003ckbd\u003eC-c j\u003c/kbd\u003e and \u003ckbd\u003eC-c k\u003c/kbd\u003e in some\n(arbitrary) sequence. Hydra allows you to:\n\n- Bind your functions in a way that pressing \u003ckbd\u003eC-c jjkk3j5k\u003c/kbd\u003e is\nequivalent to pressing \u003ckbd\u003eC-c j C-c j C-c k C-c k M-3 C-c j M-5 C-c\nk\u003c/kbd\u003e. Any key other than \u003ckbd\u003ej\u003c/kbd\u003e or \u003ckbd\u003ek\u003c/kbd\u003e exits this state.\n\n- Assign a custom hint to this group of functions, so that you know immediately\nafter pressing \u003ckbd\u003eC-c\u003c/kbd\u003e that you can follow up with \u003ckbd\u003ej\u003c/kbd\u003e or\n\u003ckbd\u003ek\u003c/kbd\u003e.\n\nIf you want to quickly understand the concept, see [the video demo](https://www.youtube.com/watch?v=_qZliI1BKzI).\n\n\u003c!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc/generate-toc again --\u003e\n**Table of Contents**\n\n- [Sample Hydras](#sample-hydras)\n    - [The one with the least amount of code](#the-one-with-the-least-amount-of-code)\n    - [The impressive-looking one](#the-impressive-looking-one)\n- [Community wiki](#community-wiki)\n- [The Rules of Hydra-tics](#the-rules-of-hydra-tics)\n    - [`hydra-awesome`](#hydra-awesome)\n    - [`awesome-map` and `awesome-binding`](#awesome-map-and-awesome-binding)\n    - [`awesome-plist`](#awesome-plist)\n        - [`:pre` and `:post`](#pre-and-post)\n        - [`:exit`](#exit)\n        - [`:foreign-keys`](#foreign-keys)\n        - [`:color`](#color)\n        - [`:timeout`](#timeout)\n        - [`:hint`](#hint)\n        - [`:bind`](#bind)\n    - [`awesome-docstring`](#awesome-docstring)\n    - [`awesome-head-1`](#awesome-head-1)\n        - [`head-binding`](#head-binding)\n        - [`head-command`](#head-command)\n        - [`head-hint`](#head-hint)\n        - [`head-plist`](#head-plist)\n\n\u003c!-- markdown-toc end --\u003e\n\n# Sample Hydras\n\n## The one with the least amount of code\n\n```elisp\n(defhydra hydra-zoom (global-map \"\u003cf2\u003e\")\n  \"zoom\"\n  (\"g\" text-scale-increase \"in\")\n  (\"l\" text-scale-decrease \"out\"))\n```\n\nWith this simple code, you can:\n\n- Start zooming in with \u003ckbd\u003e\u0026lt;f2\u0026gt; g\u003c/kbd\u003e.\n- Continue to zoom in with \u003ckbd\u003eg\u003c/kbd\u003e.\n- Or zoom out with \u003ckbd\u003el\u003c/kbd\u003e.\n- Zoom in five times at once with \u003ckbd\u003e5g\u003c/kbd\u003e.\n- Stop zooming with *any* key that isn't \u003ckbd\u003eg\u003c/kbd\u003e or \u003ckbd\u003el\u003c/kbd\u003e.\n\nFor any Hydra:\n\n- `digit-argument` can be called with \u003ckbd\u003e0\u003c/kbd\u003e-\u003ckbd\u003e9\u003c/kbd\u003e.\n- `negative-argument` can be called with \u003ckbd\u003e-\u003c/kbd\u003e.\n- `universal-argument` can be called with \u003ckbd\u003eC-u\u003c/kbd\u003e.\n\n## The impressive-looking one\n\nHere's the result of pressing \u003ckbd\u003e.\u003c/kbd\u003e in the good-old Buffer menu:\n\n![hydra-buffer-menu](http://oremacs.com/download/hydra-buffer-menu.png)\n\nThe code is large but very simple:\n\n```elisp\n(defhydra hydra-buffer-menu (:color pink\n                             :hint nil)\n  \"\n^Mark^             ^Unmark^           ^Actions^          ^Search\n^^^^^^^^-----------------------------------------------------------------\n_m_: mark          _u_: unmark        _x_: execute       _R_: re-isearch\n_s_: save          _U_: unmark up     _b_: bury          _I_: isearch\n_d_: delete        ^ ^                _g_: refresh       _O_: multi-occur\n_D_: delete up     ^ ^                _T_: files only: % -28`Buffer-menu-files-only\n_~_: modified\n\"\n  (\"m\" Buffer-menu-mark)\n  (\"u\" Buffer-menu-unmark)\n  (\"U\" Buffer-menu-backup-unmark)\n  (\"d\" Buffer-menu-delete)\n  (\"D\" Buffer-menu-delete-backwards)\n  (\"s\" Buffer-menu-save)\n  (\"~\" Buffer-menu-not-modified)\n  (\"x\" Buffer-menu-execute)\n  (\"b\" Buffer-menu-bury)\n  (\"g\" revert-buffer)\n  (\"T\" Buffer-menu-toggle-files-only)\n  (\"O\" Buffer-menu-multi-occur :color blue)\n  (\"I\" Buffer-menu-isearch-buffers :color blue)\n  (\"R\" Buffer-menu-isearch-buffers-regexp :color blue)\n  (\"c\" nil \"cancel\")\n  (\"v\" Buffer-menu-select \"select\" :color blue)\n  (\"o\" Buffer-menu-other-window \"other-window\" :color blue)\n  (\"q\" quit-window \"quit\" :color blue))\n\n(define-key Buffer-menu-mode-map \".\" 'hydra-buffer-menu/body)\n```\n\nLooking at the code, you can see `hydra-buffer-menu` as sort of a namespace construct that wraps\neach function that it's given in code that shows that hint and makes it easy to call the related\nfunctions. One additional function is created and returned as the result of `defhydra` -\n`hydra-buffer-menu/body`.  This function does nothing except setting up the hint and the keymap, and\nis usually the entry point to complex hydras.\n\nTo write your own hydras, you can:\n\n- Either modify an existing hydra to do what you want to do.\n- Or read [the rules](#the-rules-of-hydra-tics),\n  [the examples](https://github.com/abo-abo/hydra/blob/master/hydra-examples.el),\n  the docstrings and comments in the source.\n\n# Community wiki\n\nYou can find some user created hydras and more documentation in the project's\n[community wiki](https://github.com/abo-abo/hydra/wiki/). Feel free to add your\nown or edit the existing ones.\n\n# The Rules of Hydra-tics\n\nEach hydra (take `awesome` as a prefix to make it more specific) looks like this:\n\n```\n(defhydra hydra-awesome (awesome-map awesome-binding awesome-plist)\n  awesome-docstring\n  awesome-head-1\n  awesome-head-2\n  awesome-head-3\n  ...)\n```\n\n## `hydra-awesome`\n\nEach hydra needs a name, and this one is named `hydra-awesome`. You can name your hydras as you wish,\nbut I prefer to start each one with `hydra-`, because it acts as an additional namespace layer, for example:\n`hydra-zoom`, `hydra-helm`, `hydra-apropos` etc.\n\nIf you name your hydra `hydra-awesome`, the return result of `defhydra` will be `hydra-awesome/body`.\n\nHere's what `hydra-zoom/body` looks like, if you're interested:\n\n```elisp\n(defun hydra-zoom/body ()\n  \"Call the body in the \\\"hydra-zoom\\\" hydra.\n\nThe heads for the associated hydra are:\n\n\\\"g\\\":    `text-scale-increase',\n\\\"l\\\":    `text-scale-decrease'\n\nThe body can be accessed via `hydra-zoom/body', which is bound to \\\"\u003cf2\u003e\\\".\"\n  (interactive)\n  (require 'hydra)\n  (hydra-default-pre)\n  (let ((hydra--ignore nil))\n    (hydra-keyboard-quit)\n    (setq hydra-curr-body-fn\n          'hydra-zoom/body))\n  (hydra-show-hint\n   hydra-zoom/hint\n   'hydra-zoom)\n  (hydra-set-transient-map\n   hydra-zoom/keymap\n   (lambda nil\n     (hydra-keyboard-quit)\n     nil)\n   nil)\n  (setq prefix-arg\n        current-prefix-arg))\n```\n\n## `awesome-map` and `awesome-binding`\n\nThis can be any keymap, for instance, `global-map` or `isearch-mode-map`.\n\nFor this example:\n\n```elisp\n(defhydra hydra-zoom (global-map \"\u003cf2\u003e\")\n  \"zoom\"\n  (\"g\" text-scale-increase \"in\")\n  (\"l\" text-scale-decrease \"out\"))\n```\n\n- `awesome-map` is `global-map`\n- `awesome-binding` is `\"\u003cf2\u003e\"`\n\nAnd here's the relevant generated code:\n\n```elisp\n(unless (keymapp (lookup-key global-map (kbd \"\u003cf2\u003e\")))\n  (define-key global-map (kbd \"\u003cf2\u003e\") nil))\n(define-key global-map [f2 103]\n  (function hydra-zoom/text-scale-increase))\n(define-key global-map [f2 108]\n  (function hydra-zoom/text-scale-decrease))\n```\n\nAs you see, `\"\u003cf2\u003e\"` is used as a prefix for \u003ckbd\u003eg\u003c/kbd\u003e (char value 103) and \u003ckbd\u003el\u003c/kbd\u003e\n(char value 108).\n\nIf you don't want to use a map right now, you can skip it like this:\n\n```elisp\n(defhydra hydra-zoom (nil nil)\n  \"zoom\"\n  (\"g\" text-scale-increase \"in\")\n  (\"l\" text-scale-decrease \"out\"))\n```\n\nOr even simpler:\n\n```elisp\n(defhydra hydra-zoom ()\n  \"zoom\"\n  (\"g\" text-scale-increase \"in\")\n  (\"l\" text-scale-decrease \"out\"))\n```\n\nBut then you would have to bind `hydra-zoom/text-scale-increase` and\n`hydra-zoom/text-scale-decrease` yourself.\n\n## `awesome-plist`\n\nYou can read up on what a plist is in\n[the Elisp manual](https://www.gnu.org/software/emacs/manual/html_node/elisp/Property-Lists.html).\n\nYou can use `awesome-plist` to modify the behavior of each head in some way.\nBelow is a list of each key.\n\n### `:pre` and `:post`\n\nYou can specify code that will be called before each head, and after the body. For example:\n\n```elisp\n(defhydra hydra-vi (:pre (set-cursor-color \"#40e0d0\")\n                    :post (progn\n                            (set-cursor-color \"#ffffff\")\n                            (message\n                             \"Thank you, come again.\")))\n  \"vi\"\n  (\"l\" forward-char)\n  (\"h\" backward-char)\n  (\"j\" next-line)\n  (\"k\" previous-line)\n  (\"q\" nil \"quit\"))\n```\n\nThanks to `:pre`, each time any head is called, the cursor color is changed.\nAnd when the hydra quits, the cursor color will be made black again with `:post`.\n\n### `:exit`\n\nThe `:exit` key is inherited by every head (they can override it) and influences what will happen\nafter executing head's command:\n\n- `:exit nil` (the default) means that the hydra state will continue - you'll still see the hint and be able to use short bindings.\n- `:exit t` means that the hydra state will stop.\n\n### `:foreign-keys`\n\nThe `:foreign-keys` key belongs to the body and decides what to do when a key is pressed that doesn't\nbelong to any head:\n\n- `:foreign-keys nil` (the default) means that the hydra state will stop and the foreign key will\ndo whatever it was supposed to do if there was no hydra state.\n- `:foreign-keys warn` will not stop the hydra state, but instead will issue a warning without\nrunning the foreign key.\n- `:foreign-keys run` will not stop the hydra state, and try to run the foreign key.\n\n### `:color`\n\nThe `:color` key is a shortcut. It aggregates `:exit` and `:foreign-keys` key in the following way:\n\n    | color    | toggle                     |\n    |----------+----------------------------|\n    | red      |                            |\n    | blue     | :exit t                    |\n    | amaranth | :foreign-keys warn         |\n    | teal     | :foreign-keys warn :exit t |\n    | pink     | :foreign-keys run          |\n\nIt's also a trick to make you instantly aware of the current hydra keys that you're about to press:\nthe keys will be highlighted with the appropriate color.\n\n### `:timeout`\n\nThe `:timeout` key starts a timer for the corresponding amount of seconds that disables the hydra.\nCalling any head will refresh the timer.\n\n### `:hint`\n\nThe `:hint` key will be inherited by each head. Each head is allowed to override it, of course.\nOne value that makes sense is `:hint nil`. See below for an explanation of head hint.\n\n### `:bind`\n\nThe `:bind` key provides a lambda to be used to bind each head.  This is quite advanced and rarely\nused, you're not likely to need it.  But if you would like to bind your heads with e.g. `bind-key`\ninstead of `define-key` you can use this option.\n\nThe `:bind` key can be overridden by each head. This is useful if you want to have a few heads that\nare not bound outside the hydra.\n\n### `:base-map`\nUse this option if you want to override `hydra-base-map` for the current hydra.\n\n## `awesome-docstring`\n\nThis can be a simple string used to build the final hydra hint.  However, if you start it with a\nnewline, the key-highlighting and Ruby-style string interpolation becomes enabled, as you can see in\n`hydra-buffer-menu` above.\n\nTo highlight a key, just wrap it in underscores. Note that the key must belong to one of the heads.\nThe key will be highlighted with the color that is appropriate to the behavior of the key, i.e.  if\nthe key will make the hydra exit, the color will be blue.\n\nTo insert an empty character, use `^`. The only use of this is to have your code aligned as\nnicely as the result.\n\nTo insert a dynamic Elisp variable, use `%`\u0026#96; followed by the variable. Each time the variable\nchanges due to a head, the docstring will be updated. `format`-style width specifiers can be used.\n\nTo insert a dynamic Elisp expression, use e.g. `%(length (dired-get-marked-files))`.  If a head will\nchange the amount of marked files, for example, it will be appropriately updated.\n\nIf the result of the Elisp expression is a string and you don't want to quote it, use this form:\n`%s(shell-command-to-string \"du -hs\")`.\n\n## `awesome-head-1`\n\nEach head looks like this:\n\n```elisp\n(head-binding head-command head-hint head-plist)\n```\n\nFor the head `(\"g\" text-scale-increase \"in\")`:\n\n- `head-binding` is `\"g\"`.\n- `head-command` is `text-scale-increase`.\n- `head-hint` is `\"in\"`.\n- `head-plist` is `nil`.\n\n### `head-binding`\n\nThe `head-binding` is a string that can be passed to `kbd`.\n\n### `head-command`\n\nThe `head-command` can be:\n\n- command name, like `text-scale-increase`.\n- a lambda, like\n\n        (\"g\" (lambda ()\n               (interactive)\n               (let ((current-prefix-arg 4))\n                 (call-interactively #'magit-status)))\n             \"git\")\n\n- nil, which exits the hydra.\n- a single sexp, which will be wrapped in an interactive lambda.\n\nHere's an example of the last option:\n\n```elisp\n(defhydra hydra-launcher (:color blue)\n   \"Launch\"\n   (\"h\" man \"man\")\n   (\"r\" (browse-url \"http://www.reddit.com/r/emacs/\") \"reddit\")\n   (\"w\" (browse-url \"http://www.emacswiki.org/\") \"emacswiki\")\n   (\"s\" shell \"shell\")\n   (\"q\" nil \"cancel\"))\n(global-set-key (kbd \"C-c r\") 'hydra-launcher/body)\n```\n\n### `head-hint`\n\nIn case of a large body docstring, you usually don't want the head hint to show up, since\nyou've already documented it in the body docstring.\nYou can set the head hint to `nil` to do this.\n\nExample:\n\n```elisp\n(defhydra hydra-zoom (global-map \"\u003cf2\u003e\")\n  \"\nPress _g_ to zoom in.\n\"\n  (\"g\" text-scale-increase nil)\n  (\"l\" text-scale-decrease \"out\"))\n```\n\n### `head-plist`\n\nHere's a list of body keys that can be overridden in each head:\n\n- `:exit`\n- `:color`\n- `:bind`\n- `:column`\n\nUse `:column` feature to have an aligned rectangular docstring without defining it manually.\nSee [hydra-examples.el](https://github.com/abo-abo/hydra/blob/05871dd6c8af7b2268bd1a10eb9f8a3e423209cd/hydra-examples.el#L337) for an example code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabo-abo%2Fhydra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabo-abo%2Fhydra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabo-abo%2Fhydra/lists"}