Heikki @ home

Loose leaves from my tree

Custom elfeed filter functions

I've been using the great emacs RSS reader elfeed for months now. I had small suggestion to the way search filtering treats special characters and to minimize key presses, and filed an issue on it. The author Christopher Wellons fixed the issue and commented that I could speed things up by using custom functions and to bind them to single keys that are not used by the elfeed mode.

I usually scan quickly through the numerous daily news titles, then check the science news, and then read the remaining far fewer articles from other feeds. I have settled on a simple workflow that involves these three main categories of articles. Starting with all the articles, I go through news, science news and then the rest. Instead of manually changing the filter string, I have automated the process. The following table shows how my workflow neatly fits to keys 0-1-2-0.

key function note
0 elfeed-read-all resets to my default filter
1 elfeed-read-news world news, removes pesky sport news
2 elfeed-read-journals science news

Here a sample code from Emacs StackExchange that defines an elfeed function and a key binding to it. In this case, pressing R marks all visible articles read.

(defun elfeed-mark-all-as-read ()
   "Mark currently shown articles read"
 (define-key elfeed-search-mode-map (kbd "R") 'elfeed-mark-all-as-read)

To expand this to a filtering function, I need to define a filter string and apply it. Since there will be more than one category to filter, let's define a common function that takes the filter string as the first argument. The second argument is a short name that gets displayed on the echo field to provide feedback.

(defun elfeed--read-tag (filter tag)
  "Template for filtering feed categories.

FILTER is the filter string to apply, and TAG is a short name of
the displayed category.

The cursor is moved to the beginning of the first feed line."
  (setq elfeed-search-filter filter)
  (elfeed-search-update :force)
  (goto-char (point-min))
  (message (concat "elfeed: show " tag)))

It is easy to add more commands inside this function. The penultimate code line moves the cursor down to the first article. One more key press saved!

My biggest gripe with the BBC News is that world news feed contains daily sports news -- a topic I am not interested in reading on regular basis. Like the Elfeed Tips and Tricks blog entry mentions, I could mark them with junk category and ignore junk. Instead, I decided to create a function that marks them read. Since that function gets called from an other function that filters for news, that function does not need to be interactive.

(defun elfeed--remove-sports ()
  "Remove sports articles from world news"
  (setq elfeed-search-filter "@1-month-ago +unread +n sport")
  (elfeed-search-update :force)
  (message "elfeed: sports removed"))

(defun elfeed-read-news ()
  "Show global news articles"
  (elfeed--read-tag "@1-month-ago +unread +n " "news"))
(define-key elfeed-search-mode-map (kbd "1") 'elfeed-read-news)

From these code lines it is easy to see how to create functions elfeed-read-all and elfeed-read-journals. You just define your categories sensibly, give them a name (above: 'n' for news) and put them in the search string. My .emacs file has all the details.