I’d like to share some pointers for using Gnus together with notmuch rather than notmuch together with notmuch’s own Emacs interface, notmuch.el. I set about this because I recently realised that I had been poorly reimplementing lots of Gnus features in my init.el, primarily around killing threads and catching up groups, supported by a number of complex shell scripts. I’ve now switched over, and I’ve been able to somewhat simplify what’s in my init.el, and drastically simplify my notmuch configuration outside of Emacs. I’m always more comfortable with less Unix and more Lisp when it’s feasible.

  • The basic settings are gnus-search-default-engines and gnus-search-notmuch-remove-prefix, explained in (info "(gnus) Searching"), and an entry for your maildir in gnus-secondary-select-methods, explained in (info "(gnus) Maildir"). Then you will have G G and G g in the group buffer to make and save notmuch searches.

  • I think it’s important to have something equivalent to notmuch-saved-searches configured programmatically in your init.el, rather than interactively adding each saved search to the group buffer. This is because, as notmuch users know, these saved searches are more like permanent, virtual inboxes than searches. You can learn how to do this by looking at how gnus-group-make-search-group calls gnus-group-make-group. I have some code running in gnus-started-hook which does something like this for each saved search:

      (if (gnus-group-entry group)
          (gnus-group-set-parameter group 'nnselect-specs ...)
        (gnus-group-make-group ...))
    

    The idea is that if you update your saved search in your init.el, rerunning this code will update the entries in the group buffer. An alternative would be to just kill every nnselect search in the group buffer each time, and then recreate them. In addition to reading gnus-group-make-search-group, you can look in ~/.newsrc.eld to see the sort of nnselect-specs group parameters you’ll need your code to produce.

    I’ve very complicated generation of my saved searches from some variables, but that’s something I had when I was using notmuch.el, too, so perhaps I’ll describe some of the ideas in there in another post.

  • You’ll likely want to globally bind a function which starts up Gnus if it’s not already running and then executes an arbitrary notmuch search. For that you’ll want (unless (gnus-alive-p) (gnus)), and not (unless (gnus-alive-p) (gnus-no-server)). This is because you need Gnus to initialise nnmaildir before doing any notmuch searches. Gnus passes --output=files to notmuch and constructs a summary buffer of results by selecting mail that it already knows about with those filenames.

  • When you’re programmatically generating the list of groups, you might also want to programmatically generate a topics topology. This is how you do that:

      (with-current-buffer gnus-group-buffer
        (gnus-topic-mode 0)
        (setq gnus-topic-alist nil gnus-topic topology nil)
        ;; Now push to those two variables.  You can also use
        ;; `gnus-topic-move-matching' to move nnmaildir groups into, e.g.,
        ;; "misc".
        (gnus-topic-mode 1)
        (gnus-group-list-groups))
    

    If you do this in gnus-started-hook, the values for those variables Gnus saves into ~/.newsrc.eld are completely irrelevant and do not need backing up/syncing.

  • When you want to use M-g to scan for new mail in a saved search, you’ll need to have Gnus also rescan your nnmaildir inbox, else it won’t know about the filenames returned by notmuch and the messages won’t appear. This is similar to the gnus vs. gnus-no-server issue above. I’m using :before advice to gnus-request-group-scan to scan my nnmaildir inbox each time any nnselect group is to be scanned.

  • If you are used to linking to mail from Org-mode buffers, the existing support for creating links works fine, and the standard gnus: links already contain the Message-ID. But you’ll probably want opening the link to perform a notmuch search for id:foo rather than trying to use Gnus’s own jump-to-Message-ID code. You can do this using :around or :override advice for org-gnus-follow-link: look at gnus-group-read-ephemeral-search-group to do the search, and then call gnus-summary-goto-article.

I don’t think that the above is especially hacky, and don’t expect changes to Gnus to break any of it. Implementing the above for your own notmuch setup should get you something close enough to notmuch.el that you can take advantage of Gnus’ unique features without giving up too much of notmuch’s special features. However, it’s quite a bit of work, and you need to be good at Emacs Lisp. I’d suggest reading lots of the Gnus manual and determining for sure that you’ll benefit from what it can do before considering switching away from notmuch.el.

Reading through the Gnus manual, it’s been amazing to observe the extent to which I’d been trying to recreate Gnus in my init.el, quite oblivious that everything was already implemented for me so close to hand. Moreover, I used Gnus ten years ago when I was new to Emacs, so I should have known! I think that back then I didn’t really understand the idea that Gnus for mail is about reading mail like news, and so I didn’t use any of the features, back then, that more recently I’ve been unknowingly reimplementing.