In part 2 I talked about how I used Hyrdas to quickly navigate through elfeed tags. It was a nice step up but the fact that I still had to manually edit my configuration code for every new tag to update the hydra was a problem.

Basically, I had to somehow or other, take a list of all the active tags and with it build a defhydra command that will then make my Hydra.

Fortunately, emacs, being a lisp, has macros. I'm not talking about keyboard macros which I talked about in episode 15 but rather Lisp style macros. Macros let you transform code and then execute the transformed code.

The example I give in the video:

(defmacro infix (a op b) ` ( , op , a , b)) (infix 3 + 8 ) ; evaluates to 11

This transforms the 3+8 into (+ 3 8) and then evaluates it to be 11.

We can use this idea with our Hydra.

We can use the call elfeed-db-get-all-tags to get a list of all the tags in our database. I decided that if I had an uppercase letter in the tag, I'd use the lowercase version of that letter as my "hotkey" and if it didn't, I'd just use the first letter.

So, given a tag list of:

(active blogs cs eDucation emacs local misc sports star tech unread webcomics)

I'd want a "hotkey" of b for logs and d for eDucation.

The routine z/hasCap tests to see if a tag has a capital letter in it and z/get-hydra-option-key returns the final "hotkey:"

(defun z/hasCap (s) "" (let ((case-fold-search nil )) (string-match-p "[[:upper:]]" s) )) (defun z/get-hydra-option-key (s) "returns single upper case letter (converted to lower) or first" (interactive) (let ( (loc (z/hasCap s))) (if loc ( downcase ( substring s loc ( + loc 1 ))) ( substring s 0 1 ) )))

mz/make-elfeed-cats takes a list of tags and returns a list of items where each item is in the form expected by the hydra definition:

( "t" (elfeed-search-set-filter "@6-months-ago +tagname" ) "tagname" )

(defun mz/make-elfeed-cats (tags) "Returns a list of lists. Each one is line for the hydra configuratio in the form (c function hint)" (interactive) ( mapcar (lambda (tag) (let* ( (tagstring ( symbol-name tag)) (c (z/get-hydra-option-key tagstring)) ) ( list c ( append ' (elfeed-search-set-filter) ( list ( format "@6-months-ago +%s" tagstring) ))tagstring ))) tags))

Finally, here's our macro:

(defmacro mz/make-elfeed-hydra () ` (defhydra mz/hydra-elfeed () "filter" ,@ (mz/make-elfeed-cats (elfeed-db-get-all-tags)) ( "*" (elfeed-search-set-filter "@6-months-ago +star" ) "Starred" ) ( "M" elfeed-toggle-star "Mark" ) ( "A" (elfeed-search-set-filter "@6-months-ago" ) "All" ) ( "T" (elfeed-search-set-filter "@1-day-ago" ) "Today" ) ( "Q" bjm/elfeed-save-db-and-bury "Quit Elfeed" :color blue) ( "q" nil "quit" :color blue) ))

The line that starts with ,@ calls the routine that builds lines of code for all the tags in the database and the macro leaves me with the defhydra I need.

I then redefine the hydra every time I need it, just in case tags changed:

(defun mz/make-and-run-elfeed-hydra () "" (interactive) (mz/make-elfeed-hydra) (mz/hydra-elfeed/body))

and bind mz/make-and-run-elfeed-hydra to j and J in my elfeed keymap (this code goes in the bind section of my use-package elfeed section):

( "j" . mz/make-and-run-elfeed-hydra) ( "J" . mz/make-and-run-elfeed-hydra)

As long as I remember to name my tags in a way that they don't conflict with one another I can quickly navigate all around elfeed.

Macros FTW!!!!

Here's the video:

Enjoy. Relevant links: