Using mu4e

A thread on Reddit nudged me to try the Emacs mail readers again. I have tried a bunch of them (Wanderlust, Gnus, mew) but found them either too idiosyncratic or painful to use, and for the most part just read my e-mail by logging into Google directly.

However, I just installed mu / mu4e and it works really well — well enough that I prefer reading e-mail in Emacs now, as opposed to just logging into Google mail and reading it there. I ended up archiving about 10 years of mail (approximately 10k of messages) off Gmail and mu can re-index it in seconds.

Installing offlineimap

I used offlineimap to sync everything with Gmail. Steal this configuration and put it in ~/.offlineimaprc (or whatever — but don’t name it .offlineimap, because that will cause errors). For my laptop I added:

maxage = 5
autorefresh = 15

… to keep only 5 days of mail and to automatically update it every 15 minutes.

Installing mu4e

The version of mu4e available in Ubuntu 12.04LTS is really old and throws spurious errors, so you’ll need to build your own. Get mu from github and then rebuild. I needed a few extra libraries in my installation.

sudo apt-get install libglib2.0-dev libgmime-2.6-dev libxapian-dev
sudo apt-get install texinfo
autoreconf -i

Installing into Emacs

Change into mu4e, do a make / sudo make install. I used pretty much just the longer configuration in the documentation, although I put everything into a deferred load (code below), and there are the following caveats:

  • Viewing a message in a browser is handy, so I took the code from the Emacs Wiki.
  • I needed to override mu4e-html2text-command because about 10% of HTML messages weren’t rendering.
  • The documentation says to set mu4e-view-show-images to show images, but it’s actually mu4e-show-images.
(defun email () 
  (interactive)
 
  (when (not (featurep 'mu4e))
    (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e/")
 
    (require 'mu4e)
    (require 'org-mu4e)
 
    ;; defaults
 
    (setq mu4e-maildir "~/Maildir")
    (setq mu4e-drafts-folder "/gmail/[Gmail].Drafts")
    (setq mu4e-sent-folder   "/gmail/[Gmail].Sent Mail")
    (setq mu4e-trash-folder  "/gmail/[Gmail].Trash")
 
    ;; don't save message to Sent Messages, Gmail/IMAP takes care of this
    (setq mu4e-sent-messages-behavior 'delete)
 
    ;; setup some handy shortcuts
    ;; you can quickly switch to your Inbox -- press ``ji''
    ;; then, when you want archive some messages, move them to
    ;; the 'All Mail' folder by pressing ``ma''.
 
    (setq mu4e-maildir-shortcuts
          '( ("/gmail/INBOX"               . ?i)
             ("/gmail/[Gmail].IMPORTANT"   . ?!)
             ;; ("/gmail/[Gmail].Sent Mail"   . ?s)
             ;; ("/gmail/[Gmail].Trash"       . ?t)
             ("/gmail/[Gmail].All Mail"    . ?a)))
 
    ;; allow for updating mail using 'U' in the main view:
    ;; I have this running in the background anyway
    ;; (setq mu4e-get-mail-command "offlineimap")
 
    ;; something about ourselves
    (setq
     user-mail-address "user@domain"
     user-full-name  "username"
     message-signature nil)
 
    ;; sending mail -- replace USERNAME with your gmail username
    ;; also, make sure the gnutls command line utils are installed
    ;; package 'gnutls-bin' in Debian/Ubuntu
 
    (require 'smtpmail)
 
    (if (string< emacs-version "24")
        (setq message-send-mail-function 'smtpmail-send-it
              starttls-use-gnutls t
              smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil))
              smtpmail-auth-credentials
              '(("smtp.gmail.com" 587 "user@domain" nil))
              smtpmail-default-smtp-server "smtp.gmail.com"
              smtpmail-smtp-server "smtp.gmail.com"
              smtpmail-smtp-service 587)
 
      ;; alternatively, for emacs-24 you can use:
      (setq message-send-mail-function 'smtpmail-send-it
            smtpmail-stream-type 'starttls
            smtpmail-default-smtp-server "smtp.gmail.com"
            smtpmail-smtp-server "smtp.gmail.com"
            smtpmail-smtp-service 587)
      )
 
 
    ;; don't keep message buffers around
    (setq message-kill-buffer-on-exit t)
 
    ;; show images
    (setq mu4e-show-images t)
 
    ;; use imagemagick, if available
    (when (fboundp 'imagemagick-register-types)
      (imagemagick-register-types))
 
    ;;; message view action
    (defun mu4e-msgv-action-view-in-browser (msg)
      "View the body of the message in a web browser."
      (interactive)
      (let ((html (mu4e-msg-field (mu4e-message-at-point t) :body-html))
            (tmpfile (format "%s/%d.html" temporary-file-directory (random))))
        (unless html (error "No html part for this message"))
        (with-temp-file tmpfile
          (insert
           "<html>"
           "<head><meta http-equiv=\"content-type\""
           "content=\"text/html;charset=UTF-8\">"
           html))
        (browse-url (concat "file://" tmpfile))))
 
    (add-to-list 'mu4e-view-actions
                 '("View in browser" . mu4e-msgv-action-view-in-browser) t)
 
    ;; convert org mode to HTML automatically
    (setq org-mu4e-convert-to-html t)
 
    ;; need this to convert some e-mails properly
    (setq mu4e-html2text-command "html2text -utf8 -width 72")
  )
  (mu4e)
)
 
(defalias 'org-mail 'org-mu4e-compose-org-mode)

Handy Keys in mu4e

  • F — to forward
  • C — to compose
  • O — opens an attachment in detail view
  • aV — views mail in HTML mode
  • ji, j!, ja — jump to mailbox. (See mu4e-maildir-shortcuts above).
  • mi, m!, ma — move message to mailbox
  • V — turn on/off duplicates (handy if you’re looking at All Mail)
  • W — turn on/off related messages
  • / — narrow the current view via search
  • ?, d, m — mark for unread / delete / move
  • x — execute deferred marks

Org Mode

One of the neat things about mu4e is the fact that you can send messages built in org-mode, although the current implementation is a little flaky. Unless you do things the right way you’ll often get “Error 70,” and a plain unadorned e-mail will be sent out. Unfortunately, my friends and associates are not yet at that advanced stage of development that they’ll read org-mode format directly.

To use org-mode via compose, you:

  • Compose a message
  • Run M-x org-mu4e-compose-org-mode (hah, note my alias above)
  • And, finally, and most important: you must send the e-mail via ^C ^S only when the cursor is in the header. If you try to send it while the cursor is in the body of the e-mail, it won’t work.

Yeah, not ideal. But hopefully fixable and it isn’t too hard in practice; only a small percentage of the e-mails I send out are rich text.

You may want to change the default formatting options; I found that having a Table of Contents in my e-mails was a bit too pretentious, and the formatting for code blocks left too much white space. Apply the following diff:

diff --git a/mu4e/org-mu4e.el b/mu4e/org-mu4e.el
index 44d5ea1..6697486 100644
--- a/mu4e/org-mu4e.el
+++ b/mu4e/org-mu4e.el
@@ -170,7 +170,9 @@ and images in a multipart/related part."
            ;; because we probably don't want to export a huge style file
            (org-export-htmlize-output-type 'inline-css)
            ;; makes the replies with ">"s look nicer
-           (org-export-preserve-breaks t)
+           (org-export-preserve-breaks nil)
+            ;; turn off table of contents
+            (org-export-with-toc nil)
            ;; dvipng for inline latex because MathJax doesn't work in mail
            (org-export-with-LaTeX-fragments 'dvipng)
            ;; to hold attachments for inline html images

Org Mode Example

And, okay, to be honest it’s not entirely useful in the day to day, but it is pretty neat that if you need to you can send an e-mail via:

Remember we were talking about functions of the form $f(x) = x^2$ the other day?  I wanted to show you what it looked like.
 
#+name: python-data
#+BEGIN_SRC python :exports both :results replace
return [ (ix, ix*ix) for ix in xrange(0,11) ]
#+END_SRC
 
It's hard to visualize this, though, so it helps to look at the graph.
 
#+BEGIN_SRC R :var dat=python-data :exports both :results output graphics :file graph.png
library(ggplot2)
ggplot(data=dat, aes(x=V1, y=V2))+geom_point()
#+END_SRC

… to get