mu4e: by default, make changing to current context a no-op

Update mu4e-context-switch to not call the enter/leave functions when
'changing' to the current context. However, calling those functions (if
defined) can be force through a prefix arg. Document this.

Make mu4e-context-determine recognize 'policies', i.e. what to do when
no context matches. This part is WIP.
This commit is contained in:
djcb 2015-12-21 22:15:47 +02:00
parent 6bafb39960
commit 60192a60d2
3 changed files with 87 additions and 57 deletions

View File

@ -372,8 +372,8 @@ tempfile)."
(put 'mu4e-compose-parent-message 'permanent-local t) (put 'mu4e-compose-parent-message 'permanent-local t)
(let ((context (mu4e-context-determine mu4e-compose-parent-message))) (let ((context (mu4e-context-determine mu4e-compose-parent-message)))
(if context (if context
(mu4e-context-switch (mu4e-context-name context)) (mu4e-context-switch nil (mu4e-context-name context))
(when mu4e-contexts (mu4e-context-switch)))) (when mu4e-contexts (mu4e-context-switch nil))))
(run-hooks 'mu4e-compose-pre-hook) (run-hooks 'mu4e-compose-pre-hook)
;; this opens (or re-opens) a messages with all the basic headers set. ;; this opens (or re-opens) a messages with all the basic headers set.

View File

@ -59,32 +59,45 @@ for the message replied to or forwarded, and nil otherwise. Before composing a n
;; if it matches, nil otherwise ;; if it matches, nil otherwise
vars) ;; alist of variables. vars) ;; alist of variables.
(defun mu4e-context-switch (&optional name) (defun mu4e~context-ask-user (prompt)
"Let user choose some context based on its name."
(when mu4e-contexts
(let* ((names (map 'list (lambda (context) (cons (mu4e-context-name context) context))
mu4e-contexts))
(context (mu4e-read-option prompt names)))
(or context (mu4e-error "No such context")))))
(defun mu4e-context-switch (&optional force name)
"Switch context to a context with NAME which is part of "Switch context to a context with NAME which is part of
`mu4e-contexts'; if NAME is nil, query user." `mu4e-contexts'; if NAME is nil, query user.
(interactive)
If the new context is the same and the current context, only
switch (run associated functions) when prefix argument FORCE is
non-nil."
(interactive "P")
(unless mu4e-contexts (unless mu4e-contexts
(mu4e-error "No contexts defined")) (mu4e-error "No contexts defined"))
(let* ((names (map 'list (lambda (context) (let* ((names (map 'list (lambda (context)
(cons (mu4e-context-name context) context)) (cons (mu4e-context-name context) context))
mu4e-contexts)) mu4e-contexts))
(context (context
(if name (cdr-safe (assoc name names)) (if name
(mu4e-read-option "Switch to context: " names)))) (cdr-safe (assoc name names))
(mu4e~context-ask-user "Switch to context: "))))
(unless context (mu4e-error "No such context")) (unless context (mu4e-error "No such context"))
;; if new context is same as old one one switch with FORCE is set.
;; leave the current context (when (or force (not (eq context (mu4e-context-current))))
(when (and mu4e~context-current (mu4e-context-leave-func mu4e~context-current)) (when (and (mu4e-context-current) (mu4e-context-leave-func mu4e~context-current))
(funcall (mu4e-context-leave-func mu4e~context-current))) (funcall (mu4e-context-leave-func mu4e~context-current)))
;; enter the new context ;; enter the new context
(when (mu4e-context-enter-func context) (when (mu4e-context-enter-func context)
(funcall (mu4e-context-enter-func context))) (funcall (mu4e-context-enter-func context)))
(when (mu4e-context-vars context) (when (mu4e-context-vars context)
(mapc #'(lambda (cell) (mapc #'(lambda (cell)
(set (car cell) (cdr cell))) (set (car cell) (cdr cell)))
(mu4e-context-vars context))) (mu4e-context-vars context)))
(setq mu4e~context-current context) (setq mu4e~context-current context)
(mu4e-message "Switched context to %s" (mu4e-context-name context)) (mu4e-message "Switched context to %s" (mu4e-context-name context)))
context)) context))
(defun mu4e-context-autoselect () (defun mu4e-context-autoselect ()
@ -95,19 +108,26 @@ match, return the first."
(mu4e-context-switch (mu4e-context-switch
(mu4e-context-name (mu4e-context-determine nil 'pick-first))))) (mu4e-context-name (mu4e-context-determine nil 'pick-first)))))
(defun mu4e-context-determine (msg &optional pick-first) (defun mu4e-context-determine (msg &optional policy)
"Return the first context with a match-func that returns t. MSG "Return the first context with a match-func that returns t. MSG
points to the plist for the message replied to or forwarded, or points to the plist for the message replied to or forwarded, or
nil if there is no such MSG; similar to what nil if there is no such MSG; similar to what
`mu4e-compose-pre-hook' does. `mu4e-compose-pre-hook' does.
If there are contexts but none match, return nil, unless POLICY determines what to do if there are contexts but none match. The following
PICK-FIRST is non-nil, in which case return the first context." are supported:
- 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 (when mu4e-contexts
(or (find-if (lambda (context) (or (find-if (lambda (context)
(and (mu4e-context-match-func context) (and (mu4e-context-match-func context)
(funcall (mu4e-context-match-func context) msg))) mu4e-contexts) (funcall (mu4e-context-match-func context) msg))) mu4e-contexts)
(when pick-first (car mu4e-contexts))))) ;; no context found
(case policy
(pick-first (car mu4e-contexts))
(ask (mu4e~context-ask-user "Select context: "))
(otherwise nil)))))
(provide 'mu4e-context) (provide 'mu4e-context)

View File

@ -459,10 +459,10 @@ this must @emph{not} be a symbolic link.
As we have seen, we can do all of the mail retrieval @emph{outside} of As we have seen, we can do all of the mail retrieval @emph{outside} of
@command{emacs}/@t{mu4e}. However, you can also do it from within @command{emacs}/@t{mu4e}. However, you can also do it from within
@t{mu4e}. @t{mu4e}.
@subsection Basics @subsection Basics
To set up mail-retrieval from withing @t{mu4e}, set the variable To set up mail-retrieval from withing @t{mu4e}, set the variable
@code{mu4e-get-mail-command} to the program or shell command you want to @code{mu4e-get-mail-command} to the program or shell command you want to
use for retrieving mail. You can then get your e-mail using @kbd{M-x use for retrieving mail. You can then get your e-mail using @kbd{M-x
@ -500,7 +500,7 @@ indicate 'no mail'; we can handle that with:
A similar approach can be used with other mail retrieval programs, A similar approach can be used with other mail retrieval programs,
although not all of them have their exit codes documented. although not all of them have their exit codes documented.
@subsection Implicit mail retrieval @subsection Implicit mail retrieval
If you don't have a specific command for getting mail, for example If you don't have a specific command for getting mail, for example
because you are running your own mail-server, you can leave because you are running your own mail-server, you can leave
@ -1672,7 +1672,7 @@ If you don't want to include this automatically with each message,
you can set @code{mu4e-compose-signature-auto-include} to @code{nil}; you can you can set @code{mu4e-compose-signature-auto-include} to @code{nil}; you can
then still include the signature manually, using the function then still include the signature manually, using the function
@code{message-insert-signature}, typically bound to @kbd{C-c C-w}. @code{message-insert-signature}, typically bound to @kbd{C-c C-w}.
@node Other settings @node Other settings
@section Other settings @section Other settings
@ -1809,7 +1809,8 @@ Note - in the @ref{Headers view} you may see the 'friendly name' for a
list; however, when searching you need the real name. You can see the list; however, when searching you need the real name. You can see the
real name for a mailing list from the friendly name's tool-tip. real name for a mailing list from the friendly name's tool-tip.
@item Get messages with a subject soccer, Socrates, society, ...; note that the '*'-wildcard can only appear as a term's rightmost character: @item Get messages with a subject soccer, Socrates, society, ...; note that
the '*'-wildcard can only appear as a term's rightmost character:
@verbatim @verbatim
subject:soc* subject:soc*
@end verbatim @end verbatim
@ -2256,7 +2257,7 @@ example:
@menu @menu
* Defining a context:: * Defining a context::
* Default context:: * Default context::
* Contexts example:: * Contexts example::
* Contexts notes:: * Contexts notes::
* Some context tricks:: * Some context tricks::
@end menu @end menu
@ -2283,7 +2284,7 @@ deeper integration.
A @code{mu4e-context} is Lisp object with the following members: A @code{mu4e-context} is Lisp object with the following members:
@itemize @itemize
@item @t{name}: the name of the context, e.g. @t{work} or @t{private} @item @t{name}: the name of the context, e.g. @t{work} or @t{private}
@item @t{enter-func}: @item @t{enter-func}:
an optional function that takes no parameter and is invoked when entering an optional function that takes no parameter and is invoked when entering
the context the context
@item @t{leave-func}: @item @t{leave-func}:
@ -2327,26 +2328,26 @@ when starting; see the discussion in the previous section.
(setq mu4e-contexts (setq mu4e-contexts
`( ,(make-mu4e-context `( ,(make-mu4e-context
:name "Private" :name "Private"
:enter-func (lambda () (mu4e-message "Switch to the Private context")) :enter-func (lambda () (mu4e-message "Switch to the Private context"))
;; leave-func not defined ;; leave-func not defined
:match-func (lambda (msg) :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" ) :vars '( ( mail-reply-to . "aliced@@home.example.com" )
( user-mail-address . "aliced@@home.example.com" ) ( user-mail-address . "aliced@@home.example.com" )
( user-full-name . "Alice Derleth" ) ( user-full-name . "Alice Derleth" )
( mu4e-compose-signature . ( mu4e-compose-signature .
(concat (concat
"Alice Derleth\n" "Alice Derleth\n"
"Lauttasaari, Finland\n")))) "Lauttasaari, Finland\n"))))
,(make-mu4e-context ,(make-mu4e-context
:name "Work" :name "Work"
:enter-func (lambda () (mu4e-message "Switch to the Work context")) :enter-func (lambda () (mu4e-message "Switch to the Work context"))
;; leave-fun not defined ;; leave-fun not defined
:match-func (lambda (msg) :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" ) :vars '( ( mail-reply-to . "aderleth@@miskatonic.example.com" )
( user-mail-address . "aderleth@@miskatonic.example.com" ) ( user-mail-address . "aderleth@@miskatonic.example.com" )
( user-full-name . "Alice Derleth" ) ( user-full-name . "Alice Derleth" )
( mu4e-compose-signature . ( mu4e-compose-signature .
(concat (concat
"Prof. Alice Derleth\n" "Prof. Alice Derleth\n"
@ -2358,13 +2359,20 @@ when starting; see the discussion in the previous section.
Couple of notes: Couple of notes:
@itemize @itemize
@item You can manually switch the focus use @code{M-x mu4e-context-switch}, by default bound to @code{;} in headers, view and main mode. The current focus appears in the mode-line. @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 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. The current focus appears in the mode-line.
@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 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.
@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. However, with a prefix-argument (@kbd{C-u}), you can force @t{mu4e} to call
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}
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
context we are entering.
@item @code{enter-func} (if defined) is invoked before setting the variables. @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 @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 value. @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 @end itemize
@node Some context tricks @node Some context tricks
@ -2376,7 +2384,7 @@ concatenating the @code{user-mail-address} fields of all contexts:
@lisp @lisp
;; This sets `mu4e-user-mail-address-list' to the concatenation of all `user-mail-address' values ;; 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. ;; for all contexts. If you have other mail addresses as well, you'll need to add those manually.
(setq mu4e-user-mail-address-list (setq mu4e-user-mail-address-list
(delq nil (delq nil
(mapcar (lambda (context) (mapcar (lambda (context)
@ -2938,7 +2946,7 @@ such as @file{~/.emacs.d/init.el}) the following @emph{after} the
@lisp @lisp
;; Load BBDB (Method 1) ;; Load BBDB (Method 1)
(require 'bbdb-loaddefs) (require 'bbdb-loaddefs)
;; OR (Method 2) ;; OR (Method 2)
;; (require 'bbdb-loaddefs "/path/to/bbdb/lisp/bbdb-loaddefs.el") ;; (require 'bbdb-loaddefs "/path/to/bbdb/lisp/bbdb-loaddefs.el")
;; OR (Method 3) ;; OR (Method 3)
@ -3501,14 +3509,15 @@ manual} becomes @t{/&MH4wijCCMEgwSg-}). How can display such folders
correctly?} This is best solved by telling @command{offlineimap} to use UTF-8 correctly?} This is best solved by telling @command{offlineimap} to use UTF-8
instead -- see instead -- see
@url{https://github.com/djcb/mu/issues/68#issuecomment-8598652}. @url{https://github.com/djcb/mu/issues/68#issuecomment-8598652}.
@item @emph{How can I customize the function to select a folder?} @item @emph{How can I customize the function to select a folder?}
The @t{mu4e-completing-read} variable can be customized to select a The @t{mu4e-completing-read} variable can be customized to select a
folder in any way. The variable can be set to a function that receives folder in any way. The variable can be set to a function that receives
five arguments, following @t{completing-read}. The default value is five arguments, following @t{completing-read}. The default value is
@t{ido-completing-read}; to use emacs's default behaviour, set the @t{ido-completing-read}; to use emacs's default behaviour, set the
variable to @t{completing-read}. Helm users can use the same value, and variable to @t{completing-read}. Helm users can use the same value, and
by enabling @t{helm-mode} use helm-style completion. by enabling @t{helm-mode} use helm-style completion.
@item @emph{I have a lot of Maildir folders, so regenerating them each time makes things slow. What can I do?} @item @emph{I have a lot of Maildir folders, so regenerating them each time makes
things slow. What can I do?}
Set @code{mu4e-cache-maildir-list} to @code{t} (but make sure to read Set @code{mu4e-cache-maildir-list} to @code{t} (but make sure to read
its docstring). its docstring).
@ -3540,7 +3549,8 @@ messages}.
like Gmail does?} Yes -- see @ref{Including related messages}. like Gmail does?} Yes -- see @ref{Including related messages}.
@item @emph{There seem to be a lot of duplicate messages -- how can I get rid @item @emph{There seem to be a lot of duplicate messages -- how can I get rid
of them?} See @ref{Skipping duplicates}. of them?} See @ref{Skipping duplicates}.
@item @emph{How can I use the @t{eww} browser to view rich-text messages?} See @ref{Html2text functions}. @item @emph{How can I use the @t{eww} browser to view rich-text messages?}
See @ref{Html2text functions}.
@item @emph{Some messages are almost unreadable in emacs - can I view them in @item @emph{Some messages are almost unreadable in emacs - can I view them in
an external web browser?} Indeed, airlines often send messages that an external web browser?} Indeed, airlines often send messages that
heavily depend on html and are hard to digest inside emacs. Fortunately, heavily depend on html and are hard to digest inside emacs. Fortunately,
@ -3553,7 +3563,7 @@ defined for this. Simply add to your configuration:
Now, when viewing such a difficult message, type @kbd{aV}, and the message Now, when viewing such a difficult message, type @kbd{aV}, and the message
opens inside a webbrowser. You can influence the browser with opens inside a webbrowser. You can influence the browser with
@code{browse-url-generic-program}. @code{browse-url-generic-program}.
@item @emph{How can read encrypted messages that I sent?}. Since you do not own the @item @emph{How can read encrypted messages that I sent?}. Since you do not own the
recipient's key you typically cannot read those mails - so the trick is recipient's key you typically cannot read those mails - so the trick is
to encrypt outgoing mails with your key, too. This can be automated by to encrypt outgoing mails with your key, too. This can be automated by
adding the following snippet to your configuration (courtesy of user adding the following snippet to your configuration (courtesy of user
@ -3587,7 +3597,7 @@ reply-message, based on some field in the original?} See @ref{Compose hooks}.
@item @emph{And what about customizable folders for draft messages, sent @item @emph{And what about customizable folders for draft messages, sent
messages, trashed messages, based on e.g. the @t{From:} header?} See messages, trashed messages, based on e.g. the @t{From:} header?} See
@ref{Dynamic folders}. @ref{Dynamic folders}.
@item @emph{Can I define aliases for (groups of) e-mail addresses?} Sure - @item @emph{Can I define aliases for (groups of) e-mail addresses?} Sure -
see @ref{(emacs) Mail Aliases}. see @ref{(emacs) Mail Aliases}.
@item @emph{How can I automatically add some header to an outgoing message?} @item @emph{How can I automatically add some header to an outgoing message?}
Once more, see @ref{Compose hooks}. Once more, see @ref{Compose hooks}.
@ -3628,7 +3638,7 @@ to your configuration:
send-mail-function 'async-smtpmail-send-it send-mail-function 'async-smtpmail-send-it
message-send-mail-function 'async-smtpmail-send-it) message-send-mail-function 'async-smtpmail-send-it)
@end lisp @end lisp
With this, messages are sent using background emacs-instance. With this, messages are sent using background emacs-instance.
A word of warning though, this tends to not be as reliable as sending the A word of warning though, this tends to not be as reliable as sending the
message in the normal, synchronous fashion, and people have reported silent message in the normal, synchronous fashion, and people have reported silent
@ -3645,7 +3655,7 @@ Sending...done
@end verbatim @end verbatim
The first and final messages are the most important, and there may be The first and final messages are the most important, and there may be
considerable time between them, depending on the size of the message. considerable time between them, depending on the size of the message.
@item @emph{Is it possible to compose messages in a separate frame?} @item @emph{Is it possible to compose messages in a separate frame?}
Yes - set the variable @code{mu4e-compose-in-new-frame} to @code{t}. Yes - set the variable @code{mu4e-compose-in-new-frame} to @code{t}.
@item @emph{How can I apply format=flowed to my outgoing messages, enabling @item @emph{How can I apply format=flowed to my outgoing messages, enabling
receiving clients that support this feature to reflow my paragraphs?} receiving clients that support this feature to reflow my paragraphs?}