(or emacs irrelevant)

Some fun with Hydra

The code from this post has very little application. But it's kind of fun, so I'll post it. Star Trek: TNG is one of my favorite shows, so I've added some TNG characters to one of the Hydra features that I'm testing.

defhydradio statement

(require 'hydra)
(defhydradio hydra-tng ()
  (picard "_p_ Captain Jean Luc Picard:")
  (riker "_r_ Commander William Riker:")
  (data "_d_ Lieutenant Commander Data:")
  (worf "_w_ Worf:")
  (la-forge "_f_ Geordi La Forge:")
  (troi "_t_ Deanna Troi:")
  (dr-crusher "_c_ Doctor Beverly Crusher:")
  (phaser "_h_ Set phasers to " [stun kill]))

The defhydradio macro is akin to a namespace construct that defines multiple variables that can assume only certain values (either t or nil by default), and functions to cycle those values.

defhydradio implementation

Here's what you may see after a macroexpand:

(progn
  (defvar hydra-tng/picard nil
    "_p_ Captain Jean Luc Picard:")
  (put (quote hydra-tng/picard)
       (quote range)
       [nil t])
  (defun hydra-tng/picard nil
    (hydra--cycle-radio (quote hydra-tng/picard)))
  (defvar hydra-tng/riker nil
    "_r_ Commander William Riker:")
  (put (quote hydra-tng/riker)
       (quote range)
       [nil t])
  (defun hydra-tng/riker nil (hydra--cycle-radio (quote hydra-tng/riker)))
  ;; ...
  (defvar hydra-tng/names
    '(hydra-tng/picard hydra-tng/riker
      hydra-tng/data hydra-tng/worf hydra-tng/la-forge
      hydra-tng/troi hydra-tng/dr-crusher hydra-tng/phaser)))

As you can see, each list passed to defhydradio:

  • gets a prefixed variable definition
  • gets a range property for the prefixed symbol
  • gets a prefixed function definition that cycles the variable value based on the range property
  • gets added to hydra-tng/names

defhydra statement

(defhydra hydra-tng (:foreign-keys run :hint nil)
  (concat (hydra--table hydra-tng/names 7 2
                        '("  % -30s %% -3`%s"
                          "%s %%`%s"))
          "\n\n")
  ("p" (hydra-tng/picard))
  ("r" (hydra-tng/riker))
  ("d" (hydra-tng/data))
  ("w" (hydra-tng/worf))
  ("f" (hydra-tng/la-forge))
  ("t" (hydra-tng/troi))
  ("c" (hydra-tng/dr-crusher))
  ("h" (hydra-tng/phaser))
  ("b" beam-down "beam down" :exit t)
  ("o" (hydra-reset-radios hydra-tng/names) "reset")
  ("q" nil "cancel"))

The interesting statement in place of the docstring will actually evaluate to this docstring:

"  _p_ Captain Jean Luc Picard:   % -3`hydra-tng/picard^^^^    _h_ Set phasers to  %`hydra-tng/phaser
  _r_ Commander William Riker:   % -3`hydra-tng/riker^^^^^
  _d_ Lieutenant Commander Data: % -3`hydra-tng/data^^^^^^
  _w_ Worf:                      % -3`hydra-tng/worf^^^^^^
  _f_ Geordi La Forge:           % -3`hydra-tng/la-forge^^
  _t_ Deanna Troi:               % -3`hydra-tng/troi^^^^^^
  _c_ Doctor Beverly Crusher:    % -3`hydra-tng/dr-crusher

"

The first line overflows a bit, but it's clear what it is. There's some flexibility in using hydra--table, since you can:

  • redefine the row-column format (e.g. from 7x2 to 5x3)
  • add more variables to hydra-tng/names

Note also, that since hydra-tng/names holds all the names, and all the names know their default values through range, it's possible to reset them all at once with hydra-reset-radios.

Finally, here's a simple implementation of beam-down:

(defun beam-down ()
  (interactive)
  (message
   "Beaming down: %s."
   (mapconcat
    #'identity
    (delq nil
          (mapcar
           (lambda (p) (when (symbol-value p)
                    (substring (symbol-name p) 10)))
           '(hydra-tng/picard
             hydra-tng/riker
             hydra-tng/data
             hydra-tng/worf
             hydra-tng/la-forge
             hydra-tng/troi
             hydra-tng/dr-crusher)))
    ", and ")))

(global-set-key (kbd "C-c C-,") 'hydra-tng/body)

Outro

And that's it. There actually is an application of defhydradio in hydra-ox.el. It's not fully finished, but you can already try it as an alternative to org-mode export dispatch widget, most things are working.