Custom Emacs functions No. 7 - Mu4e
EDIT - 2024-11-09: See the Edits section further down.
Intro
In the name of bringing the kitchen sink inside of Emacs, I manage my mails here as well. Mu4e is my choice of a mail client.
As a quite opinionated fellow when it comes to my tools, I tend to change stuff a lot. This includes extending functions and commands that are delivered with Mu4e by default. Or even creating new ones.
Barring changes in Mu4e or related updates, these functions should remain relevant. However some where created a good while ago. Which in turn might result in “obsolete” code or easier solutions.
Commands and Functions
timu-mu4e-in-new-tab
I always, I mean ALWAYS open Mu4e in a new tab. This one does it for me with a custom keybinding.
(defun timu-mu4e-in-new-tab ()
"Open `mu4e' in a new tab with `tab-bar-new-tab'."
(interactive)
(progn
(tab-bar-new-tab)
(mu4e)))
timu-mu4e-quit
Since I always open Mu4e in a new tab, this function allows me to close the “mail-tab” upon quitting Mu4e.
(defun timu-mu4e-quit ()
"Quit `mu4e' and close the tab with `tab-bar-close-tab'."
(interactive)
(progn
(mu4e-quit)
(tab-bar-close-tab)))
timu-mu4e-execute-no-confirm
Executing marks – moving, deleting, archiving mails and more – in Mu4e (mostly in headers view) happens many many times a day. Having to confirm all of those action would result in me throwing my Mac out of the window after a while. The following function helps execute marks without confirmation.
(defun timu-mu4e-execute-no-confirm ()
"Execute all without confirmation.
Use the argument NO-COMFIRM in the command `mu4e-mark-execute-all'."
(interactive)
(mu4e-mark-execute-all 'no-confirm))
timu-mu4e-jump-to-maildir
To simplify mailbox navigation, I use completing-read
with the command below, bound to J
..
(defun timu-mu4e-jump-to-maildir ()
"Use `completing-read' to jump to a maildir.
Credit: https://emacs.stackexchange.com/a/47580/30874
Credit: https://arupajhana.wordpress.com/2014/09/26/mu4e-with-helm."
(interactive)
(let ((maildir (completing-read "Maildir: " (mu4e-get-maildirs))))
(mu4e-headers-search (format "maildir:\"%s\"" maildir))))
timu-mu4e-attach-file
This one helps add attachments to a compose buffer with completion in the minibuffer (read-file-name
).
(defun timu-mu4e-attach-file ()
"Attach a file to an email.
Use the built-in function `mml-attach-file'."
(interactive)
(let ((default-directory "~/"))
(let ((file (read-file-name "Select a file to attach: ")))
(mml-attach-file (expand-file-name file)))))
timu-mu4e-view-save-attachments
The default command for saving attachments, mu4e-view-save-attachments
automatically chooses a – in the variable mu4e-attachment-dir
– predefined directory. This is fine and dandy, but I mostly want to select the directory my self – on a case-by-case basis.
(defun timu-mu4e-view-save-attachments ()
"Save all attachements in a selected directory.
This is `mu4e-view-save-attachments' with prefix Argument."
(interactive)
(let ((current-prefix-arg '(4))
(embark-confirm-act-all nil))
(call-interactively #'mu4e-view-save-attachments)))
timu-mu4e-get-mail
The variable mu4e-get-mail-command
defines which CLI command to use to fetch emails with Mu4e. In my case – using the isync program (mbsync) – I want sometimes to only fetch mails per account or all accounts.
(defun timu-mu4e-get-mail ()
"Select the Account before syncing.
This makes the syncing of mails more flexible."
(interactive)
(let ((mu4e-get-mail-command
(concat
"/opt/homebrew/bin/mbsync "
(completing-read
"Which Account: "
'("icloud" "aimebertrand" "moclub" "--all")))))
(mu4e-update-mail-and-index t)))
Commands and functions to deal with signatures
The following two commands are extensively covered in the previous post “Signature above the cited text in mu4e”.
(defun timu-mu4e-message-insert-signature (&optional force)
"Insert a signature at the end of the buffer.
Original command is `message-insert-signature'.
See https://macowners.club/posts/signature-above-cited-text-mu4e/ for reasons.
See the documentation for the `message-signature' variable for
more information.
If FORCE is 0 (or when called interactively), the global values
of the signature variables will be consulted if the local ones
are null."
(interactive (list 0) message-mode)
(let ((timu-message-signature timu-message-signature)
(message-signature-file message-signature-file))
;; If called interactively and there's no signature to insert,
;; consult the global values to see whether there's anything they
;; have to say for themselves. This can happen when using
;; `gnus-posting-styles', for instance.
(when (and (null timu-message-signature)
(null message-signature-file)
(eq force 0))
(setq timu-message-signature (default-value 'timu-message-signature)
message-signature-file (default-value 'message-signature-file)))
(let* ((signature
(cond
((and (null timu-message-signature)
(eq force 0))
(save-excursion
(goto-char (point-max))
(not (re-search-backward message-signature-separator nil t))))
((and (null timu-message-signature)
force)
t)
((functionp timu-message-signature)
(funcall timu-message-signature))
((listp timu-message-signature)
(eval timu-message-signature t))
(t timu-message-signature)))
signature-file)
(setq signature
(cond ((stringp signature)
signature)
((and (eq t signature) message-signature-file)
(setq signature-file
(if (and message-signature-directory
;; don't actually use the signature directory
;; if message-signature-file contains a path.
(not (file-name-directory
message-signature-file)))
(expand-file-name message-signature-file
message-signature-directory)
message-signature-file))
(file-exists-p signature-file))))
(when signature
(goto-char (point-max))
;; Insert the signature.
(unless (bolp)
(newline))
(when message-signature-insert-empty-line
(newline))
(insert "...... ")
(newline)
(if (eq signature t)
(insert-file-contents signature-file)
(insert signature))
(goto-char (point-max))
(or (bolp) (newline))))))
(defun timu-mu4e-message-insert-signature-at-point (pmode)
"Function to insert signature at right point according to PMODE.
Uses `timu-mu4e-message-insert-signature'.
This is a modified version of `message-insert-signature'."
(when pmode (message-goto-body))
(interactive)
(require 'message)
(message-goto-body)
(newline)
(message-goto-body)
(save-restriction
(narrow-to-region (point) (point))
(timu-mu4e-message-insert-signature))
(message-goto-body))
Automatically switching the context
Whenever switching to specific mailbox, I want to be in the right mu4e-context. This is in short a setting set. In my case to mostly identify the correct account.
Based on my current research, there is no built-in solution to automatically switch contexts. I use the following function as a mu4e-headers-found-hook
to achieve this.
(defun timu-mu4e-switch-context ()
"Switch context of the current maildir.
Uses `mu4e--search-last-query' and regex to get the context."
(let ((new-context
(timu-get-mu4e-context)))
(if new-context
(mu4e-context-switch t new-context)
(mu4e-context-switch t "icloud"))))
… Which in turn uses the following function.
(defun timu-get-mu4e-context ()
"Extract context from `mu4e--search-last-query'."
(if (string-match "/\\(.+?\\)/.*" mu4e--search-last-query)
(match-string 1 mu4e--search-last-query) ""))
You can find more details in the older post “AutoSwitch Mu4e context depending on mailbox”.
timu-mu4e-msmtp-select-account
When sending emails, which use the CLI program msmtp
in my configuration, the ‘from’ address should be pre-populated with the correct sender address.
(defun timu-mu4e-msmtp-select-account ()
"Select the right account/context according to the from line."
(if (message-mail-p)
(save-excursion
(let*
((from (save-restriction
(message-narrow-to-headers)
(message-fetch-field "from")))
(account
(cond
((string-match timu-personal-icloud-email from) "icloud")
((string-match timu-personal-aimebertrand-email from) "aimebertrand")
((string-match timu-personal-moclub-email from) "moclub"))))
(setq message-sendmail-extra-arguments (list '"-a" account))))))
Edits
2024-11-09 - Adding/Fixing a function to save all attachments
You can find this in the new post Mu4e - Update - save attachments faster.