In Email Heaven with Emacs, mu4e, and mbsync
23 Nov 2021
All mail clients suck. This one just sucks less.
— Mutt
I was a long-time mutt user. Come to think of it, I think the reason why I learned Emacs was because that was what $EDITOR
was set to, and I needed to know how to get around so I could send email via mutt.
As more of my life has gotten sucked into Emacs, I’ve wanted to move my email into Emacs as well. I’ve been using a combination of mu4e and isync/mbsync to manage my email. I’m using Pobox for email hosting, and my inbox has never been so consistently near empty before.
Email Setup #
I keep the following in a file that I require lazily in my Emacs config:
(setq mu4e-mu-binary "/usr/local/bin/mu")
;; Folders
(setq mu4e-sent-folder "/Sent")
(setq mu4e-drafts-folder "/Drafts")
(setq mu4e-trash-folder "/Trash")
(setq mu4e-refile-folder "/Archive")
(setq mu4e-attachment-dir "~/Downloads")
;; Signature stuffs
;; (setq user-full-name "")
;; (setq user-mail-address "")
;; (setq mu4e-compose-signature "")
;; (setq smtpmail-smtp-server "")
;; (setq smtpmail-smtp-user "")
(setq smtpmail-stream-type 'starttls)
(setq smtpmail-smtp-service 587)
;; When mu upgrades, run `byte-recompile-directory' on this folder
;; (byte-recompile-directory "/usr/local/Cellar/mu/1.6.8/share/emacs/site-lisp/mu/mu4e")
(add-to-list 'load-path "/usr/local/Cellar/mu/1.6.8/share/emacs/site-lisp/mu/mu4e")
(require 'mu4e)
(setq mu4e-completing-read-function 'completing-read)
(setq mu4e-view-auto-mark-as-read t)
(setq mu4e-view-show-addresses t)
(setq mu4e-view-show-images t)
;; Org-mode integration
(setq org-mu4e-link-query-in-headers-mode nil)
;; give me ISO(ish) format date-time stamps in the header list
(setq mu4e-headers-date-format "%Y-%m-%d %H:%M")
;; allow for updating mail using 'U' in the main view:
(setq mu4e-get-mail-command "mbsync -a")
;; Fixes problem with duplicate UID errors (see
;; http://pragmaticemacs.com/emacs/fixing-duplicate-uid-errors-when-using-mbsync-and-mu4e/)
(setq mu4e-change-filenames-when-moving t)
;; customize the reply-quote-string
;; M-x find-function RET message-citation-line-format for docs
(setq message-citation-line-format "%N on %Y-%m-%d %H:%M %Z:\n")
(setq message-citation-line-function 'message-insert-formatted-citation-line)
;; Turn on word-wrap automatically when viewing emails
(add-hook 'mu4e-view-mode-hook 'turn-on-visual-line-mode)
(add-hook 'mu4e-compose-mode-hook 'turn-on-visual-line-mode)
;; Don't hard-wrap my emails as I write!
(add-hook 'mu4e-compose-mode-hook (lambda () (auto-fill-mode -1)))
;; But make sure they flow on the receving end
(setq mu4e-compose-format-flowed t)
;; How to send messages
(setq message-send-mail-function 'smtpmail-send-it)
(define-key global-map (kbd "s-m") 'mu4e)
And here’s how I get the lazy loading:
(defvar lazy-email-loadedp nil)
(defun lazy-boot-email ()
(interactive)
(when (not lazy-email-loadedp)
(message "Loading email config...")
(load-file "~/.dotfiles/email-setup.el")
(setq lazy-email-loadedp t)
(message "Loading email config...done")
(mu4e)))
(define-key global-map (kbd "s-m") 'lazy-boot-email)
That defines s-m
to the function that loads the email config file. When that file gets loaded, it overwrites the keybinding for s-m
so that it doesn’t try to reload the file.