2024-07-14
       Tags: emacs
       
       I decided  to start  using an RSS client, primarily because  I kept
       coming  back to these same few blogs with really nice  content  and
       wanted  to stay updated, but also because the signal-to-noise ratio
       on e.g. Reddit  is horrendous and mindlessly doomscrolling is not a
       good use of my time.
       
       I've  gone  through a real Emacs  ricing stint  lately, and  as the
       lines of elisp in my  config continue  to accrue, so  too does  the
       gravitational  pull  of my  favorite  text-based  operating  system
       gradually increase --- email, IRC, and now  RSS have fallen through
       the event horizon.
       I decided to pick up Elfeed and I wasn't disappointed; you can tell
       that Chris Wellons, despite having turned to the dark side [1], has
       put a lot of thought into the UX and extensibility of it.
       
       == Elfeed Hacks
       
       
       Here are a few lines from  my config that I think might be of value
       to other people:
       (use-package elfeed
           :config
           (defun elfeed-clean-trash (&optional now)
             (interactive "P")
             (with-elfeed-db-visit (entry _)
                 (when (elfeed-tagged-p 'trash entry)
                   (setf (elfeed-entry-content entry) nil)))
             (if now
                 (elfeed-db-gc)
               (run-with-idle-timer 15 nil #'elfeed-db-gc)))
       
           (defface elfeed-search-trashed-title-face
               '((((class color)) (:foreground "red" :weight extra-bold)))
             "Face used in search mode for trashed entries.")
           (push '(trash elfeed-search-trashed-title-face) elfeed-search-face-alist)
       
       So Elfeed doesn't  really have a concept of "deleting" entries, but
       you can delete the content of an entry if you're worried about disk
       space;  given  that  I subscribe to  news aggregators,  this  is  a
       legitimate concern.
           (setq-default elfeed-search-filter "-trash @6-months-ago +unread")
           (setq fysh/elfeed-filters '(("Blogs" . "-trash +blog") ("Comics" . "-trash +webcomic") ("News" . "-trash @1-week-ago +aggregator +unread")))
           (defun fysh/elfeed-pick-filter (filter)
             (interactive (list (alist-get (completing-read "Filter: " (mapcar (lambda (s) (car s)) fysh/elfeed-filters)) fysh/elfeed-filters nil nil 'equal)))
             (setq elfeed-search-filter filter)
             (elfeed-search-update--force)
             (goto-char (point-min)))
           :general-config
           ('normal 'elfeed-search-mode-map "S" #'elfeed-search-live-filter)
           ('normal 'elfeed-search-mode-map "s" #'fysh/elfeed-pick-filter)
       
       I really  like  the interactive search  feature,  but having  a few
       defaults for my most common tags is quite nice.
           :config
           (define-advice elfeed-search-show-entry (:override (entry &optional arg) quick-browse)
             (interactive (list (elfeed-search-selected :ignore-region) current-prefix-arg))
             (require 'elfeed-show)
             (when (elfeed-entry-p entry)
               (elfeed-untag entry 'unread)
               (elfeed-search-update-entry entry)
               (unless elfeed-search-remain-on-entry (forward-line))
               (pcase arg
                 ('(4) (funcall browse-url-browser-function (elfeed-entry-link entry)))
                 ('(16) (funcall browse-url-secondary-browser-function (elfeed-entry-link entry)))
                 (_ (elfeed-show-entry entry))))))
       
       Although I usually prefer using shr to  view entries,  entries from
       news aggregators just contain a link to the  post and a link to the
       comments section; for these I  can  `C-u RET` to open the
       article  directly  in my  browser (I'm using  the surprisingly nice
       `xwidget-webkit`).
       
       Speaking of news aggregators, it's rather annoying to have the same
       blog post appear multiple times from Hacker News  and  Lobsters; to
       address that,  I patched  Elfeed  to generate  the unique id for an
       entry  based  on  its  link,  causing   "hash  collisions"  between
       identical articles.
       I haven't  upstreamed  this because  it's  certainly an antipattern
       (Elfeed fetches feeds asynchronously, so  there's  a race condition
       between  which feed you fetch  a link from  first), but I  tend  to
       filter posts using tags,  not feeds,  so I like it better this way:
       if you're interested, you can get the patch here [2].
       
       Finally,  there's  an   annoying  issue  where  cached  images  are
       displayed  at their  original  resolution,  usually  exceeding  the
       dimensions of the window I'm viewing the post in.
       I originally  thought this was a bug in shr, then realized it was a
       bug  in Elfeed  [3],  but  when  fixing  Elfeed  I  realized  there
       was a related SVG bug in Emacs [4].
       If  you're  impatient  you  can  `(setq  shr-ignore-cache
       t)` as a band-aid fix.
       
       == Bonus Round: Browser Config
       
       (use-package emacs
           :requires xwidget-internal
           :init
           (defun fysh/search (start end)
             "Search selected string using a search engine."
             (interactive "r")
             (let* ((q (buffer-substring-no-properties start end))
                    (query (if (use-region-p) q
                             (read-string (format-prompt "Search" (current-word)) nil nil (current-word)))))
               (browse-url (concat fysh/search-prefix (url-hexify-string query)))))
           :general
           (leader-def "S" #'fysh/search)
       
       There's gotta be a builtin keybinding for this...
           :hook (evil-collection-setup . (lambda (&rest _) (general-def 'normal 'xwidget-webkit-mode-map "J" (lambda () (interactive) (xwidget-webkit-scroll-up (/ (frame-pixel-height) 2))))))
       
       I rarely need to scroll horizontally, and apparently  no  one  else
       does either, because it was broken [5] for quite a while ( ̄ω ̄;)
           :config
           (defun fysh/eww-browse-url-ephemeral (url &rest args)
             (eww-browse-url url args)
             (run-with-timer 5 nil (lambda () (kill-matching-buffers "eww-" nil t))))
           (setq browse-url-handlers '(
                                       ("journals\\.aps\\.org" . (lambda (url &rest args)
                                                                   (let* ((pdf (string-replace "/abstract/" "/pdf/" url)))
                                                                     (if (string-equal url pdf) (browse-url-default-browser url) (eww-browse-url (concat fysh/browse-url-proxy-prefix pdf))))))
                                       ("arxiv\\.org" . (lambda (url &rest args)
                                                          (cond ((string-match-p "/pdf/" url) (fysh/eww-browse-url-ephemeral url args))
                                                                ((string-match-p "/abs/" url) (fysh/eww-browse-url-ephemeral (string-replace "/abs/" "/pdf/" url) args))
                                                                (t (browse-url-default-browser url args)))))
                                       ("pdf$" . fysh/eww-browse-url-ephemeral)
                                       ("." . xwidget-webkit-browse-url)))
       
       One  really  nice feature  of eww is  that PDFs and  other non-text
       files will automatically open in their respective Emacs major mode;
       to  further facilitate  that,  I  mimicked  Zotero's  connectors by
       redirecting arXiv abstracts to their PDFs.
       
       `fysh/browse-url-proxy-prefix`   was  meant  to
       view  PDFs from paywalled  journals  through my university's proxy;
       eww can't auth because  there's  no  JS  engine,  so  I  considered
       reusing cookies from my primary browser, but that sounds like a lot
       more work than just Gib Lenning the papers instead 😉
           (define-advice xwidget-kill-buffer-query-function (:override () always-kill) t))
       
       I  don't  like being  asked to  kill buffers;  `(defalias
       'yes-or-no-p 'y-or-n-p)` usually does the trick, but take
       this as my parting advice for the reader.
       
       == Feeds I Read
       
       
       === Scott Aaronson [6] cs, quantum
       
       
       === Andrew Kelley [7] zig, systems
       
       
       === Xe Iaso [8] nix
       
       
       === Artemis [9] nix
       
       
       === Jade Lovelace [10] nix
       
       
       === Ian Henry [11] nix
       
       
       === Jakob Kreuze [12] emacs, security
       
       
       === Jon Sangster [13] nix, emacs
       
       
       === Chris Wellons [14] emacs, systems
       
       
       === Evan Ovadia [15] vale, systems
       
       
       === Karthinks [16] emacs
       
       
       === XKCD [17] webcomic
       
       
       === Leftover Salad [18] webcomic
       
       
       References:
 (HTM)   [1] turned to the dark side
 (TXT)   [2] here
 (HTM)   [3] bug in Elfeed
 (HTM)   [4] bug in Emacs
 (HTM)   [5] broken
 (HTM)   [6] Scott Aaronson
 (HTM)   [7] Andrew Kelley
 (HTM)   [8] Xe Iaso
 (HTM)   [9] Artemis
 (HTM)   [10] Jade Lovelace
 (HTM)   [11] Ian Henry
 (HTM)   [12] Jakob Kreuze
 (HTM)   [13] Jon Sangster
 (HTM)   [14] Chris Wellons
 (HTM)   [15] Evan Ovadia
 (HTM)   [16] Karthinks
 (HTM)   [17] XKCD
 (HTM)   [18] Leftover Salad
       >=================================================================<
       
 (DIR) Blog
 (DIR) Writeups
 (DIR) jp
       
       copyright 2026 George Huebner
 (HTM) email