diff --git a/.emacs.d/lisp/init-mu4e.el b/.emacs.d/lisp/init-mu4e.el index 775bc18a..d1e25880 100644 --- a/.emacs.d/lisp/init-mu4e.el +++ b/.emacs.d/lisp/init-mu4e.el @@ -31,7 +31,7 @@ Default to unread messages if the header buffer does not already exist." mu4e-maildir "~/.cache/mail" mu4e-get-mail-command "mbsync -a" mu4e-update-interval 90 - mu4e-headers-auto-update nil ; Don't refresh so that we don't lose the current filter upon, e.g. reading e-mails. + mu4e-headers-auto-update nil ; Don't refresh so that we don't lose the current filter upon, e.g. reading e-mails. mu4e-change-filenames-when-moving t ; Preferred for mbsync according to the man page. ;; SMTP @@ -74,6 +74,9 @@ Default to unread messages if the header buffer does not already exist." ;; Also crypt to self so that we can read sent e-mails. mml-secure-openpgp-encrypt-to-self t + ;; 'sent is good for most providers. Gmail requires 'delete. + mu4e-sent-messages-behavior 'sent + ;; Because default completion can be extended (e.g. Helm, Ivy). mu4e-completing-read-function 'completing-read) @@ -245,45 +248,7 @@ Sending unencrypted? " (define-key map (kbd "C-c C-t") 'org-mu4e-store-and-capture)) (setq org-mu4e-link-query-in-headers-mode nil)) -;;; Gmail trash fix. -(defvar ambrevar/mu4e-move-to-trash-patterns nil - "List of regexps to match for moving to trash instead of deleting them. -Matches are done against the :maildir field of the e-mail at -point. See `ambrevar/mu4e-headers-move-to-trash' and -`ambrevar/mu4e-view-move-to-trash'.") - -(defun ambrevar/mu4e-headers-move-to-trash () - (interactive) - (let ((msg-dir (mu4e-message-field (mu4e-message-at-point) :maildir))) - (if (not (seq-filter (lambda (re) - (string-match re msg-dir)) - ambrevar/mu4e-move-to-trash-patterns)) - (mu4e-headers-mark-for-trash) - (mu4e-mark-set 'move (if (functionp mu4e-trash-folder) - (funcall mu4e-trash-folder (mu4e-message-at-point)) - mu4e-trash-folder)) - (mu4e-headers-next)))) - -(defun ambrevar/mu4e-view-move-to-trash () - (interactive) - (mu4e~view-in-headers-context - (ambrevar/mu4e-headers-move-to-trash) - (mu4e~headers-move (or n 1)))) - -;;; Don't display trashed messages in bookmarks. This is useful for Gmail where -;;; the "delete" flag is not used. -(defvar ambrevar/mu4e-trash-folders nil - "List of trash folders to filter out from bookmarks.") - -(load "~/personal/mail/mu4e.el" t) - -;; Do this after setting `ambrevar/mu4e-trash-folders'. -(dolist (bookmark mu4e-bookmarks) - ;; TODO: Why mu4e-bookmark-query does not work here? - (setf (car bookmark) (concat (mapconcat (lambda (s) (format "NOT maildir:\"%s\" and " s)) - ambrevar/mu4e-trash-folders "") - (car bookmark)))) - +;; Fix replying to GitHub. (defun ambrevar/message-github () "When replying to a github message, clean up all bogus recipients. This function could be useful in `mu4e-compose-mode-hook'." @@ -387,4 +352,8 @@ If MSG is nil, use message at point." (interactive) (kill-new (mu4e-message-field (or msg (mu4e-message-at-point)) :message-id))) +(require 'patch-mu4e-account) + +(load "~/personal/mail/mu4e.el" t) + (provide 'init-mu4e) diff --git a/.emacs.d/lisp/patch-mu4e-account.el b/.emacs.d/lisp/patch-mu4e-account.el new file mode 100644 index 00000000..5d76fd9f --- /dev/null +++ b/.emacs.d/lisp/patch-mu4e-account.el @@ -0,0 +1,148 @@ +;; This patch implements a simpler mu4e-account structure. +;; It also fixes mu4e's behaviour with remote mailboxes that don't support the +;; trash flag like Gmail. +;; +;; Example +;; +;; (make-ambrevar/mu4e-account +;; ;; :name "work" +;; ;; :user-mail-address "john@doe.next" +;; ;; :sent-folder "Sent" +;; ;; :smtpmail-smtp-server "smtp.doe.net" +;; ;; :smtpmail-stream-type 'starttls +;; ;; :smtpmail-smtp-servive 587) + +;;; Trash fix for mailboxes that don't use the trash flag like Gmail. +(defvar ambrevar/mu4e-move-to-trash-patterns nil + "List of regexps to match for moving to trash instead of deleting them. +Matches are done against the :maildir field of the e-mail at +point. See `ambrevar/mu4e-headers-move-to-trash' and +`ambrevar/mu4e-view-move-to-trash'.") + +(defun ambrevar/mu4e-headers-move-to-trash () + (interactive) + (let ((msg-dir (mu4e-message-field (mu4e-message-at-point) :maildir))) + (if (not (seq-filter (lambda (re) + (string-match re msg-dir)) + ambrevar/mu4e-move-to-trash-patterns)) + (mu4e-headers-mark-for-trash) + (mu4e-mark-set 'move (if (functionp mu4e-trash-folder) + (funcall mu4e-trash-folder (mu4e-message-at-point)) + mu4e-trash-folder)) + (mu4e-headers-next)))) + +(defun ambrevar/mu4e-view-move-to-trash (&optional n) + (interactive "P") + (mu4e~view-in-headers-context + (ambrevar/mu4e-headers-move-to-trash) + (mu4e~headers-move (or n 1)))) + +(define-key mu4e-headers-mode-map (kbd "d") 'ambrevar/mu4e-headers-move-to-trash) +(define-key mu4e-view-mode-map (kbd "d") 'ambrevar/mu4e-view-move-to-trash) + +;; Structure email setup to simplify the common case. +(cl-defstruct (ambrevar/mu4e-account + (:constructor make--ambrevar/mu4e-account)) + "NAME is arbitrary, it is used by mu4e to change context. + +MAILDIR is the folder basename where the emails of this mailbox will be stored. +It's stored under `mu4e-maildir'. +MAILDIR defaults to NAME, so it smart to match the two. The folder slots will +be used relatively to MAILDIR. + +The context will be matched against CONDITION if provided, or +fallback to matching the MAILDIR. CONDITION is a function taking +the message at point as argument. + +NO-TRASH-FLAG tells `mu4e' to move to trash instead of flagging +as trashed. This is for mailboxes like Gmails that don't use the +trash flag. See `ambrevar/mu4e-move-to-trash-patterns'. + +VARS is a list of pairs of variables to set, just like in +`make-mu4e-context'." + name + ;; We set sane defaults for the following variables. They will be added to + ;; the context vars. + (user-mail-address user-mail-address) + (smtpmail-smtp-user smtpmail-smtp-user) + ;; Folders: + maildir + (drafts-folder "drafts") + (sent-folder "sent") + (trash-folder "trash") + (refile-folder "archive") + ;; Trash fix. + no-trash-flag + ;; Extra context vars. + vars + ;; Context funcs. + enter-func + leave-func + ;; Rule for matching the context. + condition) + +(defun ambrevar/mu4e-account-init (account) + "Initialize the ACCOUNT: +- Create a `mu4e-context' with all the respective fields matching CONDITION if specified. +- Add the context to the `mu4e-contexts'. +- Update the bookmarks to ignore the trash folder if NO-TRASH-FLAG is true. +- Update the `mu4e-user-mail-address-list'. + +See `ambrevar/mu4e-account' for more details." + (let (maildir) + (unless (ambrevar/mu4e-account-maildir account) + (setf (ambrevar/mu4e-account-maildir account) + (ambrevar/mu4e-account-name account))) + (unless (ambrevar/mu4e-account-smtpmail-smtp-user account) + (setf (ambrevar/mu4e-account-smtpmail-smtp-user account) + (ambrevar/mu4e-account-user-mail-address account))) + (setq maildir (concat "/" (ambrevar/mu4e-account-maildir account) "/")) + (setf (ambrevar/mu4e-account-no-trash-flag account) + (or (ambrevar/mu4e-account-no-trash-flag account) + (string-match "@gmail\\." (ambrevar/mu4e-account-user-mail-address account)) + (string-match "@googlemail\\." (ambrevar/mu4e-account-user-mail-address account)))) + ;; Seems that mu4e fails to start when no default folder is set. TODO: Report bug? + (setq mu4e-drafts-folder (concat maildir (ambrevar/mu4e-account-drafts-folder account)) + mu4e-sent-folder (concat maildir (ambrevar/mu4e-account-sent-folder account)) + mu4e-trash-folder (concat maildir (ambrevar/mu4e-account-trash-folder account)) + mu4e-refile-folder (concat maildir (ambrevar/mu4e-account-refile-folder account))) + (let ((context (make-mu4e-context + :name (ambrevar/mu4e-account-name account) + :enter-func (ambrevar/mu4e-account-enter-func account) + :leave-func (ambrevar/mu4e-account-leave-func account) + :match-func (lambda (msg) + (when msg + (or + (when (ambrevar/mu4e-account-condition account) + (funcall (ambrevar/mu4e-account-condition account) msg)) + (string-prefix-p maildir (mu4e-message-field msg :maildir))))) + :vars (append `((user-mail-address . ,(ambrevar/mu4e-account-user-mail-address account)) + (smtpmail-smtp-user . ,(ambrevar/mu4e-account-smtpmail-smtp-user account)) + (mu4e-drafts-folder . ,mu4e-drafts-folder) + (mu4e-sent-folder . ,mu4e-sent-folder) + (mu4e-trash-folder . ,mu4e-trash-folder) + (mu4e-refile-folder . ,mu4e-refile-folder)) + (ambrevar/mu4e-account-vars account))))) + (add-to-list 'mu4e-contexts context)) + (when (ambrevar/mu4e-account-no-trash-flag account) + ;; Exclude trash folder from all bookmarks. This is useful for mailboxes + ;; which don't use the "trash" flag like Gmail. + (dolist (bookmark mu4e-bookmarks) + ;; TODO: Why mu4e-bookmark-query does not work here? + (setf (car bookmark) (format "NOT maildir:\"%s\" and %s" + mu4e-trash-folder + (car bookmark))))) + (when (ambrevar/mu4e-account-no-trash-flag account) + ;; If this is a Gmail account, we add the maildir to the pattern list so + ;; that they can be properly trashed. + (add-to-list 'ambrevar/mu4e-move-to-trash-patterns (concat "^" maildir))) + ;; Required when using multiple addresses and if we don't want to + ;; reply to ourselves. + (add-to-list 'mu4e-user-mail-address-list (ambrevar/mu4e-account-user-mail-address account)) + account)) + +(defun make-ambrevar/mu4e-account (&rest args) + (let ((account (apply #'make--ambrevar/mu4e-account args))) + (ambrevar/mu4e-account-init account))) + +(provide 'patch-mu4e-account)