mu4e: context policy tweaking

As discussed in issue #751, mu4e should tried to accommodate some
different ways of choosing the context.

We add a new policy `ask-if-none', that will only query the user if
there is no context yet; this is the new default for
mu4e-context-policy, i.e. what happens when entering the main view.

The default policy for mu4e-compose-context-policy is now `ask',
i.e. ask if none of the contexts match.

The idea is for the defaults to ask when we don't know a context, and we
can't determine one using the match functions; this is probably the
least confusing. When user gets annoyed by too many question, they can
tweak the behavior to their liking. I.e. 'ask questions first, shoot
later'

Document all of this.
This commit is contained in:
djcb 2016-01-03 13:29:19 +02:00
parent 8e43e8cac3
commit b373f8e5f0
5 changed files with 150 additions and 113 deletions

View File

@ -119,24 +119,26 @@ for querying the message information."
:group 'mu4e-compose)
(defcustom mu4e-compose-context-policy nil
"Determines how mu4e should determine the context when composing a new message.
(defcustom mu4e-compose-context-policy 'ask
"Policy for determining the context when composing a new message.
If POLICY is
'always-ask, we ask the user unconditionally.
If the value is `always-ask', 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, and if there is no current context, POLICY determines what
to do:
function), this context is used. Otherwise, if none of the
contexts match, we have the following choices:
- pick-first: pick the first of the contexts available
- ask: ask the user
- otherwise, return nil. Effectively, this leaves the current context in place."
- `pick-first': pick the first of the contexts available (ie. the default)
- `ask': ask the user
- `ask-if-none': ask if there is no context yet, otherwise leave it as it is
- nil: return nil; leaves the current context as is.
Also see `mu4e-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 "Ask when there's no context yet" 'ask-if-none)
(const :tag "Pick the first context if none match" 'pick-first)
(const :tag "Don't change the context when none match" nil)
:safe 'symbolp
:group 'mu4e-compose))
@ -403,6 +405,7 @@ tempfile)."
(set (make-local-variable 'mu4e-compose-parent-message) original-msg)
(put 'mu4e-compose-parent-message 'permanent-local t)
;; maybe switch the context
(message "Autoswitch")
(mu4e~context-autoswitch mu4e-compose-parent-message
mu4e-compose-context-policy)
(run-hooks 'mu4e-compose-pre-hook)

View File

@ -130,26 +130,26 @@ POLICY specifies how to do the determination. If POLICY is
In all other cases, if any context matches (using its match
function), this context is returned. If none of the contexts
match, and if there is no current context, POLICY determines what
to do:
match, POLICY determines what to do:
- pick-first: pick the first of the contexts available
- ask: ask the user
- ask-if-none: ask if there is no context yet
- otherwise, return nil. Effectively, this leaves the current context as it is."
(when mu4e-contexts
(if (eq policy 'always-ask)
(mu4e~context-ask-user "Select context: ")
(or ;; is there a matching one?
(find-if (lambda (context)
(and (mu4e-context-match-func context)
(when (mu4e-context-match-func context)
(funcall (mu4e-context-match-func context) msg)))
mu4e-contexts)
;; no matching one; but is there a current one?
(mu4e-context-current)
;; no context found yet; consult policy
(case policy
(pick-first (car mu4e-contexts))
(ask (mu4e~context-ask-user "Select context: "))
(ask-if-none (or (mu4e-context-current)
(mu4e~context-ask-user "Select context: ")))
(otherwise nil))))))
(provide 'mu4e-context)

View File

@ -279,7 +279,7 @@ never hits the disk. Also see `mu4e~draft-insert-mail-header-separator."
(replace-match "")))))
(defun mu4e~draft-user-wants-reply-all (origmsg)
(defun mu4e~draft-reply-all-p (origmsg)
"Ask user whether she wants to reply to *all* recipients.
If there is just one recipient of ORIGMSG do nothing."
(let* ((recipnum
@ -315,11 +315,10 @@ You can append flags."
(defun mu4e~draft-common-construct ()
"Construct the common headers for each message."
(concat
(mu4e~draft-header "User-agent" mu4e-user-agent-string)
(mu4e~draft-header "User-agent" mu4e-user-agent-string)
(when mu4e-compose-auto-include-date
(mu4e~draft-header "Date" (message-make-date)))))
(defconst mu4e~draft-reply-prefix "Re: "
"String to prefix replies with.")
@ -332,7 +331,7 @@ fields will be the same as in the original."
(+ (length (mu4e~draft-create-to-lst origmsg))
(length (mu4e~draft-create-cc-lst origmsg t))))
;; reply-to-self implies reply-all
(reply-all (or reply-to-self (mu4e~draft-user-wants-reply-all origmsg)))
(reply-all (or reply-to-self (mu4e~draft-reply-all-p origmsg)))
(old-msgid (plist-get origmsg :message-id))
(subject
(concat mu4e~draft-reply-prefix

View File

@ -217,25 +217,27 @@ Suggested possible values are:
:options '(completing-read ido-completing-read)
:group 'mu4e)
(defcustom mu4e-context-policy 'pick-first
"Determines how mu4e should determine the context when starting up.
(defcustom mu4e-context-policy 'ask-if-none
"The policy to determine the context when entering the mu4e main view.
If POLICY is 'always-ask, we ask the user unconditionally.
If the value is `always-ask', 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, and if there is no current context, POLICY determines what
to do:
function), this context is used. Otherwise, if none of the
contexts match, we have the following choices:
- pick-first: pick the first of the contexts available
- ask: ask the user
- otherwise, return nil. Effectively, this leaves the current context in place.
- `pick-first': pick the first of the contexts available (ie. the default)
- `ask': ask the user
- `ask-if-none': ask if there is no context yet, otherwise leave it as it is
- nil: return nil; leaves the current context as is.
Also see `mu4e-compose-context-policy'."
:type '(choice
(const :tag "Always ask what context to use" 'always-ask)
(const :tag "Always ask what context to use, even if one matches"
'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 "Ask when there's no context yet" 'ask-if-none)
(const :tag "Pick the first context if none match" 'pick-first)
(const :tag "Don't change the context when none match" nil)
:safe 'symbolp
:group 'mu4e))
@ -779,7 +781,6 @@ when mu4e starts.")
(defvar mu4e~headers-last-query nil
"The present (most recent) query.")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; our handlers funcs

View File

@ -3,7 +3,7 @@
@include texi.texi
@c %**start of header
@setfilename mu4e.info
@settitle mu4e @value{mu-version} user manual
@settitle Mu4e @value{mu-version} user manual
@c Use proper quote and backtick for code sections in PDF output
@c Cf. Texinfo manual 14.2
@ -25,7 +25,7 @@ Documentation License.''
@end copying
@titlepage
@title @t{Mu4e} - an e-mail client for Emacs
@title @t{Mu4e} - an e-mail client for GNU/Emacs
@subtitle version @value{mu-version}
@author Dirk-Jan C. Binnema
@ -37,7 +37,7 @@ Documentation License.''
@dircategory Emacs
@direntry
* mu4e: (Mu4e). An email client for GNU/Emacs.
* mu4e: (Mu4e). An email client for GNU/Emacs.
@end direntry
@contents
@ -54,14 +54,13 @@ Documentation License.''
Welcome to @t{mu4e} @value{mu-version}!
@t{mu4e} (@t{mu}-for-emacs) is an e-mail client for GNU-Emacs version
24, built on top of the
@t{mu4e} (@t{mu}-for-emacs) is an e-mail client for GNU-Emacs version 24
or higher, built on top of the
@t{mu}@footnote{@url{http://www.djcbsoftware.nl/code/mu}} e-mail search
engine. @t{mu4e} is optimized for fast handling of large amounts of
e-mail.
Some of @t{mu4e}'s highlights:
Some of its highlights:
@itemize
@item Fully search-based: there are no folders@footnote{that is, instead of
folders, you use queries that match messages in a particular folder}, only
@ -79,11 +78,12 @@ for that though - see the @ref{FAQ}}
In this manual, we go through the installation of @t{mu4e}, do some
basic configuration and explain its daily use. We also show you how you
can customize @t{mu4e} for your needs.
can customize @t{mu4e} for your special needs.
At the end of the manual, there are some example configurations, to get you up
to speed quickly: @ref{Example configurations}. There's also an @ref{FAQ},
which should help you with some common questions.
At the end of the manual, there are some example configurations, to get
you up to speed quickly: @ref{Example configurations}. There's also an
@ref{FAQ}, which should help you with questions to some common
questions.
@menu
* Introduction:: Where be begin
@ -122,15 +122,10 @@ Appendices
@node Why another e-mail client
@section Why another e-mail client?
Fair question.
I'm not sure the world needs yet another e-mail client, but perhaps
@emph{I} do!
I (the author) spend a @emph{lot} of time dealing with e-mail, both
professionally and privately. Having an efficient e-mail client is
essential. Since none of the existing ones worked the way I wanted, I
created my own.
created my own.
@command{emacs} is an integral part of my workflow, so it made a lot of
sense to use it for e-mail as well. And as I had already written an
@ -141,16 +136,15 @@ basis.
@section Other mail clients
Under the hood, @t{mu4e} is fully search-based, similar to programs like
@t{notmuch}@footnote{@url{http://notmuchmail.org}},
@t{md}@footnote{@url{https://github.com/nicferrier/md} (inactive)} and
@t{sup}@footnote{@url{http://sup.rubyforge.org/}
(unreachable)}.
@t{notmuch}@footnote{@url{http://notmuchmail.org}} and
@t{sup}@footnote{@url{http://sup.rubyforge.org/}}.
However, @t{mu4e}'s user-interface is quite different. @t{mu4e}'s mail
handling (deleting, moving etc.) is inspired by
@emph{Wanderlust}@footnote{@url{http://www.gohome.org/wl/}} (another
@code{emacs}-based e-mail client),
@t{mutt}@footnote{@url{http://www.mutt.org/}} and @t{dired}.
@t{mutt}@footnote{@url{http://www.mutt.org/}} and the @t{dired}
file-manager for emacs.
@t{mu4e} tries to keep all the 'state' in your maildirs, so you can easily
switch between clients, synchronize over @abbr{IMAP}, backup with @t{rsync}
@ -159,7 +153,7 @@ and so on. If you delete the database, you won't lose any information.
@node What mu4e does not do
@section What @t{mu4e} does not do
There are a number of things that @t{mu4e} does @emph{not} do:
There are a number of things that @t{mu4e} does @b{not} do:
@itemize
@item @t{mu}/@t{mu4e} do @emph{not} get your e-mail messages from
a mail server. That task is delegated to other tools, such as
@ -169,7 +163,7 @@ a mail server. That task is delegated to other tools, such as
messages end up in a maildir, @t{mu4e} and @t{mu} are happy to deal with
them.
@item @t{mu4e} also does @emph{not} implement sending of messages; instead, it
depends on @t{smptmail} (@inforef{Top,,smtpmail}), which is part of
depends on @t{smtpmail} (@inforef{Top,,smtpmail}), which is part of
@command{emacs}. In addition, @t{mu4e} piggybacks on Gnus' message editor;
@inforef{Top,,message}.
@end itemize
@ -200,17 +194,13 @@ details. Also, if it is about the behavior for specific messages, please
attach the raw message (that is, the message file as it exists in your
maildir); you can of course strip off any personal information.
If you are new to all of this, the somewhat paternalistic @emph{``How to
ask questions the smart
way''}@footnote{@url{http://www.catb.org/esr/faqs/smart-questions.html}}
may be a good read.
@node Getting started
@chapter Getting started
In this chapter, we go through the installation of @t{mu4e} and its
basic setup. After we have succeeded in @ref{Getting mail}, and
@pxref{Indexing your messages}, we discuss @ref{Basic configuration}.
@pxref{Indexing your messages}, we discuss the @ref{Basic
configuration}.
After these steps, @t{mu4e} should be ready to go!
@ -2216,7 +2206,8 @@ elements:
@itemize
@item @code{:char} -- the character to display in the headers view.
@item @code{:prompt} -- the prompt to use when asking for marks (used for example when marking a whole thread).
@item @code{:prompt} -- the prompt to use when asking for marks
(used for example when marking a whole thread).
@item @code{:ask-target} -- a function run once per bulk-operation, and thus suitable for
querying the user about a target for move-like marks. If nil, the
TARGET passed to @code{:dyn-target} is nil.
@ -2277,53 +2268,62 @@ example:
@chapter Contexts
@menu
* Defining a context::
* What contexts are made of::
* Context policies::
* Contexts and special folders::
* Contexts example::
* 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 folders, e-mail addresses, mailservers etc.
It can be useful to switch between different sets of settings in
@t{mu4e}; a typical example is the case where you have different e-mail
accounts for private and work email, each with their own values for
folders, e-mail addresses, mailservers and so on.
The @code{mu4e-context} system is a @t{mu4e}-specific mechanism to allow
for that; users can be define different contexts, and either manually
switch between them, or let @t{mu4e} determine the right context when
composing a message.
for that; users can be define different @i{contexts} corresponding with
groups of setting and either manually switch between them, or let
@t{mu4e} determine the right context when composing a message based on
some user-provided function.
Note, there are a number of existing ways to switch accounts in
Note that there are a number of existing ways to switch accounts in
@t{mu4e}, for example using the method described in the @ref{Tips and
Tricks} section of this manual. Those still work - but the new mechanism
has the benefit of being a core part of @code{mu4e}, thus allowing for
deeper integration.
@node Defining a context
@section Defining a context
@node What contexts are made of
@section What context are made of
Let's see what's contained in a context. Most of it is optional.
A @code{mu4e-context} is Lisp object with the following members:
@itemize
@item @t{name}: the name of the context, e.g. @t{work} or @t{private}
@item @t{vars}:
an association-list (alist) of variable settings for this account.
@item @t{enter-func}:
an optional function that takes no parameter and is invoked when entering
the context
an (optional) function that takes no parameter and is invoked when entering
the context. You can use this for extra setup etc.
@item @t{leave-func}:
an optional function that takes no parameter and is invoked when leaving
the context
an (optional) function that takes no parameter and is invoked when leaving
the context. You can use this for clearing things up.
@item @t{match-func}:
an optional function that is invoked before replying to or forwarding a
an (optional) function that takes an @t{MSG} message plist as argument,
and returns non-@t{nil} if this context matches the situation. @t{mu4e}
uses the first context that matches, in a couple of situations:
@itemize
@item when starting @t{mu4e} to determine the
starting context; in this case, @t{MSG} is nil. You can use e.g. the
host you're running or the time of day to determine which context
matches.
@item before replying to or forwarding a
message with the given message plist as parameter, or @t{nil} when
composing a brand new message. The function should return @t{t} when
this context is the right one for this message, or @t{nil} otherwise.
The function is also invoked when starting @t{mu4e} to determine the
starting context; in that case, the message-parameter is nil. You can
use e.g. the host you're running or or perhaps the time of day or even
your location to determine the context.
@item @t{vars}:
an alist of variable settings for this account.
@item when determining the target folders for deleting, refiling etc;
see @ref{Contexts and special folders}.
@end itemize
@end itemize
@t{mu4e} uses a variable @code{mu4e-contexts}, which is a list of such
@ -2337,18 +2337,28 @@ 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:
For both of these, you can choose one of the following policies:
@itemize
@item a symbol @t{always-ask}: unconditionally ask the user what context to pick
@item a symbol @code{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}:
The other choices @b{only apply if none of the contexts match} (i.e.,
none of the contexts' match-functions returns @code{t}). We have the
following options:
@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
@item a symbol @code{ask}: ask the user if @t{mu4e} can't figure
things out the context by itself (through the match-function). This is a
good policy if there are no match functions, or if the match functions
don't cover all cases.
@item a symbol @code{ask-if-none}: if there's already a context, don't change it; otherwise,
ask the user.
@item a symbol @code{pick-first}: pick the first (default) context. This is a good choice if
you want to specify context for special case, and fall back to the first
one if none match.
@item @code{nil}: don't change the context; this is useful if you don't change
contexts very often, and e.g. manually changes contexts with @kbd{M-x
mu4e-context-switch}.
@end itemize
@node Contexts and special folders
@ -2367,10 +2377,11 @@ the account it belongs to, which is not necessarily the current context.
To make this easy to do, whenever @t{mu4e} needs to know the value for
such a special folder for a given message, it tries to determine the
appropriate context using @code{mu4e-context-determine}. If it finds
one, it let-binds the @code{vars} for that account, and then determines
the value for the folder. It does not, however, call the @t{enter-func},
since we are not really switching context.
appropriate context using @code{mu4e-context-determine} (and policy
@t{nil}; see @ref{Context policies}). If it finds a matching context, it
let-binds the @code{vars} for that account, and then determines the
value for the folder. It does not, however, call the @code{enter-func}
or @code{leave-func}, since we are not really switching context.
In practice, this what this means that a long as each of the accounts
has a good @t{match-func}, all message operations automatically find the
@ -2393,8 +2404,9 @@ when starting; see the discussion in the previous section.
:enter-func (lambda () (mu4e-message "Switch to the Private context"))
;; leave-func not defined
:match-func (lambda (msg)
(when msg (mu4e-message-contact-field-matches msg
:to "aliced@@home.example.com")))
(when msg
(mu4e-message-contact-field-matches msg
:to "aliced@@home.example.com")))
:vars '( ( mail-reply-to . "aliced@@home.example.com" )
( user-mail-address . "aliced@@home.example.com" )
( user-full-name . "Alice Derleth" )
@ -2407,8 +2419,9 @@ when starting; see the discussion in the previous section.
:enter-func (lambda () (mu4e-message "Switch to the Work context"))
;; leave-fun not defined
:match-func (lambda (msg)
(when msg (mu4e-message-contact-field-matches msg
:to "aderleth@@miskatonic.example.com")))
(when msg
(mu4e-message-contact-field-matches msg
:to "aderleth@@miskatonic.example.com")))
:vars '( ( mail-reply-to . "aderleth@@miskatonic.example.com" )
( user-mail-address . "aderleth@@miskatonic.example.com" )
( user-full-name . "Alice Derleth" )
@ -2416,24 +2429,44 @@ when starting; see the discussion in the previous section.
(concat
"Prof. Alice Derleth\n"
"Miskatonic University, Dept. of Occult Sciences\n"))))))
;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should
;; guess or ask the correct context, e.g.
;; start with the first (default) context;
;; default is to ask-if-none (ask when there's no context yet, and none match)
;; (setq mu4e-context-policy 'pick-first)
;; compose with the current context is no context matches;
;; default is to ask
;; '(setq mu4e-compose-context-policy nil)
@end lisp
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.
@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 or leave functions if the 'new' context is the same as the old one.
@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
@item The function @code{mu4e-context-current} returns the current-context;
the current context is also visible 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}
and @code{mu4e-mu-home} are not changeable after they have been set without quiting @t{mu4e} first.
@item @code{leave-func} (if defined) for the context we are leaving, is invoked before the @code{enter-func} (if defined) of the
@item You can set any kind of variable; including settings for mail servers etc.
However, settings such as @code{mu4e-maildir} and @code{mu4e-mu-home} are
not changeable after they have been set without quitting @t{mu4e} first.
@item @code{leave-func} (if defined) for the context we are leaving, is invoked
before the @code{enter-func} (if defined) of the
context we are entering.
@item @code{enter-func} (if defined) is invoked before setting the variables.
@item @code{match-func} (if defined) is invoked just before @code{mu4e-compose-pre-hook}.
@item Finally, be careful to get the quotations right -- backticks, single quotes and commas and note the '.' between variable name and its value.
@item See the variables @code{mu4e-context-policy} and
@code{mu4e-compose-context-policy} to tweak what @t{mu4e} should do when
no context matches (or if you always want to be asked).
@item Finally, be careful to get the quotations right -- backticks, single quotes
and commas and note the '.' between variable name and its value.
@end itemize
@node Some context tricks
@ -2455,7 +2488,6 @@ concatenating the @code{user-mail-address} fields of all contexts:
@end lisp
@node Dynamic folders
@chapter Dynamic folders
@ -2541,9 +2573,11 @@ describing a message}. The plist corresponds to the message at point. See
returns the first of the clauses that matches. It's important to make the last
clause a catch-all, so we always return @emph{some} folder.
@item We use
the convenience function @code{mu4e-message-contact-field-matches}, which
evaluates to @code{t} if any of the names or e-mail addresses in a contact
field (in this case, the @t{To:}-field) matches the regular expression.
the convenience function @code{mu4e-message-contact-field-matches},
which evaluates to @code{t} if any of the names or e-mail addresses in a
contact field (in this case, the @t{To:}-field) matches the regular
expression. With @t{mu4e} version 0.9.16 or newer, the contact field can
in fact be a list instead of a single value, such as @code{'(:to :cc)'}
@end itemize
@node Other dynamic folders
@ -3516,7 +3550,7 @@ time, and @code{mu4e-headers-mark-pattern} (@key{%}) to mark all messages
matching a certain regular expression.
@item @emph{@t{mu4e} seems to return a subset of all matches - how can I get
all?} For speed reasons, @t{mu4e} returns only up to the value of the variable
@code{m4ue-search-result-limit} (default: 500) matches. To show @emph{all},
@code{mu4e-search-result-limit} (default: 500) matches. To show @emph{all},
use @kbd{M-x mu4e-headers-toggle-full-search} (@key{Q}), or customize the
variable @code{mu4e-headers-full-search}. This applies to all search commands.
@item @emph{How can I get notifications when receiving mail?} There is