mu4e: Added mu4e-compose-context-switch for draft messages

Allow updating the context for the current draft message.
This commit is contained in:
Al Haji-Ali 2021-02-20 12:04:08 +00:00 committed by Dirk-Jan C. Binnema
parent 775498937d
commit b094da8cd4
3 changed files with 131 additions and 90 deletions

View File

@ -62,7 +62,10 @@
~mu4e-personal-address-p~ to check whether a given string matches a personal ~mu4e-personal-address-p~ to check whether a given string matches a personal
address. address.
- Completion for writing ~mu~ queries - TAB-Completion for writing ~mu~ queries
- Switch the context for existing draft messages using
~mu4e-compose-context-switch~ or ~C-c C-;~ in ~mu4e-compose-mode~.
* 1.4 (released, as of April 18 2020) * 1.4 (released, as of April 18 2020)
@ -235,7 +238,7 @@
For now this is experimental ("tech preview"), but might replace the For now this is experimental ("tech preview"), but might replace the
current message-view in a future release. Enable it with: current message-view in a future release. Enable it with:
(setq mu4e-view-use-gnus t) (setq mu4e-view-use-gnus t)
Thanks to Christophe Troestler for his work on fixing various encoding Thanks to Christophe Troestler for his work on fixing various encoding
issues. issues.

View File

@ -164,7 +164,7 @@ not a contradiction, but a redundant configuration.
All `sign-*' options have a `encrypt-*' analogue." All `sign-*' options have a `encrypt-*' analogue."
:type '(set :greedy t :type '(set :greedy t
(const :tag "Sign all messages" sign-all-messages) (const :tag "Sign all messages" sign-all-messages)
(const :tag "Encrypt all messages" encrypt-all-messages) (const :tag "Encrypt all messages" encrypt-all-messages)
(const :tag "Sign new messages" sign-new-messages) (const :tag "Sign new messages" sign-new-messages)
(const :tag "Encrypt new messages" encrypt-new-messages) (const :tag "Encrypt new messages" encrypt-new-messages)
@ -446,6 +446,7 @@ removing the In-Reply-To header."
(setq mu4e-compose-mode-map (setq mu4e-compose-mode-map
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index)
(define-key map (kbd "C-c C-;") 'mu4e-compose-context-switch)
(define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index)
(define-key map (kbd "C-c C-k") 'mu4e-message-kill-buffer) (define-key map (kbd "C-c C-k") 'mu4e-message-kill-buffer)
(define-key map (kbd "M-q") 'mu4e-fill-paragraph) (define-key map (kbd "M-q") 'mu4e-fill-paragraph)
@ -788,6 +789,42 @@ tempfile)."
;; if all else fails, back to the main view ;; if all else fails, back to the main view
(when (fboundp 'mu4e) (mu4e)))))) (when (fboundp 'mu4e) (mu4e))))))
(defun mu4e-compose-context-switch (&optional force name)
"Change the context for the current draft message.
Same as `mu4e-context-switch' but does two things after switching
when the buffer is in `mu4e-compose-mode':
- Changes the \"From\" field to the email address of the new context
- Moves the current message to the draft folder of the new context"
(interactive "P")
(if (derived-mode-p 'mu4e-compose-mode)
(let ((old-context (mu4e-context-current))
(has-file (file-exists-p (buffer-file-name))))
(unless (and name (not force) (eq old-context name))
(when (or (not has-file)
(not (buffer-modified-p))
(y-or-n-p "Draft must be saved before switching context. Save?"))
(unless (and (not force) (eq old-context (mu4e-context-switch nil name)))
;; Change From field to user-mail-address
(message-replace-header "From" (or (mu4e~draft-from-construct) ""))
;; Move message to mu4e-draft-folder
(if has-file
(progn (save-buffer)
(let ((msg-id (message-fetch-field "Message-ID"))
(buf (current-buffer)))
;; Remove the <>
(when (and msg-id (string-match "<\\(.*\\)>" msg-id))
(save-window-excursion
(mu4e~proc-move (match-string 1 msg-id) mu4e-drafts-folder nil t)
(kill-buffer buf))))) ;; Kill previous buffer which points to wrong file
;; No file, just change the buffer file name
(setq buffer-file-name
(format "%s/%s/cur/%s"
(mu4e-root-maildir) (mu4e-get-drafts-folder)
(file-name-nondirectory (buffer-file-name)))))))))
;; Just do the standad switch
(mu4e-context-switch force name)))
(defun mu4e-sent-handler (docid path) (defun mu4e-sent-handler (docid path)
"Handler called with DOCID and PATH for the just-sent message. "Handler called with DOCID and PATH for the just-sent message.
For Forwarded ('Passed') and Replied messages, try to set the For Forwarded ('Passed') and Replied messages, try to set the

View File

@ -753,34 +753,34 @@ The main view looks something like the following:
Basics Basics
* [j]ump to some maildir * [j]ump to some maildir
* enter a [s]earch query * enter a [s]earch query
* [C]ompose a new message * [C]ompose a new message
Bookmarks Bookmarks
* [bu] Unread messages (26119/26119) * [bu] Unread messages (26119/26119)
* [bt] Today's messages (1/7) * [bt] Today's messages (1/7)
* [bw] Last 7 days (30/126) * [bw] Last 7 days (30/126)
* [bp] Messages with images (268/2309) * [bp] Messages with images (268/2309)
Maildirs Maildirs
* [ja] /archive (3174/17990) * [ja] /archive (3174/17990)
* [ji] /inbox (0/2) * [ji] /inbox (0/2)
* [jm] /mu (1541/14884) * [jm] /mu (1541/14884)
* [js] /sent * [js] /sent
Misc Misc
* [;]Switch context * [;]Switch context
* [U]pdate email & database * [U]pdate email & database
* toggle [m]ail sending mode (currently direct) * toggle [m]ail sending mode (currently direct)
* [N]ews * [N]ews
* [A]bout mu4e * [A]bout mu4e
* [H]elp * [H]elp
* [q]uit * [q]uit
Info Info
@ -1508,8 +1508,8 @@ long messages in some external browser (see `browse-url-generic-program')."
(let ((html (or (mu4e-message-field msg :body-html) ""))) (let ((html (or (mu4e-message-field msg :body-html) "")))
(if (> (length html) 20000) (if (> (length html) 20000)
(progn (progn
(mu4e-action-view-in-browser msg) (mu4e-action-view-in-browser msg)
"[Viewing message in external browser]") "[Viewing message in external browser]")
(mu4e-shr2text msg)))) (mu4e-shr2text msg))))
(setq mu4e-html2text-command 'my-mu4e-html2text) (setq mu4e-html2text-command 'my-mu4e-html2text)
@ -1697,6 +1697,7 @@ C-c C-c send message
C-c C-d save to drafts and leave C-c C-d save to drafts and leave
C-c C-k kill the message buffer (the message remains in the draft folder) C-c C-k kill the message buffer (the message remains in the draft folder)
C-c C-a attach a file (pro-tip: drag & drop works as well) C-c C-a attach a file (pro-tip: drag & drop works as well)
C-c C-; switch the context
(mu4e-specific) (mu4e-specific)
C-S-u update mail & reindex C-S-u update mail & reindex
@ -1773,13 +1774,13 @@ Let's look at some examples. First, suppose we want to set the
"Set the From address based on the To address of the original." "Set the From address based on the To address of the original."
(let ((msg mu4e-compose-parent-message)) ;; msg is shorter... (let ((msg mu4e-compose-parent-message)) ;; msg is shorter...
(when msg (when msg
(setq user-mail-address (setq user-mail-address
(cond (cond
((mu4e-message-contact-field-matches msg :to "me@@foo.example.com") ((mu4e-message-contact-field-matches msg :to "me@@foo.example.com")
"me@@foo.example.com") "me@@foo.example.com")
((mu4e-message-contact-field-matches msg :to "me@@bar.example.com") ((mu4e-message-contact-field-matches msg :to "me@@bar.example.com")
"me@@bar.example.com") "me@@bar.example.com")
(t "me@@cuux.example.com"))))))) (t "me@@cuux.example.com")))))))
@end lisp @end lisp
Secondly, as mentioned, @code{mu4e-compose-mode-hook} is especially Secondly, as mentioned, @code{mu4e-compose-mode-hook} is especially
@ -1810,12 +1811,12 @@ Or to something context-specific:
(add-hook 'mu4e-compose-mode-hook (add-hook 'mu4e-compose-mode-hook
(lambda() (lambda()
(let* ((ctx (mu4e-context-current)) (let* ((ctx (mu4e-context-current))
(name (if ctx (mu4e-context-name ctx)))) (name (if ctx (mu4e-context-name ctx))))
(when name (when name
(cond (cond
((string= name "account1") ((string= name "account1")
(save-excursion (message-add-header "Bcc: account1@@example.com\n"))) (save-excursion (message-add-header "Bcc: account1@@example.com\n")))
((string= name "account2") ((string= name "account2")
(save-excursion (message-add-header "Bcc: account2@@example.com\n")))))))) (save-excursion (message-add-header "Bcc: account2@@example.com\n"))))))))
@end lisp @end lisp
@ -2434,7 +2435,7 @@ than @emph{n} recipients --- we could do this with the following recipe:
'("More than n recipients" '("More than n recipients"
(lambda (msg n) (lambda (msg n)
(> (+ (length (mu4e-message-field msg :to)) (> (+ (length (mu4e-message-field msg :to))
(length (mu4e-message-field msg :cc))) n)) (length (mu4e-message-field msg :cc))) n))
(lambda () (lambda ()
(read-number "Match messages with more recipients than: "))) t) (read-number "Match messages with more recipients than: "))) t)
@end lisp @end lisp
@ -2498,7 +2499,7 @@ loading @t{mu4e}):
:prompt "gtag" :prompt "gtag"
:ask-target (lambda () (read-string "What tag do you want to add?")) :ask-target (lambda () (read-string "What tag do you want to add?"))
:action (lambda (docid msg target) :action (lambda (docid msg target)
(mu4e-action-retag-message msg (concat "+" target))))) (mu4e-action-retag-message msg (concat "+" target)))))
@end lisp @end lisp
As another example, suppose we would like to ``archive and mark read'' As another example, suppose we would like to ``archive and mark read''
@ -2512,10 +2513,10 @@ loading @t{mu4e}):
:prompt "Archive" :prompt "Archive"
:show-target (lambda (target) "archive") :show-target (lambda (target) "archive")
:action (lambda (docid msg target) :action (lambda (docid msg target)
;; must come before proc-move since retag runs ;; must come before proc-move since retag runs
;; 'sed' on the file ;; 'sed' on the file
(mu4e-action-retag-message msg "-\\Inbox") (mu4e-action-retag-message msg "-\\Inbox")
(mu4e~proc-move docid nil "+S-u-N")))) (mu4e~proc-move docid nil "+S-u-N"))))
@end lisp @end lisp
Adding to @code{mu4e-marks} list allows to use the mark in bulk operations Adding to @code{mu4e-marks} list allows to use the mark in bulk operations
@ -2666,48 +2667,48 @@ 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 "Entering Private context")) :enter-func (lambda () (mu4e-message "Entering Private context"))
:leave-func (lambda () (mu4e-message "Leaving Private context")) :leave-func (lambda () (mu4e-message "Leaving Private context"))
;; we match based on the contact-fields of the message ;; we match based on the contact-fields of the message
:match-func (lambda (msg) :match-func (lambda (msg)
(when msg (when msg
(mu4e-message-contact-field-matches msg (mu4e-message-contact-field-matches msg
:to "aliced@@home.example.com"))) :to "aliced@@home.example.com")))
:vars '( ( user-mail-address . "aliced@@home.example.com" ) :vars '( ( 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"))
;; no leave-func ;; no leave-func
;; we match based on the maildir of the message ;; we match based on the maildir of the message
;; this matches maildir /Arkham and its sub-directories ;; this matches maildir /Arkham and its sub-directories
:match-func (lambda (msg) :match-func (lambda (msg)
(when msg (when msg
(string-match-p "^/Arkham" (mu4e-message-field msg :maildir)))) (string-match-p "^/Arkham" (mu4e-message-field msg :maildir))))
:vars '( ( user-mail-address . "aderleth@@miskatonic.example.com" ) :vars '( ( 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"
"Miskatonic University, Dept. of Occult Sciences\n")))) "Miskatonic University, Dept. of Occult Sciences\n"))))
,(make-mu4e-context ,(make-mu4e-context
:name "Cycling" :name "Cycling"
:enter-func (lambda () (mu4e-message "Switch to the Cycling context")) :enter-func (lambda () (mu4e-message "Switch to the Cycling context"))
;; no leave-func ;; no leave-func
;; we match based on the maildir of the message; assume all ;; we match based on the maildir of the message; assume all
;; cycling-related messages go into the /cycling maildir ;; cycling-related messages go into the /cycling maildir
:match-func (lambda (msg) :match-func (lambda (msg)
(when msg (when msg
(string= (mu4e-message-field msg :maildir) "/cycling"))) (string= (mu4e-message-field msg :maildir) "/cycling")))
:vars '( ( user-mail-address . "aderleth@@example.com" ) :vars '( ( user-mail-address . "aderleth@@example.com" )
( user-full-name . "AliceD" ) ( user-full-name . "AliceD" )
( mu4e-compose-signature . nil))))) ( mu4e-compose-signature . nil)))))
;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should
;; guess or ask the correct context, e.g. ;; guess or ask the correct context, e.g.
@ -2797,19 +2798,19 @@ message. An example should clarify this:
(cond (cond
;; messages to the mu mailing list go to the /mu folder ;; messages to the mu mailing list go to the /mu folder
((mu4e-message-contact-field-matches msg :to ((mu4e-message-contact-field-matches msg :to
"mu-discuss@@googlegroups.com") "mu-discuss@@googlegroups.com")
"/mu") "/mu")
;; messages sent directly to some spefic address me go to /private ;; messages sent directly to some spefic address me go to /private
((mu4e-message-contact-field-matches msg :to "me@@example.com") ((mu4e-message-contact-field-matches msg :to "me@@example.com")
"/private") "/private")
;; messages with football or soccer in the subject go to /football ;; messages with football or soccer in the subject go to /football
((string-match "football\\|soccer" ((string-match "football\\|soccer"
(mu4e-message-field msg :subject)) (mu4e-message-field msg :subject))
"/football") "/football")
;; messages sent by me go to the sent folder ;; messages sent by me go to the sent folder
((mu4e-message-sent-by-me msg ((mu4e-message-sent-by-me msg
(mu4e-personal-addresses)) (mu4e-personal-addresses))
mu4e-sent-folder) mu4e-sent-folder)
;; everything else goes to /archive ;; everything else goes to /archive
;; important to have a catch-all at the end! ;; important to have a catch-all at the end!
(t "/archive")))) (t "/archive"))))
@ -3126,7 +3127,7 @@ point. Requires the 'formail' tool from procmail."
(replace-regexp-in-string "\n$" "" (replace-regexp-in-string "\n$" ""
(shell-command-to-string (shell-command-to-string
(concat "formail -x " hdr " -c < " (concat "formail -x " hdr " -c < "
(shell-quote-argument (mu4e-message-field-at-point :path)))))) (shell-quote-argument (mu4e-message-field-at-point :path))))))
@end lisp @end lisp
@subsection Rewriting the message body @subsection Rewriting the message body
@ -3529,10 +3530,10 @@ well; so put in your configuration:
(let (buffers) (let (buffers)
(save-current-buffer (save-current-buffer
(dolist (buffer (buffer-list t)) (dolist (buffer (buffer-list t))
(set-buffer buffer) (set-buffer buffer)
(when (and (derived-mode-p 'message-mode) (when (and (derived-mode-p 'message-mode)
(null message-sent-message-via)) (null message-sent-message-via))
(push (buffer-name buffer) buffers)))) (push (buffer-name buffer) buffers))))
(nreverse buffers))) (nreverse buffers)))
(setq gnus-dired-mail-mode 'mu4e-user-agent) (setq gnus-dired-mail-mode 'mu4e-user-agent)