Using Emacs - 31 - elfeed part 3 - macros
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:
- Video series overview page:
- Code:
Comments
Comments powered by Disqus