mu/emacs/mu4e-tech.org

5.7 KiB
Raw Blame History

mu4e

I haven't written many emacs-fu posts recently, but that doesn't mean I haven't used emacs a lot. In fact, over the last few months I've been working on a bigger emacs-related project; the working title is mu4e, and it's an emacs-based e-mail client based on my mu maildir searcher/indexer that I discussed before. Even though I've been using mu4e myself for about two months, it's not really ready from prime-time yet - but I'm planning to have something ready this year still.

In this post, let me discuss some of the technical background, which may be interesting for others planning emacs-based front-ends to other tools.

How to make an emacs-based e-mail client

Emacs does not (as of today) support threads; but one way to do asynchronous processing is to start another process, and let emacs deal with its output. Let us see how…

Getting output from mu

One way to implement this (for mu), is to call the mu comu4eand-line tool with some parameters and then parse its output. In fact, that is how some tools do it, and it was my first approach - so I would invoke mu find and then process the output in emacs (more about that in a minute).

However, then I realized that I'd need to load the entire e-mail Xapian database for each invocation. Wouldn't it be nicer to keep a running mu instance around? Indeed, it would - so I implemented the mu server sub-comu4eand. Now, when you run mu server, you get a shell, in which you can give comu4eands to mu, and which will then spit out the results. mu server is not really meant for humans, but still I can use it manually, which is great for debugging.

The next question was what format mu should use for its output for emacs to process. Some other programs use JSON here, but I figured that it would be easier (and possibly, more efficient) just to use emacs' native s-expressions (plists to be precise). So that is what I did - and I can easily evaluate them using read-from-string.

Processing the output in emacs

So, now let's look how we process the data from mu server in emacs.

First you create a process with, for example, start-process, and then register a filter function for it, which will be invoked whenever the process has some chunk of output. Something like:

  (let ((proc (start-process <arguments>)))
    (set-process-filter proc 'my-process-filter)
    (set-process-sentinel proc 'my-process-sentinel))

Note, the process sentinel is invoked when the process is terminated so there you can clean things up.

The function my-process-filter is a user-defined function that takes the process and the chunk of output as arguments; in mu4e it looks something like (pseudo-lisp):

(defun my-process-filter (proc str)
  (setq mu4e/buf (concat mu4e/buf str)) ;; a global var updated with the new chunk
  (when <we-have-received-a-full-expression>
      <eat-expression-from mu4e/buf> 
      <evaluate-expression>))

The <evaluate-expression> de-multiplexes the s-expression we got. For example, if the s-expression looks like an e-mail message header, it will be processed by the header-handling function, which will append it to the header list. If the s-expression looks like an error message, it will be reported to the user. And so on.

Finally, let me try to answer some anticipated questions:

Why does the world need yet another e-mail client?

I don't the world needs another client, but I spend a lot of time (professionally and privately) with my e-mail client, so I'd like it to behave exactly like I want it to. An even more important goal for me was to write some bigger program in emacs lisp, to better understand the language and its idioms.

Specifically, when it comes to emacs-based clients, I have tried a few of them. I never really got into gnus; I think it is by far the most popular emacs-based mail client, but I found it hard to make behave the way I like it; and in particular, I do not like its indirect approach to Maildirs.

Then, for some years I've been using Wanderlust; a fine, very feature-rich client, but it shows its age - and especially with emacs-24, its cache file got corrupted very often, requiring me to delete them etc. Still, you will recognize some Wanderlust features in mu4e/mu.

Why not use notmuch? It seems similar.

There are certainly similarities with notmuch (and to some lesser extent, with md) the overall architecture is similar: both are scanning maildirs, using GMime and Xapian. (mu precedes notmuch by a year or so; but notmuch was the first to add an emacs front-end).

There are some differences as well. The main thing is that in notmuch's philosophy, messages are usually not moved or deleted, but instead uses tags in the database. While tags are nice, I like the 'state' to be in the messages and the folders they are in, which it easy to synchronize with other email clients (or synchronize with IMAP-folders through OfflineIMAP). I'd like to be able to move messages around, delete messages and so on. This is in fact one of the things I liked in Wanderlust, and wouldn't want to live without - so mu=/=mu4e make this really easy.

Clearly, the emacs-interface to notmuch is more mature, and the development team is bigger, so I'd give it a try. On the other hand, if you happen to like e-mail the way I like it, mu4e may be something for you.