mirror of
https://github.com/djcb/mu.git
synced 2024-06-29 07:51:04 +02:00
mu4e: let-bind context vars in mu4e-get-*-folder
When we determine the drafts/refile/trash/sent folder, see if we can find a context matching the particular message. If we find one, let-bind the vars for that context before determining the folder. This allows e.g for moving message to context-specific folder, rather than the one for the current context - useful when dealing with headers buffers with message belonging to different contexts.
This commit is contained in:
parent
825881feb3
commit
7dff782c58
|
@ -22,16 +22,20 @@
|
||||||
|
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
|
|
||||||
;; A mu4e 'context' is a a set of variable-settings and hooks, which can be used e.g. to switch
|
;; A mu4e 'context' is a a set of variable-settings and functions, which can be
|
||||||
;; between accounts.
|
;; used e.g. to switch between accounts.
|
||||||
|
|
||||||
|
(eval-when-compile (byte-compile-disable-warning 'cl-functions))
|
||||||
(require 'cl)
|
(require 'cl)
|
||||||
|
|
||||||
(require 'mu4e-utils)
|
(require 'mu4e-utils)
|
||||||
|
|
||||||
(defvar mu4e-contexts nil "The list of context objects.")
|
(defvar mu4e-contexts nil "The list of `mu4e-context' objects
|
||||||
|
describing mu4e's contexts.")
|
||||||
|
|
||||||
(defvar mu4e~context-current nil
|
(defvar mu4e~context-current nil
|
||||||
"The current context. Use `mu4e-context-switch' to change it.")
|
"The current context; for internal use. Use
|
||||||
|
`mu4e-context-switch' to change it.")
|
||||||
|
|
||||||
(defun mu4e-context-current ()
|
(defun mu4e-context-current ()
|
||||||
"Get the currently active context, or nil if there is none."
|
"Get the currently active context, or nil if there is none."
|
||||||
|
@ -47,11 +51,15 @@
|
||||||
(defstruct mu4e-context
|
(defstruct mu4e-context
|
||||||
"A mu4e context object with the following members:
|
"A mu4e context object with the following members:
|
||||||
- `name': the name of the context, eg. \"Work\" or \"Private\".'
|
- `name': the name of the context, eg. \"Work\" or \"Private\".'
|
||||||
- `enter-func': a parameterless function invoked when entering this context, or nil
|
- `enter-func': a parameterless function invoked when entering
|
||||||
- `leave-func':a parameterless fuction invoked when leaving this context, or nil
|
this context, or nil
|
||||||
- `match-func': a function called when comnposing a new messages, and takes a message plist
|
- `leave-func':a parameterless fuction invoked when leaving this
|
||||||
for the message replied to or forwarded, and nil otherwise. Before composing a new message,
|
context, or nil
|
||||||
`mu4e' switches to the first context for which `match-func' return t."
|
- `match-func': a function called when comnposing a new messages,
|
||||||
|
and 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' return t."
|
||||||
name ;; name of the context, e.g. "work"
|
name ;; name of the context, e.g. "work"
|
||||||
(enter-func nil) ;; function invoked when entering the context
|
(enter-func nil) ;; function invoked when entering the context
|
||||||
(leave-func nil) ;; function invoked when leaving the context
|
(leave-func nil) ;; function invoked when leaving the context
|
||||||
|
@ -62,7 +70,8 @@ for the message replied to or forwarded, and nil otherwise. Before composing a n
|
||||||
(defun mu4e~context-ask-user (prompt)
|
(defun mu4e~context-ask-user (prompt)
|
||||||
"Let user choose some context based on its name."
|
"Let user choose some context based on its name."
|
||||||
(when mu4e-contexts
|
(when mu4e-contexts
|
||||||
(let* ((names (map 'list (lambda (context) (cons (mu4e-context-name context) context))
|
(let* ((names (map 'list (lambda (context)
|
||||||
|
(cons (mu4e-context-name context) context))
|
||||||
mu4e-contexts))
|
mu4e-contexts))
|
||||||
(context (mu4e-read-option prompt names)))
|
(context (mu4e-read-option prompt names)))
|
||||||
(or context (mu4e-error "No such context")))))
|
(or context (mu4e-error "No such context")))))
|
||||||
|
@ -87,7 +96,8 @@ non-nil."
|
||||||
(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.
|
;; if new context is same as old one one switch with FORCE is set.
|
||||||
(when (or force (not (eq context (mu4e-context-current))))
|
(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)
|
||||||
|
@ -124,13 +134,14 @@ match, POLICY determines what to do:
|
||||||
|
|
||||||
- pick-first: pick the first of the contexts available
|
- pick-first: pick the first of the contexts available
|
||||||
- ask: ask the user
|
- ask: ask the user
|
||||||
- otherwise, return nil. Effectively, this leaves the current context in place."
|
- otherwise, return nil. Effectively, this leaves the current context as it is."
|
||||||
(when mu4e-contexts
|
(when mu4e-contexts
|
||||||
(if (eq policy 'always-ask)
|
(if (eq policy 'always-ask)
|
||||||
(mu4e~context-ask-user "Select context: ")
|
(mu4e~context-ask-user "Select context: ")
|
||||||
(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)
|
||||||
;; no context found
|
;; no context found
|
||||||
(case policy
|
(case policy
|
||||||
(pick-first (car mu4e-contexts))
|
(pick-first (car mu4e-contexts))
|
||||||
|
|
|
@ -46,8 +46,9 @@
|
||||||
(declare-function mu4e~proc-mkdir "mu4e-proc")
|
(declare-function mu4e~proc-mkdir "mu4e-proc")
|
||||||
(declare-function mu4e~proc-running-p "mu4e-proc")
|
(declare-function mu4e~proc-running-p "mu4e-proc")
|
||||||
|
|
||||||
(declare-function mu4e~context-autoswitch "mu4e-context")
|
(declare-function mu4e~context-autoswitch "mu4e-context")
|
||||||
|
(declare-function mu4e-context-determine "mu4e-context")
|
||||||
|
(declare-function mu4e-context-vars "mu4e-context")
|
||||||
(declare-function show-all "org")
|
(declare-function show-all "org")
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,26 +91,36 @@ User's addresses are set in `mu4e-user-mail-address-list')."
|
||||||
t))
|
t))
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defmacro with~mu4e-context-vars (context &rest body)
|
||||||
|
"Evaluate BODY, with variables let-bound for CONTEXT (if any).
|
||||||
|
`funcall'."
|
||||||
|
(declare (indent 2))
|
||||||
|
`(let* ((vars (and ,context (mu4e-context-vars ,context))))
|
||||||
|
(progv ;; XXX: perhaps use eval's lexical environment instead of progv?
|
||||||
|
(mapcar (lambda(cell) (car cell)) vars)
|
||||||
|
(mapcar (lambda(cell) (cdr cell)) vars)
|
||||||
|
(eval ,@body))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; the standard folders can be functions too
|
;; the standard folders can be functions too
|
||||||
(defun mu4e~get-folder (foldervar msg)
|
(defun mu4e~get-folder (foldervar msg)
|
||||||
"Get message folder FOLDERVAR.
|
"Within the mu-context of MSG, get message folder FOLDERVAR.
|
||||||
If FOLDER is a string, return it, if it is a function, evaluate
|
If FOLDER is a string, return it, if it is a function, evaluate
|
||||||
this function with MSG as parameter (which may be `nil'), and
|
this function with MSG as parameter (which may be `nil'), and
|
||||||
return the result."
|
return the result."
|
||||||
(unless (member foldervar '(mu4e-sent-folder mu4e-drafts-folder
|
(unless (member foldervar
|
||||||
mu4e-trash-folder mu4e-refile-folder))
|
'(mu4e-sent-folder mu4e-drafts-folder
|
||||||
(mu4e-error "Folder must be either mu4e-sent-folder,
|
mu4e-trash-folder mu4e-refile-folder))
|
||||||
mu4e-drafts-folder or mu4e-trash-folder (not %S)" foldervar))
|
(mu4e-error "Folder must be one of mu4e-(sent|drafts|trash|refile)-folder"))
|
||||||
(let* ((folder (symbol-value foldervar))
|
;; get the value with the vars for the relevants context let-bound
|
||||||
(val
|
(with~mu4e-context-vars (mu4e-context-determine msg nil)
|
||||||
(cond
|
(let* ((folder (symbol-value foldervar))
|
||||||
((stringp folder) folder)
|
(val
|
||||||
((functionp folder) (funcall folder msg))
|
(cond
|
||||||
(t (mu4e-error "unsupported type for %S" folder)))))
|
((stringp folder) folder)
|
||||||
(or val (mu4e-error "%S evaluates to nil" foldervar))))
|
((functionp folder) (funcall folder msg))
|
||||||
|
(t (mu4e-error "unsupported type for %S" folder)))))
|
||||||
|
(or val (mu4e-error "%S evaluates to nil" foldervar)))))
|
||||||
|
|
||||||
(defun mu4e-get-drafts-folder (&optional msg)
|
(defun mu4e-get-drafts-folder (&optional msg)
|
||||||
"Get the sent folder. See `mu4e-drafts-folder'."
|
"Get the sent folder. See `mu4e-drafts-folder'."
|
||||||
|
|
|
@ -439,11 +439,13 @@ with @t{MU4E-PATH} replaced with the actual path.
|
||||||
@section Folders
|
@section Folders
|
||||||
|
|
||||||
The next step is to tell @t{mu4e} where it can find your Maildir, and
|
The next step is to tell @t{mu4e} where it can find your Maildir, and
|
||||||
some special folders. So, for example@footnote{Note that the folders
|
some special folders.
|
||||||
(@t{mu4e-sent-folder}, @t{mu4e-drafts-folder}, @t{mu4e-trash-folder} and
|
|
||||||
|
So, for example@footnote{Note that the folders (@t{mu4e-sent-folder},
|
||||||
|
@t{mu4e-drafts-folder}, @t{mu4e-trash-folder} and
|
||||||
@t{mu4e-refile-folder}) can also be @emph{functions} that are evaluated
|
@t{mu4e-refile-folder}) can also be @emph{functions} that are evaluated
|
||||||
at runtime. This allows for dynamically changing them depending on the
|
at runtime. This allows for dynamically changing them depending on the
|
||||||
context. See @ref{Dynamic folders} for details.}:
|
situation. See @ref{Dynamic folders} for details.}:
|
||||||
@lisp
|
@lisp
|
||||||
;; these are actually the defaults
|
;; these are actually the defaults
|
||||||
(setq
|
(setq
|
||||||
|
@ -458,6 +460,9 @@ Note, @code{mu4e-maildir} takes an actual filesystem-path, the other
|
||||||
folder names are all relative to @code{mu4e-maildir}. Also note that
|
folder names are all relative to @code{mu4e-maildir}. Also note that
|
||||||
this must @emph{not} be a symbolic link.
|
this must @emph{not} be a symbolic link.
|
||||||
|
|
||||||
|
If you use @t{mu4e-context}, see @ref{Contexts and special folders} for
|
||||||
|
what that means for these special folders.
|
||||||
|
|
||||||
@node Retrieval and indexing
|
@node Retrieval and indexing
|
||||||
@section Retrieval and indexing with mu4e
|
@section Retrieval and indexing with mu4e
|
||||||
|
|
||||||
|
@ -2274,6 +2279,7 @@ example:
|
||||||
@menu
|
@menu
|
||||||
* Defining a context::
|
* Defining a context::
|
||||||
* Context policies::
|
* Context policies::
|
||||||
|
* Contexts and special folders::
|
||||||
* Contexts example::
|
* Contexts example::
|
||||||
* Some context tricks::
|
* Some context tricks::
|
||||||
@end menu
|
@end menu
|
||||||
|
@ -2345,6 +2351,31 @@ none of the contexts' match-functions returns @code{t}:
|
||||||
@item @t{nil}: don't change the context
|
@item @t{nil}: don't change the context
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
|
@node Contexts and special folders
|
||||||
|
@section Contexts and special folders
|
||||||
|
|
||||||
|
As we discussed in @ref{Folders} and @ref{Dynamic folders}, @t{mu4e}
|
||||||
|
recognizes a number of special folders: @code{mu4e-sent-folder},
|
||||||
|
@code{mu4e-drafts-folder}, @code{mu4e-trash-folder} and
|
||||||
|
@code{mu4e-refile-folder}.
|
||||||
|
|
||||||
|
When you have a headers-buffer with messages that belong to different
|
||||||
|
contexts (say, a few different accounts), it is desirable for each of
|
||||||
|
them to use the specific folders for their own context - so, for
|
||||||
|
instance, if you trash a message, it needs to go to the trash-folder for
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
appropriate folders.
|
||||||
|
|
||||||
@node Contexts example
|
@node Contexts example
|
||||||
@section Example
|
@section Example
|
||||||
|
|
||||||
|
@ -2362,7 +2393,8 @@ when starting; see the discussion in the previous section.
|
||||||
: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" )
|
||||||
|
@ -2375,7 +2407,8 @@ when starting; see the discussion in the previous section.
|
||||||
: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" )
|
||||||
|
@ -2446,6 +2479,9 @@ parameter, and returns the desired folder name. This chapter shows you how to
|
||||||
do that. For a more general discussion of how to extend @t{mu4e} and writing
|
do that. For a more general discussion of how to extend @t{mu4e} and writing
|
||||||
your own functions, see @ref{Extending mu4e}.
|
your own functions, see @ref{Extending mu4e}.
|
||||||
|
|
||||||
|
If you use @t{mu4e-context}, see @ref{Contexts and special folders} for
|
||||||
|
what that means for these special folders.
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* Smart refiling:: Automatically choose the target folder
|
* Smart refiling:: Automatically choose the target folder
|
||||||
* Other dynamic folders:: Flexible folders for sent, trash, drafts
|
* Other dynamic folders:: Flexible folders for sent, trash, drafts
|
||||||
|
|
Loading…
Reference in New Issue
Block a user