____ < Mu > ---- \ ^__^ \ (**)\_______ (__)\ )\/\ U ||----w | || ||
For something I use so much I've never really liked any of the tools I use for email. When I started using email in the mid nineties, I was using Eudora; after that I used QuickMail in my first fulltime job, then I used VM and later Lotus Notes while working at IBM, followed by Outlook, Mail.app and then Gmail. The least horrible of all these is without doubt Gmail, but the lack of a unified inbox for several accounts makes it really complicated to stay on top of my email.
Every now and then I've been trying out different non-GUI approaches to email, but none of them really sticked. Over the last month or so I've been using a combination of OfflineIMAP and msmtp for the backend stuff, and then Mutt for the frontend stuff while waiting for Thomas to provide an early alpha of his zen tool for email. With a minimal amount of tweaking this has let me write my email in Emacs, which means a lot. Just having autofill within reach saves me from looking like a complete idiot, all the keybindings are where they should be, and still … Mutt.
A few days ago, in the middle of a rant against OfflineIMAP, someone mentioned an Emacs based email client called mu4e - Mu for Emacs. I've been using it for an entire day, so I'm just about ready to conclude that the search for a nice email client has ended.
Mu4e is an Emacs frontend to Mu, which is a database for Maildir. As you probably know, Maildir is how most mail servers and tools like OfflineIMAP store email. In order to use Mu (after installing it, of course), you just tell it to go index all your email
mu index --maildir=~/Maildir
and then you're ready to start searching your email:
mu find poker mu find 'subject:poker' from:buddy
To make this work, I'm using two dedicated programs:
Maildir
directory
in my home folder.
I really like the way these programs don't need to know about each
other to work; they simply put files into my Maildir
directory,
index them and search them. I can use tools like find
, grep
,
cat
and wc
to learn what's in the mail, or I can use
higher-level tools like Mu.
Although Mu is really nice, Mu for Emacs (mu4e) is the real gem here. It's an Emacs app for email, leaning on a lot of tools to provide a kick ass email experience:
Mu4e is organized around searches, and starts by presenting you with a menu which looks like this on my computer:
Basics * [j]ump to some maildir * enter a [s]earch query * [C]ompose a new message Bookmarks * [bm] Unread on the mailing list * [bz] Today's news * [bu] Unread messages * [bt] Today's messages * [bw] Last 7 days * [bp] Messages with images Misc * [U]pdate email & database * [A]bout mu4e * [H]elp * [q]uit
I've set up some folder shortcuts, so pressing j
from the menu
lets me jump directly to my Gmail account (z
) by pressing jz
,
or my Gitorious inbox (g
) using jg
:
(setq mu4e-maildir-shortcuts '(("/Gitorious/inbox" . ?g) ("/Gmail/inbox" . ?z)))
The bookmarks section lists bookmarks, which are saved searches. Mu4e ships with a few useful defaults ("Today's messages", "Unread messages"), but it's really easy to set up new ones. The "Unread on the mailing list" bookmark, for example, is defined here:
(add-to-list 'mu4e-bookmarks '("maildir:/Gmail/gitorious-ml flag:unread" "Unread on the mailing list" ?m))
Since it's just a mu search as a string, it's really easy to
experiment with, simply use the mu
program:
mu find maildir:/Gmail/gitorious-ml flag:unread
It doesn't stop here, however. Once you're looking at a list of
messages, for example the mailing list bookmark above, you can
narrow the list of messages further using /
. Looking at the
list of messages in the mailing list, I enter
/from:thomas<RET>
and the list is filtered to show only unread messages to this
mailing list sent by someone named thomas. Widen the search again
with a \
. Oh, and did I mention that mu is really fast? These
searches complete instantly. And contrary to many Emacs apps,
especially for email, mu4e doesn't block Emacs - except when
sending messages.
If you use email, the answer is yes. If you don't use Emacs yet, you need to set aside some time for this, but it's going to be worth it.
Personally I appreciate the learning involved in setting up my Emacs environment myself, but if you're the copy/paste, I've entered my complete setup below.
Mu4e has extensions points where I needed them, and since mu4e does
not assume any special kind of structure in my ~/Maildir
, it's
not aware of my multi-account setup in any way.
This is my mu4e configuration, included because the Github org-mode rendering leaves a little to be desired.
(add-to-list 'load-path (concat dotfiles-dir "contrib/mu4e")) (require 'mu4e) (setq mu4e-get-mail-command "offlineimap") (setq mu4e-drafts-folder "/Gitorious/drafts") (setq mu4e-user-mail-address-list (list "marius@shortcut.no" "marius@gitorious.org" "zmalltalker@zmalltalker.com" "marius.mathiesen@gmail.com" "marius@gitorious.com")) ;; (setq user-mail-address-list "marius@gitorious.com") (setq message-kill-buffer-on-exit t) ;; Use fancy chars (setq mu4e-use-fancy-chars t) ;; Shortcuts (setq mu4e-maildir-shortcuts '(("/Gitorious/inbox" . ?g) ("/Gmail/inbox" . ?z) ("/Shortcut/inbox" . ?s) ("/Gmail/gitorious-ml" . ?m) )) ;; Smart refile locations (setq mu4e-refile-folder (lambda (msg) (cond ;; messages sent directly to me go to /archive ;; also `mu4e-user-mail-address-regexp' can be used ((mu4e-message-contact-field-matches msg :to "marius@gitorious") "/Gitorious/archive") ((mu4e-message-contact-field-matches msg :to "marius.mathiesen@gmail.com") "/Gmail/archive") ((mu4e-message-contact-field-matches msg :to "zmalltalker@zmalltalker.com") "/Gmail/archive") ((mu4e-message-contact-field-matches msg :to "marius@shortcut.no") "/Shortcut/archive") ;; everything else goes to /archive ;; important to have a catch-all at the end! (t "/Gmail/archive")))) ;; don't save message to Sent Messages, Gmail/IMAP takes care of this (setq mu4e-sent-messages-behavior 'delete) ;; Try to display images in mu4e (setq mu4e-view-show-images t mu4e-view-image-max-width 800) ;; sending mail (setq message-send-mail-function 'message-send-mail-with-sendmail sendmail-program "/usr/bin/msmtp" user-full-name "Marius Mårnes Mathiesen") (setq mu4e-confirm-quit nil mu4e-headers-date-format "%d/%b/%Y %H:%M" ; date format mu4e-html2text-command "html2text -utf8 -width 72" ) ;; Borrowed from http://ionrock.org/emacs-email-and-mu.html ;; Choose account label to feed msmtp -a option based on From header ;; in Message buffer; This function must be added to ;; message-send-mail-hook for on-the-fly change of From address before ;; sending message since message-send-mail-hook is processed right ;; before sending message. (defun choose-msmtp-account () (if (message-mail-p) (save-excursion (let* ((from (save-restriction (message-narrow-to-headers) (message-fetch-field "from"))) (account (cond ((string-match "marius.mathiesen@gmail.com" from) "gmail") ((string-match "zmalltalker@zmalltalker.com" from) "gmail") ((string-match "marius@shortcut.no" from) "shortcut") ((string-match "marius@gitorious.com" from) "gitorious") ((string-match "marius@gitorious.org" from) "gitorious")))) (setq message-sendmail-extra-arguments (list '"-a" account)))))) (setq message-sendmail-envelope-from 'header) (add-hook 'message-send-mail-hook 'choose-msmtp-account) (add-to-list 'mu4e-bookmarks '("maildir:/Gitorious/inbox OR maildir:/Shortcut/inbox OR maildir:/Gmail/inbox flag:unread" "Today's news" ?z)) (add-to-list 'mu4e-bookmarks '("maildir:/Gmail/gitorious-ml flag:unread" "Unread on the mailing list" ?m))
Some further customizations below.
When replying to an email I want to use the address I received this message to as the sender of the reply. This is fairly trivial:
(add-hook 'mu4e-compose-pre-hook (defun my-set-from-address () "Set the From address based on the To address of the original." (let ((msg mu4e-compose-parent-message)) ;; msg is shorter... (if msg (setq user-mail-address (cond ((mu4e-message-contact-field-matches msg :to "@gitorious") "marius@gitorious.com") ((mu4e-message-contact-field-matches msg :to "marius@shortcut.no") "marius@shortcut.no") ((mu4e-message-contact-field-matches msg :to "marius.mathiesen@gmail.com") "zmalltalker@zmalltalker.com") ((mu4e-message-contact-field-matches msg :to "zmalltalker@zmalltalker.com") "zmalltalker@zmalltalker.com") (t "marius.mathiesen@gmail.com")))))))
Wouldn't it be awesome to be able to send files from dired using your mail client?
I'll need a special version of the gnus-dired-mail-buffers function so it understands mu4e buffers as well:
(require 'gnus-dired) ;; make the `gnus-dired-mail-buffers' function also work on ;; message-mode derived modes, such as mu4e-compose-mode (defun gnus-dired-mail-buffers () "Return a list of active message buffers." (let (buffers) (save-current-buffer (dolist (buffer (buffer-list t)) (set-buffer buffer) (when (and (derived-mode-p 'message-mode) (null message-sent-message-via)) (push (buffer-name buffer) buffers)))) (nreverse buffers))) (setq gnus-dired-mail-mode 'mu4e-user-agent) (add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)
With this, I can attach a file as an attachment to a new email
message by entering C-c RET C-a
, and I'm good to go.
Although U
from the mu4e menu is simple, it's even better
(maybe) to have mu4e fetch mail (and update the mu index)
automatically. Simply set mu4e-update-interval
to the number of
seconds between each check. Let's go with every 10 minutes.
(setq mu4e-update-interval 600)