In Email Heaven with Emacs, mu4e, and mbsync

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.

Mastodon