Elfeed, the feed reader for Emacs, is built on several clever ideas. The basic mode of interaction in Elfeed is through search, specifying what kinds of feeds you want to peruse at the moment. This removes the distinction between an inbox and a search result, which works surprisingly well with the expressive search syntax. The second is integration: search results can be stored as regular bookmarks, and jumping to the bookmark from anywhere in Emacs will run the corresponding query and present you with fresh results. The third is organizing by tags instead of folders. Web services like Gmail use some of these concepts (and probably pioneered them too), but the difference between web-based readers and Emacs in speed, customizability, general hackability and use is night and day.
Elfeed’s default interface has all the right pieces, but I found it needed just a little tweaking to fit what I expect from a feed reader.
Display the feed list and current entry in a split pane setup 1:
Elfeed has two kinds of views: The
search view which shows feed entries matching a search query, and the
show view to read a specific entry. By default only one of these is active at a time, but a more useful split-pane setup is a few tweaks away:
Stay in the search view and preview entries.
There are only two pieces to this. The first is to specify the function we want Elfeed to use to display the buffer with the current entry. In this case write our own
elfeed-display-buffer to set the height.
(setq elfeed-show-entry-switch #'elfeed-display-buffer) (defun elfeed-display-buffer (buf &optional act) (pop-to-buffer buf) (set-window-text-height (get-buffer-window) (round (* 0.7 (frame-height)))))
A different but less self-contained way would be to set it to use the built in
pop-to-buffer but add an entry to
display-buffer-alist as an instruction on what to do with elfeed entry buffers.
The second is to set up a keybinding to advance to the next or previous feed in the search window, with a little elbow grease. 2
;; -*- lexical-binding: t -*- (defun elfeed-search-show-entry-pre (&optional lines) "Returns a function to scroll forward or back in the Elfeed search results, displaying entries without switching to them." (lambda (times) (interactive "p") (forward-line (* times (or lines 0))) (recenter) (call-interactively #'elfeed-search-show-entry) (select-window (previous-window)) (unless elfeed-search-remain-on-entry (forward-line -1)))) (define-key elfeed-search-mode-map (kbd "n") (elfeed-search-show-entry-pre +1)) (define-key elfeed-search-mode-map (kbd "p") (elfeed-search-show-entry-pre -1)) (define-key elfeed-search-mode-map (kbd "M-RET") (elfeed-search-show-entry-pre))
Navigate the entire database with the space bar
Alternatively, you might prefer to stay in the “show” window and just go through each entry by content instead of header. You can use the space bar to scroll the view or move to the next entry as appropriate.
Go forward (scroll or next entry) with
SPC, back with
Google Reader had one button navigation, and it is a level of laziness I have aspired to since.
(defun elfeed-scroll-up-command (&optional arg) "Scroll up or go to next feed item in Elfeed" (interactive "^P") (let ((scroll-error-top-bottom nil)) (condition-case-unless-debug nil (scroll-up-command arg) (error (elfeed-show-next))))) (defun elfeed-scroll-down-command (&optional arg) "Scroll up or go to next feed item in Elfeed" (interactive "^P") (let ((scroll-error-top-bottom nil)) (condition-case-unless-debug nil (scroll-down-command arg) (error (elfeed-show-prev))))) (define-key elfeed-show-mode-map (kbd "SPC") 'elfeed-scroll-up-command) (define-key elfeed-show-mode-map (kbd "S-SPC") 'elfeed-scroll-down-command)
By default you can tag an entry from the search results by typing “
+” followed by the tag name. This is too much work for a central organizational feature, so a little helper sorts things out.
(defun elfeed-tag-selection-as (mytag) "Returns a function that tags an elfeed entry or selection as MYTAG" (lambda () "Toggle a tag on an Elfeed search selection" (interactive) (elfeed-search-toggle-all mytag)))
Tag with a single letter keybind:
(define-key elfeed-search-mode-map "l" (elfeed-tag-selection-as 'readlater)) (define-key elfeed-search-mode-map "d" (elfeed-tag-selection-as 'junk))
Open feeds in Emacs
Elfeed will open links to feeds in your default browser with “
b”. But for text heavy posts or to look up something specific I often want to open them in place in Emacs:
Quick-preview links in
Instead of writing a function to do this from scratch, we set
eww (Emacs’ built in web browser) as the default in a short-lived
let binding and let the usual
browse-url functions handle the rest:
(defun elfeed-show-eww-open (&optional use-generic-p) "open with eww" (interactive "P") (let ((browse-url-browser-function #'eww-browse-url)) (elfeed-show-visit use-generic-p))) (defun elfeed-search-eww-open (&optional use-generic-p) "open with eww" (interactive "P") (let ((browse-url-browser-function #'eww-browse-url)) (elfeed-search-browse-url use-generic-p))) (define-key elfeed-show-mode-map (kbd "B") 'efleed-show-eww-open) (define-key elfeed-search-mode-map (kbd "B") 'efleed-search-eww-open)
We can take this idea further. The following snippet will ensure that Emacs (and thus Elfeed) defaults to opening Youtube links in
mpv (or the video player of your choice), cutting the browser out of the loop entirely. 3
(setq browse-url-browser-function '(("https:\\/\\/www\\.youtu\\.*be." . browse-url-mpv) ("." . browse-url-generic))) (defun browse-url-mpv (url &optional single) (start-process "mpv" nil "mpv" (shell-quote-argument url)))
There is Elfeed Goodies, a package on github that provides a split pane setup to Elfeed. But it’s rather large for what it does, has a dependency I don’t need (
popwin.el) for a single function and provides much else that I have no use for. ↩︎