mu4e: implement context-policies

Allow setting a policy about what context to choose when starting mu4e
and composing a message. Basically:

When you have defined contexts and you start mu4e it decides which
context to use based on the variable `mu4e-context-policy';
similarly, when you compose a new message, the context is determined
using `mu4e-compose-context-policy'.

These policies can be one of the following:
- a symbol always-ask: unconditionally ask the user what context to pick

The other choices only apply if none of the context matches (i.e., if
none of the contexts' match-functions returns t:

- symbol ask: ask the user
- a symbol pick-first: pick the first context
- nil: don't change the context
This commit is contained in:
djcb 2015-12-23 22:01:51 +02:00
parent 4bca0d0739
commit 113c024632
5 changed files with 113 additions and 51 deletions

View File

@ -112,12 +112,34 @@ for example:
The various `message-' functions from `message-mode' are available
for querying the message information."
:type '(choice (const :tag "move message to mu4e-sent-folder" sent)
(const :tag "move message to mu4e-trash-folder" trash)
(const :tag "delete message" delete))
:type '(choice (const :tag "move message to mu4e-sent-folder" sent)
(const :tag "move message to mu4e-trash-folder" trash)
(const :tag "delete message" delete))
:safe 'symbolp
:group 'mu4e-compose)
(defcustom mu4e-compose-context-policy nil
"Determines how mu4e should determine the context when composing a new message.
If POLICY is
'always-ask, we ask the user unconditionally.
In all other cases, if any context matches (using its match
function), this context is returned. If none of the contexts
match, POLICY determines what to do:
- pick-first: pick the first of the contexts available
- ask: ask the user
- otherwise, return nil. Effectively, this leaves the current context in place."
:type '(choice
(const :tag "Always ask what context to use" 'always-ask)
(const :tag "Ask if none of the contexts match" 'ask)
(const :tag "Pick the default (first) context if none match" 'pick-first)
(const :tag "Don't change the context when none match" nil)
:safe 'symbolp
:group 'mu4e-compose))
(defcustom mu4e-compose-pre-hook nil
"Hook run just *before* message composition starts.
If the compose-type is either 'reply' or 'forward', the variable
@ -370,10 +392,9 @@ tempfile)."
;; message being forwarded or replied to, otherwise it is nil.
(set (make-local-variable 'mu4e-compose-parent-message) original-msg)
(put 'mu4e-compose-parent-message 'permanent-local t)
(let ((context (mu4e-context-determine mu4e-compose-parent-message)))
(if context
(mu4e-context-switch nil (mu4e-context-name context))
(when mu4e-contexts (mu4e-context-switch nil))))
;; maybe switch the context
(mu4e~context-autoswitch mu4e-compose-parent-message
mu4e-compose-context-policy)
(run-hooks 'mu4e-compose-pre-hook)
;; this opens (or re-opens) a messages with all the basic headers set.

View File

@ -100,13 +100,13 @@ non-nil."
(mu4e-message "Switched context to %s" (mu4e-context-name context)))
context))
(defun mu4e-context-autoselect ()
(defun mu4e~context-autoswitch (&optional msg policy)
"When contexts are defined but there is no context yet, switch
to the first whose :match-func return non-nil. If none of them
match, return the first."
(when (and mu4e-contexts (not (mu4e-context-current)))
(mu4e-context-switch
(mu4e-context-name (mu4e-context-determine nil 'pick-first)))))
match, return the first. For MSG and POLICY, see `mu4e-context-determine'."
(when mu4e-contexts
(let ((context (mu4e-context-determine msg policy)))
(when context (mu4e-context-switch (mu4e-context-name context))))))
(defun mu4e-context-determine (msg &optional policy)
"Return the first context with a match-func that returns t. MSG
@ -114,20 +114,27 @@ points to the plist for the message replied to or forwarded, or
nil if there is no such MSG; similar to what
`mu4e-compose-pre-hook' does.
POLICY determines what to do if there are contexts but none match. The following
are supported:
POLICY specifies how to do the determination. If POLICY is
'always-ask, we ask the user unconditionally.
In all other cases, if any context matches (using its match
function), this context is returned. If none of the contexts
match, POLICY determines what to do:
- pick-first: pick the first of the contexts available
- ask: ask the user
- otherwise, return nil. Effectively, this leaves the current context in place."
(when mu4e-contexts
(or (find-if (lambda (context)
(and (mu4e-context-match-func context)
(funcall (mu4e-context-match-func context) msg))) mu4e-contexts)
;; no context found
(case policy
(pick-first (car mu4e-contexts))
(ask (mu4e~context-ask-user "Select context: "))
(otherwise nil)))))
(if (eq policy 'always-ask)
(mu4e~context-ask-user "Select context: ")
(or (find-if (lambda (context)
(and (mu4e-context-match-func context)
(funcall (mu4e-context-match-func context) msg))) mu4e-contexts)
;; no context found
(case policy
(pick-first (car mu4e-contexts))
(ask (mu4e~context-ask-user "Select context: "))
(otherwise nil))))))
(provide 'mu4e-context)

View File

@ -46,7 +46,7 @@
(declare-function mu4e~proc-mkdir "mu4e-proc")
(declare-function mu4e~proc-running-p "mu4e-proc")
(declare-function mu4e-context-autoselect "mu4e-context")
(declare-function mu4e~context-autoswitch "mu4e-context")
(declare-function show-all "org")
@ -737,9 +737,9 @@ first.
If mu4e is already running, execute function FUNC (if non-nil).
Otherwise, check various requirements, then start mu4e. When
successful, call FUNC (if non-nil) afterwards."
;; auto-select some account
(mu4e-context-autoselect)
;; maybe switch the context
(mu4e~context-autoswitch mu4e-compose-parent-message
mu4e-compose-context-policy)
;; if we're already running, simply go to the main view
(if (mu4e-running-p) ;; already running?
(when func ;; yes! run func if defined

View File

@ -114,7 +114,6 @@ better with e.g. offlineimap."
:group 'mu4e
:safe 'booleanp)
(defcustom mu4e-attachment-dir (expand-file-name "~/")
"Default directory for saving attachments.
This can be either a string (a file system path), or a function
@ -218,6 +217,29 @@ Suggested possible values are:
:options '(completing-read ido-completing-read)
:group 'mu4e)
(defcustom mu4e-context-policy 'ask
"Determines how mu4e should determine the context when starting up.
If POLICY is 'always-ask, we ask the user unconditionally.
In all other cases, if any context matches (using its match
function), this context is returned. If none of the contexts
match, POLICY determines what to do:
- pick-first: pick the first of the contexts available
- ask: ask the user
- otherwise, return nil. Effectively, this leaves the current context in place.
Also see `mu4e-compose-context-policy'."
:type '(choice
(const :tag "Always ask what context to use" 'always-ask)
(const :tag "Ask if none of the contexts match" 'ask)
(const :tag "Pick the default (first) context if none match" 'pick-first)
(const :tag "Don't change the context when none match" nil)
:safe 'symbolp
:group 'mu4e))
;; crypto
(defgroup mu4e-crypto nil
"Crypto-related settings."

View File

@ -37,7 +37,7 @@ Documentation License.''
@dircategory Emacs
@direntry
* mu4e: (mu4e). An email client for Emacs.
* mu4e: (Mu4e). An email client for GNU/Emacs.
@end direntry
@contents
@ -1990,8 +1990,8 @@ also match this extra search pattern. @key{\} takes you back to the previous
query, so, effectively 'widens' the search. Technically, narrowing the results
of query @t{x} with expression @t{y} implies doing a search @t{(x) AND y}.
Note, messages that were not in your in your original search results because
of @code{mu4e-headers-results-limit}, may show up in the narrowed query.
Note that messages that were not in your original search results because
of @code{mu4e-headers-results-limit} may show up in the narrowed query.
@subsection Including related messages
@anchor{Including related messages}
@ -2256,16 +2256,15 @@ example:
@menu
* Defining a context::
* Default context::
* Context policies::
* Contexts example::
* Contexts notes::
* Some context tricks::
@end menu
It can be useful to be able to switch between different sets of settings
in @t{mu4e}; typical examples include the case where you have different
e-mail accounts for private and work email, each with their own settings
for e-mail addresses, mailservers etc.
for folders, e-mail addresses, mailservers etc.
The @code{mu4e-context} system is a @t{mu4e}-specific mechanism to allow
for that; users can be define different contexts, and either manually
@ -2307,19 +2306,34 @@ an alist of variable settings for this account.
@t{mu4e} uses a variable @code{mu4e-contexts}, which is a list of such
objects.
@node Default context
@section Default context
@node Context policies
@section Context policies
When you have defined contexts and you start @t{mu4e}, it automatically
switches to the first context whose @code{match-func} returns
non-nil. If none of them do, it picks the first. So, put your 'default'
context as the first in @code{mu4e-contexts}.
When you have defined contexts and you start @t{mu4e} it decides which
context to use based on the variable @code{mu4e-context-policy};
similarly, when you compose a new message, the context is determined
using @code{mu4e-compose-context-policy}.
These policies can be one of the following:
@itemize
@item a symbol @t{always-ask}: unconditionally ask the user what context to pick
@end itemize
The other choices only apply if none of the context matches (i.e., if
none of the contexts' match-functions returns @code{t}:
@itemize
@item a symbol @t{ask}: ask the user
@item a symbol @t{pick-first}: pick the first context
@item @t{nil}: don't change the context
@end itemize
@node Contexts example
@section Example
Let's look at an example; we define two contexts, 'Private' and 'Work'
for a fictional user Alice Derleth.
Let's explain how contexts work by looking at an example. We define two
contexts, 'Private' and 'Work' for a fictional user @emph{Alice
Derleth}.
Note that in this case, we automatically switch to the first context
when starting; see the discussion in the previous section.
@ -2354,16 +2368,13 @@ when starting; see the discussion in the previous section.
"Miskatonic University, Dept. of Occult Sciences\n"))))))
@end lisp
@node Contexts notes
@section Context notes
Couple of notes:
A couple of notes about this example:
@itemize
@item You can manually switch the focus use @code{M-x mu4e-context-switch}, by default bound to @kbd{;} in headers, view and main mode.
The current focus appears in the mode-line.
@item Normally, @code{M-x mu4e-context-switch} does not call the enter/leave functions if the 'new' context is the same as the old one.
However, with a prefix-argument (@kbd{C-u}), you can force @t{mu4e} to call
those function even in that case.
@item Normally, @code{M-x mu4e-context-switch} does not call the enter or leave functions if the 'new' context is the same as the old one.
However, with a prefix-argument (@kbd{C-u}), you can force @t{mu4e} to
invoke those function even in that case.
@item The function @code{mu4e-context-current} returns the current-context; the current context is also visiable in the mode-line when in
headers, view or main mode.
@item You can set any kind of variable; including settings for mail servers etc. However, settings like @code{mu4e-maildir}
@ -2378,13 +2389,13 @@ context we are entering.
@node Some context tricks
@section Some context tricks
It is possible to automatically fill @code{mu4e-user-address-list} by
concatenating the @code{user-mail-address} fields of all contexts:
@lisp
;; This sets `mu4e-user-mail-address-list' to the concatenation of all `user-mail-address' values
;; for all contexts. If you have other mail addresses as well, you'll need to add those manually.
;; This sets `mu4e-user-mail-address-list' to the concatenation of all
;; `user-mail-address' values for all contexts. If you have other mail
;; addresses as well, you'll need to add those manually.
(setq mu4e-user-mail-address-list
(delq nil
(mapcar (lambda (context)
@ -2906,6 +2917,7 @@ two days, you could add this to @code{org-capture-templates}:
If you use the functionality a lot, you may want to define key-bindings
for that in headers and view mode:
@lisp
(define-key mu4e-headers-mode-map (kbd "C-c c") 'org-mu4e-store-and-capture)
(define-key mu4e-view-mode-map (kbd "C-c c") 'org-mu4e-store-and-capture)