summaryrefslogtreecommitdiff
path: root/mu4e
diff options
context:
space:
mode:
authorDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2019-04-17 22:59:30 +0300
committerGitHub <noreply@github.com>2019-04-17 22:59:30 +0300
commit2ec1b460768340b5c34ef5d08ca5bae8696bd941 (patch)
tree25765e92b4a24df9ce08c7074b0e88be732d8a42 /mu4e
parente9970fb890206fc7a25fd25403e2c8f33e8e4047 (diff)
parent610bc915477af9e93d3b5c136b8682d1f75921ef (diff)
Merge pull request #1390 from Ambrevar/easy-accounts
mu4e: Easy accounts with make-mu4e-context-account
Diffstat (limited to 'mu4e')
-rw-r--r--mu4e/mu4e-context.el122
-rw-r--r--mu4e/mu4e-headers.el27
-rw-r--r--mu4e/mu4e-view.el9
-rw-r--r--mu4e/mu4e.texi71
4 files changed, 223 insertions, 6 deletions
diff --git a/mu4e/mu4e-context.el b/mu4e/mu4e-context.el
index 4368877..eaafdd8 100644
--- a/mu4e/mu4e-context.el
+++ b/mu4e/mu4e-context.el
@@ -22,7 +22,7 @@
;;; Commentary:
-;; A mu4e 'context' is a a set of variable-settings and functions, which can be
+;; A mu4e 'context' is a set of variable-settings and functions, which can be
;; used e.g. to switch between accounts.
(require 'cl-lib)
(require 'mu4e-utils)
@@ -69,13 +69,129 @@ none."
that takes a message plist for the message replied to or
forwarded, and nil otherwise. Before composing a new message,
`mu4e' switches to the first context for which `match-func'
- returns t."
+ returns t.
+- `vars': variables to set when entering context."
name ;; name of the context, e.g. "work"
(enter-func nil) ;; function invoked when entering the context
(leave-func nil) ;; function invoked when leaving the context
(match-func nil) ;; function that takes a msg-proplist, and return t
;; if it matches, nil otherwise
- vars) ;; alist of variables.
+ vars) ;; alist of variables.
+
+(defvar mu4e-no-trash-providers '("gmail.com" "googlemail.com")
+ "List of email providers that don't support the trash flag.")
+
+(cl-defun make-mu4e-context-account (name &key
+ enter-func
+ leave-func
+ match-func
+ vars
+ ;; 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
+ ;; Rule for matching the context.
+ predicate)
+ "Create a context with sane defaults (see `make-mu4e-context').
+Also:
+- Add the context to the `mu4e-contexts'.
+- Update the bookmarks to ignore the trash folder if NO-TRASH-FLAG is non-nil.
+- Update the `mu4e-user-mail-address-list'.
+
+Options beyond those of `make-mu4e-context':
+- `user-mail-address': Defaults to the global value when the context is created.
+- `smtpmail-smtp-user': Defaults to the global value if non-nil when the context
+ is created, or the context `user-mail-address' otherwise.
+- `maildir': Mailbox folder name in as stored in `mu4e-maildir' (just the name,
+ there must be no '/'). Defaults to `name'.
+- `drafts-folder': Context value of `mu4e-drafts-folder'. Defaults to
+ \"drafts\".
+- `sent-folder': Context value of `mu4e-sent-folder'. Defaults to \"sent\".
+- `trash-folder': Context value of `mu4e-trash-folder'. Defaults to \"trash\".
+- `refile-folder': Context value of `mu4e-refile-folder'. Defaults to
+ \"refile\".
+- `no-trash-flag': If non-nil, the maildir will be added to
+ `mu4e-move-to-trash-patterns' so that trashing moves the message instead of flagging.
+- `predicate': A function that takes a message and returns non-nil if it matches
+ the context. This is only used if `match-func' is not provided, in which case
+ the context is always matched against the message folder.
+
+Example of a mailbox where only the sent-folder differs from the
+default folders (see `make-mu4e-context' and `mu4e-context'):
+
+ (let ((gandi-smtp-vars '((smtpmail-smtp-server . \"mail.gandi.net\")
+ (smtpmail-stream-type . starttls)
+ (smtpmail-smtp-service . 587))))
+ (make-mu4e-context-account
+ :name \"personal\"
+ :user-mail-address \"john@doe.xyz\"
+ :sent-folder \"Sent\"
+ :vars gandi-smtp-vars)
+ (make-mu4e-context-account
+ :name \"work\"
+ :user-mail-address \"john@work.org\"
+ :sent-folder \"Sent\"
+ :predicate (lambda (msg)
+ (mu4e-message-contact-field-matches
+ msg '(:from :to) \"boss@work.org\"))
+ :vars gandi-smtp-vars))"
+ (cl-assert name)
+ (setq maildir (concat "/" (or maildir name) "/")
+ smtpmail-smtp-user (or smtpmail-smtp-user user-mail-address)
+ no-trash-flag (or no-trash-flag
+ (string-match (regexp-opt mu4e-no-trash-providers)
+ user-mail-address)))
+ (when no-trash-flag
+ ;; 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: mu4e-bookmark-query does not work here, why?
+ (setf (car bookmark) (format "NOT maildir:\"%s\" and %s"
+ mu4e-trash-folder
+ (car bookmark))))
+ ;; If this is a Gmail context, we add the maildir to the pattern list so
+ ;; that they can be properly trashed.
+ (add-to-list 'mu4e-move-to-trash-patterns (concat "^" maildir)))
+ ;; TODO: Seems that mu4e fails to start when no default folder is set.
+ ;; The following setq is a workaround.
+ (setq mu4e-drafts-folder (concat maildir drafts-folder)
+ mu4e-sent-folder (concat maildir sent-folder)
+ mu4e-trash-folder (concat maildir trash-folder)
+ mu4e-refile-folder (concat maildir refile-folder))
+ (let ((context (make-mu4e-context :name name
+ :enter-func enter-func
+ :leave-func leave-func
+ :match-func match-func
+ :vars vars)))
+ (unless (mu4e-context-match-func context)
+ (setf (mu4e-context-match-func context)
+ `(lambda (msg)
+ (when msg
+ (or
+ ,(when predicate
+ `(funcall ,predicate msg))
+ (string-prefix-p ,maildir (mu4e-message-field msg :maildir)))))))
+ (setf (mu4e-context-vars context)
+ (append `((user-mail-address . ,user-mail-address)
+ (smtpmail-smtp-user . ,smtpmail-smtp-user)
+ (mu4e-drafts-folder . ,mu4e-drafts-folder)
+ (mu4e-sent-folder . ,mu4e-sent-folder)
+ (mu4e-trash-folder . ,mu4e-trash-folder)
+ (mu4e-refile-folder . ,mu4e-refile-folder))
+ (mu4e-context-vars context)))
+ ;; Required when using multiple addresses and if we don't want to
+ ;; reply to ourselves.
+ (add-to-list 'mu4e-user-mail-address-list user-mail-address)
+ (add-to-list 'mu4e-contexts context)
+ context))
(defun mu4e~context-ask-user (prompt)
"Let user choose some context based on its name."
diff --git a/mu4e/mu4e-headers.el b/mu4e/mu4e-headers.el
index e6247df..3ecaacf 100644
--- a/mu4e/mu4e-headers.el
+++ b/mu4e/mu4e-headers.el
@@ -742,6 +742,29 @@ after the end of the search results."
(mu4e~headers-defun-mark-for unread)
(mu4e~headers-defun-mark-for action)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defvar mu4e-move-to-trash-patterns '()
+ "List of regexps to match for moving to trash instead of flagging them.
+This is particularly useful for mailboxes that don't use the
+trash flag like Gmail. See `mu4e-headers-mark-or-move-to-trash'
+and `mu4e-view-mark-or-move-to-trash'.")
+
+(defun mu4e-headers-mark-or-move-to-trash ()
+ "Mark message for \"move\" to the trash folder if the message
+maildir matches any regexp in `mu4e-move-to-trash-patterns'.
+Otherwise mark with the \"trash\" flag.
+Also see `mu4e-view-mark-or-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))
+ 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))))
+
;;; headers-mode and mode-map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar mu4e-headers-mode-map nil
"Keymap for *mu4e-headers* buffers.")
@@ -801,8 +824,8 @@ after the end of the search results."
(define-key map "y" 'mu4e-select-other-view)
;; marking/unmarking ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (define-key map (kbd "<backspace>") 'mu4e-headers-mark-for-trash)
- (define-key map (kbd "d") 'mu4e-headers-mark-for-trash)
+ (define-key map (kbd "<backspace>") 'mu4e-headers-mark-or-move-to-trash)
+ (define-key map (kbd "d") 'mu4e-headers-mark-or-move-to-trash)
(define-key map (kbd "<delete>") 'mu4e-headers-mark-for-delete)
(define-key map (kbd "<deletechar>") 'mu4e-headers-mark-for-delete)
(define-key map (kbd "D") 'mu4e-headers-mark-for-delete)
diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el
index 4e8378d..5a12fee 100644
--- a/mu4e/mu4e-view.el
+++ b/mu4e/mu4e-view.el
@@ -743,7 +743,7 @@ FUNC should be a function taking two arguments:
(define-key map "A" (if mu4e-view-use-gnus 'ignore 'mu4e-view-attachment-action))
;; marking/unmarking
- (define-key map "d" 'mu4e-view-mark-for-trash)
+ (define-key map "d" 'mu4e-view-mark-or-move-to-trash)
(define-key map (kbd "<delete>") 'mu4e-view-mark-for-delete)
(define-key map (kbd "<deletechar>") 'mu4e-view-mark-for-delete)
(define-key map (kbd "D") 'mu4e-view-mark-for-delete)
@@ -1487,6 +1487,13 @@ list."
(mu4e~view-in-headers-context
(mu4e-mark-execute-all)))
+(defun mu4e-view-mark-or-move-to-trash (&optional n)
+ "See `mu4e-headers-mark-or-move-to-trash'."
+ (interactive "P")
+ (mu4e~view-in-headers-context
+ (mu4e-headers-mark-or-move-to-trash)
+ (mu4e~headers-move (or n 1))))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; URL handling
(defun mu4e~view-get-urls-num (prompt &optional multi)
diff --git a/mu4e/mu4e.texi b/mu4e/mu4e.texi
index 0bc5ab2..4115683 100644
--- a/mu4e/mu4e.texi
+++ b/mu4e/mu4e.texi
@@ -2183,6 +2183,7 @@ can happen in both the @ref{Headers view} and the @ref{Message view}.
* Marking messages::Selecting message do something with them
* What to mark for::What can we do with them
* Executing the marks::Do it
+* Trashing messages::Exceptions for mailboxes like Gmail
* Leaving the headers buffer::Handling marks automatically when leaving
* Built-in marking functions::Helper functions for dealing with them
* Custom mark functions::Define your own mark function
@@ -2258,6 +2259,29 @@ A hook, @code{mu4e-mark-execute-pre-hook}, is available which is run
right before execution of each mark. The hook is called with two
arguments, the mark and the message itself.
+@node Trashing messages
+@section Trashing messages
+
+For regular mailboxes, trashing works like other marks: when executed,
+the message is flagged as trashed. Depending on your mailbox provider,
+the trash flag is used to automatically move the message to the trash
+folder (@code{mu4e-trash-folder}) for instance.
+
+Some mailboxes behave differently however and they don't interpret the
+trash flag. In cases like Gmail, the message must be @emph{moved} to
+the trash folder and the trash flag must not be used.
+
+@code{mu4e} has provisions for non-standard mailboxes: if a message
+maildir matches a regular expression in
+@code{mu4e-move-to-trash-patterns} then the message is moved instead of
+being flagged. When a context is created with
+@code{make-mu4e-context-account} (see @ref{Account setup helper}), the
+pattern is automatically added for you.
+
+This should work fine for Gmail and similar mailboxes. Note that in the
+case of Gmail, you might have to configure your mailbox ``expunge''
+settings.
+
@node Leaving the headers buffer
@section Leaving the headers buffer
@@ -2415,6 +2439,7 @@ example:
* Context policies::How to determine the current context
* Contexts and special folders::Using context variables to determine them
* Contexts example::How to define contexts
+* Account setup helper::Easy context creation with sane defaults
* Some context tricks::Other thing to do with contexts
@end menu
@@ -2626,6 +2651,52 @@ no context matches (or if you always want to be asked).
and commas and note the '.' between variable name and its value.
@end itemize
+@node Account setup helper
+@section Account setup helper
+
+Contexts can be cumbersome to set up. Thankfully @code{mu4e} provides a
+helper function @code{make-mu4e-context-account} to easily get started.
+The function helps initializing the context plus a couple of variables
+with sane defaults.
+Everything should work out of the box in most cases.
+
+A short example for two contexts:
+
+@lisp
+(let ((gandi-smtp-vars '((smtpmail-smtp-server . "mail.gandi.net")
+ (smtpmail-stream-type . starttls)
+ (smtpmail-smtp-service . 587))))
+ (make-mu4e-context-account
+ :name "personal"
+ :user-mail-address "john@doe.xyz"
+ :sent-folder "Sent"
+ :vars gandi-smtp-vars)
+ (make-mu4e-context-account
+ :name "work"
+ :user-mail-address "john@work.org"
+ :sent-folder "Sent"
+ :predicate (lambda (msg)
+ (mu4e-message-contact-field-matches
+ msg '(:from :to) "boss@work.org"))
+ :vars gandi-smtp-vars))
+@end lisp
+
+A couple of things to note:
+
+@itemize
+@item Only the @code{name} slot is mandatory.
+@item The maildir default to the context name.
+@item Folders only need to be given a name, not a relative path.
+They will be automatically stored under the maildir.
+@item When the @code{match-func} is not provided, the context is matched
+against @code{predicate} if provided or the maildir of the current
+message otherwise.
+@end itemize
+
+If the context created by @code{make-mu4e-context-account} is not
+enough, you can display the generated context with e.g. @code{M-x
+describe-variable mu4e-contexts} and tweak the result as needed.
+
@node Some context tricks
@section Some context tricks