diff options
Diffstat (limited to 'modules/mail-config.el')
| -rw-r--r-- | modules/mail-config.el | 171 |
1 files changed, 132 insertions, 39 deletions
diff --git a/modules/mail-config.el b/modules/mail-config.el index f71d6eeb5..1d8a98c97 100644 --- a/modules/mail-config.el +++ b/modules/mail-config.el @@ -48,6 +48,56 @@ (defvar message-send-mail-function nil) (defvar message-sendmail-envelope-from nil) +(declare-function mu4e-message-field "mu4e-message") + +;; ----------------------------- Declarations ---------------------------------- +;; mu4e/org-msg load lazily, so the byte-compiler can't see these package +;; functions and variables when this module is compiled standalone. Declare +;; them to silence free-variable / undefined-function warnings without forcing +;; an eager require (which would defeat lazy loading). The cj/... entries are +;; forward references: defined later in this file's `:config' block, or in +;; mu4e-org-contacts-integration (required at load time inside that block). + +(declare-function mu4e-headers-mark-for-each-if "mu4e-mark") +(declare-function mu4e-search "mu4e-search") +(declare-function mu4e-view-refresh "mu4e-view") +(declare-function message-add-header "message") +(declare-function org-msg-edit-mode "org-msg") +(declare-function no-auto-fill "mail-config") +(declare-function cj/disable-company-in-mu4e-compose "mail-config") +(declare-function cj/disable-ispell-in-email-headers "mail-config") +(declare-function cj/activate-mu4e-org-contacts-integration + "mu4e-org-contacts-integration") + +;; Package variables assigned in the lazy `:config' blocks below. +(defvar mu4e-compose-keep-self-cc) +(defvar mu4e-root-maildir) +(defvar mu4e-show-images) +(defvar org-msg-extra-css) + +;; Refile (archive) target dispatch. A per-context `mu4e-refile-folder' string +;; is unsafe: mu4e context :vars are sticky, so a value set when one context is +;; active leaks into a later context that doesn't set its own -- archiving one +;; account's mail into another's folder. A single function evaluated per +;; message at refile time avoids that. Only cmail has a real synced Archive +;; folder; the Gmail-backed accounts (gmail, dmail) sync no archive maildir, so +;; refiling them would move mail into an unsynced, server-invisible folder +;; (silent loss) -- signal instead. +(defun cj/mu4e--refile-folder-for-maildir (maildir) + "Return the refile (archive) folder for MAILDIR, or signal when none exists. +MAILDIR is a mu4e :maildir string such as \"/cmail/INBOX\"." + (cond + ((not (stringp maildir)) + (user-error "Cannot refile: message has no maildir")) + ((string-prefix-p "/cmail" maildir) "/cmail/Archive") + (t + (user-error "No archive folder syncs for this account; refile disabled to avoid moving mail into an unsynced folder")))) + +(defun cj/mu4e--refile-folder (msg) + "Refile-folder function for `mu4e-refile-folder'. +Dispatch on MSG's maildir via `cj/mu4e--refile-folder-for-maildir'." + (cj/mu4e--refile-folder-for-maildir (and msg (mu4e-message-field msg :maildir)))) + (defcustom cj/smtpmail-debug-enabled nil "Non-nil means enable verbose SMTP transport debug logging. @@ -136,6 +186,12 @@ Prompts user for the action when executing." (display-buffer-reuse-window display-buffer-same-window) (inhibit-same-window . nil))) +;; Keep global font-lock out of the mu4e buffers. mu4e paints header lines, the +;; main menu, and view headers with manual `face' text properties; global +;; font-lock strips them (the same failure the dashboard hit), leaving the +;; buffers unthemed. Excluding these modes keeps mu4e's faces. +(cj/exclude-from-global-font-lock 'mu4e-headers-mode 'mu4e-main-mode 'mu4e-view-mode) + (use-package mu4e :ensure nil ;; mu4e gets installed by installing 'mu' via the system package manager :load-path "/usr/share/emacs/site-lisp/mu4e/" @@ -166,12 +222,16 @@ Prompts user for the action when executing." ;; (setq mu4e-compose-format-flowed t) ;; plain text mails must flow correctly for recipients (setq mu4e-compose-keep-self-cc t) ;; keep me in the cc list - (setq mu4e-compose-signature-auto-include nil) ;; don't include signature by default + (with-suppressed-warnings ((obsolete mu4e-compose-signature-auto-include) + (free-vars mu4e-compose-signature-auto-include)) + (setq mu4e-compose-signature-auto-include nil)) ;; don't include signature by default (setq mu4e-confirm-quit nil) ;; don't ask when quitting (setq mu4e-context-policy 'pick-first) ;; start with the first (default) context (setq mu4e-headers-auto-update nil) ;; updating headers buffer on email is too jarring (setq mu4e-root-maildir mail-dir) ;; root directory for all email accounts - (setq mu4e-maildir mail-dir) ;; same as above (for newer mu4e) + (with-suppressed-warnings ((obsolete mu4e-maildir) + (free-vars mu4e-maildir)) + (setq mu4e-maildir mail-dir)) ;; same as above (for newer mu4e) (setq mu4e-sent-messages-behavior 'delete) ;; don't save to "Sent", IMAP does this already (setq mu4e-show-images t) ;; show embedded images ;; (setq mu4e-update-interval 600) ;; check for new mail every 10 minutes (600 seconds) @@ -183,12 +243,16 @@ Prompts user for the action when executing." ;; This will be automatically disabled when org-msg is active (setq mu4e-compose-format-flowed t) - (setq mu4e-html2text-command 'mu4e-shr2text) ;; email conversion to html via shr2text + (with-suppressed-warnings ((obsolete mu4e-html2text-command) + (free-vars mu4e-html2text-command)) + (setq mu4e-html2text-command 'mu4e-shr2text)) ;; email conversion to html via shr2text (setq mu4e-mu-binary (executable-find "mu")) (setq mu4e-get-mail-command (cj/mail--mbsync-command)) ;; command to sync mail - (setq mu4e-user-mail-address-list '("c@cjennings.net" - "craigmartinjennings@gmail.com" - "craig.jennings@deepsat.com")) + (with-suppressed-warnings ((obsolete mu4e-user-mail-address-list) + (free-vars mu4e-user-mail-address-list)) + (setq mu4e-user-mail-address-list '("c@cjennings.net" + "craigmartinjennings@gmail.com" + "craig.jennings@deepsat.com"))) (setq mu4e-index-update-error-warning nil) ;; don't warn me about spurious sync issues ;; ------------------------------ Mu4e Contexts ------------------------------ @@ -217,7 +281,8 @@ Prompts user for the action when executing." :vars '((user-mail-address . "c@cjennings.net") (user-full-name . "Craig Jennings") (mu4e-drafts-folder . "/cmail/Drafts") - (mu4e-sent-folder . "/cmail/Sent"))) + (mu4e-sent-folder . "/cmail/Sent") + (mu4e-trash-folder . "/cmail/Trash"))) (make-mu4e-context :name "deepsat.com" @@ -232,6 +297,12 @@ Prompts user for the action when executing." (mu4e-starred-folder . "/dmail/Starred") (mu4e-trash-folder . "/dmail/Trash"))))) + ;; Refile target is computed per message (see `cj/mu4e--refile-folder'), not + ;; set per context, because mu4e context :vars are sticky and would leak one + ;; account's archive folder into another. cmail archives to /cmail/Archive; + ;; gmail/dmail signal rather than move mail into an unsynced folder. + (setq mu4e-refile-folder #'cj/mu4e--refile-folder) + (setq mu4e-maildir-shortcuts '(("/cmail/Inbox" . ?i) ("/cmail/Sent" . ?s) @@ -257,7 +328,7 @@ Prompts user for the action when executing." :key ?d))) (defun no-auto-fill () - "Turn off \'auto-fill-mode\'." + "Turn off `auto-fill-mode'." (auto-fill-mode -1)) (add-hook 'mu4e-compose-mode-hook #'no-auto-fill) @@ -279,19 +350,23 @@ Prompts user for the action when executing." ;; also see org-msg below ;; Prefer HTML over plain text when both are available - (setq mu4e-view-prefer-html t) + (with-suppressed-warnings ((obsolete mu4e-view-prefer-html) + (free-vars mu4e-view-prefer-html)) + (setq mu4e-view-prefer-html t)) ;; Use a better HTML renderer with more control - (setq mu4e-html2text-command - (cond - ;; Best option: pandoc (if available) - ((executable-find "pandoc") - "pandoc -f html -t plain --reference-links") - ;; Good option: w3m (better tables/formatting) - ((executable-find "w3m") - "w3m -dump -T text/html -cols 72 -o display_link_number=true") - ;; Fallback: built-in shr - (t 'mu4e-shr2text))) + (with-suppressed-warnings ((obsolete mu4e-html2text-command) + (free-vars mu4e-html2text-command)) + (setq mu4e-html2text-command + (cond + ;; Best option: pandoc (if available) + ((executable-find "pandoc") + "pandoc -f html -t plain --reference-links") + ;; Good option: w3m (better tables/formatting) + ((executable-find "w3m") + "w3m -dump -T text/html -cols 72 -o display_link_number=true") + ;; Fallback: built-in shr + (t 'mu4e-shr2text)))) ;; Configure shr (built-in HTML renderer) for better display (setq shr-use-colors nil) ; Don't use colors in terminal @@ -301,8 +376,10 @@ Prompts user for the action when executing." (setq shr-bullet "• ") ; Nice bullet points ;; Block remote images by default (privacy/security) - (setq mu4e-view-show-images t) - (setq mu4e-view-image-max-width 800) + (with-suppressed-warnings ((obsolete mu4e-view-show-images mu4e-view-image-max-width) + (free-vars mu4e-view-show-images mu4e-view-image-max-width)) + (setq mu4e-view-show-images t) + (setq mu4e-view-image-max-width 800)) ;; ------------------------------- View Actions ------------------------------ ;; define view and article menus @@ -379,6 +456,34 @@ Prompts user for the action when executing." (cj/activate-mu4e-org-contacts-integration)) ;; end use-package mu4e +;; ----------------------- Account Navigation Keymaps -------------------------- +;; The C-; e c/d/g submaps jump to each account's inbox views. Built from one +;; template so the maildir prefix is the only per-account difference. + +;; eval-and-compile so the builder is defined when org-msg's :preface (below) +;; calls it during byte-compilation, not only at load. +(eval-and-compile + (defun cj/--mail-account-search-queries (account) + "Return an alist of (KEY . QUERY) mu4e searches for ACCOUNT's inbox. +ACCOUNT is the maildir account name (\"cmail\", \"dmail\", \"gmail\"). The four +entries scope inbox / unread / flagged / large searches to that account's +INBOX maildir." + (let ((base (format "maildir:/%s/INBOX" account))) + (list (cons "i" base) + (cons "u" (concat base " AND flag:unread AND NOT flag:trashed")) + (cons "s" (concat base " AND flag:flagged")) + (cons "l" (concat base " AND size:5M..999M"))))) + + (defun cj/--mail-make-account-map (account) + "Build a mu4e navigation keymap for ACCOUNT (a maildir account name). +Keys i/u/s/l run the inbox/unread/flagged/large searches from +`cj/--mail-account-search-queries', each scoped to ACCOUNT." + (let ((map (make-sparse-keymap))) + (dolist (entry (cj/--mail-account-search-queries account) map) + (let ((query (cdr entry))) + (keymap-set map (car entry) + (lambda () (interactive) (mu4e-search query)))))))) + ;; ---------------------------------- Org-Msg ---------------------------------- ;; user composes org mode; recipient receives html @@ -387,24 +492,12 @@ Prompts user for the action when executing." :defer 1 :after (org mu4e) :preface - (defvar-keymap cj/mail-cmail-map - :doc "cmail account navigation" - "i" (lambda () (interactive) (mu4e-search "maildir:/cmail/INBOX")) - "u" (lambda () (interactive) (mu4e-search "maildir:/cmail/INBOX AND flag:unread AND NOT flag:trashed")) - "s" (lambda () (interactive) (mu4e-search "maildir:/cmail/INBOX AND flag:flagged")) - "l" (lambda () (interactive) (mu4e-search "maildir:/cmail/INBOX AND size:5M..999M"))) - (defvar-keymap cj/mail-dmail-map - :doc "deepsat account navigation" - "i" (lambda () (interactive) (mu4e-search "maildir:/dmail/INBOX")) - "u" (lambda () (interactive) (mu4e-search "maildir:/dmail/INBOX AND flag:unread AND NOT flag:trashed")) - "s" (lambda () (interactive) (mu4e-search "maildir:/dmail/INBOX AND flag:flagged")) - "l" (lambda () (interactive) (mu4e-search "maildir:/dmail/INBOX AND size:5M..999M"))) - (defvar-keymap cj/mail-gmail-map - :doc "gmail account navigation" - "i" (lambda () (interactive) (mu4e-search "maildir:/gmail/INBOX")) - "u" (lambda () (interactive) (mu4e-search "maildir:/gmail/INBOX AND flag:unread AND NOT flag:trashed")) - "s" (lambda () (interactive) (mu4e-search "maildir:/gmail/INBOX AND flag:flagged")) - "l" (lambda () (interactive) (mu4e-search "maildir:/gmail/INBOX AND size:5M..999M"))) + (defvar cj/mail-cmail-map (cj/--mail-make-account-map "cmail") + "cmail account navigation.") + (defvar cj/mail-dmail-map (cj/--mail-make-account-map "dmail") + "deepsat account navigation.") + (defvar cj/mail-gmail-map (cj/--mail-make-account-map "gmail") + "gmail account navigation.") (defvar-keymap cj/email-map :doc "Email operations and account navigation" "A" #'org-msg-attach-attach |
