;;; email/mu4e/config.el -*- lexical-binding: t; -*- (defvar +mu4e-backend 'mbsync "Which backend to use. Can either be offlineimap, mbsync or nil (manual).") ;; ;;; Packages (add-to-list 'auto-mode-alist '("\\.\\(?:offlineimap\\|mbsync\\)rc\\'" . conf-mode)) (use-package! mu4e :commands mu4e mu4e-compose-new :init (provide 'html2text) ; disable obsolete package (setq mu4e-maildir "~/.mail" mu4e-attachment-dir "~/.mail/.attachments" mu4e-user-mail-address-list nil) :config (pcase +mu4e-backend (`mbsync (setq mu4e-get-mail-command "mbsync -a" mu4e-change-filenames-when-moving t)) (`offlineimap (setq mu4e-get-mail-command "offlineimap -o -q"))) (setq mu4e-update-interval nil mu4e-compose-format-flowed t ; visual-line-mode + auto-fill upon sending mu4e-view-show-addresses t mu4e-sent-messages-behavior 'sent mu4e-hide-index-messages t ;; try to show images mu4e-view-show-images t mu4e-view-image-max-width 800 ;; configuration for sending mail message-send-mail-function #'smtpmail-send-it smtpmail-stream-type 'starttls message-kill-buffer-on-exit t ; close after sending ;; start with the first (default) context; mu4e-context-policy 'pick-first ;; compose with the current context, or ask mu4e-compose-context-policy 'ask-if-none ;; use helm/ivy mu4e-completing-read-function (cond ((featurep! :completion ivy) #'ivy-completing-read) ((featurep! :completion helm) #'completing-read) (t #'ido-completing-read)) ;; no need to ask mu4e-confirm-quit nil ;; remove 'lists' column mu4e-headers-fields '((:account . 12) (:human-date . 12) (:flags . 4) (:from . 25) (:subject))) ;; set mail user agent (setq mail-user-agent 'mu4e-user-agent) ;; Use fancy icons (setq mu4e-headers-has-child-prefix '("+" . "") mu4e-headers-empty-parent-prefix '("-" . "") mu4e-headers-first-child-prefix '("\\" . "") mu4e-headers-duplicate-prefix '("=" . "") mu4e-headers-default-prefix '("|" . "") mu4e-headers-draft-mark '("D" . "") mu4e-headers-flagged-mark '("F" . "") mu4e-headers-new-mark '("N" . "") mu4e-headers-passed-mark '("P" . "") mu4e-headers-replied-mark '("R" . "") mu4e-headers-seen-mark '("S" . "") mu4e-headers-trashed-mark '("T" . "") mu4e-headers-attach-mark '("a" . "") mu4e-headers-encrypted-mark '("x" . "") mu4e-headers-signed-mark '("s" . "") mu4e-headers-unread-mark '("u" . "")) ;; Add a column to display what email account the email belongs to. (add-to-list 'mu4e-header-info-custom '(:account :name "Account" :shortname "Account" :help "Which account this email belongs to" :function (lambda (msg) (let ((maildir (mu4e-message-field msg :maildir))) (format "%s" (substring maildir 1 (string-match-p "/" maildir 1))))))) (defadvice! +mu4e--refresh-current-view-a (&rest _) :after #'mu4e-mark-execute-all (mu4e-headers-rerun-search)) (when (featurep! :tools flyspell) (add-hook 'mu4e-compose-mode-hook #'flyspell-mode)) ;; Wrap text in messages (setq-hook! 'mu4e-view-mode-hook truncate-lines nil) (when (fboundp 'imagemagick-register-types) (imagemagick-register-types)) (set-evil-initial-state! '(mu4e-main-mode mu4e-view-mode mu4e-headers-mode mu4e-compose-mode mu4e~update-mail-mode) 'normal) (map! :localleader :map mu4e-compose-mode-map :desc "send and exit" "s" #'message-send-and-exit :desc "kill buffer" "d" #'message-kill-buffer :desc "save draft" "S" #'message-dont-send :desc "attach" "a" #'mail-add-attachment)) (use-package! mu4e-maildirs-extension :after mu4e :config (mu4e-maildirs-extension) (setq mu4e-maildirs-extension-title nil)) (use-package! org-mu4e :hook (mu4e-compose-mode . org-mu4e-compose-org-mode) :config (setq org-mu4e-link-query-in-headers-mode nil org-mu4e-convert-to-html t) ;; Only render to html once. If the first send fails for whatever reason, ;; org-mu4e would do so each time you try again. (add-hook! 'message-send-hook (setq-local org-mu4e-convert-to-html nil))) ;; ;;; Gmail integration (when (featurep! +gmail) (after! mu4e ;; don't save message to Sent Messages, Gmail/IMAP takes care of this (setq mu4e-sent-messages-behavior 'delete ;; don't need to run cleanup after indexing for gmail mu4e-index-cleanup nil ;; because gmail uses labels as folders we can use lazy check since ;; messages don't really "move" mu4e-index-lazy-check t) ;; In my workflow, emails won't be moved at all. Only their flags/labels are ;; changed. Se we redefine the trash and refile marks not to do any moving. ;; However, the real magic happens in `+mu4e|gmail-fix-flags'. ;; ;; Gmail will handle the rest. (defun +mu4e--mark-seen (docid _msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "+S-u-N")) (delq! 'delete mu4e-marks #'assq) (setf (alist-get 'trash mu4e-marks) (list :char '("d" . "▼") :prompt "dtrash" :dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg)) :action #'+mu4e--mark-seen) ;; Refile will be my "archive" function. (alist-get 'refile mu4e-marks) (list :char '("d" . "▼") :prompt "dtrash" :dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg)) :action #'+mu4e--mark-seen)) ;; This hook correctly modifies gmail flags on emails when they are marked. ;; Without it, refiling (archiving), trashing, and flagging (starring) email ;; won't properly result in the corresponding gmail action, since the marks ;; are ineffectual otherwise. (add-hook! 'mu4e-mark-execute-pre-hook (defun +mu4e-gmail-fix-flags-h (mark msg) (pcase mark (`trash (mu4e-action-retag-message msg "-\\Inbox,+\\Trash,-\\Draft")) (`refile (mu4e-action-retag-message msg "-\\Inbox")) (`flag (mu4e-action-retag-message msg "+\\Starred")) (`unflag (mu4e-action-retag-message msg "-\\Starred")))))))