Emacs carnival: Elevator pitch (or How I learned to love RSS for the fediverse)

This blog post is written as part of the August 2025 edition of the Emacs Carnival with a focus on an elevator pitch for Emacs.

The pitch

Emacs is the only tool you need to use for manipulating text, including writing prose, programming, file management, social media. This is due to a combination of three features of Emacs: discovery, stable, malleable. No other tool I am aware of has this combination.

discovery
You can find out exactly what is going on all the time. There is a direct link to documentation and code from the interface. This includes finding out what each key stroke will do and what every function (interactive commands or otherwise) is meant to do. The help system includes full manuals on Emacs generally but also large systems within Emacs (gnus, Calc, org mode, magit, etc.). It also allows you to search for symbols which includes variables and functions. When a help window pops up in response to a query you might have made, you get not only the documentation but also an actual link to the code where the variable or function is defined.
stable
Although Emacs is continually being developed, features that existed in Emacs years, maybe decades, ago still function as they did. I have lines in my Emacs initialization that date back to when I started using Emacs mumble years ago. They continue to do what they did back then. I seldom need to worry about having to update my configuration due to developments in Emacs. The user interface is the same now (albeit prettier) than it was back in 1984 (say 😉).
malleable
Arguably the most special aspect is that everything Emacs does can be modified by you, its user. No other tool is this flexible. This includes the following:
  • most functionality has associated variables that you can adjust to get the behaviour you want, often through the built-in customization system (M-x customize RET).
  • many modes (e.g. text mode, org mode, gnus) have hooks which allow you to tell Emacs to execute code when something specific happens, e.g. you save a file. The code can be as simple as asking for lines to wrap visually (visual-line-mode) on the screen or as complex as inserting text directly in the buffer you are editing (e.g. setting the date of an org document using the time-stamp package).
  • as the code for every single aspect of Emacs is available (just a couple of key strokes away), you can actually edit that code1 and immediately make it active through eval-defun, for example, bound to M-C-x by default in Elisp mode. Emacs is a live Lisp environment.

For completeness, these are some other pitches from others participating in this month's Emacs carnival: consistency, universal, no reason not to, it works for you, & well worth it!

Case study

What follows is an example of what is possible in Emacs that would be either impossible in any other system or very difficult.

My social media presence is mostly on the fediverse via mastodon. I follow many people and also follow various hashtags. As the fediverse has no algorithm deciding what you should see, hashtags are used to create implicit collections of toots of interest. Due to the large number of people and hashtags I follow, my home timeline is rather busy. I will often miss toots (posts) that I would like to have read. Emacs to the rescue!

I use gnus in Emacs to read my email and newsgroups, the latter including a wide range of blogs via gwene. Gnus, unsurprisingly,2 also supports RSS feeds. And mastodon allows you to access individual feeds, including people and hashtags, using RSS. So I use the RSS capability in gnus to read posts from specific individuals and, more importantly, specific hashtags (one of which is #Emacs obviously).

The problem is that the RSS entries that are created by mastodon, actually ActivityPub more generally, are not fully populated. They do not specify an author. They also do not have a subject for the entry but that's not necessarily the fault of ActivityPub, more that toots do not have titles generally. So the result is that when I open a group in gnus for an RSS feed, say for the #Emacs hashtag, I see this:

emacs-elevator-pitch-gnus-summary-before.png

Figure 1: Screenshot of the gnus summary buffer for the Emacs hashtag RSS feed.

This is not exactly brimming full with information! Implicitly, I know that all of these articles (in gnus parlance) are toots that have the #Emacs hashtag. But that's all I know. It would be nice if I could have a presentation of the summary giving some more details. For instance, it would be nice to know the author of each article.

So, an itch to scratch. This is where Emacs shines in comparison with any other tool. As noted above, I believe the strength (the superpower) of Emacs is the combination of discoverability, stability, and malleability. Let's scratch that itch.

First, discover how gnus, and specifically, the RSS backend generates the information it displays in the gnus summary buffer. The backend is nnrss so we use one of the discovery tools built into Emacs:

M-x find-library RET nnrss RET

which opens up the file that implements the nnrss backend: /home/[...]/pd/emacs/share/emacs/31.0.50/lisp/gnus/nnrss.el.gz in the case of the system I am currently using.3 It's then a case of having a look at the source code. Eventually, I find that the function nnrss-retrieve-headers does the job of extracting the relevant information.

Looking at this function, I see that the normal approaches to customising the behaviour of Emacs, specifically custom variables and hooks, are not possible here. I can, however, make a copy of this function and modify it. If I ensure that my copy is loaded after the default version, I can have this function do anything I want without (hopefully) affecting the rest of gnus's behaviour.

Long story short, I modify the function to extract the author of the article (the toot) from the URL of the toot. I also extract, from the actual contents of the toot, the hashtags the author included. The list of hashtags is used as the subject for each article.

The result is shown here (with older messages using the default and newer ones with my modified function):

emacs-elevator-pitch-gnus-summary-after.png

Figure 2: Screenshot of the gnus summary buffer for the Emacs hashtag RSS feed.

The lines from the highlighted line downwards correspond to toots that have appeared since I modified the code. Previously retrieved article headers are visible above the hightlighted line.

The new summary information is immediately much more informative. Further, gnus has grouped (threaded) some of the articles in this group based on their common subject. This is not real threading but I find it useful nonetheless, more than I had expected when I wrote this code.

The only aspect I have not mentioned so far is stability. The stability aspect comes into play by noting that copying and changing the behaviour of a single function in a library or package may lead to bit rot over time. If the author or authors of the specific package are continually developing the package, I would have to follow their progress and ensure that my own copy, with my modifications, was updated to be consistent.4

Looking at the version log for nnrss.el, I see that the last substantial change (not including, for instance, changing the copyright year), was in 2021. I should be fine for some time, maybe. Time will tell!

Technical details

I changed two lines in nnrss.el in the nnrss-retrieve-headers function. The first line, line 142 of nnrss.el (the version I based my modifications on), deals with the author and was originally:

(or (nth 4 e) "(nobody)")

and is now

(or (nth 4 e) (esf/mastodon-url-author (nth 2 e)))

The e variable is a list of details of the current RSS post. The 4th (0-based index) entry of the list is the author. The 2nd is the URL of the RSS post. I wrote a simple function to convert the URL to the usual way of writing user handles in the fediverse:

(defun esf/mastodon-url-author (url)
  (if url
      (replace-regexp-in-string "https://\\([^/]+\\)/\\(.*\\)/[^/]*$" "\\2@\\1" url)
    "(nobody on mastodon)"))

The second line I changed, for the subject, was line 139:

(or (nth 3 e) "")

and which now is

(or (nth 3 e)
    (esf/mastodon-subject-tags-from-message (nth 6 e)))

which uses the 6th entry in e, the full body of the contents of the RSS entry. My new function collects the tags found in that entry:

(defun esf/mastodon-subject-tags-from-message (contents)
  (if contents
      (let ((subject ""))
        (with-temp-buffer
          (insert contents)
          (goto-char (point-min))
          (while (search-forward-regexp "#<span>\\([^<]+\\)</span>" nil t)
            (setq subject (concat subject (match-string 1) " "))))
        subject)
    "(no tags)"))

These are not guaranteed to work properly for all RSS entries but they seem to work for that subset that I look at.

Blog navigation

References

Footnotes:

1

You do need to be able to understand and write Emacs Lisp but that's a good thing: Lisp is a wonderful language and well worth some effort in learning, and you have all of the Emacs code available to see real world examples!

2

Unsurprising because the general aphorism about Emacs is if you want to do something, Emacs already likely has that feature.

3

I typically build Emacs myself from the latest development version every few weeks or so. The location of the actual file will differ for you. Try it and see!

4

It is often interesting to see people on the Interweb make sometimes absurd comments along the line of This package has not had any changes in 6 months. It must have been abandoned. I will have to find something else to use. When a package has been written to do a particular job, and if it does so correctly and fully, it no longer needs changing. Much of Emacs fits into this category. Not abandoned, just stable.

Author: Copyright © Professor Emeritus Eric S Fraga

Created: 2025-08-14 Thu 14:49

Validate