From b094da8cd48f6489b4b599ed7427200c08f70d84 Mon Sep 17 00:00:00 2001 From: Al Haji-Ali Date: Sat, 20 Feb 2021 12:04:08 +0000 Subject: [PATCH] mu4e: Added mu4e-compose-context-switch for draft messages Allow updating the context for the current draft message. --- NEWS.org | 7 +- mu4e/mu4e-compose.el | 39 +++++++++- mu4e/mu4e.texi | 175 ++++++++++++++++++++++--------------------- 3 files changed, 131 insertions(+), 90 deletions(-) diff --git a/NEWS.org b/NEWS.org index 3d7b7383..0aee79e9 100644 --- a/NEWS.org +++ b/NEWS.org @@ -62,7 +62,10 @@ ~mu4e-personal-address-p~ to check whether a given string matches a personal 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) @@ -235,7 +238,7 @@ For now this is experimental ("tech preview"), but might replace the 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 issues. diff --git a/mu4e/mu4e-compose.el b/mu4e/mu4e-compose.el index 200eee2d..45f58912 100644 --- a/mu4e/mu4e-compose.el +++ b/mu4e/mu4e-compose.el @@ -164,7 +164,7 @@ not a contradiction, but a redundant configuration. All `sign-*' options have a `encrypt-*' analogue." :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 "Sign new messages" sign-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 (let ((map (make-sparse-keymap))) (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-k") 'mu4e-message-kill-buffer) (define-key map (kbd "M-q") 'mu4e-fill-paragraph) @@ -788,6 +789,42 @@ tempfile)." ;; if all else fails, back to the main view (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) "Handler called with DOCID and PATH for the just-sent message. For Forwarded ('Passed') and Replied messages, try to set the diff --git a/mu4e/mu4e.texi b/mu4e/mu4e.texi index ebd05883..66acdcbd 100644 --- a/mu4e/mu4e.texi +++ b/mu4e/mu4e.texi @@ -753,34 +753,34 @@ The main view looks something like the following: Basics - * [j]ump to some maildir - * enter a [s]earch query - * [C]ompose a new message + * [j]ump to some maildir + * enter a [s]earch query + * [C]ompose a new message Bookmarks - * [bu] Unread messages (26119/26119) - * [bt] Today's messages (1/7) - * [bw] Last 7 days (30/126) - * [bp] Messages with images (268/2309) + * [bu] Unread messages (26119/26119) + * [bt] Today's messages (1/7) + * [bw] Last 7 days (30/126) + * [bp] Messages with images (268/2309) Maildirs - * [ja] /archive (3174/17990) - * [ji] /inbox (0/2) - * [jm] /mu (1541/14884) - * [js] /sent + * [ja] /archive (3174/17990) + * [ji] /inbox (0/2) + * [jm] /mu (1541/14884) + * [js] /sent Misc - * [;]Switch context - * [U]pdate email & database - * toggle [m]ail sending mode (currently direct) + * [;]Switch context + * [U]pdate email & database + * toggle [m]ail sending mode (currently direct) - * [N]ews - * [A]bout mu4e - * [H]elp - * [q]uit + * [N]ews + * [A]bout mu4e + * [H]elp + * [q]uit 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) ""))) (if (> (length html) 20000) (progn - (mu4e-action-view-in-browser msg) - "[Viewing message in external browser]") + (mu4e-action-view-in-browser msg) + "[Viewing message in external browser]") (mu4e-shr2text msg)))) (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-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-; switch the context (mu4e-specific) 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." (let ((msg mu4e-compose-parent-message)) ;; msg is shorter... (when msg - (setq user-mail-address - (cond - ((mu4e-message-contact-field-matches msg :to "me@@foo.example.com") - "me@@foo.example.com") - ((mu4e-message-contact-field-matches msg :to "me@@bar.example.com") - "me@@bar.example.com") - (t "me@@cuux.example.com"))))))) + (setq user-mail-address + (cond + ((mu4e-message-contact-field-matches msg :to "me@@foo.example.com") + "me@@foo.example.com") + ((mu4e-message-contact-field-matches msg :to "me@@bar.example.com") + "me@@bar.example.com") + (t "me@@cuux.example.com"))))))) @end lisp 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 (lambda() (let* ((ctx (mu4e-context-current)) - (name (if ctx (mu4e-context-name ctx)))) + (name (if ctx (mu4e-context-name ctx)))) (when name - (cond - ((string= name "account1") + (cond + ((string= name "account1") (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")))))))) @end lisp @@ -2434,7 +2435,7 @@ than @emph{n} recipients --- we could do this with the following recipe: '("More than n recipients" (lambda (msg n) (> (+ (length (mu4e-message-field msg :to)) - (length (mu4e-message-field msg :cc))) n)) + (length (mu4e-message-field msg :cc))) n)) (lambda () (read-number "Match messages with more recipients than: "))) t) @end lisp @@ -2498,7 +2499,7 @@ loading @t{mu4e}): :prompt "gtag" :ask-target (lambda () (read-string "What tag do you want to add?")) :action (lambda (docid msg target) - (mu4e-action-retag-message msg (concat "+" target))))) + (mu4e-action-retag-message msg (concat "+" target))))) @end lisp As another example, suppose we would like to ``archive and mark read'' @@ -2512,10 +2513,10 @@ loading @t{mu4e}): :prompt "Archive" :show-target (lambda (target) "archive") :action (lambda (docid msg target) - ;; must come before proc-move since retag runs - ;; 'sed' on the file - (mu4e-action-retag-message msg "-\\Inbox") - (mu4e~proc-move docid nil "+S-u-N")))) + ;; must come before proc-move since retag runs + ;; 'sed' on the file + (mu4e-action-retag-message msg "-\\Inbox") + (mu4e~proc-move docid nil "+S-u-N")))) @end lisp 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 `( ,(make-mu4e-context - :name "Private" - :enter-func (lambda () (mu4e-message "Entering Private context")) + :name "Private" + :enter-func (lambda () (mu4e-message "Entering Private context")) :leave-func (lambda () (mu4e-message "Leaving Private context")) - ;; we match based on the contact-fields of the message - :match-func (lambda (msg) - (when msg - (mu4e-message-contact-field-matches msg - :to "aliced@@home.example.com"))) - :vars '( ( user-mail-address . "aliced@@home.example.com" ) - ( user-full-name . "Alice Derleth" ) - ( mu4e-compose-signature . - (concat - "Alice Derleth\n" - "Lauttasaari, Finland\n")))) + ;; we match based on the contact-fields of the message + :match-func (lambda (msg) + (when msg + (mu4e-message-contact-field-matches msg + :to "aliced@@home.example.com"))) + :vars '( ( user-mail-address . "aliced@@home.example.com" ) + ( user-full-name . "Alice Derleth" ) + ( mu4e-compose-signature . + (concat + "Alice Derleth\n" + "Lauttasaari, Finland\n")))) ,(make-mu4e-context - :name "Work" - :enter-func (lambda () (mu4e-message "Switch to the Work context")) - ;; no leave-func - ;; we match based on the maildir of the message - ;; this matches maildir /Arkham and its sub-directories - :match-func (lambda (msg) - (when msg - (string-match-p "^/Arkham" (mu4e-message-field msg :maildir)))) - :vars '( ( user-mail-address . "aderleth@@miskatonic.example.com" ) - ( user-full-name . "Alice Derleth" ) - ( mu4e-compose-signature . - (concat - "Prof. Alice Derleth\n" - "Miskatonic University, Dept. of Occult Sciences\n")))) + :name "Work" + :enter-func (lambda () (mu4e-message "Switch to the Work context")) + ;; no leave-func + ;; we match based on the maildir of the message + ;; this matches maildir /Arkham and its sub-directories + :match-func (lambda (msg) + (when msg + (string-match-p "^/Arkham" (mu4e-message-field msg :maildir)))) + :vars '( ( user-mail-address . "aderleth@@miskatonic.example.com" ) + ( user-full-name . "Alice Derleth" ) + ( mu4e-compose-signature . + (concat + "Prof. Alice Derleth\n" + "Miskatonic University, Dept. of Occult Sciences\n")))) ,(make-mu4e-context - :name "Cycling" - :enter-func (lambda () (mu4e-message "Switch to the Cycling context")) - ;; no leave-func - ;; we match based on the maildir of the message; assume all - ;; cycling-related messages go into the /cycling maildir - :match-func (lambda (msg) - (when msg - (string= (mu4e-message-field msg :maildir) "/cycling"))) - :vars '( ( user-mail-address . "aderleth@@example.com" ) - ( user-full-name . "AliceD" ) - ( mu4e-compose-signature . nil))))) + :name "Cycling" + :enter-func (lambda () (mu4e-message "Switch to the Cycling context")) + ;; no leave-func + ;; we match based on the maildir of the message; assume all + ;; cycling-related messages go into the /cycling maildir + :match-func (lambda (msg) + (when msg + (string= (mu4e-message-field msg :maildir) "/cycling"))) + :vars '( ( user-mail-address . "aderleth@@example.com" ) + ( user-full-name . "AliceD" ) + ( mu4e-compose-signature . nil))))) ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should ;; guess or ask the correct context, e.g. @@ -2797,19 +2798,19 @@ message. An example should clarify this: (cond ;; messages to the mu mailing list go to the /mu folder ((mu4e-message-contact-field-matches msg :to - "mu-discuss@@googlegroups.com") - "/mu") + "mu-discuss@@googlegroups.com") + "/mu") ;; messages sent directly to some spefic address me go to /private ((mu4e-message-contact-field-matches msg :to "me@@example.com") - "/private") + "/private") ;; messages with football or soccer in the subject go to /football ((string-match "football\\|soccer" - (mu4e-message-field msg :subject)) - "/football") + (mu4e-message-field msg :subject)) + "/football") ;; messages sent by me go to the sent folder ((mu4e-message-sent-by-me msg - (mu4e-personal-addresses)) - mu4e-sent-folder) + (mu4e-personal-addresses)) + mu4e-sent-folder) ;; everything else goes to /archive ;; important to have a catch-all at the end! (t "/archive")))) @@ -3126,7 +3127,7 @@ point. Requires the 'formail' tool from procmail." (replace-regexp-in-string "\n$" "" (shell-command-to-string (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 @subsection Rewriting the message body @@ -3529,10 +3530,10 @@ well; so put in your configuration: (let (buffers) (save-current-buffer (dolist (buffer (buffer-list t)) - (set-buffer buffer) - (when (and (derived-mode-p 'message-mode) - (null message-sent-message-via)) - (push (buffer-name buffer) buffers)))) + (set-buffer buffer) + (when (and (derived-mode-p 'message-mode) + (null message-sent-message-via)) + (push (buffer-name buffer) buffers)))) (nreverse buffers))) (setq gnus-dired-mail-mode 'mu4e-user-agent)