Star and unstar articles in elfeed

Following on from a couple of previous posts on elfeed, the excellent feed reader for Emacs, I wanted to share some code to enable the starring and unstarring of articles for future reference.

This code is closely based on this article, so all credit should go in that direction. My only addition is to add the ability to unstar an article.

;; code to add and remove a starred tag to elfeed article
;; based on http://matt.hackinghistory.ca/2015/11/22/elfeed/

;; add a star
(defun bjm/elfeed-star ()
  "Apply starred to all selected entries."
  (interactive )
  (let* ((entries (elfeed-search-selected))
         (tag (intern "starred")))

    (cl-loop for entry in entries do (elfeed-tag entry tag))
    (mapc #'elfeed-search-update-entry entries)
    (unless (use-region-p) (forward-line))))

;; remove a start
(defun bjm/elfeed-unstar ()
  "Remove starred tag from all selected entries."
  (interactive )
  (let* ((entries (elfeed-search-selected))
         (tag (intern "starred")))

    (cl-loop for entry in entries do (elfeed-untag entry tag))
    (mapc #'elfeed-search-update-entry entries)
    (unless (use-region-p) (forward-line))))

;; face for starred articles
(defface elfeed-search-starred-title-face
  '((t :foreground "#f77"))
  "Marks a starred Elfeed entry.")

(push '(starred elfeed-search-starred-title-face) elfeed-search-face-alist)

This code sets up the required functions to add and remove the “starred” tag from articles, and sets a face colour to indicate articles that have been starred.

I bind these to the keys “*” to add a star and “8” to remove the star (easy to remember):

;; add keybindings
(eval-after-load 'elfeed-search
  '(define-key elfeed-search-mode-map (kbd "*") 'bjm/elfeed-star))
(eval-after-load 'elfeed-search
  '(define-key elfeed-search-mode-map (kbd "8") 'bjm/elfeed-unstar))

Now, entering the search filter “+starred” in elfeed will show the starred articles, and as described previously you can save a bookmark to that filter (I called mine “elfeed-starred”) and assign a key like “S” to jump directly to the starred articles:

;;shortcut to jump to starred bookmark
(defun bjm/elfeed-show-starred ()
  (bookmark-jump "elfeed-starred"))
(define-key elfeed-search-mode-map (kbd "S") 'bjm/elfeed-show-starred)

Update 2016-09-27

Commenter Galrog has a much simpler implementation. I’ve copied it here so the formatting is nicer:

(defalias 'elfeed-toggle-star
  (elfeed-expose #'elfeed-search-toggle-all 'star))

(eval-after-load 'elfeed-search
  '(define-key elfeed-search-mode-map (kbd "m") 'elfeed-toggle-star))

A tweak to elfeed filtering

I wrote recently about my enthusiasm for the elfeed feed reader. Here is a microscopic tweak to the way elfeed search filters work to better suit my use.

By default, if I switch to a bookmarked filter to view e.g. my feeds tagged with Emacs (as discussed in the previous post), and then hit s to run a live filter, I can type something like “Xah” to dynamically narrow the list of stories to those containing that string. The only problem is I actually have to type ” Xah”, i.e. with a space before the filter text, since it is appended to the filter that is already present “+unread +emacs” in this case.

Since life is too short to type extra spaces, I wrote a simple wrapper for the elfeed filter command:

;;insert space before elfeed filter
(defun bjm/elfeed-search-live-filter-space ()
  "Insert space when running elfeed filter"
  (let ((elfeed-search-filter (concat elfeed-search-filter " ")))

I add this to the elfeed keybindings when I initialise the package

(use-package elfeed
  :ensure t
  :bind (:map elfeed-search-mode-map
              ("A" . bjm/elfeed-show-all)
              ("E" . bjm/elfeed-show-emacs)
              ("D" . bjm/elfeed-show-daily)
              ("/" . bjm/elfeed-search-live-filter-space)
              ("q" . bjm/elfeed-save-db-and-bury)))

and now I can use / to filter my articles without needing the extra space.

Read your RSS feeds in emacs with elfeed

The package elfeed is an excellent feed reader for Emacs. The project web page has great documentation, and you should read that to cover the basics. I thought I’d share some of the details of my setup. I’ll split this up into a few posts, but today I’ll cover my basic setup and some teaks to give me shortcut keys to groups of feeds, and to help with the syncing of my feeds between multiple machines.

First I have an org-mode file with my list of feeds. Here is an excerpt.

* blogs                                                        :elfeed:
** daily                                                        :daily:
*** http://telescoper.wordpress.com/feed/
*** http://xkcd.com/rss.xml
*** http://timharford.com/feed/
*** http://understandinguncertainty.org/rss.xml
** emacs                                                        :emacs:
*** http://www.reddit.com/r/emacs/.rss
*** http://planet.emacsen.org/atom.xml
*** http://feeds.feedburner.com/XahsEmacsBlog
*** http://pragmaticemacs.com/feed/
*** [[http://emacs.stackexchange.com/feeds][SX]]

We need to use the package elfeed-org to tell elfeed to use the org file above:

;; use an org file to organise feeds
(use-package elfeed-org
  :ensure t
  (setq rmh-elfeed-org-files (list "/path/to/elfeed.org")))

Note how in the org file I have used an org-mode link to the stack exchange feed; the description part of the link (SX in this case) will be used as the feed title in elfeed. I have also collected some feeds together, under the tags “daily” and “emacs”. These tags are used by elfeed to let you view sets of feeds. The first time we run elfeed we will add bookmarks to allow us to jump to those sets of feeds, and to do that we’ll need some simple helper functions, which we need to add to our emacs config file.

;; elfeed feed reader                                                     ;;
;;shortcut functions
(defun bjm/elfeed-show-all ()
  (bookmark-jump "elfeed-all"))
(defun bjm/elfeed-show-emacs ()
  (bookmark-jump "elfeed-emacs"))
(defun bjm/elfeed-show-daily ()
  (bookmark-jump "elfeed-daily"))

Now, I use elfeed on two machines and I keep the elfeed database (stored in ~/.elfeed by default) synchronised between those machines. (I use unison, but you could use dropbox etc). For this to work well we need to make sure elfeed updates its database whenever we quit it, and reads it again when we resume. Here are some helper functions for this:

;;functions to support syncing .elfeed between machines
;;makes sure elfeed reads index from disk before launching
(defun bjm/elfeed-load-db-and-open ()
  "Wrapper to load the elfeed db from disk before opening"

;;write to disk when quiting
(defun bjm/elfeed-save-db-and-bury ()
  "Wrapper to save the elfeed db to disk before burying buffer"

Now let’s install and configure elfeed

(use-package elfeed
  :ensure t
  :bind (:map elfeed-search-mode-map
              ("A" . bjm/elfeed-show-all)
              ("E" . bjm/elfeed-show-emacs)
              ("D" . bjm/elfeed-show-daily)
              ("q" . bjm/elfeed-save-db-and-bury)))

With that, we can launch elfeed with M-x bjm/elfeed-load-db-and-open and (for the first time only) create the bookmarks to our tags. Hit G to refresh the feeds and then create a bookmark with C-x r m and name it elfeed-all to match the name we used in the function above. Now hit s to edit the search filter and add +emacs to the filter string. This should show only your emacs feeds. Create a bookmark named elfeed-emacs for this filter and do the same for any other tags you have used.

With the bookmarks saved, you can now use E in the elfeed window to jump to your Emacs feeds and so on.

If you use q to quit elfeed when you are done, then the database will be saved and your latest changes will be synced to any other machines.

In future posts I’ll look at starring and saving articles with org-mode and a nice simple tweak to feed filtering.


Thanks to the commenters for pointing out I initially forgot to mention elfeed-org – this has been fixed now.