Email templates in mu4e with yasnippet

This is the second in a series of posts on using mu4e for email in emacs. When I moved from thunderbird to mu4e, there were some thunderbird extensions that I missed. One was quicktext which I used to create simple email templates for common sorts of emails. This is easily replicated in emacs using yasnippet.

As a simple example, I made a snippet to expand the key all into

Hi all,



The snippet is

# -*- mode: snippet -*-
# name: hi all
# key: all
# --
Hi all,



This snippet is saved in the message-mode subdirectory of my snippets directory, since message-mode is the major mode for email composition in mu4e.

We can be a bit smarter than this with a snippet that takes the name of the email recipient and adds that to the template. The following is close to binchen’s instructions with a few small modifications. There are two parts to this: a snippet that expands out as normal, and a lisp function that it calls when it expands to extract the recipient’s name.

First, the lisp function

;; function to return first name of email recipients
;; used by yasnippet
;; inspired by
(defun bjm/mu4e-get-names-for-yasnippet ()
  "Return comma separated string of names for an email"
  (let ((email-name "") str email-string email-list email-name2 tmpname)
      (goto-char (point-min))
      ;; first line in email could be some hidden line containing NO to field
      (setq str (buffer-substring-no-properties (point-min) (point-max))))
    ;; take name from TO field - match series of names
    (when (string-match "^To: \"?\\(.+\\)" str)
      (setq email-string (match-string 1 str)))
    ;;split to list by comma
    (setq email-list (split-string email-string " *, *"))
    ;;loop over emails
    (dolist (tmpstr email-list)
      ;;get first word of email string
      (setq tmpname (car (split-string tmpstr " ")))
      ;;remove whitespace or ""
      (setq tmpname (replace-regexp-in-string "[ \"]" "" tmpname))
      ;;join to string
      (setq email-name
            (concat email-name ", " tmpname)))
    ;;remove initial comma
    (setq email-name (replace-regexp-in-string "^, " "" email-name))

    ;;see if we want to use the name in the FROM field
    ;;get name in FROM field if available, but only if there is only
    ;;one name in TO field
    (if (< (length email-list) 2)
        (when (string-match "^\\([^ ,\n]+\\).+writes:$" str)
          (progn (setq email-name2 (match-string 1 str))
                 ;;prefer name in FROM field if TO field has "@"
                 (when (string-match "@" email-name)
                   (setq email-name email-name2))

This function takes the first name of the email recipient from the “To:” field of the message. It also looks for a name in the “Joe Bloggs writes:” text that mu4e generates when you reply to an email. This is populated from the “From:” field of the email being replied to and sometimes gives a better match for the name. The function compares the two name strings and prefers the “To:” name unless it contains an “@” in which case it chooses the “From:” name (this is the addition I made to binchen‘s version). You could make this more sophisticated but it works pretty well for me as is.

Now we need a snippet to expand:

# -*- mode: snippet -*-
# name: dear name
# key: dear
# --
Dear ${1:`(bjm/mu4e-get-names-for-yasnippet)`},


Best wishes,

Note how our function is straightforwardly called by the snippet to give “Dear NAME”.

Hopefully this gives you some ideas of how to make useful email templates with yasnippet. In future posts I’ll talk about how I have added attachment reminders and delayed sending features to mu4e.

Update 11/3/2016

I updated the code to give a comma separated list of names in the case of more than one recipient.