mu4e: Fix indentation

This commit is contained in:
Jonas Bernoulli
2020-02-11 12:00:46 +01:00
parent be1ba1ce68
commit 6790c0d015
18 changed files with 3284 additions and 3285 deletions

View File

@ -47,8 +47,8 @@
"Count the number of lines in the e-mail MSG. "Count the number of lines in the e-mail MSG.
Works for headers view and message-view." Works for headers view and message-view."
(message "Number of lines: %s" (message "Number of lines: %s"
(shell-command-to-string (shell-command-to-string
(concat "wc -l < " (shell-quote-argument (mu4e-message-field msg :path)))))) (concat "wc -l < " (shell-quote-argument (mu4e-message-field msg :path))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -66,12 +66,12 @@ Works for the message view."
(unless (file-executable-p mu4e-msg2pdf) (unless (file-executable-p mu4e-msg2pdf)
(mu4e-error "Program msg2pdf not found; please set `mu4e-msg2pdf'")) (mu4e-error "Program msg2pdf not found; please set `mu4e-msg2pdf'"))
(let* ((pdf (let* ((pdf
(shell-command-to-string (shell-command-to-string
(concat mu4e-msg2pdf " " (concat mu4e-msg2pdf " "
(shell-quote-argument (mu4e-message-field msg :path)) (shell-quote-argument (mu4e-message-field msg :path))
" 2> /dev/null"))) " 2> /dev/null")))
(pdf (and pdf (> (length pdf) 5) (pdf (and pdf (> (length pdf) 5)
(substring pdf 0 -1)))) ;; chop \n (substring pdf 0 -1)))) ;; chop \n
(unless (and pdf (file-exists-p pdf)) (unless (and pdf (file-exists-p pdf))
(mu4e-warn "Failed to create PDF file")) (mu4e-warn "Failed to create PDF file"))
(find-file pdf))) (find-file pdf)))
@ -100,22 +100,22 @@ Works for the message view."
"Write MSG's body (either html or text) to a temporary file; "Write MSG's body (either html or text) to a temporary file;
return the filename." return the filename."
(let* ((html (mu4e-message-field msg :body-html)) (let* ((html (mu4e-message-field msg :body-html))
(txt (mu4e-message-field msg :body-txt)) (txt (mu4e-message-field msg :body-txt))
(tmpfile (mu4e-make-temp-file "html")) (tmpfile (mu4e-make-temp-file "html"))
(attachments (cl-remove-if (lambda (part) (attachments (cl-remove-if (lambda (part)
(or (null (plist-get part :attachment)) (or (null (plist-get part :attachment))
(null (plist-get part :cid)))) (null (plist-get part :cid))))
(mu4e-message-field msg :parts)))) (mu4e-message-field msg :parts))))
(unless (or html txt) (unless (or html txt)
(mu4e-error "No body part for this message")) (mu4e-error "No body part for this message"))
(with-temp-buffer (with-temp-buffer
(insert "<head><meta charset=\"UTF-8\"></head>\n") (insert "<head><meta charset=\"UTF-8\"></head>\n")
(insert (concat "<p><strong>From</strong>: " (insert (concat "<p><strong>From</strong>: "
(mu4e~action-header-to-html msg :from) "</br>")) (mu4e~action-header-to-html msg :from) "</br>"))
(insert (concat "<strong>To</strong>: " (insert (concat "<strong>To</strong>: "
(mu4e~action-header-to-html msg :to) "</br>")) (mu4e~action-header-to-html msg :to) "</br>"))
(insert (concat "<strong>Date</strong>: " (insert (concat "<strong>Date</strong>: "
(format-time-string mu4e-view-date-format (mu4e-message-field msg :date)) "</br>")) (format-time-string mu4e-view-date-format (mu4e-message-field msg :date)) "</br>"))
(insert (concat "<strong>Subject</strong>: " (mu4e-message-field msg :subject) "</p>")) (insert (concat "<strong>Subject</strong>: " (mu4e-message-field msg :subject) "</p>"))
(insert (or html (concat "<pre>" txt "</pre>"))) (insert (or html (concat "<pre>" txt "</pre>")))
(write-file tmpfile) (write-file tmpfile)
@ -123,20 +123,20 @@ return the filename."
(mapc (lambda (attachment) (mapc (lambda (attachment)
(goto-char (point-min)) (goto-char (point-min))
(while (re-search-forward (format "src=\"cid:%s\"" (while (re-search-forward (format "src=\"cid:%s\""
(plist-get attachment :cid)) nil t) (plist-get attachment :cid)) nil t)
(if (plist-get attachment :temp) (if (plist-get attachment :temp)
(replace-match (format "src=\"%s\"" (replace-match (format "src=\"%s\""
(plist-get attachment :temp))) (plist-get attachment :temp)))
(replace-match (format "src=\"%s%s\"" temporary-file-directory (replace-match (format "src=\"%s%s\"" temporary-file-directory
(plist-get attachment :name))) (plist-get attachment :name)))
(let ((tmp-attachment-name (let ((tmp-attachment-name
(format "%s%s" temporary-file-directory (format "%s%s" temporary-file-directory
(plist-get attachment :name)))) (plist-get attachment :name))))
(mu4e~proc-extract 'save (mu4e-message-field msg :docid) (mu4e~proc-extract 'save (mu4e-message-field msg :docid)
(plist-get attachment :index) (plist-get attachment :index)
mu4e-decryption-policy tmp-attachment-name) mu4e-decryption-policy tmp-attachment-name)
(mu4e-remove-file-later tmp-attachment-name))))) (mu4e-remove-file-later tmp-attachment-name)))))
attachments) attachments)
(save-buffer) (save-buffer)
tmpfile))) tmpfile)))
@ -146,7 +146,7 @@ You can influence the browser to use with the variable
`browse-url-generic-program', and see the discussion of privacy `browse-url-generic-program', and see the discussion of privacy
aspects in `(mu4e) Displaying rich-text messages'." aspects in `(mu4e) Displaying rich-text messages'."
(browse-url (concat "file://" (browse-url (concat "file://"
(mu4e~write-body-to-html msg)))) (mu4e~write-body-to-html msg))))
(defun mu4e-action-view-with-xwidget (msg) (defun mu4e-action-view-with-xwidget (msg)
"View the body of MSG inside xwidget-webkit. "View the body of MSG inside xwidget-webkit.
@ -155,7 +155,7 @@ privacy aspects in `(mu4e) Displaying rich-text messages'."
(unless (fboundp 'xwidget-webkit-browse-url) (unless (fboundp 'xwidget-webkit-browse-url)
(mu4e-error "No xwidget support available")) (mu4e-error "No xwidget support available"))
(xwidget-webkit-browse-url (xwidget-webkit-browse-url
(concat "file://" (mu4e~write-body-to-html msg)) t)) (concat "file://" (mu4e~write-body-to-html msg)) t))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -172,7 +172,7 @@ privacy aspects in `(mu4e) Displaying rich-text messages'."
(with-temp-buffer (with-temp-buffer
(insert (mu4e-message-field msg :body-txt)) (insert (mu4e-message-field msg :body-txt))
(shell-command-on-region (point-min) (point-max) (shell-command-on-region (point-min) (point-max)
mu4e-text2speech-command))) mu4e-text2speech-command)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -216,23 +216,23 @@ file where you store your org-contacts."
(unless mu4e-org-contacts-file (unless mu4e-org-contacts-file
(mu4e-error "Variable `mu4e-org-contacts-file' is nil")) (mu4e-error "Variable `mu4e-org-contacts-file' is nil"))
(let* ((sender (car-safe (mu4e-message-field msg :from))) (let* ((sender (car-safe (mu4e-message-field msg :from)))
(name (car-safe sender)) (email (cdr-safe sender)) (name (car-safe sender)) (email (cdr-safe sender))
(blurb (blurb
(format (format
(concat (concat
"* %%?%s\n" "* %%?%s\n"
":PROPERTIES:\n" ":PROPERTIES:\n"
":EMAIL: %s\n" ":EMAIL: %s\n"
":NICK:\n" ":NICK:\n"
":BIRTHDAY:\n" ":BIRTHDAY:\n"
":END:\n\n") ":END:\n\n")
(or name email "") (or name email "")
(or email ""))) (or email "")))
(key "mu4e-add-org-contact-key") (key "mu4e-add-org-contact-key")
(org-capture-templates (org-capture-templates
(append org-capture-templates (append org-capture-templates
(list (list key "contacts" 'entry (list (list key "contacts" 'entry
(list 'file mu4e-org-contacts-file) blurb))))) (list 'file mu4e-org-contacts-file) blurb)))))
(message "%S" org-capture-templates) (message "%S" org-capture-templates)
(when (fboundp 'org-capture) (when (fboundp 'org-capture)
(org-capture nil key)))) (org-capture nil key))))
@ -252,16 +252,16 @@ file where you store your org-contacts."
(unless prompt (unless prompt
(setq prompt "Target directory:")) (setq prompt "Target directory:"))
(file-truename (file-truename
(completing-read prompt 'read-file-name-internal #'file-directory-p (completing-read prompt 'read-file-name-internal #'file-directory-p
nil nil 'mu4e~patch-directory-history))) nil nil 'mu4e~patch-directory-history)))
(defun mu4e-action-git-apply-patch (msg) (defun mu4e-action-git-apply-patch (msg)
"Apply `MSG' as a git patch." "Apply `MSG' as a git patch."
(let ((path (mu4e~read-patch-directory "Target directory: "))) (let ((path (mu4e~read-patch-directory "Target directory: ")))
(let ((default-directory path)) (let ((default-directory path))
(shell-command (shell-command
(format "git apply %s" (format "git apply %s"
(shell-quote-argument (mu4e-message-field msg :path))))))) (shell-quote-argument (mu4e-message-field msg :path)))))))
(defun mu4e-action-git-apply-mbox (msg &optional signoff) (defun mu4e-action-git-apply-mbox (msg &optional signoff)
"Apply `MSG' a git patch with optional `SIGNOFF'. "Apply `MSG' a git patch with optional `SIGNOFF'.
@ -270,15 +270,15 @@ If the `default-directory' matches the most recent history entry don't
bother asking for the git tree again (useful for bulk actions)." bother asking for the git tree again (useful for bulk actions)."
(let ((cwd (substring-no-properties (let ((cwd (substring-no-properties
(or (car mu4e~patch-directory-history) (or (car mu4e~patch-directory-history)
"not-a-dir")))) "not-a-dir"))))
(unless (and (stringp cwd) (string= default-directory cwd)) (unless (and (stringp cwd) (string= default-directory cwd))
(setq cwd (mu4e~read-patch-directory "Target directory: "))) (setq cwd (mu4e~read-patch-directory "Target directory: ")))
(let ((default-directory cwd)) (let ((default-directory cwd))
(shell-command (shell-command
(format "git am %s %s" (format "git am %s %s"
(if signoff "--signoff" "") (if signoff "--signoff" "")
(shell-quote-argument (mu4e-message-field msg :path))))))) (shell-quote-argument (mu4e-message-field msg :path)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -302,7 +302,7 @@ messages can lead to messages with multiple tags headers.")
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(if (re-search-forward regexp nil t) (if (re-search-forward regexp nil t)
t t
nil)))) nil))))
(defun mu4e~replace-first-line-matching (regexp to-string path) (defun mu4e~replace-first-line-matching (regexp to-string path)
@ -312,7 +312,7 @@ messages can lead to messages with multiple tags headers.")
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(if (re-search-forward regexp nil t) (if (re-search-forward regexp nil t)
(replace-match to-string nil nil))))) (replace-match to-string nil nil)))))
(defun mu4e-action-retag-message (msg &optional retag-arg) (defun mu4e-action-retag-message (msg &optional retag-arg)
"Change tags of MSG with RETAG-ARG. "Change tags of MSG with RETAG-ARG.
@ -322,33 +322,33 @@ RETAG-ARG is a comma-separated list of additions and removals.
Example: +tag,+long tag,-oldtag Example: +tag,+long tag,-oldtag
would add 'tag' and 'long tag', and remove 'oldtag'." would add 'tag' and 'long tag', and remove 'oldtag'."
(let* ( (let* (
(path (mu4e-message-field msg :path)) (path (mu4e-message-field msg :path))
(oldtags (mu4e-message-field msg :tags)) (oldtags (mu4e-message-field msg :tags))
(tags-completion (tags-completion
(append (append
mu4e-action-tags-completion-list mu4e-action-tags-completion-list
(mapcar (lambda (tag) (format "+%s" tag)) (mapcar (lambda (tag) (format "+%s" tag))
mu4e-action-tags-completion-list) mu4e-action-tags-completion-list)
(mapcar (lambda (tag) (format "-%s" tag)) (mapcar (lambda (tag) (format "-%s" tag))
oldtags))) oldtags)))
(retag (if retag-arg (retag (if retag-arg
(split-string retag-arg ",") (split-string retag-arg ",")
(completing-read-multiple "Tags: " tags-completion))) (completing-read-multiple "Tags: " tags-completion)))
(header mu4e-action-tags-header) (header mu4e-action-tags-header)
(sep (cond ((string= header "Keywords") ", ") (sep (cond ((string= header "Keywords") ", ")
((string= header "X-Label") " ") ((string= header "X-Label") " ")
((string= header "X-Keywords") ", ") ((string= header "X-Keywords") ", ")
(t ", "))) (t ", ")))
(taglist (if oldtags (copy-sequence oldtags) '())) (taglist (if oldtags (copy-sequence oldtags) '()))
tagstr) tagstr)
(dolist (tag retag taglist) (dolist (tag retag taglist)
(cond (cond
((string-match "^\\+\\(.+\\)" tag) ((string-match "^\\+\\(.+\\)" tag)
(setq taglist (push (match-string 1 tag) taglist))) (setq taglist (push (match-string 1 tag) taglist)))
((string-match "^\\-\\(.+\\)" tag) ((string-match "^\\-\\(.+\\)" tag)
(setq taglist (delete (match-string 1 tag) taglist))) (setq taglist (delete (match-string 1 tag) taglist)))
(t (t
(setq taglist (push tag taglist))))) (setq taglist (push tag taglist)))))
(setq taglist (sort (delete-dups taglist) 'string<)) (setq taglist (sort (delete-dups taglist) 'string<))
(setq tagstr (mapconcat 'identity taglist sep)) (setq tagstr (mapconcat 'identity taglist sep))
@ -357,15 +357,15 @@ would add 'tag' and 'long tag', and remove 'oldtag'."
(setq tagstr (replace-regexp-in-string "[/]" "\\&" tagstr)) (setq tagstr (replace-regexp-in-string "[/]" "\\&" tagstr))
(if (not (mu4e~contains-line-matching (concat header ":.*") path)) (if (not (mu4e~contains-line-matching (concat header ":.*") path))
;; Add tags header just before the content ;; Add tags header just before the content
(mu4e~replace-first-line-matching (mu4e~replace-first-line-matching
"^$" (concat header ": " tagstr "\n") path) "^$" (concat header ": " tagstr "\n") path)
;; replaces keywords, restricted to the header ;; replaces keywords, restricted to the header
(mu4e~replace-first-line-matching (mu4e~replace-first-line-matching
(concat header ":.*") (concat header ":.*")
(concat header ": " tagstr) (concat header ": " tagstr)
path)) path))
(mu4e-message (concat "tagging: " (mapconcat 'identity taglist ", "))) (mu4e-message (concat "tagging: " (mapconcat 'identity taglist ", ")))
(mu4e-refresh-message path))) (mu4e-refresh-message path)))
@ -378,12 +378,12 @@ the message."
(let ((msgid (mu4e-message-field msg :message-id))) (let ((msgid (mu4e-message-field msg :message-id)))
(when msgid (when msgid
(let ((mu4e-headers-show-threads t) (let ((mu4e-headers-show-threads t)
(mu4e-headers-include-related t)) (mu4e-headers-include-related t))
(mu4e-headers-search (mu4e-headers-search
(format "msgid:%s" msgid) (format "msgid:%s" msgid)
nil nil nil nil nil nil
msgid (and (eq major-mode 'mu4e-view-mode) msgid (and (eq major-mode 'mu4e-view-mode)
(not (eq mu4e-split-view 'single-window)))))))) (not (eq mu4e-split-view 'single-window))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(provide 'mu4e-actions) (provide 'mu4e-actions)

View File

@ -109,8 +109,8 @@ symbols, for example:
The various `message-' functions from `message-mode' are available The various `message-' functions from `message-mode' are available
for querying the message information." for querying the message information."
:type '(choice (const :tag "move message to mu4e-sent-folder" sent) :type '(choice (const :tag "move message to mu4e-sent-folder" sent)
(const :tag "move message to mu4e-trash-folder" trash) (const :tag "move message to mu4e-trash-folder" trash)
(const :tag "delete message" delete)) (const :tag "delete message" delete))
:group 'mu4e-compose) :group 'mu4e-compose)
(defcustom mu4e-compose-context-policy 'ask (defcustom mu4e-compose-context-policy 'ask
@ -129,11 +129,11 @@ contexts match, we have the following choices:
Also see `mu4e-context-policy'." Also see `mu4e-context-policy'."
:type '(choice :type '(choice
(const :tag "Always ask what context to use" always-ask) (const :tag "Always ask what context to use" always-ask)
(const :tag "Ask if none of the contexts match" ask) (const :tag "Ask if none of the contexts match" ask)
(const :tag "Ask when there's no context yet" ask-if-none) (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 "Pick the first context if none match" pick-first)
(const :tag "Don't change the context when none match" nil)) (const :tag "Don't change the context when none match" nil))
:safe 'symbolp :safe 'symbolp
:group 'mu4e-compose) :group 'mu4e-compose)
@ -146,10 +146,10 @@ We have the following choices:
- `encrypt': encrypt the reply, but don't sign it. - `encrypt': encrypt the reply, but don't sign it.
- anything else: do nothing." - anything else: do nothing."
:type '(choice :type '(choice
(const :tag "Sign the reply" sign) (const :tag "Sign the reply" sign)
(const :tag "Sign and encrypt the reply" sign-and-encrypt) (const :tag "Sign and encrypt the reply" sign-and-encrypt)
(const :tag "Encrypt the reply" encrypt) (const :tag "Encrypt the reply" encrypt)
(const :tag "Don't do anything" nil)) (const :tag "Don't do anything" nil))
:safe 'symbolp :safe 'symbolp
:group 'mu4e-compose) :group 'mu4e-compose)
@ -162,10 +162,10 @@ We have the following choices:
- `encrypt': encrypt the reply, but don't sign it. - `encrypt': encrypt the reply, but don't sign it.
- anything else: do nothing." - anything else: do nothing."
:type '(choice :type '(choice
(const :tag "Sign the reply" sign) (const :tag "Sign the reply" sign)
(const :tag "Sign and encrypt the reply" sign-and-encrypt) (const :tag "Sign and encrypt the reply" sign-and-encrypt)
(const :tag "Encrypt the reply" encrypt) (const :tag "Encrypt the reply" encrypt)
(const :tag "Don't do anything" nil)) (const :tag "Don't do anything" nil))
:safe 'symbolp :safe 'symbolp
:group 'mu4e-compose) :group 'mu4e-compose)
@ -173,7 +173,7 @@ We have the following choices:
'mu4e-compose-crypto-reply-policy' variable is deprecated. 'mu4e-compose-crypto-reply-policy' variable is deprecated.
'mu4e-compose-crypto-reply-plain-policy' and 'mu4e-compose-crypto-reply-plain-policy' and
'mu4e-compose-crypto-reply-encrypted-policy' should be used instead" 'mu4e-compose-crypto-reply-encrypted-policy' should be used instead"
"2017-09-02") "2017-09-02")
(defcustom mu4e-compose-format-flowed nil (defcustom mu4e-compose-format-flowed nil
"Whether to compose messages to be sent as format=flowed. "Whether to compose messages to be sent as format=flowed.
@ -211,10 +211,10 @@ This is a symbol, `new', `forward', `reply' or `edit'.")
(unless (file-exists-p path) (unless (file-exists-p path)
(mu4e-warn "Message file not found")) (mu4e-warn "Message file not found"))
(mml-attach-file (mml-attach-file
path path
"message/rfc822" "message/rfc822"
(or (plist-get msg :subject) "No subject") (or (plist-get msg :subject) "No subject")
"attachment"))) "attachment")))
(defun mu4e-compose-attach-captured-message () (defun mu4e-compose-attach-captured-message ()
"Insert the last captured message file as an attachment. "Insert the last captured message file as an attachment.
@ -243,25 +243,25 @@ Messages are captured with `mu4e-action-capture-message'."
"Maybe setup Fcc, based on `mu4e-sent-messages-behavior'. "Maybe setup Fcc, based on `mu4e-sent-messages-behavior'.
If needed, set the Fcc header, and register the handler function." If needed, set the Fcc header, and register the handler function."
(let* ((sent-behavior (let* ((sent-behavior
;; Note; we cannot simply use functionp here, since at least ;; Note; we cannot simply use functionp here, since at least
;; delete is a function, too... ;; delete is a function, too...
(if (member mu4e-sent-messages-behavior '(delete trash sent)) (if (member mu4e-sent-messages-behavior '(delete trash sent))
mu4e-sent-messages-behavior mu4e-sent-messages-behavior
(if (functionp mu4e-sent-messages-behavior) (if (functionp mu4e-sent-messages-behavior)
(funcall mu4e-sent-messages-behavior) (funcall mu4e-sent-messages-behavior)
mu4e-sent-messages-behavior))) mu4e-sent-messages-behavior)))
(mdir (mdir
(cl-case sent-behavior (cl-case sent-behavior
(delete nil) (delete nil)
(trash (mu4e-get-trash-folder mu4e-compose-parent-message)) (trash (mu4e-get-trash-folder mu4e-compose-parent-message))
(sent (mu4e-get-sent-folder mu4e-compose-parent-message)) (sent (mu4e-get-sent-folder mu4e-compose-parent-message))
(otherwise (otherwise
(mu4e-error "Unsupported value '%S' (mu4e-error "Unsupported value '%S'
`mu4e-sent-messages-behavior'" `mu4e-sent-messages-behavior'"
mu4e-sent-messages-behavior)))) mu4e-sent-messages-behavior))))
(fccfile (and mdir (fccfile (and mdir
(concat (mu4e-root-maildir) mdir "/cur/" (concat (mu4e-root-maildir) mdir "/cur/"
(mu4e~draft-message-filename-construct "S"))))) (mu4e~draft-message-filename-construct "S")))))
;; if there's an fcc header, add it to the file ;; if there's an fcc header, add it to the file
(when fccfile (when fccfile
(message-add-header (concat "Fcc: " fccfile "\n")) (message-add-header (concat "Fcc: " fccfile "\n"))
@ -269,22 +269,22 @@ If needed, set the Fcc header, and register the handler function."
;; etc. if you run it after mu4e so, (hack hack) we reset it to the old ;; etc. if you run it after mu4e so, (hack hack) we reset it to the old
;; handler after we've done our thing. ;; handler after we've done our thing.
(setq message-fcc-handler-function (setq message-fcc-handler-function
(let ((maildir mdir) (let ((maildir mdir)
(old-handler message-fcc-handler-function)) (old-handler message-fcc-handler-function))
(lambda (file) (lambda (file)
(setq message-fcc-handler-function old-handler) ;; reset the fcc handler (setq message-fcc-handler-function old-handler) ;; reset the fcc handler
(let ((mdir-path (concat (mu4e-root-maildir) maildir))) (let ((mdir-path (concat (mu4e-root-maildir) maildir)))
;; Create the full maildir structure for the sent folder if it doesn't exist. ;; Create the full maildir structure for the sent folder if it doesn't exist.
;; `mu4e~proc-mkdir` runs asynchronously but no matter whether it runs before or after ;; `mu4e~proc-mkdir` runs asynchronously but no matter whether it runs before or after
;; `write-file`, the sent maildir ends up in the correct state. ;; `write-file`, the sent maildir ends up in the correct state.
(unless (file-exists-p mdir-path) (unless (file-exists-p mdir-path)
(mu4e~proc-mkdir mdir-path))) (mu4e~proc-mkdir mdir-path)))
(write-file file) ;; writing maildirs files is easy (write-file file) ;; writing maildirs files is easy
(mu4e~proc-add file))))))) ;; update the database (mu4e~proc-add file))))))) ;; update the database
(defvar mu4e-compose-hidden-headers (defvar mu4e-compose-hidden-headers
`("^References:" "^Face:" "^X-Face:" `("^References:" "^Face:" "^X-Face:"
"^X-Draft-From:" "^User-agent:") "^X-Draft-From:" "^User-agent:")
"Hidden headers when composing.") "Hidden headers when composing.")
(defun mu4e~compose-hide-headers () (defun mu4e~compose-hide-headers ()
@ -301,25 +301,25 @@ Just after saving we restore it; thus, the separator should never
appear on disk. Also update the Date and ensure we have a appear on disk. Also update the Date and ensure we have a
Message-ID." Message-ID."
(add-hook 'before-save-hook (add-hook 'before-save-hook
(lambda() (lambda()
;; replace the date ;; replace the date
(save-excursion (save-excursion
(message-remove-header "Date") (message-remove-header "Date")
(message-generate-headers '(Date Message-ID)) (message-generate-headers '(Date Message-ID))
(save-match-data (save-match-data
(mu4e~draft-remove-mail-header-separator)))) nil t) (mu4e~draft-remove-mail-header-separator)))) nil t)
(add-hook 'after-save-hook (add-hook 'after-save-hook
(lambda () (lambda ()
(save-match-data (save-match-data
(mu4e~compose-set-friendly-buffer-name) (mu4e~compose-set-friendly-buffer-name)
(mu4e~draft-insert-mail-header-separator) (mu4e~draft-insert-mail-header-separator)
;; hide some headers again ;; hide some headers again
(mu4e~compose-hide-headers) (mu4e~compose-hide-headers)
(widen) (widen)
(set-buffer-modified-p nil) (set-buffer-modified-p nil)
(mu4e-message "Saved (%d lines)" (count-lines (point-min) (point-max))) (mu4e-message "Saved (%d lines)" (count-lines (point-min) (point-max)))
;; update the file on disk -- ie., without the separator ;; update the file on disk -- ie., without the separator
(mu4e~proc-add (buffer-file-name)))) nil t)) (mu4e~proc-add (buffer-file-name)))) nil t))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; address completion; inspired by org-contacts.el and ;; address completion; inspired by org-contacts.el and
@ -327,37 +327,37 @@ Message-ID."
(defun mu4e~compose-complete-handler (str pred action) (defun mu4e~compose-complete-handler (str pred action)
"Complete address STR with predication PRED for ACTION." "Complete address STR with predication PRED for ACTION."
(cond (cond
((eq action nil) ((eq action nil)
(try-completion str mu4e~contacts pred)) (try-completion str mu4e~contacts pred))
((eq action t) ((eq action t)
(all-completions str mu4e~contacts pred)) (all-completions str mu4e~contacts pred))
((eq action 'metadata) ((eq action 'metadata)
;; our contacts are already sorted - just need to tell the ;; our contacts are already sorted - just need to tell the
;; completion machinery not to try to undo that... ;; completion machinery not to try to undo that...
'(metadata '(metadata
(display-sort-function . identity) (display-sort-function . identity)
(cycle-sort-function . identity))))) (cycle-sort-function . identity)))))
(defun mu4e~compose-complete-contact (&optional start) (defun mu4e~compose-complete-contact (&optional start)
"Complete the text at START with a contact. "Complete the text at START with a contact.
Ie. either 'name <email>' or 'email')." Ie. either 'name <email>' or 'email')."
(interactive) (interactive)
(let ((mail-abbrev-mode-regexp mu4e~compose-address-fields-regexp) (let ((mail-abbrev-mode-regexp mu4e~compose-address-fields-regexp)
(eoh ;; end-of-headers (eoh ;; end-of-headers
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(search-forward-regexp mail-header-separator nil t)))) (search-forward-regexp mail-header-separator nil t))))
;; try to complete only when we're in the headers area, ;; try to complete only when we're in the headers area,
;; looking at an address field. ;; looking at an address field.
(when (and eoh (> eoh (point)) (mail-abbrev-in-expansion-header-p)) (when (and eoh (> eoh (point)) (mail-abbrev-in-expansion-header-p))
(let* ((end (point)) (let* ((end (point))
(start (start
(or start (or start
(save-excursion (save-excursion
(re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*") (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
(goto-char (match-end 0)) (goto-char (match-end 0))
(point))))) (point)))))
(list start end 'mu4e~compose-complete-handler))))) (list start end 'mu4e~compose-complete-handler)))))
(defun mu4e~compose-setup-completion () (defun mu4e~compose-setup-completion ()
"Set up auto-completion of addresses." "Set up auto-completion of addresses."
@ -365,7 +365,7 @@ Ie. either 'name <email>' or 'email')."
(set (make-local-variable 'completion-cycle-threshold) 7) (set (make-local-variable 'completion-cycle-threshold) 7)
(add-to-list (make-local-variable 'completion-styles) 'substring) (add-to-list (make-local-variable 'completion-styles) 'substring)
(add-hook 'completion-at-point-functions (add-hook 'completion-at-point-functions
'mu4e~compose-complete-contact nil t)) 'mu4e~compose-complete-contact nil t))
(defun mu4e~remove-refs-maybe () (defun mu4e~remove-refs-maybe ()
"Remove References: if In-Reply-To: is missing. "Remove References: if In-Reply-To: is missing.
@ -379,12 +379,12 @@ removing the In-Reply-To header."
"Keymap for \"*mu4e-compose*\" buffers.") "Keymap for \"*mu4e-compose*\" buffers.")
(unless mu4e-compose-mode-map (unless mu4e-compose-mode-map
(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-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)
map))) map)))
(defun mu4e-fill-paragraph (&optional region) (defun mu4e-fill-paragraph (&optional region)
"Re-layout either the whole message or REGION. "Re-layout either the whole message or REGION.
@ -395,11 +395,11 @@ set, this simply executes `fill-paragraph'."
;; Inspired by https://www.emacswiki.org/emacs/UnfillParagraph ;; Inspired by https://www.emacswiki.org/emacs/UnfillParagraph
(interactive (progn (barf-if-buffer-read-only) '(t))) (interactive (progn (barf-if-buffer-read-only) '(t)))
(if mu4e-compose-format-flowed (if mu4e-compose-format-flowed
(let ((fill-column (point-max)) (let ((fill-column (point-max))
(use-hard-newlines nil)); rfill "across" hard newlines (use-hard-newlines nil)); rfill "across" hard newlines
(when (use-region-p) (when (use-region-p)
(delete-trailing-whitespace (region-beginning) (region-end))) (delete-trailing-whitespace (region-beginning) (region-end)))
(fill-paragraph nil region)) (fill-paragraph nil region))
(when (use-region-p) (when (use-region-p)
(delete-trailing-whitespace (region-beginning) (region-end))) (delete-trailing-whitespace (region-beginning) (region-end)))
(fill-paragraph nil region))) (fill-paragraph nil region)))
@ -408,7 +408,7 @@ set, this simply executes `fill-paragraph'."
(interactive) (interactive)
(setq use-hard-newlines (not use-hard-newlines)) (setq use-hard-newlines (not use-hard-newlines))
(if use-hard-newlines (if use-hard-newlines
(turn-off-auto-fill) (turn-off-auto-fill)
(turn-on-auto-fill))) (turn-on-auto-fill)))
(defun mu4e~compose-remap-faces () (defun mu4e~compose-remap-faces ()
@ -417,23 +417,23 @@ Our parent `message-mode' uses font-locking for the compose
buffers; lets remap its faces so it uses the ones for mu4e." buffers; lets remap its faces so it uses the ones for mu4e."
;; normal headers ;; normal headers
(face-remap-add-relative 'message-header-name (face-remap-add-relative 'message-header-name
'((:inherit mu4e-header-key-face))) '((:inherit mu4e-header-key-face)))
(face-remap-add-relative 'message-header-other (face-remap-add-relative 'message-header-other
'((:inherit mu4e-header-value-face))) '((:inherit mu4e-header-value-face)))
;; special headers ;; special headers
(face-remap-add-relative 'message-header-from (face-remap-add-relative 'message-header-from
'((:inherit mu4e-contact-face))) '((:inherit mu4e-contact-face)))
(face-remap-add-relative 'message-header-to (face-remap-add-relative 'message-header-to
'((:inherit mu4e-contact-face))) '((:inherit mu4e-contact-face)))
(face-remap-add-relative 'message-header-cc (face-remap-add-relative 'message-header-cc
'((:inherit mu4e-contact-face))) '((:inherit mu4e-contact-face)))
(face-remap-add-relative 'message-header-bcc (face-remap-add-relative 'message-header-bcc
'((:inherit mu4e-contact-face))) '((:inherit mu4e-contact-face)))
(face-remap-add-relative 'message-header-subject (face-remap-add-relative 'message-header-subject
'((:inherit mu4e-special-header-value-face))) '((:inherit mu4e-special-header-value-face)))
;; citation ;; citation
(face-remap-add-relative 'message-cited-text (face-remap-add-relative 'message-cited-text
'((:inherit mu4e-cited-1-face)))) '((:inherit mu4e-cited-1-face))))
(define-derived-mode mu4e-compose-mode message-mode "mu4e:compose" (define-derived-mode mu4e-compose-mode message-mode "mu4e:compose"
"Major mode for the mu4e message composition, derived from `message-mode'. "Major mode for the mu4e message composition, derived from `message-mode'.
@ -465,68 +465,68 @@ buffers; lets remap its faces so it uses the ones for mu4e."
;; offer completion for e-mail addresses ;; offer completion for e-mail addresses
(when mu4e-compose-complete-addresses (when mu4e-compose-complete-addresses
(unless mu4e~contacts ;; work-around for https://github.com/djcb/mu/issues/1016 (unless mu4e~contacts ;; work-around for https://github.com/djcb/mu/issues/1016
(mu4e~request-contacts-maybe)) (mu4e~request-contacts-maybe))
(mu4e~compose-setup-completion)) (mu4e~compose-setup-completion))
(if mu4e-compose-format-flowed (if mu4e-compose-format-flowed
(progn (progn
(turn-off-auto-fill) (turn-off-auto-fill)
(setq truncate-lines nil (setq truncate-lines nil
word-wrap t word-wrap t
mml-enable-flowed t mml-enable-flowed t
use-hard-newlines t) use-hard-newlines t)
(visual-line-mode t)) (visual-line-mode t))
(setq mml-enable-flowed nil)) (setq mml-enable-flowed nil))
(let ((keymap (lookup-key message-mode-map [menu-bar text]))) (let ((keymap (lookup-key message-mode-map [menu-bar text])))
(when keymap (when keymap
(define-key-after (define-key-after
keymap keymap
[mu4e-hard-newlines] [mu4e-hard-newlines]
'(menu-item "Format=flowed" mu4e-toggle-use-hard-newlines '(menu-item "Format=flowed" mu4e-toggle-use-hard-newlines
:button (:toggle . use-hard-newlines) :button (:toggle . use-hard-newlines)
:help "Toggle format=flowed" :help "Toggle format=flowed"
:visible (eq major-mode 'mu4e-compose-mode) :visible (eq major-mode 'mu4e-compose-mode)
:enable mu4e-compose-format-flowed) :enable mu4e-compose-format-flowed)
'sep) 'sep)
(define-key-after (define-key-after
keymap keymap
[mu4e-electric-quote-mode] [mu4e-electric-quote-mode]
'(menu-item "Electric quote" electric-quote-local-mode '(menu-item "Electric quote" electric-quote-local-mode
:button (:toggle . electric-quote-mode) :button (:toggle . electric-quote-mode)
:help "Toggle Electric quote mode" :help "Toggle Electric quote mode"
:visible (and (eq major-mode 'mu4e-compose-mode) :visible (and (eq major-mode 'mu4e-compose-mode)
(functionp 'electric-quote-local-mode))) (functionp 'electric-quote-local-mode)))
'mu4e-hard-newlines))) 'mu4e-hard-newlines)))
(when (lookup-key mml-mode-map [menu-bar Attachments]) (when (lookup-key mml-mode-map [menu-bar Attachments])
(define-key-after (define-key-after
(lookup-key mml-mode-map [menu-bar Attachments]) (lookup-key mml-mode-map [menu-bar Attachments])
[mu4e-compose-attach-captured-message] [mu4e-compose-attach-captured-message]
'(menu-item "Attach captured message" '(menu-item "Attach captured message"
mu4e-compose-attach-captured-message mu4e-compose-attach-captured-message
:help "Attach message captured in Headers View (with 'a c')" :help "Attach message captured in Headers View (with 'a c')"
:visible (eq major-mode 'mu4e-compose-mode)) :visible (eq major-mode 'mu4e-compose-mode))
(quote Attach\ External...))) (quote Attach\ External...)))
;; setup the fcc-stuff, if needed ;; setup the fcc-stuff, if needed
(add-hook 'message-send-hook (add-hook 'message-send-hook
(lambda () ;; mu4e~compose-save-before-sending (lambda () ;; mu4e~compose-save-before-sending
;; when in-reply-to was removed, remove references as well. ;; when in-reply-to was removed, remove references as well.
(when (eq mu4e-compose-type 'reply) (when (eq mu4e-compose-type 'reply)
(mu4e~remove-refs-maybe)) (mu4e~remove-refs-maybe))
(when use-hard-newlines (when use-hard-newlines
(mu4e-send-harden-newlines)) (mu4e-send-harden-newlines))
;; for safety, always save the draft before sending ;; for safety, always save the draft before sending
(set-buffer-modified-p t) (set-buffer-modified-p t)
(save-buffer) (save-buffer)
(mu4e~compose-setup-fcc-maybe) (mu4e~compose-setup-fcc-maybe)
(widen)) nil t) (widen)) nil t)
;; when the message has been sent. ;; when the message has been sent.
(add-hook 'message-sent-hook (add-hook 'message-sent-hook
(lambda () ;; mu4e~compose-mark-after-sending (lambda () ;; mu4e~compose-mark-after-sending
(setq mu4e-sent-func 'mu4e-sent-handler) (setq mu4e-sent-func 'mu4e-sent-handler)
(mu4e~proc-sent (buffer-file-name))) nil t)) (mu4e~proc-sent (buffer-file-name))) nil t))
;; mark these two hooks as permanent-local, so they'll survive mode-changes ;; mark these two hooks as permanent-local, so they'll survive mode-changes
;; (put 'mu4e~compose-save-before-sending 'permanent-local-hook t) ;; (put 'mu4e~compose-save-before-sending 'permanent-local-hook t)
(put 'mu4e~compose-mark-after-sending 'permanent-local-hook t)) (put 'mu4e~compose-mark-after-sending 'permanent-local-hook t))
@ -544,17 +544,17 @@ buffers; lets remap its faces so it uses the ones for mu4e."
(defun mu4e~compose-set-friendly-buffer-name (&optional compose-type) (defun mu4e~compose-set-friendly-buffer-name (&optional compose-type)
"Set some user-friendly buffer name based on the COMPOSE-TYPE." "Set some user-friendly buffer name based on the COMPOSE-TYPE."
(let* ((subj (message-field-value "subject")) (let* ((subj (message-field-value "subject"))
(subj (unless (and subj (string-match "^[:blank:]*$" subj)) subj)) (subj (unless (and subj (string-match "^[:blank:]*$" subj)) subj))
(str (or subj (str (or subj
(cl-case compose-type (cl-case compose-type
(reply "*reply*") (reply "*reply*")
(forward "*forward*") (forward "*forward*")
(otherwise "*draft*"))))) (otherwise "*draft*")))))
(rename-buffer (generate-new-buffer-name (rename-buffer (generate-new-buffer-name
(truncate-string-to-width str (truncate-string-to-width str
mu4e~compose-buffer-max-name-length mu4e~compose-buffer-max-name-length
nil nil t) nil nil t)
(buffer-name))))) (buffer-name)))))
(defun mu4e~compose-crypto-reply (parent compose-type) (defun mu4e~compose-crypto-reply (parent compose-type)
"Possibly encrypt or sign a message based on PARENT and COMPOSE-TYPE. "Possibly encrypt or sign a message based on PARENT and COMPOSE-TYPE.
@ -562,17 +562,17 @@ When composing a reply to an encrypted message, we can
automatically encrypt that reply. When the message is unencrypted, automatically encrypt that reply. When the message is unencrypted,
we can decide what we want to do." we can decide what we want to do."
(if (and (eq compose-type 'reply) (if (and (eq compose-type 'reply)
(and parent (member 'encrypted (mu4e-message-field parent :flags)))) (and parent (member 'encrypted (mu4e-message-field parent :flags))))
(cl-case mu4e-compose-crypto-reply-encrypted-policy (cl-case mu4e-compose-crypto-reply-encrypted-policy
(sign (mml-secure-message-sign)) (sign (mml-secure-message-sign))
(encrypt (mml-secure-message-encrypt)) (encrypt (mml-secure-message-encrypt))
(sign-and-encrypt (mml-secure-message-sign-encrypt)) (sign-and-encrypt (mml-secure-message-sign-encrypt))
(message "Do nothing")) (message "Do nothing"))
(cl-case mu4e-compose-crypto-reply-plain-policy (cl-case mu4e-compose-crypto-reply-plain-policy
(sign (mml-secure-message-sign)) (sign (mml-secure-message-sign))
(encrypt (mml-secure-message-encrypt)) (encrypt (mml-secure-message-encrypt))
(sign-and-encrypt (mml-secure-message-sign-encrypt)) (sign-and-encrypt (mml-secure-message-sign-encrypt))
(message "Do nothing"))) (message "Do nothing")))
) )
@ -599,21 +599,21 @@ tempfile)."
;; message being forwarded or replied to, otherwise it is nil. ;; message being forwarded or replied to, otherwise it is nil.
(set (make-local-variable 'mu4e-compose-parent-message) original-msg) (set (make-local-variable 'mu4e-compose-parent-message) original-msg)
(put 'mu4e-compose-parent-message 'permanent-local t) (put 'mu4e-compose-parent-message 'permanent-local t)
;; remember the compose-type ;; remember the compose-type
(set (make-local-variable 'mu4e-compose-type) compose-type) (set (make-local-variable 'mu4e-compose-type) compose-type)
(put 'mu4e-compose-type 'permanent-local t) (put 'mu4e-compose-type 'permanent-local t)
;; maybe switch the context ;; maybe switch the context
(mu4e~context-autoswitch mu4e-compose-parent-message (mu4e~context-autoswitch mu4e-compose-parent-message
mu4e-compose-context-policy) mu4e-compose-context-policy)
(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.
(let ((winconf (current-window-configuration))) (let ((winconf (current-window-configuration)))
(condition-case nil (condition-case nil
(mu4e-draft-open compose-type original-msg) (mu4e-draft-open compose-type original-msg)
(quit (set-window-configuration winconf) (quit (set-window-configuration winconf)
(mu4e-message "Operation aborted") (mu4e-message "Operation aborted")
(cl-return-from mu4e~compose-handler)))) (cl-return-from mu4e~compose-handler))))
;; insert mail-header-separator, which is needed by message mode to separate ;; insert mail-header-separator, which is needed by message mode to separate
;; headers and body. will be removed before saving to disk ;; headers and body. will be removed before saving to disk
(mu4e~draft-insert-mail-header-separator) (mu4e~draft-insert-mail-header-separator)
@ -625,23 +625,23 @@ tempfile)."
(goto-char (point-max)) ;; put attachments at the end (goto-char (point-max)) ;; put attachments at the end
(if (and (eq compose-type 'forward) mu4e-compose-forward-as-attachment) (if (and (eq compose-type 'forward) mu4e-compose-forward-as-attachment)
(mu4e-compose-attach-message original-msg) (mu4e-compose-attach-message original-msg)
(dolist (att includes) (dolist (att includes)
(mml-attach-file (mml-attach-file
(plist-get att :file-name) (plist-get att :mime-type))))) (plist-get att :file-name) (plist-get att :mime-type)))))
(mu4e~compose-set-friendly-buffer-name compose-type) (mu4e~compose-set-friendly-buffer-name compose-type)
;; now jump to some useful positions, and start writing that mail! ;; now jump to some useful positions, and start writing that mail!
(if (member compose-type '(new forward)) (if (member compose-type '(new forward))
(message-goto-to) (message-goto-to)
;; otherwise, it depends... ;; otherwise, it depends...
(cl-case message-cite-reply-position (cl-case message-cite-reply-position
((above traditional) ((above traditional)
(message-goto-body)) (message-goto-body))
(t (t
(when (message-goto-signature) (when (message-goto-signature)
(forward-line -2))))) (forward-line -2)))))
;; bind to `mu4e-compose-parent-message' of compose buffer ;; bind to `mu4e-compose-parent-message' of compose buffer
(set (make-local-variable 'mu4e-compose-parent-message) original-msg) (set (make-local-variable 'mu4e-compose-parent-message) original-msg)
@ -670,11 +670,11 @@ tempfile)."
"Try to go back to some previous buffer, in the order view->headers->main." "Try to go back to some previous buffer, in the order view->headers->main."
(unless (eq mu4e-split-view 'single-window) (unless (eq mu4e-split-view 'single-window)
(if (buffer-live-p (mu4e-get-view-buffer)) (if (buffer-live-p (mu4e-get-view-buffer))
(switch-to-buffer (mu4e-get-view-buffer)) (switch-to-buffer (mu4e-get-view-buffer))
(if (buffer-live-p (mu4e-get-headers-buffer)) (if (buffer-live-p (mu4e-get-headers-buffer))
(switch-to-buffer (mu4e-get-headers-buffer)) (switch-to-buffer (mu4e-get-headers-buffer))
;; 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-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.
@ -687,9 +687,9 @@ appropriate flag at the message forwarded or replied-to."
;; this seems a bit hamfisted... ;; this seems a bit hamfisted...
(dolist (buf (buffer-list)) (dolist (buf (buffer-list))
(when (and (buffer-file-name buf) (when (and (buffer-file-name buf)
(string= (buffer-file-name buf) path)) (string= (buffer-file-name buf) path))
(if message-kill-buffer-on-exit (if message-kill-buffer-on-exit
(kill-buffer buf)))) (kill-buffer buf))))
(mu4e~switch-back-to-mu4e-buffer) (mu4e~switch-back-to-mu4e-buffer)
(mu4e-message "Message sent")) (mu4e-message "Message sent"))
@ -703,8 +703,8 @@ It restores mu4e window layout after killing the compose-buffer."
(when (not (equal current-buffer (current-buffer))) (when (not (equal current-buffer (current-buffer)))
;; Restore mu4e ;; Restore mu4e
(if mu4e-compose-in-new-frame (if mu4e-compose-in-new-frame
(delete-frame) (delete-frame)
(mu4e~switch-back-to-mu4e-buffer))))) (mu4e~switch-back-to-mu4e-buffer)))))
(defun mu4e~compose-set-parent-flag (path) (defun mu4e~compose-set-parent-flag (path)
"Set flags for replied-t and forwarded for the message at PATH. "Set flags for replied-t and forwarded for the message at PATH.
@ -728,25 +728,25 @@ buffer."
(let ((buf (find-file-noselect path))) (let ((buf (find-file-noselect path)))
(when buf (when buf
(with-current-buffer buf (with-current-buffer buf
(message-narrow-to-headers-or-head) (message-narrow-to-headers-or-head)
(let ((in-reply-to (message-fetch-field "in-reply-to")) (let ((in-reply-to (message-fetch-field "in-reply-to"))
(forwarded-from) (forwarded-from)
(references (message-fetch-field "references"))) (references (message-fetch-field "references")))
(unless in-reply-to (unless in-reply-to
(when references (when references
(with-temp-buffer ;; inspired by `message-shorten-references'. (with-temp-buffer ;; inspired by `message-shorten-references'.
(insert references) (insert references)
(goto-char (point-min)) (goto-char (point-min))
(let ((refs)) (let ((refs))
(while (re-search-forward "<[^ <]+@[^ <]+>" nil t) (while (re-search-forward "<[^ <]+@[^ <]+>" nil t)
(push (match-string 0) refs)) (push (match-string 0) refs))
;; the last will be the first ;; the last will be the first
(setq forwarded-from (cl-first refs)))))) (setq forwarded-from (cl-first refs))))))
;; remove the <> ;; remove the <>
(when (and in-reply-to (string-match "<\\(.*\\)>" in-reply-to)) (when (and in-reply-to (string-match "<\\(.*\\)>" in-reply-to))
(mu4e~proc-move (match-string 1 in-reply-to) nil "+R-N")) (mu4e~proc-move (match-string 1 in-reply-to) nil "+R-N"))
(when (and forwarded-from (string-match "<\\(.*\\)>" forwarded-from)) (when (and forwarded-from (string-match "<\\(.*\\)>" forwarded-from))
(mu4e~proc-move (match-string 1 forwarded-from) nil "+P-N"))))))) (mu4e~proc-move (match-string 1 forwarded-from) nil "+P-N")))))))
(defun mu4e-compose (compose-type) (defun mu4e-compose (compose-type)
"Start composing a message of COMPOSE-TYPE. "Start composing a message of COMPOSE-TYPE.
@ -760,31 +760,31 @@ Symbol `edit' is only allowed for draft messages."
(unless (member compose-type '(reply forward edit resend new)) (unless (member compose-type '(reply forward edit resend new))
(mu4e-error "Invalid compose type '%S'" compose-type)) (mu4e-error "Invalid compose type '%S'" compose-type))
(when (and (eq compose-type 'edit) (when (and (eq compose-type 'edit)
(not (member 'draft (mu4e-message-field msg :flags)))) (not (member 'draft (mu4e-message-field msg :flags))))
(mu4e-warn "Editing is only allowed for draft messages")) (mu4e-warn "Editing is only allowed for draft messages"))
;; 'new is special, since it takes no existing message as arg; therefore, we ;; 'new is special, since it takes no existing message as arg; therefore, we
;; don't need to involve the backend, and call the handler *directly* ;; don't need to involve the backend, and call the handler *directly*
(if (eq compose-type 'new) (if (eq compose-type 'new)
(mu4e~compose-handler 'new) (mu4e~compose-handler 'new)
;; otherwise, we need the doc-id ;; otherwise, we need the doc-id
(let* ((docid (mu4e-message-field msg :docid)) (let* ((docid (mu4e-message-field msg :docid))
;; decrypt (or not), based on `mu4e-decryption-policy'. ;; decrypt (or not), based on `mu4e-decryption-policy'.
(decrypt (decrypt
(and (member 'encrypted (mu4e-message-field msg :flags)) (and (member 'encrypted (mu4e-message-field msg :flags))
(if (eq mu4e-decryption-policy 'ask) (if (eq mu4e-decryption-policy 'ask)
(yes-or-no-p (mu4e-format "Decrypt message?")) (yes-or-no-p (mu4e-format "Decrypt message?"))
mu4e-decryption-policy)))) mu4e-decryption-policy))))
;; if there's a visible view window, select that before starting ;; if there's a visible view window, select that before starting
;; composing a new message, so that one will be replaced by the compose ;; composing a new message, so that one will be replaced by the compose
;; window. The 10-or-so line headers buffer is not a good place to write ;; window. The 10-or-so line headers buffer is not a good place to write
;; it... ;; it...
(unless (eq mu4e-split-view 'single-window) (unless (eq mu4e-split-view 'single-window)
(let ((viewwin (get-buffer-window (mu4e-get-view-buffer)))) (let ((viewwin (get-buffer-window (mu4e-get-view-buffer))))
(when (window-live-p viewwin) (when (window-live-p viewwin)
(select-window viewwin)))) (select-window viewwin))))
;; talk to the backend ;; talk to the backend
(mu4e~proc-compose compose-type decrypt docid))))) (mu4e~proc-compose compose-type decrypt docid)))))
(defun mu4e-compose-reply () (defun mu4e-compose-reply ()
"Compose a reply for the message at point in the headers buffer." "Compose a reply for the message at point in the headers buffer."
@ -820,7 +820,7 @@ draft message."
;;;###autoload ;;;###autoload
(defun mu4e~compose-mail (&optional to subject other-headers _continue (defun mu4e~compose-mail (&optional to subject other-headers _continue
_switch-function yank-action _send-actions _return-action) _switch-function yank-action _send-actions _return-action)
"This is mu4e's implementation of `compose-mail'. "This is mu4e's implementation of `compose-mail'.
Quoting its docstring: Quoting its docstring:
Start composing a mail message to send. Start composing a mail message to send.
@ -874,14 +874,14 @@ buffer buried."
;; yank message ;; yank message
(if (bufferp yank-action) (if (bufferp yank-action)
(list 'insert-buffer yank-action) (list 'insert-buffer yank-action)
yank-action) yank-action)
;; try to put the user at some reasonable spot... ;; try to put the user at some reasonable spot...
(if (not to) (if (not to)
(message-goto-to) (message-goto-to)
(if (not subject) (if (not subject)
(message-goto-subject) (message-goto-subject)
(message-goto-body)))) (message-goto-body))))
;; happily, we can re-use most things from message mode ;; happily, we can re-use most things from message mode
@ -933,7 +933,7 @@ is supplied, or Transient Mark mode is enabled and the mark is active."
(region-active-p) (region-active-p)
(push-mark)) (push-mark))
(let ((old-position (point)) (let ((old-position (point))
(message-position (save-excursion (message-goto-body) (point)))) (message-position (save-excursion (message-goto-body) (point))))
(goto-char (point-max)) (goto-char (point-max))
(when (re-search-backward message-signature-separator message-position t) (when (re-search-backward message-signature-separator message-position t)
(forward-line -1)) (forward-line -1))

View File

@ -1,4 +1,4 @@
; mu4e-context.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;;; mu4e-context.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*-
;; ;;
;; Copyright (C) 2015-2020 Dirk-Jan C. Binnema ;; Copyright (C) 2015-2020 Dirk-Jan C. Binnema
@ -49,16 +49,16 @@ none."
(let ((ctx mu4e~context-current)) (let ((ctx mu4e~context-current))
(when output (when output
(mu4e-message "Current context: %s" (mu4e-message "Current context: %s"
(if ctx (mu4e-context-name ctx) "<none>"))) (if ctx (mu4e-context-name ctx) "<none>")))
ctx)) ctx))
(defun mu4e-context-label () (defun mu4e-context-label ()
"Propertized string with the current context name, or \"\" if "Propertized string with the current context name, or \"\" if
there is none." there is none."
(if (mu4e-context-current) (if (mu4e-context-current)
(concat "[" (propertize (mu4e~quote-for-modeline (concat "[" (propertize (mu4e~quote-for-modeline
(mu4e-context-name (mu4e-context-current))) (mu4e-context-name (mu4e-context-current)))
'face 'mu4e-context-face) "]") "")) 'face 'mu4e-context-face) "]") ""))
(cl-defstruct mu4e-context (cl-defstruct mu4e-context
"A mu4e context object with the following members: "A mu4e context object with the following members:
@ -194,9 +194,9 @@ default folders (see `make-mu4e-context' and `mu4e-context'):
"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 (cl-map 'list (lambda (context) (let* ((names (cl-map 'list (lambda (context)
(cons (mu4e-context-name context) 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")))))
(defun mu4e-context-switch (&optional force name) (defun mu4e-context-switch (&optional force name)
@ -210,25 +210,25 @@ non-nil."
(unless mu4e-contexts (unless mu4e-contexts
(mu4e-error "No contexts defined")) (mu4e-error "No contexts defined"))
(let* ((names (cl-map 'list (lambda (context) (let* ((names (cl-map 'list (lambda (context)
(cons (mu4e-context-name context) context)) (cons (mu4e-context-name context) context))
mu4e-contexts)) mu4e-contexts))
(context (context
(if name (if name
(cdr-safe (assoc name names)) (cdr-safe (assoc name names))
(mu4e~context-ask-user "Switch to context: ")))) (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. ;; 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) (when (and (mu4e-context-current)
(mu4e-context-leave-func 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)
(run-hooks 'mu4e-context-changed-hook) (run-hooks 'mu4e-context-changed-hook)
@ -242,7 +242,7 @@ match, return the first. For MSG and POLICY, see `mu4e-context-determine'."
(when (and mu4e-contexts (not mu4e~context-current)) (when (and mu4e-contexts (not mu4e~context-current))
(let ((context (mu4e-context-determine msg policy))) (let ((context (mu4e-context-determine msg policy)))
(when context (mu4e-context-switch (when context (mu4e-context-switch
nil (mu4e-context-name context)))))) nil (mu4e-context-name context))))))
(defun mu4e-context-determine (msg &optional policy) (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
@ -263,18 +263,18 @@ match, POLICY determines what to do:
- otherwise, return nil. Effectively, this leaves the current context as it is." - 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 ;; is there a matching one? (or ;; is there a matching one?
(cl-find-if (lambda (context) (cl-find-if (lambda (context)
(when (mu4e-context-match-func context) (when (mu4e-context-match-func context)
(funcall (mu4e-context-match-func context) msg))) (funcall (mu4e-context-match-func context) msg)))
mu4e-contexts) mu4e-contexts)
;; no context found yet; consult policy ;; no context found yet; consult policy
(cl-case policy (cl-case policy
(pick-first (car mu4e-contexts)) (pick-first (car mu4e-contexts))
(ask (mu4e~context-ask-user "Select context: ")) (ask (mu4e~context-ask-user "Select context: "))
(ask-if-none (or (mu4e-context-current) (ask-if-none (or (mu4e-context-current)
(mu4e~context-ask-user "Select context: "))) (mu4e~context-ask-user "Select context: ")))
(otherwise nil)))))) (otherwise nil))))))
(provide 'mu4e-context) (provide 'mu4e-context)

View File

@ -61,13 +61,13 @@
;; Probably this can be moved to mu4e-view.el. ;; Probably this can be moved to mu4e-view.el.
(add-hook 'mu4e-view-mode-hook (add-hook 'mu4e-view-mode-hook
(lambda () (lambda ()
(set (make-local-variable 'bookmark-make-record-function) (set (make-local-variable 'bookmark-make-record-function)
'mu4e-view-bookmark-make-record))) 'mu4e-view-bookmark-make-record)))
;; And this can be moved to mu4e-headers.el. ;; And this can be moved to mu4e-headers.el.
(add-hook 'mu4e-headers-mode-hook (add-hook 'mu4e-headers-mode-hook
(lambda () (lambda ()
(set (make-local-variable 'bookmark-make-record-function) (set (make-local-variable 'bookmark-make-record-function)
'mu4e-view-bookmark-make-record))) 'mu4e-view-bookmark-make-record)))
(defun mu4e-view-bookmark-make-record () (defun mu4e-view-bookmark-make-record ()
"Make a bookmark entry for a mu4e buffer. Note that this is an "Make a bookmark entry for a mu4e buffer. Note that this is an
@ -81,9 +81,9 @@ emacs bookmark, not to be confused with `mu4e-bookmarks'."
(subject (or (plist-get msg :subject) "No subject"))) (subject (or (plist-get msg :subject) "No subject")))
`(,subject `(,subject
,@(bookmark-make-record-default 'no-file 'no-context) ,@(bookmark-make-record-default 'no-file 'no-context)
(location . (,query . ,docid)) (location . (,query . ,docid))
(mode . ,mode) (mode . ,mode)
(handler . mu4e-bookmark-jump)))) (handler . mu4e-bookmark-jump))))
(defun mu4e-bookmark-jump (bookmark) (defun mu4e-bookmark-jump (bookmark)
"Handler function for record returned by `mu4e-view-bookmark-make-record'. "Handler function for record returned by `mu4e-view-bookmark-make-record'.
@ -102,8 +102,8 @@ BOOKMARK is a bookmark name or a bookmark record."
(run-with-timer 0.1 nil (run-with-timer 0.1 nil
(lambda (bmk) (lambda (bmk)
(bookmark-default-handler (bookmark-default-handler
`("" (buffer . ,(current-buffer)) . `("" (buffer . ,(current-buffer)) .
,(bookmark-get-bookmark-record bmk)))) ,(bookmark-get-bookmark-record bmk))))
bookmark)))) bookmark))))
@ -132,7 +132,7 @@ For example for bogofile, use \"/usr/bin/bogofilter -Sn < %s\"")
(let* ((path (shell-quote-argument (mu4e-message-field msg :path))) (let* ((path (shell-quote-argument (mu4e-message-field msg :path)))
(command (format mu4e-register-as-spam-cmd path))) ;; re-register msg as spam (command (format mu4e-register-as-spam-cmd path))) ;; re-register msg as spam
(shell-command command)) (shell-command command))
(mu4e-mark-at-point 'delete nil)) (mu4e-mark-at-point 'delete nil))
(defun mu4e-register-msg-as-ham (msg) (defun mu4e-register-msg-as-ham (msg)
"Mark message as ham." "Mark message as ham."
@ -140,7 +140,7 @@ For example for bogofile, use \"/usr/bin/bogofilter -Sn < %s\"")
(let* ((path (shell-quote-argument(mu4e-message-field msg :path))) (let* ((path (shell-quote-argument(mu4e-message-field msg :path)))
(command (format mu4e-register-as-ham-cmd path))) ;; re-register msg as ham (command (format mu4e-register-as-ham-cmd path))) ;; re-register msg as ham
(shell-command command)) (shell-command command))
(mu4e-mark-at-point 'something nil)) (mu4e-mark-at-point 'something nil))
;; (add-to-list 'mu4e-view-actions ;; (add-to-list 'mu4e-view-actions
;; '("sMark as spam" . mu4e-view-register-msg-as-spam) t) ;; '("sMark as spam" . mu4e-view-register-msg-as-spam) t)
@ -178,8 +178,8 @@ buffers found, compose a new message and then attach the file."
(files-to-attach (files-to-attach
(delq nil (mapcar (delq nil (mapcar
(lambda (f) (if (or (not (file-exists-p f)) (file-directory-p f)) (lambda (f) (if (or (not (file-exists-p f)) (file-directory-p f))
nil nil
(expand-file-name f))) (expand-file-name f)))
(eshell-flatten-list (reverse args)))))) (eshell-flatten-list (reverse args))))))
;; warn if user tries to attach without any files marked ;; warn if user tries to attach without any files marked
(if (null files-to-attach) (if (null files-to-attach)
@ -205,7 +205,7 @@ buffers found, compose a new message and then attach the file."
;; if buffer was found, set buffer to destination buffer, and attach files ;; if buffer was found, set buffer to destination buffer, and attach files
(if (not (eq destination 'nil)) (if (not (eq destination 'nil))
(progn (set-buffer destination) (progn (set-buffer destination)
(goto-char (point-max)) ;attach at end of buffer (goto-char (point-max)) ; attach at end of buffer
(while files-to-attach (while files-to-attach
(mml-attach-file (car files-to-attach) (mml-attach-file (car files-to-attach)
(or (mm-default-file-encoding (car files-to-attach)) (or (mm-default-file-encoding (car files-to-attach))

View File

@ -56,9 +56,9 @@ This is the mu4e-specific version of
\(i.e. the blob at the bottom of messages). This is the \(i.e. the blob at the bottom of messages). This is the
mu4e-specific version of `message-signature'." mu4e-specific version of `message-signature'."
:type '(choice string :type '(choice string
(const :tag "None" nil) (const :tag "None" nil)
(const :tag "Contents of signature file" t) (const :tag "Contents of signature file" t)
function sexp) function sexp)
:risky t :risky t
:group 'mu4e-compose) :group 'mu4e-compose)
@ -68,7 +68,7 @@ mu4e-specific version of `message-signature'."
:group 'mu4e-compose) :group 'mu4e-compose)
(make-obsolete-variable 'mu4e-compose-auto-include-date (make-obsolete-variable 'mu4e-compose-auto-include-date
"This is done unconditionally now" "1.3.5") "This is done unconditionally now" "1.3.5")
(defcustom mu4e-compose-in-new-frame nil (defcustom mu4e-compose-in-new-frame nil
"Whether to compose messages in a new frame." "Whether to compose messages in a new frame."
@ -95,7 +95,7 @@ its settings apply."
;; set the the signature separator to 'loose', since in the real world, ;; set the the signature separator to 'loose', since in the real world,
;; many message don't follow the standard... ;; many message don't follow the standard...
(let ((message-signature-separator "^-- *$") (let ((message-signature-separator "^-- *$")
(message-signature-insert-empty-line t)) (message-signature-insert-empty-line t))
(funcall mu4e-compose-cite-function)) (funcall mu4e-compose-cite-function))
(pop-mark) (pop-mark)
(goto-char (point-min)) (goto-char (point-min))
@ -107,8 +107,8 @@ If VAL is nil, return nil."
;; note: the propertize here is currently useless, since gnus sets its own ;; note: the propertize here is currently useless, since gnus sets its own
;; later. ;; later.
(when val (format "%s: %s\n" (when val (format "%s: %s\n"
(propertize hdr 'face 'mu4e-header-key-face) (propertize hdr 'face 'mu4e-header-key-face)
(propertize val 'face 'mu4e-header-value-face)))) (propertize val 'face 'mu4e-header-value-face))))
(defconst mu4e~max-reference-num 21 (defconst mu4e~max-reference-num 21
"Specifies the maximum number of References:. "Specifies the maximum number of References:.
@ -119,7 +119,7 @@ As suggested by `message-shorten-references'.")
Beginning with CUTth Beginning with CUTth
one. Code borrowed from `message-shorten-1'." one. Code borrowed from `message-shorten-1'."
(setcdr (nthcdr (- cut 2) list) (setcdr (nthcdr (- cut 2) list)
(nthcdr (+ (- cut 2) surplus 1) list))) (nthcdr (+ (- cut 2) surplus 1) list)))
(defun mu4e~draft-references-construct (msg) (defun mu4e~draft-references-construct (msg)
"Construct the value of the References: header based on MSG. "Construct the value of the References: header based on MSG.
@ -129,15 +129,15 @@ that :references includes the old in-reply-to as well) and the
message-id. If the message-id is empty, returns the old message-id. If the message-id is empty, returns the old
References. If both are empty, return nil." References. If both are empty, return nil."
(let* ( ;; these are the ones from the message being replied to / forwarded (let* ( ;; these are the ones from the message being replied to / forwarded
(refs (mu4e-message-field msg :references)) (refs (mu4e-message-field msg :references))
(msgid (mu4e-message-field msg :message-id)) (msgid (mu4e-message-field msg :message-id))
;; now, append in ;; now, append in
(refs (if (and msgid (not (string= msgid ""))) (refs (if (and msgid (not (string= msgid "")))
(append refs (list msgid)) refs)) (append refs (list msgid)) refs))
;; no doubles ;; no doubles
(refs (cl-delete-duplicates refs :test #'equal)) (refs (cl-delete-duplicates refs :test #'equal))
(refnum (length refs)) (refnum (length refs))
(cut 2)) (cut 2))
;; remove some refs when there are too many ;; remove some refs when there are too many
(when (> refnum mu4e~max-reference-num) (when (> refnum mu4e~max-reference-num)
(let ((surplus (- refnum mu4e~max-reference-num))) (let ((surplus (- refnum mu4e~max-reference-num)))
@ -154,13 +154,13 @@ This is specified as a comma-separated list of e-mail addresses.
If LST is nil, returns nil." If LST is nil, returns nil."
(when lst (when lst
(mapconcat (mapconcat
(lambda (addrcell) (lambda (addrcell)
(let ((name (car addrcell)) (let ((name (car addrcell))
(email (cdr addrcell))) (email (cdr addrcell)))
(if name (if name
(format "%s <%s>" (mu4e~rfc822-quoteit name) email) (format "%s <%s>" (mu4e~rfc822-quoteit name) email)
(format "%s" email)))) (format "%s" email))))
lst ", "))) lst ", ")))
(defun mu4e~draft-address-cell-equal (cell1 cell2) (defun mu4e~draft-address-cell-equal (cell1 cell2)
"Return t if CELL1 and CELL2 have the same e-mail address. "Return t if CELL1 and CELL2 have the same e-mail address.
@ -168,8 +168,8 @@ The comparison is done case-insensitively. If the cells done
match return nil. CELL1 and CELL2 are cons cells of the match return nil. CELL1 and CELL2 are cons cells of the
form (NAME . EMAIL)." form (NAME . EMAIL)."
(string= (string=
(downcase (or (cdr cell1) "")) (downcase (or (cdr cell1) ""))
(downcase (or (cdr cell2) "")))) (downcase (or (cdr cell2) ""))))
(defun mu4e~draft-create-to-lst (origmsg) (defun mu4e~draft-create-to-lst (origmsg)
@ -180,16 +180,16 @@ whatever was in the To: field before, goes to the Cc:-list (if
we're doing a reply-to-all). Special case: if we were the sender we're doing a reply-to-all). Special case: if we were the sender
of the original, we simple copy the list form the original." of the original, we simple copy the list form the original."
(let ((reply-to (let ((reply-to
(or (plist-get origmsg :reply-to) (plist-get origmsg :from)))) (or (plist-get origmsg :reply-to) (plist-get origmsg :from))))
(cl-delete-duplicates reply-to :test #'mu4e~draft-address-cell-equal) (cl-delete-duplicates reply-to :test #'mu4e~draft-address-cell-equal)
(if mu4e-compose-dont-reply-to-self (if mu4e-compose-dont-reply-to-self
(cl-delete-if (cl-delete-if
(lambda (to-cell) (lambda (to-cell)
(cl-member-if (cl-member-if
(lambda (addr) (lambda (addr)
(string= (downcase addr) (downcase (cdr to-cell)))) (string= (downcase addr) (downcase (cdr to-cell))))
(mu4e-personal-addresses))) (mu4e-personal-addresses)))
reply-to) reply-to)
reply-to))) reply-to)))
@ -198,24 +198,24 @@ of the original, we simple copy the list form the original."
I.e. return all the addresses in ADDRS not matching I.e. return all the addresses in ADDRS not matching
`mu4e-compose-reply-ignore-address'." `mu4e-compose-reply-ignore-address'."
(cond (cond
((null mu4e-compose-reply-ignore-address) ((null mu4e-compose-reply-ignore-address)
addrs) addrs)
((functionp mu4e-compose-reply-ignore-address) ((functionp mu4e-compose-reply-ignore-address)
(cl-remove-if
(lambda (elt)
(funcall mu4e-compose-reply-ignore-address (cdr elt)))
addrs))
(t
;; regexp or list of regexps
(let* ((regexp mu4e-compose-reply-ignore-address)
(regexp (if (listp regexp)
(mapconcat (lambda (elt) (concat "\\(" elt "\\)"))
regexp "\\|")
regexp)))
(cl-remove-if (cl-remove-if
(lambda (elt) (lambda (elt)
(funcall mu4e-compose-reply-ignore-address (cdr elt))) (string-match regexp (cdr elt)))
addrs)) addrs)))))
(t
;; regexp or list of regexps
(let* ((regexp mu4e-compose-reply-ignore-address)
(regexp (if (listp regexp)
(mapconcat (lambda (elt) (concat "\\(" elt "\\)"))
regexp "\\|")
regexp)))
(cl-remove-if
(lambda (elt)
(string-match regexp (cdr elt)))
addrs)))))
(defun mu4e~draft-create-cc-lst (origmsg &optional reply-all include-from) (defun mu4e~draft-create-cc-lst (origmsg &optional reply-all include-from)
"Create a list of address for the Cc: in a new message. "Create a list of address for the Cc: in a new message.
@ -223,37 +223,37 @@ This is based on the original message ORIGMSG, and whether it's a
REPLY-ALL." REPLY-ALL."
(when reply-all (when reply-all
(let* ((cc-lst ;; get the cc-field from the original, remove dups (let* ((cc-lst ;; get the cc-field from the original, remove dups
(cl-delete-duplicates (cl-delete-duplicates
(append (append
(plist-get origmsg :to) (plist-get origmsg :to)
(plist-get origmsg :cc) (plist-get origmsg :cc)
(when include-from(plist-get origmsg :from)) (when include-from(plist-get origmsg :from))
(plist-get origmsg :list-post)) (plist-get origmsg :list-post))
:test #'mu4e~draft-address-cell-equal)) :test #'mu4e~draft-address-cell-equal))
;; now we have the basic list, but we must remove ;; now we have the basic list, but we must remove
;; addresses also in the To: list ;; addresses also in the To: list
(cc-lst (cc-lst
(cl-delete-if (cl-delete-if
(lambda (cc-cell) (lambda (cc-cell)
(cl-find-if (cl-find-if
(lambda (to-cell) (lambda (to-cell)
(mu4e~draft-address-cell-equal cc-cell to-cell)) (mu4e~draft-address-cell-equal cc-cell to-cell))
(mu4e~draft-create-to-lst origmsg))) (mu4e~draft-create-to-lst origmsg)))
cc-lst)) cc-lst))
;; remove ignored addresses ;; remove ignored addresses
(cc-lst (mu4e~strip-ignored-addresses cc-lst)) (cc-lst (mu4e~strip-ignored-addresses cc-lst))
;; finally, we need to remove ourselves from the cc-list ;; finally, we need to remove ourselves from the cc-list
;; unless mu4e-compose-keep-self-cc is non-nil ;; unless mu4e-compose-keep-self-cc is non-nil
(cc-lst (cc-lst
(if (or mu4e-compose-keep-self-cc (null user-mail-address)) (if (or mu4e-compose-keep-self-cc (null user-mail-address))
cc-lst cc-lst
(cl-delete-if (cl-delete-if
(lambda (cc-cell) (lambda (cc-cell)
(cl-member-if (cl-member-if
(lambda (addr) (lambda (addr)
(string= (downcase addr) (downcase (cdr cc-cell)))) (string= (downcase addr) (downcase (cdr cc-cell))))
(mu4e-personal-addresses))) (mu4e-personal-addresses)))
cc-lst)))) cc-lst))))
cc-lst))) cc-lst)))
(defun mu4e~draft-recipients-construct (field origmsg &optional reply-all include-from) (defun mu4e~draft-recipients-construct (field origmsg &optional reply-all include-from)
@ -262,13 +262,13 @@ REPLY-ALL."
and (optionally) REPLY-ALL which indicates this is a reply-to-all and (optionally) REPLY-ALL which indicates this is a reply-to-all
message. Return nil if there are no recipients for the particular field." message. Return nil if there are no recipients for the particular field."
(mu4e~draft-recipients-list-to-string (mu4e~draft-recipients-list-to-string
(cl-case field (cl-case field
(:to (:to
(mu4e~draft-create-to-lst origmsg)) (mu4e~draft-create-to-lst origmsg))
(:cc (:cc
(mu4e~draft-create-cc-lst origmsg reply-all include-from)) (mu4e~draft-create-cc-lst origmsg reply-all include-from))
(otherwise (otherwise
(mu4e-error "Unsupported field"))))) (mu4e-error "Unsupported field")))))
;;; RFC2822 handling of phrases in mail-addresses ;;; RFC2822 handling of phrases in mail-addresses
;;; The optional display-name contains a phrase, it sits before the angle-addr ;;; The optional display-name contains a phrase, it sits before the angle-addr
@ -284,14 +284,14 @@ The reverse of the RFC atext definition is then tested.
If it matches, nil is returned, if not, it is an 'rfc822-atom, which If it matches, nil is returned, if not, it is an 'rfc822-atom, which
is returned." is returned."
(cond (cond
((= (length ph) 0) 'rfc822-empty) ((= (length ph) 0) 'rfc822-empty)
((= (aref ph 0) ?\") ((= (aref ph 0) ?\")
(if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph) (if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph)
'rfc822-quoted-string 'rfc822-quoted-string
'rfc822-containing-quote)) ; starts with quote, but doesn't end with one 'rfc822-containing-quote)) ; starts with quote, but doesn't end with one
((string-match-p "[\"]" ph) 'rfc822-containing-quote) ((string-match-p "[\"]" ph) 'rfc822-containing-quote)
((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil) ((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil)
(t 'rfc822-atom))) (t 'rfc822-atom)))
(defun mu4e~rfc822-quoteit (ph) (defun mu4e~rfc822-quoteit (ph)
"Quote an RFC822 phrase PH only if necessary. "Quote an RFC822 phrase PH only if necessary.
@ -299,12 +299,12 @@ Atoms and quoted strings don't need quotes. The rest do. In
case a phrase contains a quote, it will be escaped." case a phrase contains a quote, it will be escaped."
(let ((type (mu4e~rfc822-phrase-type ph))) (let ((type (mu4e~rfc822-phrase-type ph)))
(cond (cond
((eq type 'rfc822-atom) ph) ((eq type 'rfc822-atom) ph)
((eq type 'rfc822-quoted-string) ph) ((eq type 'rfc822-quoted-string) ph)
((eq type 'rfc822-containing-quote) ((eq type 'rfc822-containing-quote)
(format "\"%s\"" (format "\"%s\""
(replace-regexp-in-string "\"" "\\\\\"" ph))) (replace-regexp-in-string "\"" "\\\\\"" ph)))
(t (format "\"%s\"" ph))))) (t (format "\"%s\"" ph)))))
(defun mu4e~draft-from-construct () (defun mu4e~draft-from-construct ()
@ -313,7 +313,7 @@ This is based on the variable `user-full-name' and
`user-mail-address'; if the latter is nil, function returns nil." `user-mail-address'; if the latter is nil, function returns nil."
(when user-mail-address (when user-mail-address
(if user-full-name (if user-full-name
(format "%s <%s>" (mu4e~rfc822-quoteit user-full-name) user-mail-address) (format "%s <%s>" (mu4e~rfc822-quoteit user-full-name) user-mail-address)
(format "%s" user-mail-address)))) (format "%s" user-mail-address))))
@ -333,23 +333,23 @@ separator is never written to the message file. Also see
;; make sure there's not one already ;; make sure there's not one already
(mu4e~draft-remove-mail-header-separator) (mu4e~draft-remove-mail-header-separator)
(let ((sepa (propertize mail-header-separator (let ((sepa (propertize mail-header-separator
'intangible t 'intangible t
;; don't make this read-only, message-mode ;; don't make this read-only, message-mode
;; seems to require it being writable in some cases ;; seems to require it being writable in some cases
;;'read-only "Can't touch this" ;;'read-only "Can't touch this"
'rear-nonsticky t 'rear-nonsticky t
'font-lock-face 'mu4e-compose-separator-face))) 'font-lock-face 'mu4e-compose-separator-face)))
(widen) (widen)
;; search for the first empty line ;; search for the first empty line
(goto-char (point-min)) (goto-char (point-min))
(if (search-forward-regexp "^$" nil t) (if (search-forward-regexp "^$" nil t)
(progn (progn
(replace-match sepa) (replace-match sepa)
;; `message-narrow-to-headers` searches for a ;; `message-narrow-to-headers` searches for a
;; `mail-header-separator` followed by a new line. Therefore, we ;; `mail-header-separator` followed by a new line. Therefore, we
;; must insert a newline if on the last line of the buffer. ;; must insert a newline if on the last line of the buffer.
(when (= (point) (point-max)) (when (= (point) (point-max))
(insert "\n"))) (insert "\n")))
(progn ;; no empty line? then prepend one (progn ;; no empty line? then prepend one
(goto-char (point-max)) (goto-char (point-max))
(insert "\n" sepa)))))) (insert "\n" sepa))))))
@ -372,15 +372,15 @@ never hits the disk. Also see
"Ask user whether she wants to reply to *all* recipients. "Ask user whether she wants to reply to *all* recipients.
If there is just one recipient of ORIGMSG do nothing." If there is just one recipient of ORIGMSG do nothing."
(let* ((recipnum (let* ((recipnum
(+ (length (mu4e~draft-create-to-lst origmsg)) (+ (length (mu4e~draft-create-to-lst origmsg))
(length (mu4e~draft-create-cc-lst origmsg t)))) (length (mu4e~draft-create-cc-lst origmsg t))))
(response (response
(if (< recipnum 2) (if (< recipnum 2)
'all ;; with less than 2 recipients, we can reply to 'all' 'all ;; with less than 2 recipients, we can reply to 'all'
(mu4e-read-option (mu4e-read-option
"Reply to " "Reply to "
`( (,(format "all %d recipients" recipnum) . all) `( (,(format "all %d recipients" recipnum) . all)
("sender only" . sender-only)))))) ("sender only" . sender-only))))))
(eq response 'all))) (eq response 'all)))
(defun mu4e~draft-message-filename-construct (&optional flagstr) (defun mu4e~draft-message-filename-construct (&optional flagstr)
@ -389,25 +389,25 @@ It looks something like
<time>-<random>.<hostname>:2, <time>-<random>.<hostname>:2,
You can append flags." You can append flags."
(let* ((sysname (if (fboundp 'system-name) (let* ((sysname (if (fboundp 'system-name)
(system-name) (system-name)
(with-no-warnings system-name))) (with-no-warnings system-name)))
(sysname (if (string= sysname "") "localhost" sysname)) (sysname (if (string= sysname "") "localhost" sysname))
(hostname (downcase (hostname (downcase
(save-match-data (save-match-data
(substring sysname (substring sysname
(string-match "^[^.]+" sysname) (string-match "^[^.]+" sysname)
(match-end 0)))))) (match-end 0))))))
(format "%s.%04x%04x%04x%04x.%s:2,%s" (format "%s.%04x%04x%04x%04x.%s:2,%s"
(format-time-string "%s" (current-time)) (format-time-string "%s" (current-time))
(random 65535) (random 65535) (random 65535) (random 65535) (random 65535) (random 65535) (random 65535) (random 65535)
hostname (or flagstr "")))) hostname (or flagstr ""))))
(defun mu4e~draft-common-construct () (defun mu4e~draft-common-construct ()
"Construct the common headers for each message." "Construct the common headers for each message."
(concat (concat
(when mu4e-user-agent-string (when mu4e-user-agent-string
(mu4e~draft-header "User-agent" mu4e-user-agent-string)) (mu4e~draft-header "User-agent" mu4e-user-agent-string))
(mu4e~draft-header "Date" (message-make-date)))) (mu4e~draft-header "Date" (message-make-date))))
(defconst mu4e~draft-reply-prefix "Re: " (defconst mu4e~draft-reply-prefix "Re: "
"String to prefix replies with.") "String to prefix replies with.")
@ -415,81 +415,81 @@ You can append flags."
(defun mu4e~draft-reply-construct-recipients (origmsg) (defun mu4e~draft-reply-construct-recipients (origmsg)
"Determine the to/cc recipients for a reply message." "Determine the to/cc recipients for a reply message."
(let* ((reply-to-self (mu4e-message-contact-field-matches-me origmsg :from)) (let* ((reply-to-self (mu4e-message-contact-field-matches-me origmsg :from))
;; reply-to-self implies reply-all ;; reply-to-self implies reply-all
(reply-all (or reply-to-self (reply-all (or reply-to-self
(eq mu4e-compose-reply-recipients 'all) (eq mu4e-compose-reply-recipients 'all)
(and (not (eq mu4e-compose-reply-recipients 'sender)) (and (not (eq mu4e-compose-reply-recipients 'sender))
(mu4e~draft-reply-all-p origmsg))))) (mu4e~draft-reply-all-p origmsg)))))
(concat (concat
(if reply-to-self (if reply-to-self
;; When we're replying to ourselves, simply keep the same headers. ;; When we're replying to ourselves, simply keep the same headers.
(concat (concat
(mu4e~draft-header "To" (mu4e~draft-recipients-list-to-string (mu4e~draft-header "To" (mu4e~draft-recipients-list-to-string
(mu4e-message-field origmsg :to))) (mu4e-message-field origmsg :to)))
(mu4e~draft-header "Cc" (mu4e~draft-recipients-list-to-string (mu4e~draft-header "Cc" (mu4e~draft-recipients-list-to-string
(mu4e-message-field origmsg :cc)))) (mu4e-message-field origmsg :cc))))
;; if there's no-one in To, copy the CC-list ;; if there's no-one in To, copy the CC-list
(if (zerop (length (mu4e~draft-create-to-lst origmsg))) (if (zerop (length (mu4e~draft-create-to-lst origmsg)))
(mu4e~draft-header "To" (mu4e~draft-recipients-construct (mu4e~draft-header "To" (mu4e~draft-recipients-construct
:cc origmsg reply-all)) :cc origmsg reply-all))
;; otherwise... ;; otherwise...
(concat (concat
(mu4e~draft-header "To" (mu4e~draft-recipients-construct :to origmsg)) (mu4e~draft-header "To" (mu4e~draft-recipients-construct :to origmsg))
(mu4e~draft-header "Cc" (mu4e~draft-recipients-construct :cc origmsg reply-all)))))))) (mu4e~draft-header "Cc" (mu4e~draft-recipients-construct :cc origmsg reply-all))))))))
(defun mu4e~draft-reply-construct-recipients-list (origmsg) (defun mu4e~draft-reply-construct-recipients-list (origmsg)
"Determine the to/cc recipients for a reply message to a "Determine the to/cc recipients for a reply message to a
mailing-list." mailing-list."
(let* ( ;; reply-to-self implies reply-all (let* ( ;; reply-to-self implies reply-all
(list-post (plist-get origmsg :list-post)) (list-post (plist-get origmsg :list-post))
(from (plist-get origmsg :from)) (from (plist-get origmsg :from))
(recipnum (recipnum
(+ (length (mu4e~draft-create-to-lst origmsg)) (+ (length (mu4e~draft-create-to-lst origmsg))
(length (mu4e~draft-create-cc-lst origmsg t t)))) (length (mu4e~draft-create-cc-lst origmsg t t))))
(reply-type (reply-type
(mu4e-read-option (mu4e-read-option
"Reply to mailing-list " "Reply to mailing-list "
`( (,(format "all %d recipient(s)" recipnum) . all) `( (,(format "all %d recipient(s)" recipnum) . all)
(,(format "list-only (%s)" (cdar list-post)) . list-only) (,(format "list-only (%s)" (cdar list-post)) . list-only)
(,(format "sender-only (%s)" (cdar from)) . sender-only))))) (,(format "sender-only (%s)" (cdar from)) . sender-only)))))
(cl-case reply-type (cl-case reply-type
(all (all
(concat (concat
(mu4e~draft-header "To" (mu4e~draft-recipients-construct :to origmsg)) (mu4e~draft-header "To" (mu4e~draft-recipients-construct :to origmsg))
(mu4e~draft-header "Cc" (mu4e~draft-recipients-construct :cc origmsg t t)))) (mu4e~draft-header "Cc" (mu4e~draft-recipients-construct :cc origmsg t t))))
(list-only (list-only
(mu4e~draft-header "To" (mu4e~draft-header "To"
(mu4e~draft-recipients-list-to-string list-post))) (mu4e~draft-recipients-list-to-string list-post)))
(sender-only (sender-only
(mu4e~draft-header "To" (mu4e~draft-header "To"
(mu4e~draft-recipients-list-to-string from)))))) (mu4e~draft-recipients-list-to-string from))))))
(defun mu4e~draft-reply-construct (origmsg) (defun mu4e~draft-reply-construct (origmsg)
"Create a draft message as a reply to ORIGMSG. "Create a draft message as a reply to ORIGMSG.
Replying-to-self is special; in that case, the To and Cc fields Replying-to-self is special; in that case, the To and Cc fields
will be the same as in the original." will be the same as in the original."
(let* ((old-msgid (plist-get origmsg :message-id)) (let* ((old-msgid (plist-get origmsg :message-id))
(subject (concat mu4e~draft-reply-prefix (subject (concat mu4e~draft-reply-prefix
(message-strip-subject-re (message-strip-subject-re
(or (plist-get origmsg :subject) "")))) (or (plist-get origmsg :subject) ""))))
(list-post (plist-get origmsg :list-post))) (list-post (plist-get origmsg :list-post)))
(concat (concat
(mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) (mu4e~draft-header "From" (or (mu4e~draft-from-construct) ""))
(mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address)
(if list-post ;; mailing-lists are a bit special. (if list-post ;; mailing-lists are a bit special.
(mu4e~draft-reply-construct-recipients-list origmsg) (mu4e~draft-reply-construct-recipients-list origmsg)
(mu4e~draft-reply-construct-recipients origmsg)) (mu4e~draft-reply-construct-recipients origmsg))
(mu4e~draft-header "Subject" subject) (mu4e~draft-header "Subject" subject)
(mu4e~draft-header "References" (mu4e~draft-header "References"
(mu4e~draft-references-construct origmsg)) (mu4e~draft-references-construct origmsg))
(mu4e~draft-common-construct) (mu4e~draft-common-construct)
(when old-msgid (when old-msgid
(mu4e~draft-header "In-reply-to" (format "<%s>" old-msgid))) (mu4e~draft-header "In-reply-to" (format "<%s>" old-msgid)))
"\n\n" "\n\n"
(mu4e~draft-cite-original origmsg)))) (mu4e~draft-cite-original origmsg))))
(defconst mu4e~draft-forward-prefix "Fwd: " (defconst mu4e~draft-forward-prefix "Fwd: "
"String to prefix replies with.") "String to prefix replies with.")
@ -497,34 +497,34 @@ will be the same as in the original."
(defun mu4e~draft-forward-construct (origmsg) (defun mu4e~draft-forward-construct (origmsg)
"Create a draft forward message for original message ORIGMSG." "Create a draft forward message for original message ORIGMSG."
(let ((subject (let ((subject
(or (plist-get origmsg :subject) ""))) (or (plist-get origmsg :subject) "")))
(concat (concat
(mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) (mu4e~draft-header "From" (or (mu4e~draft-from-construct) ""))
(mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address)
(mu4e~draft-header "To" "") (mu4e~draft-header "To" "")
(mu4e~draft-common-construct) (mu4e~draft-common-construct)
(mu4e~draft-header "References" (mu4e~draft-header "References"
(mu4e~draft-references-construct origmsg)) (mu4e~draft-references-construct origmsg))
(mu4e~draft-header "Subject" (mu4e~draft-header "Subject"
(concat (concat
;; if there's no Fwd: yet, prepend it ;; if there's no Fwd: yet, prepend it
(if (string-match "^Fwd:" subject) (if (string-match "^Fwd:" subject)
"" ""
mu4e~draft-forward-prefix) mu4e~draft-forward-prefix)
subject)) subject))
(unless mu4e-compose-forward-as-attachment (unless mu4e-compose-forward-as-attachment
(concat (concat
"\n\n" "\n\n"
(mu4e~draft-cite-original origmsg)))))) (mu4e~draft-cite-original origmsg))))))
(defun mu4e~draft-newmsg-construct () (defun mu4e~draft-newmsg-construct ()
"Create a new message." "Create a new message."
(concat (concat
(mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) (mu4e~draft-header "From" (or (mu4e~draft-from-construct) ""))
(mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address)
(mu4e~draft-header "To" "") (mu4e~draft-header "To" "")
(mu4e~draft-header "Subject" "") (mu4e~draft-header "Subject" "")
(mu4e~draft-common-construct))) (mu4e~draft-common-construct)))
(defvar mu4e~draft-drafts-folder nil (defvar mu4e~draft-drafts-folder nil
"The drafts-folder for this compose buffer. "The drafts-folder for this compose buffer.
@ -533,13 +533,13 @@ This is based on `mu4e-drafts-folder', which is evaluated once.")
(defun mu4e~draft-open-file (path) (defun mu4e~draft-open-file (path)
"Open the the draft file at PATH." "Open the the draft file at PATH."
(if mu4e-compose-in-new-frame (if mu4e-compose-in-new-frame
(find-file-other-frame path) (find-file-other-frame path)
(find-file path))) (find-file path)))
(defun mu4e~draft-determine-path (draft-dir) (defun mu4e~draft-determine-path (draft-dir)
"Determines the path for a new draft file in DRAFT-DIR." "Determines the path for a new draft file in DRAFT-DIR."
(format "%s/%s/cur/%s" (format "%s/%s/cur/%s"
(mu4e-root-maildir) draft-dir (mu4e~draft-message-filename-construct "DS"))) (mu4e-root-maildir) draft-dir (mu4e~draft-message-filename-construct "DS")))
(defun mu4e-draft-open (compose-type &optional msg) (defun mu4e-draft-open (compose-type &optional msg)
@ -558,39 +558,39 @@ will be created from either `mu4e~draft-reply-construct', or
(cl-case compose-type (cl-case compose-type
(edit (edit
;; case-1: re-editing a draft messages. in this case, we do know the ;; case-1: re-editing a draft messages. in this case, we do know the
;; full path, but we cannot really know 'drafts folder'... we make a ;; full path, but we cannot really know 'drafts folder'... we make a
;; guess ;; guess
(setq draft-dir (mu4e~guess-maildir (mu4e-message-field msg :path))) (setq draft-dir (mu4e~guess-maildir (mu4e-message-field msg :path)))
(mu4e~draft-open-file (mu4e-message-field msg :path))) (mu4e~draft-open-file (mu4e-message-field msg :path)))
(resend (resend
;; case-2: copy some exisisting message to a draft message, then edit ;; case-2: copy some exisisting message to a draft message, then edit
;; that. ;; that.
(setq draft-dir (mu4e~guess-maildir (mu4e-message-field msg :path))) (setq draft-dir (mu4e~guess-maildir (mu4e-message-field msg :path)))
(let ((draft-path (mu4e~draft-determine-path draft-dir))) (let ((draft-path (mu4e~draft-determine-path draft-dir)))
(copy-file (mu4e-message-field msg :path) draft-path) (copy-file (mu4e-message-field msg :path) draft-path)
(mu4e~draft-open-file draft-path))) (mu4e~draft-open-file draft-path)))
((reply forward new) ((reply forward new)
;; case-3: creating a new message; in this case, we can determine ;; case-3: creating a new message; in this case, we can determine
;; mu4e-get-drafts-folder ;; mu4e-get-drafts-folder
(setq draft-dir (mu4e-get-drafts-folder msg)) (setq draft-dir (mu4e-get-drafts-folder msg))
(let ((draft-path (mu4e~draft-determine-path draft-dir)) (let ((draft-path (mu4e~draft-determine-path draft-dir))
(initial-contents (initial-contents
(cl-case compose-type (cl-case compose-type
(reply (mu4e~draft-reply-construct msg)) (reply (mu4e~draft-reply-construct msg))
(forward (mu4e~draft-forward-construct msg)) (forward (mu4e~draft-forward-construct msg))
(new (mu4e~draft-newmsg-construct))))) (new (mu4e~draft-newmsg-construct)))))
(mu4e~draft-open-file draft-path) (mu4e~draft-open-file draft-path)
(insert initial-contents) (insert initial-contents)
(newline) (newline)
;; include the message signature (if it's set) ;; include the message signature (if it's set)
(if (and mu4e-compose-signature-auto-include mu4e-compose-signature) (if (and mu4e-compose-signature-auto-include mu4e-compose-signature)
(let ((message-signature mu4e-compose-signature)) (let ((message-signature mu4e-compose-signature))
(save-excursion (save-excursion
(message-insert-signature) (message-insert-signature)
(mu4e~fontify-signature)))))) (mu4e~fontify-signature))))))
(t (mu4e-error "Unsupported compose-type %S" compose-type))) (t (mu4e-error "Unsupported compose-type %S" compose-type)))
;; if we didn't find a draft folder yet, try some default ;; if we didn't find a draft folder yet, try some default
(unless draft-dir (unless draft-dir

File diff suppressed because it is too large Load Diff

View File

@ -52,34 +52,34 @@
(cl-defmethod gnus-icalendar-event:inline-reply-buttons :around (cl-defmethod gnus-icalendar-event:inline-reply-buttons :around
((event gnus-icalendar-event) handle) ((event gnus-icalendar-event) handle)
(if (and (boundp 'mu4e~view-rendering) (if (and (boundp 'mu4e~view-rendering)
(gnus-icalendar-event:rsvp event)) (gnus-icalendar-event:rsvp event))
(let ((method (gnus-icalendar-event:method event))) (let ((method (gnus-icalendar-event:method event)))
(when (or (string= method "REQUEST") (string= method "PUBLISH")) (when (or (string= method "REQUEST") (string= method "PUBLISH"))
`(("Accept" mu4e-icalendar-reply (,handle accepted ,event)) `(("Accept" mu4e-icalendar-reply (,handle accepted ,event))
("Tentative" mu4e-icalendar-reply (,handle tentative ,event)) ("Tentative" mu4e-icalendar-reply (,handle tentative ,event))
("Decline" mu4e-icalendar-reply (,handle declined ,event))))) ("Decline" mu4e-icalendar-reply (,handle declined ,event)))))
(cl-call-next-method event handle)))) (cl-call-next-method event handle))))
(defun mu4e-icalendar-reply (data) (defun mu4e-icalendar-reply (data)
"Reply to the text/calendar event present in DATA." "Reply to the text/calendar event present in DATA."
;; Based on `gnus-icalendar-reply'. ;; Based on `gnus-icalendar-reply'.
(let* ((handle (car data)) (let* ((handle (car data))
(status (cadr data)) (status (cadr data))
(event (caddr data)) (event (caddr data))
(gnus-icalendar-additional-identities (mu4e-personal-addresses)) (gnus-icalendar-additional-identities (mu4e-personal-addresses))
(reply (gnus-icalendar-with-decoded-handle handle (reply (gnus-icalendar-with-decoded-handle handle
(gnus-icalendar-event-reply-from-buffer (gnus-icalendar-event-reply-from-buffer
(current-buffer) status (gnus-icalendar-identities)))) (current-buffer) status (gnus-icalendar-identities))))
(msg (mu4e-message-at-point 'noerror)) (msg (mu4e-message-at-point 'noerror))
(charset (cdr (assoc 'charset (mm-handle-type handle))))) (charset (cdr (assoc 'charset (mm-handle-type handle)))))
(when reply (when reply
(cl-labels (cl-labels
((fold-icalendar-buffer ((fold-icalendar-buffer
() ()
(goto-char (point-min)) (goto-char (point-min))
(while (re-search-forward "^\\(.\\{72\\}\\)\\(.+\\)$" nil t) (while (re-search-forward "^\\(.\\{72\\}\\)\\(.+\\)$" nil t)
(replace-match "\\1\n \\2") (replace-match "\\1\n \\2")
(goto-char (line-beginning-position))))) (goto-char (line-beginning-position)))))
(with-current-buffer (get-buffer-create gnus-icalendar-reply-bufname) (with-current-buffer (get-buffer-create gnus-icalendar-reply-bufname)
(delete-region (point-min) (point-max)) (delete-region (point-min) (point-max))
@ -95,7 +95,7 @@
(gnus-icalendar--update-org-event event status)) (gnus-icalendar--update-org-event event status))
(when mu4e-icalendar-diary-file (when mu4e-icalendar-diary-file
(mu4e~icalendar-insert-diary event status (mu4e~icalendar-insert-diary event status
mu4e-icalendar-diary-file)))))) mu4e-icalendar-diary-file))))))
(defun mu4e~icalendar-delete-citation () (defun mu4e~icalendar-delete-citation ()
"Function passed to `mu4e-compose-cite-function' to remove the citation." "Function passed to `mu4e-compose-cite-function' to remove the citation."
@ -107,17 +107,17 @@
"See `mu4e-sent-handler' for DOCID and PATH." "See `mu4e-sent-handler' for DOCID and PATH."
(mu4e-sent-handler docid path) (mu4e-sent-handler docid path)
(let* ((docid (mu4e-message-field original-msg :docid)) (let* ((docid (mu4e-message-field original-msg :docid))
(markdescr (assq 'trash mu4e-marks)) (markdescr (assq 'trash mu4e-marks))
(action (plist-get (cdr markdescr) :action)) (action (plist-get (cdr markdescr) :action))
(target (mu4e-get-trash-folder original-msg))) (target (mu4e-get-trash-folder original-msg)))
(with-current-buffer (mu4e-get-headers-buffer) (with-current-buffer (mu4e-get-headers-buffer)
(run-hook-with-args 'mu4e-mark-execute-pre-hook 'trash original-msg) (run-hook-with-args 'mu4e-mark-execute-pre-hook 'trash original-msg)
(funcall action docid original-msg target)) (funcall action docid original-msg target))
(when (and (mu4e~headers-view-this-message-p docid) (when (and (mu4e~headers-view-this-message-p docid)
(buffer-live-p (mu4e-get-view-buffer))) (buffer-live-p (mu4e-get-view-buffer)))
(switch-to-buffer (mu4e-get-view-buffer)) (switch-to-buffer (mu4e-get-view-buffer))
(or (mu4e-view-headers-next) (or (mu4e-view-headers-next)
(kill-buffer-and-window)))))) (kill-buffer-and-window))))))
(defun mu4e-icalendar-reply-ical (original-msg event status buffer-name) (defun mu4e-icalendar-reply-ical (original-msg event status buffer-name)
"Reply to ORIGINAL-MSG containing invitation EVENT with STATUS. "Reply to ORIGINAL-MSG containing invitation EVENT with STATUS.
@ -126,8 +126,8 @@ STATUS values. BUFFER-NAME is the name of the buffer holding the
response in icalendar format." response in icalendar format."
(let ((message-signature nil)) (let ((message-signature nil))
(let ((mu4e-compose-cite-function #'mu4e~icalendar-delete-citation) (let ((mu4e-compose-cite-function #'mu4e~icalendar-delete-citation)
(mu4e-sent-messages-behavior 'delete) (mu4e-sent-messages-behavior 'delete)
(mu4e-compose-reply-recipients 'sender)) (mu4e-compose-reply-recipients 'sender))
(mu4e~compose-handler 'reply original-msg)) (mu4e~compose-handler 'reply original-msg))
;; Make sure the recipient is the organizer ;; Make sure the recipient is the organizer
(let ((organizer (gnus-icalendar-event:organizer event))) (let ((organizer (gnus-icalendar-event:organizer event)))
@ -141,23 +141,23 @@ response in icalendar format."
(mml-insert-multipart "alternative") (mml-insert-multipart "alternative")
(mml-insert-part "text/plain") (mml-insert-part "text/plain")
(let ((reply-event (gnus-icalendar-event-from-buffer (let ((reply-event (gnus-icalendar-event-from-buffer
buffer-name (mu4e-personal-addresses)))) buffer-name (mu4e-personal-addresses))))
(insert (gnus-icalendar-event->gnus-calendar reply-event status))) (insert (gnus-icalendar-event->gnus-calendar reply-event status)))
(forward-line 1); move past closing tag (forward-line 1); move past closing tag
(mml-attach-buffer buffer-name "text/calendar; method=REPLY; charset=utf-8") (mml-attach-buffer buffer-name "text/calendar; method=REPLY; charset=utf-8")
(message-remove-header "Subject") (message-remove-header "Subject")
(message-goto-subject) (message-goto-subject)
(insert (capitalize (symbol-name status)) (insert (capitalize (symbol-name status))
": " (gnus-icalendar-event:summary event)) ": " (gnus-icalendar-event:summary event))
(set-buffer-modified-p nil); not yet modified by user (set-buffer-modified-p nil); not yet modified by user
(when mu4e-icalendar-trash-after-reply (when mu4e-icalendar-trash-after-reply
;; Override `mu4e-sent-handler' set by `mu4e-compose-mode' to ;; Override `mu4e-sent-handler' set by `mu4e-compose-mode' to
;; also trash the message (thus must be appended to hooks). ;; also trash the message (thus must be appended to hooks).
(add-hook (add-hook
'message-sent-hook 'message-sent-hook
(lambda () (setq mu4e-sent-func (lambda () (setq mu4e-sent-func
(mu4e~icalendar-trash-message original-msg))) (mu4e~icalendar-trash-message original-msg)))
t t)))) t t))))
(defun mu4e~icalendar-insert-diary (event reply-status filename) (defun mu4e~icalendar-insert-diary (event reply-status filename)
@ -166,20 +166,20 @@ REPLY-STATUS is the status of the reply. The possible values are
given in the doc of `gnus-icalendar-event-reply-from-buffer'." given in the doc of `gnus-icalendar-event-reply-from-buffer'."
;; FIXME: handle recurring events ;; FIXME: handle recurring events
(let* ((beg (gnus-icalendar-event:start-time event)) (let* ((beg (gnus-icalendar-event:start-time event))
(beg-date (format-time-string "%d/%m/%Y" beg)) (beg-date (format-time-string "%d/%m/%Y" beg))
(beg-time (format-time-string "%H:%M" beg)) (beg-time (format-time-string "%H:%M" beg))
(end (gnus-icalendar-event:end-time event)) (end (gnus-icalendar-event:end-time event))
(end-date (format-time-string "%d/%m/%Y" end)) (end-date (format-time-string "%d/%m/%Y" end))
(end-time (format-time-string "%H:%M" end)) (end-time (format-time-string "%H:%M" end))
(summary (gnus-icalendar-event:summary event)) (summary (gnus-icalendar-event:summary event))
(location (gnus-icalendar-event:location event)) (location (gnus-icalendar-event:location event))
(status (capitalize (symbol-name reply-status))) (status (capitalize (symbol-name reply-status)))
(txt (if location (txt (if location
(format "%s (%s)\n %s " summary status location) (format "%s (%s)\n %s " summary status location)
(format "%s (%s)" summary status)))) (format "%s (%s)" summary status))))
(with-temp-buffer (with-temp-buffer
(if (string= beg-date end-date) (if (string= beg-date end-date)
(insert beg-date " " beg-time "-" end-time " " txt "\n") (insert beg-date " " beg-time "-" end-time " " txt "\n")
(insert beg-date " " beg-time " Start of: " txt "\n") (insert beg-date " " beg-time " Start of: " txt "\n")
(insert beg-date " " end-time " End of: " txt "\n")) (insert beg-date " " end-time " End of: " txt "\n"))
(write-region (point-min) (point-max) filename t)))) (write-region (point-min) (point-max) filename t))))

View File

@ -71,9 +71,8 @@
"Major mode for the mu4e main screen. "Major mode for the mu4e main screen.
\\{mu4e-main-mode-map}." \\{mu4e-main-mode-map}."
(use-local-map mu4e-main-mode-map) (use-local-map mu4e-main-mode-map)
(setq (setq truncate-lines t)
truncate-lines t (setq overwrite-mode 'overwrite-mode-binary)
overwrite-mode 'overwrite-mode-binary)
;; show context in mode-string ;; show context in mode-string
(make-local-variable 'global-mode-string) (make-local-variable 'global-mode-string)
@ -88,23 +87,23 @@ when STR is clicked (using RET or mouse-2); if FUNC-OR-SHORTCUT is
a string, execute the corresponding keyboard action when it is a string, execute the corresponding keyboard action when it is
clicked." clicked."
(let ((newstr (let ((newstr
(replace-regexp-in-string (replace-regexp-in-string
"\\[\\(..?\\)\\]" "\\[\\(..?\\)\\]"
(lambda(m) (lambda(m)
(format "[%s]" (format "[%s]"
(propertize (match-string 1 m) 'face 'mu4e-highlight-face))) (propertize (match-string 1 m) 'face 'mu4e-highlight-face)))
str)) str))
(map (make-sparse-keymap)) (map (make-sparse-keymap))
(func (if (functionp func-or-shortcut) (func (if (functionp func-or-shortcut)
func-or-shortcut func-or-shortcut
(if (stringp func-or-shortcut) (if (stringp func-or-shortcut)
(lambda()(interactive) (lambda()(interactive)
(execute-kbd-macro func-or-shortcut)))))) (execute-kbd-macro func-or-shortcut))))))
(define-key map [mouse-2] func) (define-key map [mouse-2] func)
(define-key map (kbd "RET") func) (define-key map (kbd "RET") func)
(put-text-property 0 (length newstr) 'keymap map newstr) (put-text-property 0 (length newstr) 'keymap map newstr)
(put-text-property (string-match "\\[.+$" newstr) (put-text-property (string-match "\\[.+$" newstr)
(- (length newstr) 1) 'mouse-face 'highlight newstr) (- (length newstr) 1) 'mouse-face 'highlight newstr)
newstr)) newstr))
@ -125,8 +124,8 @@ clicked."
concat (concat concat (concat
;; menu entry ;; menu entry
(mu4e~main-action-str (mu4e~main-action-str
(concat "\t* [b" key "] " name) (concat "\t* [b" key "] " name)
(concat "b" key)) (concat "b" key))
;; append all/unread numbers, if available. ;; append all/unread numbers, if available.
(if qcounts (if qcounts
(let ((unread (plist-get (car qcounts) :unread)) (let ((unread (plist-get (car qcounts) :unread))
@ -144,99 +143,99 @@ clicked."
(defun mu4e~key-val (key val &optional unit) (defun mu4e~key-val (key val &optional unit)
"Return a key / value pair." "Return a key / value pair."
(concat (concat
" * " " * "
(propertize (format "%-20s" key) 'face 'mu4e-header-title-face) (propertize (format "%-20s" key) 'face 'mu4e-header-title-face)
": " ": "
(propertize val 'face 'mu4e-header-key-face) (propertize val 'face 'mu4e-header-key-face)
(if unit (if unit
(propertize (concat " " unit) 'face 'mu4e-header-title-face) (propertize (concat " " unit) 'face 'mu4e-header-title-face)
"") "")
"\n")) "\n"))
;; NEW ;; NEW
;; This is the old `mu4e~main-view' function but without ;; This is the old `mu4e~main-view' function but without
;; buffer switching at the end. ;; buffer switching at the end.
(defun mu4e~main-view-real (_ignore-auto _noconfirm) (defun mu4e~main-view-real (_ignore-auto _noconfirm)
(let ((buf (get-buffer-create mu4e~main-buffer-name)) (let ((buf (get-buffer-create mu4e~main-buffer-name))
(inhibit-read-only t)) (inhibit-read-only t))
(with-current-buffer buf (with-current-buffer buf
(erase-buffer) (erase-buffer)
(insert (insert
"* " "* "
(propertize "mu4e" 'face 'mu4e-header-key-face) (propertize "mu4e" 'face 'mu4e-header-key-face)
(propertize " - mu for emacs version " 'face 'mu4e-title-face) (propertize " - mu for emacs version " 'face 'mu4e-title-face)
(propertize mu4e-mu-version 'face 'mu4e-header-key-face) (propertize mu4e-mu-version 'face 'mu4e-header-key-face)
"\n\n" "\n\n"
(propertize " Basics\n\n" 'face 'mu4e-title-face) (propertize " Basics\n\n" 'face 'mu4e-title-face)
(mu4e~main-action-str (mu4e~main-action-str
"\t* [j]ump to some maildir\n" 'mu4e-jump-to-maildir) "\t* [j]ump to some maildir\n" 'mu4e-jump-to-maildir)
(mu4e~main-action-str (mu4e~main-action-str
"\t* enter a [s]earch query\n" 'mu4e-search) "\t* enter a [s]earch query\n" 'mu4e-search)
(mu4e~main-action-str (mu4e~main-action-str
"\t* [C]ompose a new message\n" 'mu4e-compose-new) "\t* [C]ompose a new message\n" 'mu4e-compose-new)
"\n" "\n"
(propertize " Bookmarks\n\n" 'face 'mu4e-title-face) (propertize " Bookmarks\n\n" 'face 'mu4e-title-face)
(mu4e~main-bookmarks) (mu4e~main-bookmarks)
"\n" "\n"
(propertize " Misc\n\n" 'face 'mu4e-title-face) (propertize " Misc\n\n" 'face 'mu4e-title-face)
(mu4e~main-action-str "\t* [;]Switch context\n" 'mu4e-context-switch) (mu4e~main-action-str "\t* [;]Switch context\n" 'mu4e-context-switch)
(mu4e~main-action-str "\t* [U]pdate email & database\n" (mu4e~main-action-str "\t* [U]pdate email & database\n"
'mu4e-update-mail-and-index) 'mu4e-update-mail-and-index)
;; show the queue functions if `smtpmail-queue-dir' is defined ;; show the queue functions if `smtpmail-queue-dir' is defined
(if (file-directory-p smtpmail-queue-dir) (if (file-directory-p smtpmail-queue-dir)
(mu4e~main-view-queue) (mu4e~main-view-queue)
"") "")
"\n" "\n"
(mu4e~main-action-str "\t* [N]ews\n" 'mu4e-news) (mu4e~main-action-str "\t* [N]ews\n" 'mu4e-news)
(mu4e~main-action-str "\t* [A]bout mu4e\n" 'mu4e-about) (mu4e~main-action-str "\t* [A]bout mu4e\n" 'mu4e-about)
(mu4e~main-action-str "\t* [H]elp\n" 'mu4e-display-manual) (mu4e~main-action-str "\t* [H]elp\n" 'mu4e-display-manual)
(mu4e~main-action-str "\t* [q]uit\n" 'mu4e-quit) (mu4e~main-action-str "\t* [q]uit\n" 'mu4e-quit)
"\n" "\n"
(propertize " Info\n\n" 'face 'mu4e-title-face) (propertize " Info\n\n" 'face 'mu4e-title-face)
(mu4e~key-val "database-path" (mu4e-database-path)) (mu4e~key-val "database-path" (mu4e-database-path))
(mu4e~key-val "maildir" (mu4e-root-maildir)) (mu4e~key-val "maildir" (mu4e-root-maildir))
(mu4e~key-val "in store" (mu4e~key-val "in store"
(format "%d" (plist-get mu4e~server-props :doccount)) "messages")) (format "%d" (plist-get mu4e~server-props :doccount)) "messages"))
(mu4e-main-mode)))) (mu4e-main-mode))))
(defun mu4e~main-view-queue () (defun mu4e~main-view-queue ()
"Display queue-related actions in the main view." "Display queue-related actions in the main view."
(concat (concat
(mu4e~main-action-str "\t* toggle [m]ail sending mode " (mu4e~main-action-str "\t* toggle [m]ail sending mode "
'mu4e~main-toggle-mail-sending-mode) 'mu4e~main-toggle-mail-sending-mode)
"(currently " "(currently "
(propertize (if smtpmail-queue-mail "queued" "direct") (propertize (if smtpmail-queue-mail "queued" "direct")
'face 'mu4e-header-key-face) 'face 'mu4e-header-key-face)
")\n" ")\n"
(let ((queue-size (mu4e~main-queue-size))) (let ((queue-size (mu4e~main-queue-size)))
(if (zerop queue-size) (if (zerop queue-size)
"" ""
(mu4e~main-action-str (mu4e~main-action-str
(format "\t* [f]lush %s queued %s\n" (format "\t* [f]lush %s queued %s\n"
(propertize (int-to-string queue-size) (propertize (int-to-string queue-size)
'face 'mu4e-header-key-face) 'face 'mu4e-header-key-face)
(if (> queue-size 1) "mails" "mail")) (if (> queue-size 1) "mails" "mail"))
'smtpmail-send-queued-mail))))) 'smtpmail-send-queued-mail)))))
(defun mu4e~main-queue-size () (defun mu4e~main-queue-size ()
"Return, as an int, the number of emails in the queue." "Return, as an int, the number of emails in the queue."
(condition-case nil (condition-case nil
(with-temp-buffer (with-temp-buffer
(insert-file-contents (expand-file-name smtpmail-queue-index-file (insert-file-contents (expand-file-name smtpmail-queue-index-file
smtpmail-queue-dir)) smtpmail-queue-dir))
(count-lines (point-min) (point-max))) (count-lines (point-min) (point-max)))
(error 0))) (error 0)))
(defun mu4e~main-view () (defun mu4e~main-view ()
"Create the mu4e main-view, and switch to it." "Create the mu4e main-view, and switch to it."
(if (eq mu4e-split-view 'single-window) (if (eq mu4e-split-view 'single-window)
(if (buffer-live-p (mu4e-get-headers-buffer)) (if (buffer-live-p (mu4e-get-headers-buffer))
(switch-to-buffer (mu4e-get-headers-buffer)) (switch-to-buffer (mu4e-get-headers-buffer))
(mu4e~main-menu)) (mu4e~main-menu))
(mu4e~main-view-real nil nil) (mu4e~main-view-real nil nil)
(switch-to-buffer mu4e~main-buffer-name) (switch-to-buffer mu4e~main-buffer-name)
(goto-char (point-min))) (goto-char (point-min)))
@ -253,7 +252,7 @@ clicked."
(mu4e-error "`smtpmail-queue-dir' does not exist")) (mu4e-error "`smtpmail-queue-dir' does not exist"))
(setq smtpmail-queue-mail (not smtpmail-queue-mail)) (setq smtpmail-queue-mail (not smtpmail-queue-mail))
(message (concat "Outgoing mail will now be " (message (concat "Outgoing mail will now be "
(if smtpmail-queue-mail "queued" "sent directly"))) (if smtpmail-queue-mail "queued" "sent directly")))
(unless (eq mu4e-split-view 'single-window) (unless (eq mu4e-split-view 'single-window)
(let ((curpos (point))) (let ((curpos (point)))
(mu4e~main-view-real nil nil) (mu4e~main-view-real nil nil)
@ -263,29 +262,29 @@ clicked."
"mu4e main view in the minibuffer." "mu4e main view in the minibuffer."
(interactive) (interactive)
(let ((key (let ((key
(read-key (read-key
(mu4e-format (mu4e-format
"%s" "%s"
(concat (concat
(mu4e~main-action-str "[j]ump " 'mu4e-jump-to-maildir) (mu4e~main-action-str "[j]ump " 'mu4e-jump-to-maildir)
(mu4e~main-action-str "[s]earch " 'mu4e-search) (mu4e~main-action-str "[s]earch " 'mu4e-search)
(mu4e~main-action-str "[C]ompose " 'mu4e-compose-new) (mu4e~main-action-str "[C]ompose " 'mu4e-compose-new)
(mu4e~main-action-str "[b]ookmarks " 'mu4e-headers-search-bookmark) (mu4e~main-action-str "[b]ookmarks " 'mu4e-headers-search-bookmark)
(mu4e~main-action-str "[;]Switch context " 'mu4e-context-switch) (mu4e~main-action-str "[;]Switch context " 'mu4e-context-switch)
(mu4e~main-action-str "[U]pdate " 'mu4e-update-mail-and-index) (mu4e~main-action-str "[U]pdate " 'mu4e-update-mail-and-index)
(mu4e~main-action-str "[N]ews " 'mu4e-news) (mu4e~main-action-str "[N]ews " 'mu4e-news)
(mu4e~main-action-str "[A]bout " 'mu4e-about) (mu4e~main-action-str "[A]bout " 'mu4e-about)
(mu4e~main-action-str "[H]elp " 'mu4e-display-manual)))))) (mu4e~main-action-str "[H]elp " 'mu4e-display-manual))))))
(unless (member key '(?\C-g ?\C-\[)) (unless (member key '(?\C-g ?\C-\[))
(let ((mu4e-command (lookup-key mu4e-main-mode-map (string key) t))) (let ((mu4e-command (lookup-key mu4e-main-mode-map (string key) t)))
(if mu4e-command (if mu4e-command
(condition-case err (condition-case err
(let ((mu4e-hide-index-messages t)) (let ((mu4e-hide-index-messages t))
(call-interactively mu4e-command)) (call-interactively mu4e-command))
(error (when (cadr err) (message (cadr err))))) (error (when (cadr err) (message (cadr err)))))
(message (mu4e-format "key %s not bound to a command" (string key)))) (message (mu4e-format "key %s not bound to a command" (string key))))
(when (or (not mu4e-command) (eq mu4e-command 'mu4e-context-switch)) (when (or (not mu4e-command) (eq mu4e-command 'mu4e-context-switch))
(sit-for 1) (sit-for 1)
(mu4e~main-menu)))))) (mu4e~main-menu))))))
(provide 'mu4e-main) (provide 'mu4e-main)

View File

@ -44,8 +44,8 @@ Value is one of the following symbols:
- `apply' automatically apply the marks before doing anything else - `apply' automatically apply the marks before doing anything else
- `ignore' automatically ignore the marks without asking" - `ignore' automatically ignore the marks without asking"
:type '(choice (const ask :tag "ask user whether to ignore marks") :type '(choice (const ask :tag "ask user whether to ignore marks")
(const apply :tag "apply marks without asking") (const apply :tag "apply marks without asking")
(const ignore :tag "ignore marks without asking")) (const ignore :tag "ignore marks without asking"))
:group 'mu4e-headers) :group 'mu4e-headers)
(defcustom mu4e-mark-execute-pre-hook nil (defcustom mu4e-mark-execute-pre-hook nil
@ -96,98 +96,98 @@ TARGET (optional) is the target directory (for 'move')")
(defun mu4e~mark-find-headers-buffer () (defun mu4e~mark-find-headers-buffer ()
"Find the headers buffer, if any." "Find the headers buffer, if any."
(cl-find-if (cl-find-if
(lambda (b) (lambda (b)
(with-current-buffer b (with-current-buffer b
(eq major-mode 'mu4e-headers-mode))) (eq major-mode 'mu4e-headers-mode)))
(buffer-list))) (buffer-list)))
(defmacro mu4e~mark-in-context (&rest body) (defmacro mu4e~mark-in-context (&rest body)
"Evaluate BODY in the context of the headers buffer. "Evaluate BODY in the context of the headers buffer.
The current buffer must be either a headers or view buffer." The current buffer must be either a headers or view buffer."
`(cond `(cond
((eq major-mode 'mu4e-headers-mode) ,@body) ((eq major-mode 'mu4e-headers-mode) ,@body)
((eq major-mode 'mu4e-view-mode) ((eq major-mode 'mu4e-view-mode)
(when (buffer-live-p (mu4e-get-headers-buffer)) (when (buffer-live-p (mu4e-get-headers-buffer))
(let* ((msg (mu4e-message-at-point)) (let* ((msg (mu4e-message-at-point))
(docid (mu4e-message-field msg :docid))) (docid (mu4e-message-field msg :docid)))
(with-current-buffer (mu4e-get-headers-buffer) (with-current-buffer (mu4e-get-headers-buffer)
(if (mu4e~headers-goto-docid docid) (if (mu4e~headers-goto-docid docid)
,@body ,@body
(mu4e-error "Cannot find message in headers buffer")))))) (mu4e-error "Cannot find message in headers buffer"))))))
(t (t
;; even in other modes (e.g. mu4e-main-mode we try to find ;; even in other modes (e.g. mu4e-main-mode we try to find
;; the headers buffer ;; the headers buffer
(let ((hbuf (mu4e~mark-find-headers-buffer))) (let ((hbuf (mu4e~mark-find-headers-buffer)))
(if (buffer-live-p hbuf) (if (buffer-live-p hbuf)
(with-current-buffer hbuf ,@body) (with-current-buffer hbuf ,@body)
,@body))))) ,@body)))))
(defconst mu4e-marks (defconst mu4e-marks
'((refile '((refile
:char ("r" . "") :char ("r" . "")
:prompt "refile" :prompt "refile"
:dyn-target (lambda (target msg) (mu4e-get-refile-folder msg)) :dyn-target (lambda (target msg) (mu4e-get-refile-folder msg))
:action (lambda (docid msg target) :action (lambda (docid msg target)
(mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")))
(delete (delete
:char ("D" . "x") :char ("D" . "x")
:prompt "Delete" :prompt "Delete"
:show-target (lambda (target) "delete") :show-target (lambda (target) "delete")
:action (lambda (docid msg target) (mu4e~proc-remove docid))) :action (lambda (docid msg target) (mu4e~proc-remove docid)))
(flag (flag
:char ("+" . "") :char ("+" . "")
:prompt "+flag" :prompt "+flag"
:show-target (lambda (target) "flag") :show-target (lambda (target) "flag")
:action (lambda (docid msg target) :action (lambda (docid msg target)
(mu4e~proc-move docid nil "+F-u-N"))) (mu4e~proc-move docid nil "+F-u-N")))
(move (move
:char ("m" . "") :char ("m" . "")
:prompt "move" :prompt "move"
:ask-target mu4e~mark-get-move-target :ask-target mu4e~mark-get-move-target
:action (lambda (docid msg target) :action (lambda (docid msg target)
(mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")))
(read (read
:char ("!" . "") :char ("!" . "")
:prompt "!read" :prompt "!read"
:show-target (lambda (target) "read") :show-target (lambda (target) "read")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N")))
(trash (trash
:char ("d" . "") :char ("d" . "")
:prompt "dtrash" :prompt "dtrash"
:dyn-target (lambda (target msg) (mu4e-get-trash-folder msg)) :dyn-target (lambda (target msg) (mu4e-get-trash-folder msg))
:action (lambda (docid msg target) (mu4e~proc-move docid :action (lambda (docid msg target) (mu4e~proc-move docid
(mu4e~mark-check-target target) "+T-N"))) (mu4e~mark-check-target target) "+T-N")))
(unflag (unflag
:char ("-" . "") :char ("-" . "")
:prompt "-unflag" :prompt "-unflag"
:show-target (lambda (target) "unflag") :show-target (lambda (target) "unflag")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N")))
(untrash (untrash
:char ("=" . "") :char ("=" . "")
:prompt "=untrash" :prompt "=untrash"
:show-target (lambda (target) "untrash") :show-target (lambda (target) "untrash")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-T"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "-T")))
(unread (unread
:char ("?" . "") :char ("?" . "")
:prompt "?unread" :prompt "?unread"
:show-target (lambda (target) "unread") :show-target (lambda (target) "unread")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N")))
(unmark (unmark
:char " " :char " "
:prompt "unmark" :prompt "unmark"
:action (mu4e-error "No action for unmarking")) :action (mu4e-error "No action for unmarking"))
(action (action
:char ( "a" . "") :char ( "a" . "")
:prompt "action" :prompt "action"
:ask-target (lambda () (mu4e-read-option "Action: " mu4e-headers-actions)) :ask-target (lambda () (mu4e-read-option "Action: " mu4e-headers-actions))
:action (lambda (docid msg actionfunc) :action (lambda (docid msg actionfunc)
(save-excursion (save-excursion
(when (mu4e~headers-goto-docid docid) (when (mu4e~headers-goto-docid docid)
(mu4e-headers-action actionfunc))))) (mu4e-headers-action actionfunc)))))
(something (something
:char ("*" . "") :char ("*" . "")
:prompt "*something" :prompt "*something"
:action (mu4e-error "No action for deferred mark"))) :action (mu4e-error "No action for deferred mark")))
"The list of all the possible marks. "The list of all the possible marks.
This is an alist mapping mark symbols to their properties. The This is an alist mapping mark symbols to their properties. The
@ -223,76 +223,76 @@ The following marks are available, and the corresponding props:
MARK TARGET description MARK TARGET description
---------------------------------------------------------- ----------------------------------------------------------
`refile' y mark this message for archiving `refile' y mark this message for archiving
`something' n mark this message for *something* (decided later) `something' n mark this message for *something* (decided later)
`delete' n remove the message `delete' n remove the message
`flag' n mark this message for flagging `flag' n mark this message for flagging
`move' y move the message to some folder `move' y move the message to some folder
`read' n mark the message as read `read' n mark the message as read
`trash' y trash the message to some folder `trash' y trash the message to some folder
`unflag' n mark this message for unflagging `unflag' n mark this message for unflagging
`untrash' n remove the 'trashed' flag from a message `untrash' n remove the 'trashed' flag from a message
`unmark' n unmark this message `unmark' n unmark this message
`unread' n mark the message as unread `unread' n mark the message as unread
`action' y mark the message for some action." `action' y mark the message for some action."
(interactive) (interactive)
(let* ((msg (mu4e-message-at-point)) (let* ((msg (mu4e-message-at-point))
(docid (mu4e-message-field msg :docid)) (docid (mu4e-message-field msg :docid))
;; get a cell with the mark char and the 'target' 'move' already has a ;; get a cell with the mark char and the 'target' 'move' already has a
;; target (the target folder) the other ones get a pseudo "target", as ;; target (the target folder) the other ones get a pseudo "target", as
;; info for the user. ;; info for the user.
(markdesc (cdr (or (assq mark mu4e-marks) (markdesc (cdr (or (assq mark mu4e-marks)
(mu4e-error "Invalid mark %S" mark)))) (mu4e-error "Invalid mark %S" mark))))
(get-markkar (get-markkar
(lambda (char) (lambda (char)
(if (listp char) (if (listp char)
(if mu4e-use-fancy-chars (cdr char) (car char)) (if mu4e-use-fancy-chars (cdr char) (car char))
char))) char)))
(markkar (funcall get-markkar (plist-get markdesc :char))) (markkar (funcall get-markkar (plist-get markdesc :char)))
(target (mu4e~mark-get-dyn-target mark target)) (target (mu4e~mark-get-dyn-target mark target))
(show-fct (plist-get markdesc :show-target)) (show-fct (plist-get markdesc :show-target))
(shown-target (if show-fct (shown-target (if show-fct
(funcall show-fct target) (funcall show-fct target)
(if target (format "%S" target))))) (if target (format "%S" target)))))
(unless docid (mu4e-warn "No message on this line")) (unless docid (mu4e-warn "No message on this line"))
(unless (eq major-mode 'mu4e-headers-mode) (unless (eq major-mode 'mu4e-headers-mode)
(mu4e-error "Not in headers-mode")) (mu4e-error "Not in headers-mode"))
(save-excursion (save-excursion
(when (mu4e~headers-mark docid markkar) (when (mu4e~headers-mark docid markkar)
;; update the hash -- remove everything current, and if add the new ;; update the hash -- remove everything current, and if add the new
;; stuff, unless we're unmarking ;; stuff, unless we're unmarking
(remhash docid mu4e~mark-map) (remhash docid mu4e~mark-map)
;; remove possible overlays ;; remove possible overlays
(remove-overlays (line-beginning-position) (line-end-position)) (remove-overlays (line-beginning-position) (line-end-position))
;; now, let's set a mark (unless we were unmarking) ;; now, let's set a mark (unless we were unmarking)
(unless (eql mark 'unmark) (unless (eql mark 'unmark)
(puthash docid (cons mark target) mu4e~mark-map) (puthash docid (cons mark target) mu4e~mark-map)
;; when we have a target (ie., when moving), show the target folder in ;; when we have a target (ie., when moving), show the target folder in
;; an overlay ;; an overlay
(when (and shown-target mu4e-headers-show-target) (when (and shown-target mu4e-headers-show-target)
(let* ((targetstr (propertize (concat "-> " shown-target " ") (let* ((targetstr (propertize (concat "-> " shown-target " ")
'face 'mu4e-system-face)) 'face 'mu4e-system-face))
;; mu4e~headers-goto-docid docid t \will take us just after ;; mu4e~headers-goto-docid docid t \will take us just after
;; the docid cookie and then we skip the mu4e~mark-fringe ;; the docid cookie and then we skip the mu4e~mark-fringe
(start (+ (length mu4e~mark-fringe) (start (+ (length mu4e~mark-fringe)
(mu4e~headers-goto-docid docid t))) (mu4e~headers-goto-docid docid t)))
(overlay (make-overlay start (+ start (length targetstr))))) (overlay (make-overlay start (+ start (length targetstr)))))
(overlay-put overlay 'display targetstr) (overlay-put overlay 'display targetstr)
docid))))))) docid)))))))
(defun mu4e~mark-get-move-target () (defun mu4e~mark-get-move-target ()
"Ask for a move target, and propose to create it if it does not exist." "Ask for a move target, and propose to create it if it does not exist."
(interactive) (interactive)
;; (mu4e-message-at-point) ;; raises error if there is none ;; (mu4e-message-at-point) ;; raises error if there is none
(let* ((target (mu4e-ask-maildir "Move message to: ")) (let* ((target (mu4e-ask-maildir "Move message to: "))
(target (if (string= (substring target 0 1) "/") (target (if (string= (substring target 0 1) "/")
target target
(concat "/" target))) (concat "/" target)))
(fulltarget (concat (mu4e-root-maildir) target))) (fulltarget (concat (mu4e-root-maildir) target)))
(when (or (file-directory-p fulltarget) (when (or (file-directory-p fulltarget)
(and (yes-or-no-p (and (yes-or-no-p
(format "%s does not exist. Create now?" fulltarget)) (format "%s does not exist. Create now?" fulltarget))
(mu4e~proc-mkdir fulltarget))) (mu4e~proc-mkdir fulltarget)))
target))) target)))
(defun mu4e~mark-ask-target (mark) (defun mu4e~mark-ask-target (mark)
@ -305,7 +305,7 @@ The following marks are available, and the corresponding props:
The result may depend on the message at point." The result may depend on the message at point."
(let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target))) (let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target)))
(if getter (if getter
(funcall getter target (mu4e-message-at-point)) (funcall getter target (mu4e-message-at-point))
target))) target)))
(defun mu4e-mark-set (mark &optional target) (defun mu4e-mark-set (mark &optional target)
@ -314,37 +314,37 @@ Optionally, provide TARGET (for moves)."
(unless target (unless target
(setq target (mu4e~mark-ask-target mark))) (setq target (mu4e~mark-ask-target mark)))
(if (not (use-region-p)) (if (not (use-region-p))
;; single message ;; single message
(mu4e-mark-at-point mark target) (mu4e-mark-at-point mark target)
;; mark all messages in the region. ;; mark all messages in the region.
(save-excursion (save-excursion
(let ((cant-go-further) (eor (region-end))) (let ((cant-go-further) (eor (region-end)))
(goto-char (region-beginning)) (goto-char (region-beginning))
(while (and (< (point) eor) (not cant-go-further)) (while (and (< (point) eor) (not cant-go-further))
(mu4e-mark-at-point mark target) (mu4e-mark-at-point mark target)
(setq cant-go-further (not (mu4e-headers-next)))))))) (setq cant-go-further (not (mu4e-headers-next))))))))
(defun mu4e-mark-restore (docid) (defun mu4e-mark-restore (docid)
"Restore the visual mark for the message with DOCID." "Restore the visual mark for the message with DOCID."
(let ((markcell (gethash docid mu4e~mark-map))) (let ((markcell (gethash docid mu4e~mark-map)))
(when markcell (when markcell
(save-excursion (save-excursion
(when (mu4e~headers-goto-docid docid) (when (mu4e~headers-goto-docid docid)
(mu4e-mark-at-point (car markcell) (cdr markcell))))))) (mu4e-mark-at-point (car markcell) (cdr markcell)))))))
(defun mu4e~mark-get-markpair (prompt &optional allow-something) (defun mu4e~mark-get-markpair (prompt &optional allow-something)
"Ask user with PROMPT for a mark and return (MARK . TARGET). "Ask user with PROMPT for a mark and return (MARK . TARGET).
If ALLOW-SOMETHING is non-nil, allow the 'something' pseudo mark If ALLOW-SOMETHING is non-nil, allow the 'something' pseudo mark
as well." as well."
(let* ((marks (mapcar (lambda (markdescr) (let* ((marks (mapcar (lambda (markdescr)
(cons (plist-get (cdr markdescr) :prompt) (cons (plist-get (cdr markdescr) :prompt)
(car markdescr))) (car markdescr)))
mu4e-marks)) mu4e-marks))
(marks (marks
(if allow-something (if allow-something
marks (cl-remove-if (lambda (m) (eq 'something (cdr m))) marks))) marks (cl-remove-if (lambda (m) (eq 'something (cdr m))) marks)))
(mark (mu4e-read-option prompt marks)) (mark (mu4e-read-option prompt marks))
(target (mu4e~mark-ask-target mark))) (target (mu4e~mark-ask-target mark)))
(cons mark target))) (cons mark target)))
(defun mu4e-mark-resolve-deferred-marks () (defun mu4e-mark-resolve-deferred-marks ()
@ -353,24 +353,24 @@ If there are such marks, replace them with a _real_ mark (ask the
user which one)." user which one)."
(interactive) (interactive)
(mu4e~mark-in-context (mu4e~mark-in-context
(let ((markpair)) (let ((markpair))
(maphash (maphash
(lambda (docid val) (lambda (docid val)
(let ((mark (car val))) (let ((mark (car val)))
(when (eql mark 'something) (when (eql mark 'something)
(unless markpair (unless markpair
(setq markpair (setq markpair
(mu4e~mark-get-markpair "Set deferred mark(s) to: " nil))) (mu4e~mark-get-markpair "Set deferred mark(s) to: " nil)))
(save-excursion (save-excursion
(when (mu4e~headers-goto-docid docid) (when (mu4e~headers-goto-docid docid)
(mu4e-mark-set (car markpair) (cdr markpair))))))) (mu4e-mark-set (car markpair) (cdr markpair)))))))
mu4e~mark-map)))) mu4e~mark-map))))
(defun mu4e~mark-check-target (target) (defun mu4e~mark-check-target (target)
"Check if TARGET exists; if not, offer to create it." "Check if TARGET exists; if not, offer to create it."
(let ((fulltarget (concat (mu4e-root-maildir) target))) (let ((fulltarget (concat (mu4e-root-maildir) target)))
(if (not (mu4e-create-maildir-maybe fulltarget)) (if (not (mu4e-create-maildir-maybe fulltarget))
(mu4e-error "Target dir %s does not exist " fulltarget) (mu4e-error "Target dir %s does not exist " fulltarget)
target))) target)))
(defun mu4e-mark-execute-all (&optional no-confirmation) (defun mu4e-mark-execute-all (&optional no-confirmation)
@ -387,30 +387,30 @@ work well.
If NO-CONFIRMATION is non-nil, don't ask user for confirmation." If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
(interactive) (interactive)
(mu4e~mark-in-context (mu4e~mark-in-context
(let* ((marknum (hash-table-count mu4e~mark-map)) (let* ((marknum (hash-table-count mu4e~mark-map))
(prompt (format "Are you sure you want to execute %d mark%s?" (prompt (format "Are you sure you want to execute %d mark%s?"
marknum (if (> marknum 1) "s" "")))) marknum (if (> marknum 1) "s" ""))))
(if (zerop marknum) (if (zerop marknum)
(mu4e-warn "Nothing is marked") (mu4e-warn "Nothing is marked")
(mu4e-mark-resolve-deferred-marks) (mu4e-mark-resolve-deferred-marks)
(when (or no-confirmation (y-or-n-p prompt)) (when (or no-confirmation (y-or-n-p prompt))
(maphash (maphash
(lambda (docid val) (lambda (docid val)
(let* ((mark (car val)) (target (cdr val)) (let* ((mark (car val)) (target (cdr val))
(markdescr (assq mark mu4e-marks)) (markdescr (assq mark mu4e-marks))
(msg (save-excursion (msg (save-excursion
(mu4e~headers-goto-docid docid) (mu4e~headers-goto-docid docid)
(mu4e-message-at-point)))) (mu4e-message-at-point))))
;; note: whenever you do something with the message, ;; note: whenever you do something with the message,
;; it looses its N (new) flag ;; it looses its N (new) flag
(if markdescr (if markdescr
(progn (progn
(run-hook-with-args (run-hook-with-args
'mu4e-mark-execute-pre-hook mark msg) 'mu4e-mark-execute-pre-hook mark msg)
(funcall (plist-get (cdr markdescr) :action) (funcall (plist-get (cdr markdescr) :action)
docid msg target)) docid msg target))
(mu4e-error "Unrecognized mark %S" mark)))) (mu4e-error "Unrecognized mark %S" mark))))
mu4e~mark-map)) mu4e~mark-map))
(mu4e-mark-unmark-all) (mu4e-mark-unmark-all)
(message nil))))) (message nil)))))
@ -418,16 +418,16 @@ If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
"Unmark all marked messages." "Unmark all marked messages."
(interactive) (interactive)
(mu4e~mark-in-context (mu4e~mark-in-context
(when (or (null mu4e~mark-map) (zerop (hash-table-count mu4e~mark-map))) (when (or (null mu4e~mark-map) (zerop (hash-table-count mu4e~mark-map)))
(mu4e-warn "Nothing is marked")) (mu4e-warn "Nothing is marked"))
(maphash (maphash
(lambda (docid _val) (lambda (docid _val)
(save-excursion (save-excursion
(when (mu4e~headers-goto-docid docid) (when (mu4e~headers-goto-docid docid)
(mu4e-mark-set 'unmark)))) (mu4e-mark-set 'unmark))))
mu4e~mark-map) mu4e~mark-map)
;; in any case, clear the marks map ;; in any case, clear the marks map
(mu4e~mark-clear))) (mu4e~mark-clear)))
(defun mu4e-mark-docid-marked-p (docid) (defun mu4e-mark-docid-marked-p (docid)
"Is the given DOCID marked?" "Is the given DOCID marked?"
@ -437,7 +437,7 @@ If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
(defun mu4e-mark-marks-num () (defun mu4e-mark-marks-num ()
"Return the number of mark-instances in the current buffer." "Return the number of mark-instances in the current buffer."
(mu4e~mark-in-context (mu4e~mark-in-context
(if mu4e~mark-map (hash-table-count mu4e~mark-map) 0))) (if mu4e~mark-map (hash-table-count mu4e~mark-map) 0)))
(defun mu4e-mark-handle-when-leaving () (defun mu4e-mark-handle-when-leaving ()
"Handle any mark-instances in the current buffer when leaving. "Handle any mark-instances in the current buffer when leaving.
@ -446,18 +446,18 @@ function is to be called before any further action (like searching,
quitting the buffer) is taken; returning t means 'take the following quitting the buffer) is taken; returning t means 'take the following
action', return nil means 'don't do anything'." action', return nil means 'don't do anything'."
(mu4e~mark-in-context (mu4e~mark-in-context
(let ((marknum (mu4e-mark-marks-num)) (let ((marknum (mu4e-mark-marks-num))
(what mu4e-headers-leave-behavior)) (what mu4e-headers-leave-behavior))
(unless (zerop marknum) ;; nothing to do? (unless (zerop marknum) ;; nothing to do?
(when (eq what 'ask) (when (eq what 'ask)
(setq what (mu4e-read-option (setq what (mu4e-read-option
(format "There are %d existing mark(s); should we: " (format "There are %d existing mark(s); should we: "
marknum) marknum)
'( ("apply marks" . apply) '( ("apply marks" . apply)
("ignore marks?" . ignore))))) ("ignore marks?" . ignore)))))
;; we determined what to do... now do it ;; we determined what to do... now do it
(when (eq what 'apply) (when (eq what 'apply)
(mu4e-mark-execute-all t)))))) (mu4e-mark-execute-all t))))))
(provide 'mu4e-mark) (provide 'mu4e-mark)
;;; mu4e-mark.el ends here ;;; mu4e-mark.el ends here

View File

@ -35,7 +35,7 @@
(defcustom mu4e-html2text-command (defcustom mu4e-html2text-command
(if (fboundp 'shr-insert-document) (if (fboundp 'shr-insert-document)
'mu4e-shr2text 'mu4e-shr2text
(progn (require 'html2text) 'html2text)) (progn (require 'html2text) 'html2text))
"Either a shell command or a function that converts from html to plain text. "Either a shell command or a function that converts from html to plain text.
@ -124,7 +124,7 @@ Some notes on the format:
Message view use the actual message file, and do include these fields." Message view use the actual message file, and do include these fields."
;; after all this documentation, the spectacular implementation ;; after all this documentation, the spectacular implementation
(if msg (if msg
(plist-get msg field) (plist-get msg field)
(mu4e-error "Message must be non-nil"))) (mu4e-error "Message must be non-nil")))
(defsubst mu4e-message-field (msg field) (defsubst mu4e-message-field (msg field)
@ -136,16 +136,16 @@ Like `mu4e-message-field-nil', but will sanitize nil values:
Thus, function will return nil for empty lists, non-existing body-txt or body-html." Thus, function will return nil for empty lists, non-existing body-txt or body-html."
(let ((val (mu4e-message-field-raw msg field))) (let ((val (mu4e-message-field-raw msg field)))
(cond (cond
(val (val
val) ;; non-nil -> just return it val) ;; non-nil -> just return it
((member field '(:subject :message-id :path :maildir :in-reply-to)) ((member field '(:subject :message-id :path :maildir :in-reply-to))
"") ;; string fields except body-txt, body-html: nil -> "" "") ;; string fields except body-txt, body-html: nil -> ""
((member field '(:body-html :body-txt)) ((member field '(:body-html :body-txt))
val) val)
((member field '(:docid :size)) ((member field '(:docid :size))
0) ;; numeric type: nil -> 0 0) ;; numeric type: nil -> 0
(t (t
val)))) ;; otherwise, just return nil val)))) ;; otherwise, just return nil
(defsubst mu4e-message-has-field (msg field) (defsubst mu4e-message-has-field (msg field)
"If MSG has a FIELD return t, nil otherwise." "If MSG has a FIELD return t, nil otherwise."
@ -158,7 +158,7 @@ no such message. If optional NOERROR is non-nil, do not raise an
error when there is no message at point." error when there is no message at point."
(let ((msg (or (get-text-property (point) 'msg) mu4e~view-message))) (let ((msg (or (get-text-property (point) 'msg) mu4e~view-message)))
(if msg (if msg
msg msg
(unless noerror (mu4e-warn "No message at point"))))) (unless noerror (mu4e-warn "No message at point")))))
(defsubst mu4e-message-field-at-point (field) (defsubst mu4e-message-field-at-point (field)
@ -176,19 +176,19 @@ Determine whether we want
to use html or text. The decision is based on PREFER-HTML and to use html or text. The decision is based on PREFER-HTML and
whether the message supports the given representation." whether the message supports the given representation."
(let* ((txt (mu4e-message-field msg :body-txt)) (let* ((txt (mu4e-message-field msg :body-txt))
(html (mu4e-message-field msg :body-html)) (html (mu4e-message-field msg :body-html))
(txt-len (length txt)) (txt-len (length txt))
(html-len (length html)) (html-len (length html))
(txt-limit (* mu4e-view-html-plaintext-ratio-heuristic txt-len)) (txt-limit (* mu4e-view-html-plaintext-ratio-heuristic txt-len))
(txt-limit (if (>= txt-limit 0) txt-limit most-positive-fixnum))) (txt-limit (if (>= txt-limit 0) txt-limit most-positive-fixnum)))
(cond (cond
; user prefers html --> use html if there is ; user prefers html --> use html if there is
(prefer-html (> html-len 0)) (prefer-html (> html-len 0))
;; otherwise (user prefers text) still use html if there is not enough ;; otherwise (user prefers text) still use html if there is not enough
;; text ;; text
((< txt-limit html-len) t) ((< txt-limit html-len) t)
;; otherwise, use text ;; otherwise, use text
(t nil)))) (t nil))))
(defun mu4e~message-body-has-content-type-param (msg param) (defun mu4e~message-body-has-content-type-param (msg param)
"Does the MSG have a content-type parameter PARAM?" "Does the MSG have a content-type parameter PARAM?"
@ -207,29 +207,29 @@ will use that. Normally, this function prefers the text part,
unless PREFER-HTML is non-nil." unless PREFER-HTML is non-nil."
(setq mu4e~message-body-html (mu4e~message-use-html-p msg prefer-html)) (setq mu4e~message-body-html (mu4e~message-use-html-p msg prefer-html))
(let ((body (let ((body
(if mu4e~message-body-html (if mu4e~message-body-html
;; use an htmml body ;; use an htmml body
(cond (cond
((stringp mu4e-html2text-command) ((stringp mu4e-html2text-command)
(mu4e~html2text-shell msg mu4e-html2text-command)) (mu4e~html2text-shell msg mu4e-html2text-command))
((functionp mu4e-html2text-command) ((functionp mu4e-html2text-command)
(if (help-function-arglist mu4e-html2text-command) (if (help-function-arglist mu4e-html2text-command)
(funcall mu4e-html2text-command msg) (funcall mu4e-html2text-command msg)
;; oldskool parameterless mu4e-html2text-command ;; oldskool parameterless mu4e-html2text-command
(mu4e~html2text-wrapper mu4e-html2text-command msg))) (mu4e~html2text-wrapper mu4e-html2text-command msg)))
(t (mu4e-error "Invalid `mu4e-html2text-command'"))) (t (mu4e-error "Invalid `mu4e-html2text-command'")))
;; use a text body ;; use a text body
(or (with-temp-buffer (or (with-temp-buffer
(insert (or (mu4e-message-field msg :body-txt) "")) (insert (or (mu4e-message-field msg :body-txt) ""))
(if (mu4e~safe-iequal "flowed" (if (mu4e~safe-iequal "flowed"
(mu4e~message-body-has-content-type-param (mu4e~message-body-has-content-type-param
msg "format")) msg "format"))
(fill-flowed nil (fill-flowed nil
(mu4e~safe-iequal (mu4e~safe-iequal
"yes" "yes"
(mu4e~message-body-has-content-type-param (mu4e~message-body-has-content-type-param
msg "delsp")))) msg "delsp"))))
(buffer-string)) "")))) (buffer-string)) ""))))
(dolist (func mu4e-message-body-rewrite-functions) (dolist (func mu4e-message-body-rewrite-functions)
(setq body (funcall func msg body))) (setq body (funcall func msg body)))
body)) body))
@ -245,10 +245,10 @@ replace with."
(goto-char (point-min)) (goto-char (point-min))
(while (re-search-forward "[ (while (re-search-forward "[
 ’]" nil t)  ’]" nil t)
(replace-match (replace-match
(cond (cond
((string= (match-string 0) "’") "'") ((string= (match-string 0) "’") "'")
((string= (match-string 0) " ") " ") ((string= (match-string 0) " ") " ")
(t "")))) (t ""))))
(buffer-string))) (buffer-string)))
@ -260,22 +260,22 @@ address) regular expressions RX. If there is a match, return
address) regular expressions RX. If there is a match, return address) regular expressions RX. If there is a match, return
non-nil; otherwise return nil. RX can also be a list of regular non-nil; otherwise return nil. RX can also be a list of regular
expressions, in which case any of those are tried for a match." expressions, in which case any of those are tried for a match."
(if (and cfield (listp cfield)) (if (and cfield (listp cfield))
(or (mu4e-message-contact-field-matches msg (car cfield) rx) (or (mu4e-message-contact-field-matches msg (car cfield) rx)
(mu4e-message-contact-field-matches msg (cdr cfield) rx)) (mu4e-message-contact-field-matches msg (cdr cfield) rx))
(when cfield (when cfield
(if (listp rx) (if (listp rx)
;; if rx is a list, try each one of them for a match ;; if rx is a list, try each one of them for a match
(cl-find-if (cl-find-if
(lambda (a-rx) (mu4e-message-contact-field-matches msg cfield a-rx)) (lambda (a-rx) (mu4e-message-contact-field-matches msg cfield a-rx))
rx) rx)
;; not a list, check the rx ;; not a list, check the rx
(cl-find-if (cl-find-if
(lambda (ct) (lambda (ct)
(let ((name (car ct)) (email (cdr ct))) (let ((name (car ct)) (email (cdr ct)))
(or (or
(and name (string-match rx name)) (and name (string-match rx name))
(and email (string-match rx email))))) (and email (string-match rx email)))))
(mu4e-message-field msg cfield)))))) (mu4e-message-field msg cfield))))))
(defun mu4e-message-contact-field-matches-me (msg cfield) (defun mu4e-message-contact-field-matches-me (msg cfield)
@ -285,12 +285,12 @@ that is, any of the e-mail address in
that is, any of the e-mail address in that is, any of the e-mail address in
`(mu4e-personal-addresses)'. Returns the contact cell that `(mu4e-personal-addresses)'. Returns the contact cell that
matched, or nil." matched, or nil."
(cl-find-if (cl-find-if
(lambda (cc-cell) (lambda (cc-cell)
(cl-member-if (cl-member-if
(lambda (addr) (lambda (addr)
(string= (downcase addr) (downcase (cdr cc-cell)))) (string= (downcase addr) (downcase (cdr cc-cell))))
(mu4e-personal-addresses))) (mu4e-personal-addresses)))
(mu4e-message-field msg cfield))) (mu4e-message-field msg cfield)))
(defsubst mu4e-message-part-field (msgpart field) (defsubst mu4e-message-part-field (msgpart field)
@ -322,27 +322,27 @@ Return the buffer contents."
"Convert html in MSG to text using the shr engine. "Convert html in MSG to text using the shr engine.
This can be used in `mu4e-html2text-command' in a new enough This can be used in `mu4e-html2text-command' in a new enough
Emacs. Based on code by Titus von der Malsburg." Emacs. Based on code by Titus von der Malsburg."
(mu4e~html2text-wrapper (mu4e~html2text-wrapper
(lambda () (lambda ()
(let ( (let (
;; When HTML emails contain references to remote images, ;; When HTML emails contain references to remote images,
;; retrieving these images leaks information. For example, ;; retrieving these images leaks information. For example,
;; the sender can see when I openend the email and from which ;; the sender can see when I openend the email and from which
;; computer (IP address). For this reason, it is preferrable ;; computer (IP address). For this reason, it is preferrable
;; to not retrieve images. ;; to not retrieve images.
;; See this discussion on mu-discuss: ;; See this discussion on mu-discuss:
;; https://groups.google.com/forum/#!topic/mu-discuss/gr1cwNNZnXo ;; https://groups.google.com/forum/#!topic/mu-discuss/gr1cwNNZnXo
(shr-inhibit-images t)) (shr-inhibit-images t))
(shr-render-region (point-min) (point-max)))) msg)) (shr-render-region (point-min) (point-max)))) msg))
(defun mu4e~html2text-shell (msg _cmd) (defun mu4e~html2text-shell (msg _cmd)
"Convert html2 text in MSG using a shell function CMD." "Convert html2 text in MSG using a shell function CMD."
(mu4e~html2text-wrapper (mu4e~html2text-wrapper
(lambda () (lambda ()
(let* ((tmp-file (mu4e-make-temp-file "html"))) (let* ((tmp-file (mu4e-make-temp-file "html")))
(write-region (point-min) (point-max) tmp-file) (write-region (point-min) (point-max) tmp-file)
(erase-buffer) (erase-buffer)
(call-process-shell-command mu4e-html2text-command tmp-file t t) (call-process-shell-command mu4e-html2text-command tmp-file t t)
(delete-file tmp-file))) msg)) (delete-file tmp-file))) msg))
(provide 'mu4e-message) (provide 'mu4e-message)

View File

@ -64,46 +64,46 @@ Example usage:
(defun mu4e~org-store-link-query () (defun mu4e~org-store-link-query ()
"Store a link to a mu4e query." "Store a link to a mu4e query."
(let* ((query (mu4e-last-query)) (let* ((query (mu4e-last-query))
(date (format-time-string (org-time-stamp-format))) (date (format-time-string (org-time-stamp-format)))
;; seems we get an error when there's no date... ;; seems we get an error when there's no date...
(link (concat "mu4e:query:" query))) (link (concat "mu4e:query:" query)))
(org-store-link-props (org-store-link-props
:type "mu4e" :type "mu4e"
:query query :query query
:date date) :date date)
(org-add-link-props (org-add-link-props
:link link :link link
:description (format "mu4e-query: '%s'" query)) :description (format "mu4e-query: '%s'" query))
link)) link))
(defun mu4e~org-first-address (msg field) (defun mu4e~org-first-address (msg field)
"Get address field FIELD from MSG as a string or nil." "Get address field FIELD from MSG as a string or nil."
(let* ((val (plist-get msg field)) (let* ((val (plist-get msg field))
(name (when val (car (car val)))) (name (when val (car (car val))))
(addr (when val (cdr (car val))))) (addr (when val (cdr (car val)))))
(when val (when val
(if name (if name
(format "%s <%s>" name addr) (format "%s <%s>" name addr)
(format "%s" addr))))) (format "%s" addr)))))
(defun mu4e~org-store-link-message () (defun mu4e~org-store-link-message ()
"Store a link to a mu4e message." "Store a link to a mu4e message."
(let* ((msg (mu4e-message-at-point)) (let* ((msg (mu4e-message-at-point))
(msgid (or (plist-get msg :message-id) "<none>")) (msgid (or (plist-get msg :message-id) "<none>"))
(date (plist-get msg :date)) (date (plist-get msg :date))
(date (format-time-string (org-time-stamp-format) date)) (date (format-time-string (org-time-stamp-format) date))
;; seems we get an error when there's no date... ;; seems we get an error when there's no date...
(link (concat "mu4e:msgid:" msgid))) (link (concat "mu4e:msgid:" msgid)))
(org-store-link-props (org-store-link-props
:type "mu4e" :type "mu4e"
:message-id msgid :message-id msgid
:to (mu4e~org-first-address msg :to) :to (mu4e~org-first-address msg :to)
:from (mu4e~org-first-address msg :from) :from (mu4e~org-first-address msg :from)
:date date :date date
:subject (plist-get msg :subject)) :subject (plist-get msg :subject))
(org-add-link-props (org-add-link-props
:link link :link link
:description (funcall mu4e-org-link-desc-func msg)) :description (funcall mu4e-org-link-desc-func msg))
link)) link))
(defun mu4e-org-store-link () (defun mu4e-org-store-link ()
@ -113,8 +113,8 @@ It links to the last known query when in `mu4e-headers-mode' with
a specific message, based on its message-id, so that links stay a specific message, based on its message-id, so that links stay
valid even after moving the message around." valid even after moving the message around."
(if (and (eq major-mode 'mu4e-headers-mode) (if (and (eq major-mode 'mu4e-headers-mode)
mu4e-org-link-query-in-headers-mode) mu4e-org-link-query-in-headers-mode)
(mu4e~org-store-link-query) (mu4e~org-store-link-query)
(when (mu4e-message-at-point t) (when (mu4e-message-at-point t)
(mu4e~org-store-link-message)))) (mu4e~org-store-link-message))))
@ -122,8 +122,8 @@ valid even after moving the message around."
;; org-link-set-parameters method ;; org-link-set-parameters method
(if (fboundp 'org-link-set-parameters) (if (fboundp 'org-link-set-parameters)
(org-link-set-parameters "mu4e" (org-link-set-parameters "mu4e"
:follow #'mu4e-org-open :follow #'mu4e-org-open
:store #'mu4e-org-store-link) :store #'mu4e-org-store-link)
(org-add-link-type "mu4e" 'mu4e-org-open) (org-add-link-type "mu4e" 'mu4e-org-open)
(add-hook 'org-store-link-functions 'mu4e-org-store-link)) (add-hook 'org-store-link-functions 'mu4e-org-store-link))
@ -133,11 +133,11 @@ Open the mu4e message (for links starting with 'msgid:') or run
the query (for links starting with 'query:')." the query (for links starting with 'query:')."
(require 'mu4e) (require 'mu4e)
(cond (cond
((string-match "^msgid:\\(.+\\)" link) ((string-match "^msgid:\\(.+\\)" link)
(mu4e-view-message-with-message-id (match-string 1 link))) (mu4e-view-message-with-message-id (match-string 1 link)))
((string-match "^query:\\(.+\\)" link) ((string-match "^query:\\(.+\\)" link)
(mu4e-headers-search (match-string 1 link) current-prefix-arg)) (mu4e-headers-search (match-string 1 link) current-prefix-arg))
(t (mu4e-error "Unrecognized link type '%s'" link)))) (t (mu4e-error "Unrecognized link type '%s'" link))))
(make-obsolete 'org-mu4e-open 'mu4e-org-open "1.3.6") (make-obsolete 'org-mu4e-open 'mu4e-org-open "1.3.6")

View File

@ -40,7 +40,7 @@
"Each expression starts with a length cookie: "Each expression starts with a length cookie:
<`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.")
(defconst mu4e~cookie-post "\377" (defconst mu4e~cookie-post "\377"
"Each expression starts with a length cookie: "Each expression starts with a length cookie:
<`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.")
(defconst mu4e~cookie-matcher-rx (defconst mu4e~cookie-matcher-rx
(concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)" mu4e~cookie-post) (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)" mu4e~cookie-post)
@ -57,8 +57,8 @@ Match 1 will be the length (in hex).")
(defun mu4e~proc-running-p () (defun mu4e~proc-running-p ()
"Whether the mu process is running." "Whether the mu process is running."
(when (and mu4e~proc-process (when (and mu4e~proc-process
(memq (process-status mu4e~proc-process) (memq (process-status mu4e~proc-process)
'(run open listen connect stop))) '(run open listen connect stop)))
t)) t))
(defsubst mu4e~proc-eat-sexp-from-buf () (defsubst mu4e~proc-eat-sexp-from-buf ()
@ -73,23 +73,23 @@ removed."
;; mu4e~cookie-matcher-rx: ;; mu4e~cookie-matcher-rx:
;; (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)]" mu4e~cookie-post) ;; (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)]" mu4e~cookie-post)
(let ((b (string-match mu4e~cookie-matcher-rx mu4e~proc-buf)) (let ((b (string-match mu4e~cookie-matcher-rx mu4e~proc-buf))
(sexp-len) (objcons)) (sexp-len) (objcons))
(when b (when b
(setq sexp-len (string-to-number (match-string 1 mu4e~proc-buf) 16)) (setq sexp-len (string-to-number (match-string 1 mu4e~proc-buf) 16))
;; does mu4e~proc-buf contain the full sexp? ;; does mu4e~proc-buf contain the full sexp?
(when (>= (length mu4e~proc-buf) (+ sexp-len (match-end 0))) (when (>= (length mu4e~proc-buf) (+ sexp-len (match-end 0)))
;; clear-up start ;; clear-up start
(setq mu4e~proc-buf (substring mu4e~proc-buf (match-end 0))) (setq mu4e~proc-buf (substring mu4e~proc-buf (match-end 0)))
;; note: we read the input in binary mode -- here, we take the part ;; note: we read the input in binary mode -- here, we take the part
;; that is the sexp, and convert that to utf-8, before we interpret ;; that is the sexp, and convert that to utf-8, before we interpret
;; it. ;; it.
(setq objcons (read-from-string (setq objcons (read-from-string
(decode-coding-string (decode-coding-string
(substring mu4e~proc-buf 0 sexp-len) (substring mu4e~proc-buf 0 sexp-len)
'utf-8 t))) 'utf-8 t)))
(when objcons (when objcons
(setq mu4e~proc-buf (substring mu4e~proc-buf sexp-len)) (setq mu4e~proc-buf (substring mu4e~proc-buf sexp-len))
(car objcons))))))) (car objcons)))))))
(defun mu4e~proc-filter (_proc str) (defun mu4e~proc-filter (_proc str)
@ -154,81 +154,81 @@ The server output is as follows:
(let ((sexp (mu4e~proc-eat-sexp-from-buf))) (let ((sexp (mu4e~proc-eat-sexp-from-buf)))
(with-local-quit (with-local-quit
(while sexp (while sexp
(mu4e-log 'from-server "%S" sexp) (mu4e-log 'from-server "%S" sexp)
(cond (cond
;; a header plist can be recognized by the existence of a :date field ;; a header plist can be recognized by the existence of a :date field
((plist-get sexp :date) ((plist-get sexp :date)
(funcall mu4e-header-func sexp)) (funcall mu4e-header-func sexp))
;; the found sexp, we receive after getting all the headers ;; the found sexp, we receive after getting all the headers
((plist-get sexp :found) ((plist-get sexp :found)
(funcall mu4e-found-func (plist-get sexp :found))) (funcall mu4e-found-func (plist-get sexp :found)))
;; viewing a specific message ;; viewing a specific message
((plist-get sexp :view) ((plist-get sexp :view)
(funcall mu4e-view-func (plist-get sexp :view))) (funcall mu4e-view-func (plist-get sexp :view)))
;; receive an erase message ;; receive an erase message
((plist-get sexp :erase) ((plist-get sexp :erase)
(funcall mu4e-erase-func)) (funcall mu4e-erase-func))
;; receive a :sent message ;; receive a :sent message
((plist-get sexp :sent) ((plist-get sexp :sent)
(funcall mu4e-sent-func (funcall mu4e-sent-func
(plist-get sexp :docid) (plist-get sexp :docid)
(plist-get sexp :path))) (plist-get sexp :path)))
;; received a pong message ;; received a pong message
((plist-get sexp :pong) ((plist-get sexp :pong)
(funcall mu4e-pong-func sexp)) (funcall mu4e-pong-func sexp))
;; received a contacts message ;; received a contacts message
;; note: we use 'member', to match (:contacts nil) ;; note: we use 'member', to match (:contacts nil)
((plist-member sexp :contacts) ((plist-member sexp :contacts)
(funcall mu4e-contacts-func (funcall mu4e-contacts-func
(plist-get sexp :contacts) (plist-get sexp :contacts)
(plist-get sexp :tstamp))) (plist-get sexp :tstamp)))
;; something got moved/flags changed ;; something got moved/flags changed
((plist-get sexp :update) ((plist-get sexp :update)
(funcall mu4e-update-func (funcall mu4e-update-func
(plist-get sexp :update) (plist-get sexp :update)
(plist-get sexp :move) (plist-get sexp :move)
(plist-get sexp :maybe-view))) (plist-get sexp :maybe-view)))
;; a message got removed ;; a message got removed
((plist-get sexp :remove) ((plist-get sexp :remove)
(funcall mu4e-remove-func (plist-get sexp :remove))) (funcall mu4e-remove-func (plist-get sexp :remove)))
;; start composing a new message ;; start composing a new message
((plist-get sexp :compose) ((plist-get sexp :compose)
(funcall mu4e-compose-func (funcall mu4e-compose-func
(plist-get sexp :compose) (plist-get sexp :compose)
(plist-get sexp :original) (plist-get sexp :original)
(plist-get sexp :include))) (plist-get sexp :include)))
;; do something with a temporary file ;; do something with a temporary file
((plist-get sexp :temp) ((plist-get sexp :temp)
(funcall mu4e-temp-func (funcall mu4e-temp-func
(plist-get sexp :temp) ;; name of the temp file (plist-get sexp :temp) ;; name of the temp file
(plist-get sexp :what) ;; what to do with it (plist-get sexp :what) ;; what to do with it
;; (pipe|emacs|open-with...) ;; (pipe|emacs|open-with...)
(plist-get sexp :docid) ;; docid of the message (plist-get sexp :docid) ;; docid of the message
(plist-get sexp :param)));; parameter for the action (plist-get sexp :param)));; parameter for the action
;; get some info ;; get some info
((plist-get sexp :info) ((plist-get sexp :info)
(funcall mu4e-info-func sexp)) (funcall mu4e-info-func sexp))
;; receive an error ;; receive an error
((plist-get sexp :error) ((plist-get sexp :error)
(funcall mu4e-error-func (funcall mu4e-error-func
(plist-get sexp :error) (plist-get sexp :error)
(plist-get sexp :message))) (plist-get sexp :message)))
(t (mu4e-message "Unexpected data from server [%S]" sexp))) (t (mu4e-message "Unexpected data from server [%S]" sexp)))
(setq sexp (mu4e~proc-eat-sexp-from-buf)))))) (setq sexp (mu4e~proc-eat-sexp-from-buf))))))
(defun mu4e~escape (str) (defun mu4e~escape (str)
"Escape string STR for transport. "Escape string STR for transport.
@ -252,12 +252,12 @@ Start the process if needed."
(unless (file-executable-p mu4e-mu-binary) (unless (file-executable-p mu4e-mu-binary)
(mu4e-error (format "`mu4e-mu-binary' (%S) not found" mu4e-mu-binary))) (mu4e-error (format "`mu4e-mu-binary' (%S) not found" mu4e-mu-binary)))
(let* ((process-connection-type nil) ;; use a pipe (let* ((process-connection-type nil) ;; use a pipe
(args (when mu4e-mu-home `(,(format"--muhome=%s" mu4e-mu-home)))) (args (when mu4e-mu-home `(,(format"--muhome=%s" mu4e-mu-home))))
(args (cons "server" args))) (args (cons "server" args)))
(setq mu4e~proc-buf "") (setq mu4e~proc-buf "")
(setq mu4e~proc-process (apply 'start-process (setq mu4e~proc-process (apply 'start-process
mu4e~proc-name mu4e~proc-name mu4e~proc-name mu4e~proc-name
mu4e-mu-binary args)) mu4e-mu-binary args))
;; register a function for (:info ...) sexps ;; register a function for (:info ...) sexps
(unless mu4e~proc-process (unless mu4e~proc-process
(mu4e-error "Failed to start the mu4e backend")) (mu4e-error "Failed to start the mu4e backend"))
@ -269,7 +269,7 @@ Start the process if needed."
(defun mu4e~proc-kill () (defun mu4e~proc-kill ()
"Kill the mu server process." "Kill the mu server process."
(let* ((buf (get-buffer mu4e~proc-name)) (let* ((buf (get-buffer mu4e~proc-name))
(proc (and (buffer-live-p buf) (get-buffer-process buf)))) (proc (and (buffer-live-p buf) (get-buffer-process buf))))
(when proc (when proc
(let ((delete-exited-processes t)) (let ((delete-exited-processes t))
(mu4e~call-mu '(quit))) (mu4e~call-mu '(quit)))
@ -277,8 +277,8 @@ Start the process if needed."
(ignore-errors (ignore-errors
(signal-process proc 'SIGINT)))) (signal-process proc 'SIGINT))))
(setq (setq
mu4e~proc-process nil mu4e~proc-process nil
mu4e~proc-buf nil)) mu4e~proc-buf nil))
;; error codes are defined in src/mu-util ;; error codes are defined in src/mu-util
;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent") ;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent")
@ -289,20 +289,20 @@ Start the process if needed."
(setq mu4e~proc-process nil) (setq mu4e~proc-process nil)
(setq mu4e~proc-buf "") ;; clear any half-received sexps (setq mu4e~proc-buf "") ;; clear any half-received sexps
(cond (cond
((eq status 'signal) ((eq status 'signal)
(cond (cond
((or(eq code 9) (eq code 2)) (message nil)) ((or(eq code 9) (eq code 2)) (message nil))
;;(message "the mu server process has been stopped")) ;;(message "the mu server process has been stopped"))
(t (error (format "mu server process received signal %d" code))))) (t (error (format "mu server process received signal %d" code)))))
((eq status 'exit) ((eq status 'exit)
(cond (cond
((eq code 0) ((eq code 0)
(message nil)) ;; don't do anything (message nil)) ;; don't do anything
((eq code 19) ((eq code 19)
(error "Database is locked by another process")) (error "Database is locked by another process"))
(t (error "Mu server process ended with exit code %d" code)))) (t (error "Mu server process ended with exit code %d" code))))
(t (t
(error "Something bad happened to the mu server process"))))) (error "Something bad happened to the mu server process")))))
(defun mu4e~call-mu (form) (defun mu4e~call-mu (form)
"Call 'mu' with some command." "Call 'mu' with some command."
@ -314,7 +314,7 @@ Start the process if needed."
(defun mu4e~docid-msgid-param (docid-or-msgid) (defun mu4e~docid-msgid-param (docid-or-msgid)
"Construct a backend parameter based on DOCID-OR-MSGID." "Construct a backend parameter based on DOCID-OR-MSGID."
(if (stringp docid-or-msgid) (if (stringp docid-or-msgid)
`(:msgid ,(mu4e~escape docid-or-msgid)) `(:msgid ,(mu4e~escape docid-or-msgid))
`(:docid ,docid-or-msgid))) `(:docid ,docid-or-msgid)))
(defun mu4e~proc-add (path) (defun mu4e~proc-add (path)
@ -332,9 +332,9 @@ editing, resending) with DOCID or nil for type `new'.
The result is delivered to the function registered as The result is delivered to the function registered as
`mu4e-compose-func'." `mu4e-compose-func'."
(mu4e~call-mu `(compose (mu4e~call-mu `(compose
:type ,type :type ,type
:extract-encrypted ,decrypt :extract-encrypted ,decrypt
:docid ,docid))) :docid ,docid)))
(defun mu4e~proc-contacts (personal after tstamp) (defun mu4e~proc-contacts (personal after tstamp)
"Ask for contacts with PERSONAL AFTER TSTAMP. "Ask for contacts with PERSONAL AFTER TSTAMP.
@ -343,12 +343,12 @@ response. If PERSONAL is non-nil, only get personal contacts, if
AFTER is non-nil, get only contacts seen AFTER (the time_t AFTER is non-nil, get only contacts seen AFTER (the time_t
value)." value)."
(mu4e~call-mu `(contacts (mu4e~call-mu `(contacts
:personal ,personal :personal ,personal
:after ,(or after nil) :after ,(or after nil)
:tstamp ,(or tstamp nil)))) :tstamp ,(or tstamp nil))))
(defun mu4e~proc-extract (action docid index decrypt (defun mu4e~proc-extract (action docid index decrypt
&optional path what param) &optional path what param)
"Perform ACTION on part with DOCID INDEX DECRYPT PATH WHAT PARAM. "Perform ACTION on part with DOCID INDEX DECRYPT PATH WHAT PARAM.
Use a message with DOCID and perform ACTION on it (as symbol, Use a message with DOCID and perform ACTION on it (as symbol,
either `save', `open', `temp') which mean: * save: save the part either `save', `open', `temp') which mean: * save: save the part
@ -357,16 +357,16 @@ with the default application registered for doing so * temp: save
to a temporary file, then respond with to a temporary file, then respond with
(:temp <path> :what <what> :param <param>)." (:temp <path> :what <what> :param <param>)."
(mu4e~call-mu `(extract (mu4e~call-mu `(extract
:action ,action :action ,action
:docid ,docid :docid ,docid
:index ,index :index ,index
:decrypt ,decrypt :decrypt ,decrypt
:path ,path :path ,path
:what ,what :what ,what
:param ,param))) :param ,param)))
(defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups (defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups
include-related) include-related)
"Run QUERY with THREADS SORTFIELD SORTDIR MAXNUM SKIP-DUPS INCLUDE-RELATED. "Run QUERY with THREADS SORTFIELD SORTDIR MAXNUM SKIP-DUPS INCLUDE-RELATED.
If THREADS is non-nil, show results in threaded fashion, SORTFIELD If THREADS is non-nil, show results in threaded fashion, SORTFIELD
is a symbol describing the field to sort by (or nil); see is a symbol describing the field to sort by (or nil); see
@ -383,13 +383,13 @@ kind of result. The variables `mu4e-error-func' contain the
function that will be called for, resp., a message (header row) function that will be called for, resp., a message (header row)
or an error." or an error."
(mu4e~call-mu `(find (mu4e~call-mu `(find
:query ,query :query ,query
:threads ,threads :threads ,threads
:sortfield ,sortfield :sortfield ,sortfield
:reverse ,(if (eq sortdir 'descending) t nil) :reverse ,(if (eq sortdir 'descending) t nil)
:maxnum ,maxnum :maxnum ,maxnum
:skip-dups ,skip-dups :skip-dups ,skip-dups
:include-related ,include-related))) :include-related ,include-related)))
(defun mu4e~proc-index (&optional cleanup lazy-check) (defun mu4e~proc-index (&optional cleanup lazy-check)
"Index messages with possible CLEANUP and LAZY-CHECK. "Index messages with possible CLEANUP and LAZY-CHECK.
@ -440,15 +440,15 @@ Returns either (:update ... ) or (:error ) sexp, which are handled my
(unless (or maildir flags) (unless (or maildir flags)
(mu4e-error "At least one of maildir and flags must be specified")) (mu4e-error "At least one of maildir and flags must be specified"))
(unless (or (not maildir) (unless (or (not maildir)
(file-exists-p (concat (mu4e-root-maildir) "/" maildir "/"))) (file-exists-p (concat (mu4e-root-maildir) "/" maildir "/")))
(mu4e-error "Target dir does not exist")) (mu4e-error "Target dir does not exist"))
(mu4e~call-mu `(move (mu4e~call-mu `(move
:docid ,(if (stringp docid-or-msgid) nil docid-or-msgid) :docid ,(if (stringp docid-or-msgid) nil docid-or-msgid)
:msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil) :msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil)
:flags ,(or flags nil) :flags ,(or flags nil)
:maildir ,(or maildir nil) :maildir ,(or maildir nil)
:rename ,(and maildir mu4e-change-filenames-when-moving) :rename ,(and maildir mu4e-change-filenames-when-moving)
:noview ,no-view))) :noview ,no-view)))
(defun mu4e~proc-ping (&optional queries) (defun mu4e~proc-ping (&optional queries)
"Sends a ping to the mu server, expecting a (:pong ...) in response. "Sends a ping to the mu server, expecting a (:pong ...) in response.
@ -477,10 +477,10 @@ attached to the message, and return them as temp files. DECRYPT
if necessary. The result will be delivered to the function if necessary. The result will be delivered to the function
registered as `mu4e-view-func'." registered as `mu4e-view-func'."
(mu4e~call-mu `(view (mu4e~call-mu `(view
:docid ,(if (stringp docid-or-msgid) nil docid-or-msgid) :docid ,(if (stringp docid-or-msgid) nil docid-or-msgid)
:msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil) :msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil)
:extract-images ,images :extract-images ,images
:extract-encrypted ,decrypt))) :extract-encrypted ,decrypt)))
(defun mu4e~proc-view-path (path &optional images decrypt) (defun mu4e~proc-view-path (path &optional images decrypt)
"View message at PATH.. "View message at PATH..
@ -489,9 +489,9 @@ attached to the message, and return them as temp files. The
result will be delivered to the function registered as result will be delivered to the function registered as
`mu4e-view-func'. Optionally DECRYPT." `mu4e-view-func'. Optionally DECRYPT."
(mu4e~call-mu `(view (mu4e~call-mu `(view
:path ,path :path ,path
:extract-images ,images :extract-images ,images
:extract-encrypted ,decrypt))) :extract-encrypted ,decrypt)))
(provide 'mu4e-proc) (provide 'mu4e-proc)
;;; mu4e-proc.el ends here ;;; mu4e-proc.el ends here

View File

@ -55,15 +55,15 @@
(defun mu4e-speedbar-install-variables () (defun mu4e-speedbar-install-variables ()
"Install those variables used by speedbar to enhance mu4e." "Install those variables used by speedbar to enhance mu4e."
(add-hook 'mu4e-context-changed-hook (add-hook 'mu4e-context-changed-hook
(lambda() (lambda()
(when (buffer-live-p speedbar-buffer) (when (buffer-live-p speedbar-buffer)
(with-current-buffer speedbar-buffer (with-current-buffer speedbar-buffer
(let ((inhibit-read-only t)) (let ((inhibit-read-only t))
(mu4e-speedbar-buttons)))))) (mu4e-speedbar-buttons))))))
(dolist (keymap (dolist (keymap
'( mu4e-main-speedbar-key-map '( mu4e-main-speedbar-key-map
mu4e-headers-speedbar-key-map mu4e-headers-speedbar-key-map
mu4e-view-speedbar-key-map)) mu4e-view-speedbar-key-map))
(unless keymap (unless keymap
(setq keymap (speedbar-make-specialized-keymap)) (setq keymap (speedbar-make-specialized-keymap))
(define-key keymap "RET" 'speedbar-edit-line) (define-key keymap "RET" 'speedbar-edit-line)
@ -71,7 +71,7 @@
;; Make sure our special speedbar major mode is loaded ;; Make sure our special speedbar major mode is loaded
(if (featurep 'speedbar) (if (featurep 'speedbar)
(mu4e-speedbar-install-variables) (mu4e-speedbar-install-variables)
(add-hook 'speedbar-load-hook 'mu4e-speedbar-install-variables)) (add-hook 'speedbar-load-hook 'mu4e-speedbar-install-variables))
(defun mu4e~speedbar-render-maildir-list () (defun mu4e~speedbar-render-maildir-list ()
@ -80,19 +80,19 @@
(when (buffer-live-p speedbar-buffer) (when (buffer-live-p speedbar-buffer)
(with-current-buffer speedbar-buffer (with-current-buffer speedbar-buffer
(mapcar (lambda (maildir-name) (mapcar (lambda (maildir-name)
(speedbar-insert-button (speedbar-insert-button
(concat " " maildir-name) (concat " " maildir-name)
'mu4e-highlight-face 'mu4e-highlight-face
'highlight 'highlight
'mu4e~speedbar-maildir 'mu4e~speedbar-maildir
maildir-name)) maildir-name))
(mu4e-get-maildirs))))) (mu4e-get-maildirs)))))
(defun mu4e~speedbar-maildir (&optional _text token _ident) (defun mu4e~speedbar-maildir (&optional _text token _ident)
"Jump to maildir TOKEN. TEXT and INDENT are not used." "Jump to maildir TOKEN. TEXT and INDENT are not used."
(dframe-with-attached-buffer (dframe-with-attached-buffer
(mu4e-headers-search (concat "\"maildir:" token "\"") (mu4e-headers-search (concat "\"maildir:" token "\"")
current-prefix-arg))) current-prefix-arg)))
(defun mu4e~speedbar-render-bookmark-list () (defun mu4e~speedbar-render-bookmark-list ()
"Insert the list of bookmarks in the speedbar" "Insert the list of bookmarks in the speedbar"
@ -100,17 +100,17 @@
(mapcar (lambda (bookmark) (mapcar (lambda (bookmark)
(unless (plist-get bookmark :hide) (unless (plist-get bookmark :hide)
(speedbar-insert-button (speedbar-insert-button
(concat " " (plist-get bookmark :name)) (concat " " (plist-get bookmark :name))
'mu4e-highlight-face 'mu4e-highlight-face
'highlight 'highlight
'mu4e~speedbar-bookmark 'mu4e~speedbar-bookmark
(plist-get bookmark :query)))) (plist-get bookmark :query))))
(mu4e-bookmarks))) (mu4e-bookmarks)))
(defun mu4e~speedbar-bookmark (&optional _text token _ident) (defun mu4e~speedbar-bookmark (&optional _text token _ident)
"Run bookmarked query TOKEN. TEXT and INDENT are not used." "Run bookmarked query TOKEN. TEXT and INDENT are not used."
(dframe-with-attached-buffer (dframe-with-attached-buffer
(mu4e-headers-search token current-prefix-arg))) (mu4e-headers-search token current-prefix-arg)))
;;;###autoload ;;;###autoload
(defun mu4e-speedbar-buttons (&optional _buffer) (defun mu4e-speedbar-buttons (&optional _buffer)

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@
defaults, based on the XDG Base Directory Specification." defaults, based on the XDG Base Directory Specification."
:group 'mu4e :group 'mu4e
:type '(choice (const :tag "Default location" nil) :type '(choice (const :tag "Default location" nil)
(directory :tag "Specify location")) (directory :tag "Specify location"))
:safe 'stringp) :safe 'stringp)
(defcustom mu4e-mu-binary (executable-find "mu") (defcustom mu4e-mu-binary (executable-find "mu")
@ -52,7 +52,7 @@ path."
:safe 'stringp) :safe 'stringp)
(make-obsolete-variable 'mu4e-maildir (make-obsolete-variable 'mu4e-maildir
"determined by server; see `mu4e-root-maildir'." "1.3.8") "determined by server; see `mu4e-root-maildir'." "1.3.8")
(defcustom mu4e-org-support t (defcustom mu4e-org-support t
"Support org-mode links." "Support org-mode links."
@ -103,7 +103,7 @@ corresponding message file in the filesystem.
Having this option as t ensures that no non-existing messages are Having this option as t ensures that no non-existing messages are
shown but can also be quite slow with large message stores." shown but can also be quite slow with large message stores."
:type 'boolean :group 'mu4e :safe 'booleanp) :type 'boolean :group 'mu4e :safe 'booleanp)
(defcustom mu4e-index-lazy-check nil (defcustom mu4e-index-lazy-check nil
"Whether to only use a 'lazy check' during reindexing. "Whether to only use a 'lazy check' during reindexing.
@ -119,7 +119,7 @@ faster." :type 'boolean :group 'mu4e :safe 'booleanp)
If nil, don't update automatically. Note, changes in If nil, don't update automatically. Note, changes in
`mu4e-update-interval' only take effect after restarting mu4e." `mu4e-update-interval' only take effect after restarting mu4e."
:type '(choice (const :tag "No automatic update" nil) :type '(choice (const :tag "No automatic update" nil)
(integer :tag "Seconds")) (integer :tag "Seconds"))
:group 'mu4e :group 'mu4e
:safe 'integerp) :safe 'integerp)
@ -156,11 +156,11 @@ the attachment dir. See Info node `(mu4e) Attachments' for details."
;; don't use the older vars anymore ;; don't use the older vars anymore
(make-obsolete-variable 'mu4e-user-mail-address-regexp (make-obsolete-variable 'mu4e-user-mail-address-regexp
'mu4e-user-mail-address-list "0.9.9.x") 'mu4e-user-mail-address-list "0.9.9.x")
(make-obsolete-variable 'mu4e-my-email-addresses (make-obsolete-variable 'mu4e-my-email-addresses
'mu4e-user-mail-address-list "0.9.9.x") 'mu4e-user-mail-address-list "0.9.9.x")
(make-obsolete-variable 'mu4e-user-mail-address-list (make-obsolete-variable 'mu4e-user-mail-address-list
"determined by server; see `mu4e-personal-addresses'." "1.3.8") "determined by server; see `mu4e-personal-addresses'." "1.3.8")
(defcustom mu4e-use-fancy-chars nil (defcustom mu4e-use-fancy-chars nil
"When set, allow fancy (Unicode) characters for marks/threads. "When set, allow fancy (Unicode) characters for marks/threads.
@ -200,18 +200,18 @@ are plists" "1.3.7")
(defcustom mu4e-bookmarks (defcustom mu4e-bookmarks
'(( :name "Unread messages" '(( :name "Unread messages"
:query "flag:unread AND NOT flag:trashed" :query "flag:unread AND NOT flag:trashed"
:key ?u) :key ?u)
( :name "Today's messages" ( :name "Today's messages"
:query "date:today..now" :query "date:today..now"
:key ?t) :key ?t)
( :name "Last 7 days" ( :name "Last 7 days"
:query "date:7d..now" :query "date:7d..now"
:show-unread t :show-unread t
:key ?w) :key ?w)
( :name "Messages with images" ( :name "Messages with images"
:query "mime:image/*" :query "mime:image/*"
:key ?p)) :key ?p))
"List of pre-defined queries that are shown on the main screen. "List of pre-defined queries that are shown on the main screen.
Each of the list elements is a plist with at least: Each of the list elements is a plist with at least:
@ -247,9 +247,9 @@ A symbol which is either:
Also see `mu4e-headers-visible-lines' Also see `mu4e-headers-visible-lines'
and `mu4e-headers-visible-columns'." and `mu4e-headers-visible-columns'."
:type '(choice (const :tag "Split horizontally" horizontal) :type '(choice (const :tag "Split horizontally" horizontal)
(const :tag "Split vertically" vertical) (const :tag "Split vertically" vertical)
(const :tag "Single window" single-window) (const :tag "Single window" single-window)
(const :tag "Don't split" nil)) (const :tag "Don't split" nil))
:group 'mu4e-headers) :group 'mu4e-headers)
(defcustom mu4e-view-max-specpdl-size 4096 (defcustom mu4e-view-max-specpdl-size 4096
@ -263,7 +263,7 @@ and `mu4e-headers-visible-columns'."
:group 'mu4e-view) :group 'mu4e-view)
(make-obsolete-variable 'mu4e-show-images (make-obsolete-variable 'mu4e-show-images
'mu4e-view-show-images "0.9.9.x") 'mu4e-view-show-images "0.9.9.x")
(defcustom mu4e-confirm-quit t (defcustom mu4e-confirm-quit t
"Whether to confirm to quit mu4e." "Whether to confirm to quit mu4e."
@ -306,12 +306,12 @@ contexts match, we have the following choices:
Also see `mu4e-compose-context-policy'." Also see `mu4e-compose-context-policy'."
:type '(choice :type '(choice
(const :tag "Always ask what context to use, even if one matches" (const :tag "Always ask what context to use, even if one matches"
always-ask) always-ask)
(const :tag "Ask if none of the contexts match" ask) (const :tag "Ask if none of the contexts match" ask)
(const :tag "Ask when there's no context yet" ask-if-none) (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 "Pick the first context if none match" pick-first)
(const :tag "Don't change the context when none match" nil)) (const :tag "Don't change the context when none match" nil))
:group 'mu4e) :group 'mu4e)
;; crypto ;; crypto
@ -331,8 +331,8 @@ The setting is a symbol:
* `ask': ask before decrypting anything * `ask': ask before decrypting anything
* nil: don't try to decrypt anything." * nil: don't try to decrypt anything."
:type '(choice (const :tag "Try to decrypt automatically" t) :type '(choice (const :tag "Try to decrypt automatically" t)
(const :tag "Ask before decrypting anything" ask) (const :tag "Ask before decrypting anything" ask)
(const :tag "Don't try to decrypt anything" nil)) (const :tag "Don't try to decrypt anything" nil))
:group 'mu4e-crypto) :group 'mu4e-crypto)
;; completion; we put them here rather than in mu4e-compose, as mu4e-utils needs ;; completion; we put them here rather than in mu4e-compose, as mu4e-utils needs
@ -376,14 +376,14 @@ time-based restriction."
"Return the name and the mail-address of a CONTACT. "Return the name and the mail-address of a CONTACT.
It is used as the identity function for converting contacts to It is used as the identity function for converting contacts to
their canonical counterpart; useful as an example." their canonical counterpart; useful as an example."
(let ((name (plist-get contact :name)) (let ((name (plist-get contact :name))
(mail (plist-get contact :mail))) (mail (plist-get contact :mail)))
(list :name name :mail mail))) (list :name name :mail mail)))
(make-obsolete-variable 'mu4e-contacts-rewrite-function (make-obsolete-variable 'mu4e-contacts-rewrite-function
"mu4e-contact-process-function (see docstring)" "mu4e 1.3.2") "mu4e-contact-process-function (see docstring)" "mu4e 1.3.2")
(make-obsolete-variable 'mu4e-compose-complete-ignore-address-regexp (make-obsolete-variable 'mu4e-compose-complete-ignore-address-regexp
"mu4e-contact-process-function (see docstring)" "mu4e 1.3.2") "mu4e-contact-process-function (see docstring)" "mu4e 1.3.2")
(defcustom mu4e-contact-process-function nil (defcustom mu4e-contact-process-function nil
"Function for processing contact information for use in auto-completion. "Function for processing contact information for use in auto-completion.
@ -406,10 +406,10 @@ an RFC-2822-compatible e-mail address."
This can be a regexp matching the address, a list of regexps or a This can be a regexp matching the address, a list of regexps or a
predicate function. A value of nil keeps all the addresses." predicate function. A value of nil keeps all the addresses."
:type '(choice :type '(choice
(const nil) (const nil)
function function
string string
(repeat string)) (repeat string))
:group 'mu4e-compose) :group 'mu4e-compose)
(defcustom mu4e-compose-reply-recipients 'ask (defcustom mu4e-compose-reply-recipients 'ask
@ -417,8 +417,8 @@ predicate function. A value of nil keeps all the addresses."
May be 'ask, 'all, 'sender. Note that that only applies to May be 'ask, 'all, 'sender. Note that that only applies to
non-mailing-list message; for those, mu4e always asks." non-mailing-list message; for those, mu4e always asks."
:type '(choice ask :type '(choice ask
all all
sender) sender)
:group 'mu4e-compose) :group 'mu4e-compose)
(defcustom mu4e-compose-reply-to-address nil (defcustom mu4e-compose-reply-to-address nil
@ -434,8 +434,8 @@ Useful when this is not equal to the From: address."
;; backward compatibility ;; backward compatibility
(make-obsolete-variable 'mu4e-reply-to-address (make-obsolete-variable 'mu4e-reply-to-address
'mu4e-compose-reply-to-address 'mu4e-compose-reply-to-address
"v0.9.9") "v0.9.9")
(defcustom mu4e-compose-keep-self-cc nil (defcustom mu4e-compose-keep-self-cc nil
"When non-nil. keep your e-mail address in Cc: when replying." "When non-nil. keep your e-mail address in Cc: when replying."
@ -477,8 +477,8 @@ parameter refers to the original message being replied to / being
forwarded / re-edited and is nil otherwise. `mu4e-drafts-folder' forwarded / re-edited and is nil otherwise. `mu4e-drafts-folder'
is only evaluated once." is only evaluated once."
:type '(choice :type '(choice
(string :tag "Folder name") (string :tag "Folder name")
(function :tag "Function return folder name")) (function :tag "Function return folder name"))
:group 'mu4e-folders) :group 'mu4e-folders)
(defcustom mu4e-refile-folder "/archive" (defcustom mu4e-refile-folder "/archive"
@ -488,8 +488,8 @@ function that takes a message (a msg plist, see
`mu4e-message-field'), and returns a folder. Note that the `mu4e-message-field'), and returns a folder. Note that the
message parameter refers to the message-at-point." message parameter refers to the message-at-point."
:type '(choice :type '(choice
(string :tag "Folder name") (string :tag "Folder name")
(function :tag "Function return folder name")) (function :tag "Function return folder name"))
:group 'mu4e-folders) :group 'mu4e-folders)
(defcustom mu4e-sent-folder "/sent" (defcustom mu4e-sent-folder "/sent"
@ -500,8 +500,8 @@ function that takes a message (a msg plist, see
message parameter refers to the original message being replied to message parameter refers to the original message being replied to
/ being forwarded / re-edited, and is nil otherwise." / being forwarded / re-edited, and is nil otherwise."
:type '(choice :type '(choice
(string :tag "Folder name") (string :tag "Folder name")
(function :tag "Function return folder name")) (function :tag "Function return folder name"))
:group 'mu4e-folders) :group 'mu4e-folders)
(defcustom mu4e-trash-folder "/trash" (defcustom mu4e-trash-folder "/trash"
@ -516,8 +516,8 @@ message-at-point. When using it when composing a message (see
message being replied to / being forwarded / re-edited, and is message being replied to / being forwarded / re-edited, and is
nil otherwise." nil otherwise."
:type '(choice :type '(choice
(string :tag "Folder name") (string :tag "Folder name")
(function :tag "Function return folder name")) (function :tag "Function return folder name"))
:group 'mu4e-folders) :group 'mu4e-folders)
(defcustom mu4e-maildir-shortcuts nil (defcustom mu4e-maildir-shortcuts nil
@ -741,115 +741,115 @@ mu4e-compose-mode."
;; headers info ;; headers info
(defconst mu4e-header-info (defconst mu4e-header-info
'( (:attachments . '( (:attachments
( :name "Attachments" . ( :name "Attachments"
:shortname "Atts" :shortname "Atts"
:help "Message attachments" :help "Message attachments"
:require-full t :require-full t
:sortable nil)) :sortable nil))
(:bcc . (:bcc
( :name "Bcc" . ( :name "Bcc"
:shortname "Bcc" :shortname "Bcc"
:help "Blind Carbon-Copy recipients for the message" :help "Blind Carbon-Copy recipients for the message"
:sortable t)) :sortable t))
(:cc . (:cc
( :name "Cc" . ( :name "Cc"
:shortname "Cc" :shortname "Cc"
:help "Carbon-Copy recipients for the message" :help "Carbon-Copy recipients for the message"
:sortable t)) :sortable t))
(:date . (:date
( :name "Date" . ( :name "Date"
:shortname "Date" :shortname "Date"
:help "Date/time when the message was written" :help "Date/time when the message was written"
:sortable t)) :sortable t))
(:human-date . (:human-date .
( :name "Date" ( :name "Date"
:shortname "Date" :shortname "Date"
:help "Date/time when the message was written." :help "Date/time when the message was written."
:sortable :date)) :sortable :date))
(:flags . (:flags .
( :name "Flags" ( :name "Flags"
:shortname "Flgs" :shortname "Flgs"
:help "Flags for the message" :help "Flags for the message"
:sortable nil)) :sortable nil))
(:from . (:from .
( :name "From" ( :name "From"
:shortname "From" :shortname "From"
:help "The sender of the message" :help "The sender of the message"
:sortable t)) :sortable t))
(:from-or-to . (:from-or-to .
( :name "From/To" ( :name "From/To"
:shortname "From/To" :shortname "From/To"
:help "Sender of the message if it's not me; otherwise the recipient" :help "Sender of the message if it's not me; otherwise the recipient"
:sortable nil)) :sortable nil))
(:maildir . (:maildir .
( :name "Maildir" ( :name "Maildir"
:shortname "Maildir" :shortname "Maildir"
:help "Maildir for this message" :help "Maildir for this message"
:sortable t)) :sortable t))
(:list . (:list .
( :name "List-Id" ( :name "List-Id"
:shortname "List" :shortname "List"
:help "Mailing list id for this message" :help "Mailing list id for this message"
:sortable t)) :sortable t))
(:mailing-list . (:mailing-list .
( :name "List" ( :name "List"
:shortname "List" :shortname "List"
:help "Mailing list friendly name for this message" :help "Mailing list friendly name for this message"
:sortable :list)) :sortable :list))
(:message-id . (:message-id .
( :name "Message-Id" ( :name "Message-Id"
:shortname "MsgID" :shortname "MsgID"
:help "Message-Id for this message" :help "Message-Id for this message"
:sortable nil)) :sortable nil))
(:path . (:path .
( :name "Path" ( :name "Path"
:shortname "Path" :shortname "Path"
:help "Full filesystem path to the message" :help "Full filesystem path to the message"
:sortable t)) :sortable t))
(:signature . (:signature .
( :name "Signature" ( :name "Signature"
:shortname "Sgn" :shortname "Sgn"
:help "Check for the cryptographic signature" :help "Check for the cryptographic signature"
:require-full t :require-full t
:sortable nil)) :sortable nil))
(:decryption . (:decryption .
( :name "Decryption" ( :name "Decryption"
:shortname "Dec" :shortname "Dec"
:help "Check the cryptographic decryption status" :help "Check the cryptographic decryption status"
:require-full t :require-full t
:sortable nil)) :sortable nil))
(:size . (:size .
( :name "Size" ( :name "Size"
:shortname "Size" :shortname "Size"
:help "Size of the message" :help "Size of the message"
:sortable t)) :sortable t))
(:subject . (:subject .
( :name "Subject" ( :name "Subject"
:shortname "Subject" :shortname "Subject"
:help "Subject of the message" :help "Subject of the message"
:sortable t)) :sortable t))
(:tags . (:tags .
( :name "Tags" ( :name "Tags"
:shortname "Tags" :shortname "Tags"
:help "Tags for the message" :help "Tags for the message"
:sortable nil)) :sortable nil))
(:thread-subject . (:thread-subject .
( :name "Subject" ( :name "Subject"
:shortname "Subject" :shortname "Subject"
:help "Subject of the thread" :help "Subject of the thread"
:sortable :subject)) :sortable :subject))
(:to . (:to .
( :name "To" ( :name "To"
:shortname "To" :shortname "To"
:help "Recipient of the message" :help "Recipient of the message"
:sortable t)) :sortable t))
(:user-agent . (:user-agent .
( :name "User-Agent" ( :name "User-Agent"
:shortname "UA" :shortname "UA"
:help "Program used for writing this message" :help "Program used for writing this message"
:require-full t :require-full t
:sortable t))) :sortable t)))
"An alist of all possible header fields and information about them. "An alist of all possible header fields and information about them.
This is used in the user-interface (the column headers in the header list, and This is used in the user-interface (the column headers in the header list, and
the fields the message view). the fields the message view).
@ -873,15 +873,15 @@ Note, `:sortable' is not supported for custom header fields.")
(defvar mu4e-header-info-custom (defvar mu4e-header-info-custom
'( (:recipnum . '( (:recipnum .
( :name "Number of recipients" ( :name "Number of recipients"
:shortname "Recip#" :shortname "Recip#"
:help "Number of recipients for this message" :help "Number of recipients for this message"
:function :function
(lambda (msg) (lambda (msg)
(format "%d" (format "%d"
(+ (length (mu4e-message-field msg :to)) (+ (length (mu4e-message-field msg :to))
(length (mu4e-message-field msg :cc)))))))) (length (mu4e-message-field msg :cc))))))))
"A list of custom (user-defined) headers. "A list of custom (user-defined) headers.
The format is similar The format is similar
to `mu4e-header-info', but adds a :function property, which to `mu4e-header-info', but adds a :function property, which
should point to a function that takes a message p-list as should point to a function that takes a message p-list as
@ -897,7 +897,7 @@ argument, and returns a string. See the default value of
(defconst mu4e~headers-buffer-name "*mu4e-headers*" (defconst mu4e~headers-buffer-name "*mu4e-headers*"
"Name of the buffer for message headers.") "Name of the buffer for message headers.")
; view ;; view
(defconst mu4e~view-buffer-name "*mu4e-view*" (defconst mu4e~view-buffer-name "*mu4e-view*"
"Name for the message view buffer.") "Name for the message view buffer.")
@ -919,7 +919,7 @@ mu4e-compose.")
(defun mu4e-root-maildir() (defun mu4e-root-maildir()
"Get the root maildir." "Get the root maildir."
(let ((root-maildir (and mu4e~server-props (let ((root-maildir (and mu4e~server-props
(plist-get mu4e~server-props :root-maildir)))) (plist-get mu4e~server-props :root-maildir))))
(unless root-maildir (unless root-maildir
(mu4e-error "root maildir unknown; did you start mu4e?")) (mu4e-error "root maildir unknown; did you start mu4e?"))
root-maildir)) root-maildir))
@ -927,7 +927,7 @@ mu4e-compose.")
(defun mu4e-database-path() (defun mu4e-database-path()
"Get the mu4e database path" "Get the mu4e database path"
(let ((path (and mu4e~server-props (let ((path (and mu4e~server-props
(plist-get mu4e~server-props :database-path)))) (plist-get mu4e~server-props :database-path))))
(unless path (unless path
(mu4e-error "database-path unknown; did you start mu4e?")) (mu4e-error "database-path unknown; did you start mu4e?"))
path)) path))
@ -936,7 +936,7 @@ mu4e-compose.")
"Get the user's personal addresses, if any. If none are set on the server-side, "Get the user's personal addresses, if any. If none are set on the server-side,
fall back to the obsolete `mu4e-user-mail-address-list'." fall back to the obsolete `mu4e-user-mail-address-list'."
(let ((addrs (and mu4e~server-props (let ((addrs (and mu4e~server-props
(plist-get mu4e~server-props :personal-addresses)))) (plist-get mu4e~server-props :personal-addresses))))
(if addrs addrs mu4e-user-mail-address-list))) (if addrs addrs mu4e-user-mail-address-list)))
(defun mu4e-server-version() (defun mu4e-server-version()

File diff suppressed because it is too large Load Diff

View File

@ -60,9 +60,9 @@ window, unless BACKGROUND (prefix-argument) is non-nil."
"Quit the mu4e session." "Quit the mu4e session."
(interactive) (interactive)
(if mu4e-confirm-quit (if mu4e-confirm-quit
(when (y-or-n-p (mu4e-format "Are you sure you want to quit?")) (when (y-or-n-p (mu4e-format "Are you sure you want to quit?"))
(mu4e~stop)) (mu4e~stop))
(mu4e~stop))) (mu4e~stop)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(provide 'mu4e) (provide 'mu4e)

View File

@ -58,21 +58,21 @@
(defun org~mu4e-mime-file (ext path id) (defun org~mu4e-mime-file (ext path id)
"Create a file of type EXT at PATH with ID for an attachment." "Create a file of type EXT at PATH with ID for an attachment."
(format (concat "<#part type=\"%s\" filename=\"%s\" " (format (concat "<#part type=\"%s\" filename=\"%s\" "
"disposition=inline id=\"<%s>\">\n<#/part>\n") "disposition=inline id=\"<%s>\">\n<#/part>\n")
ext path id)) ext path id))
(defun org~mu4e-mime-multipart (plain html &optional images) (defun org~mu4e-mime-multipart (plain html &optional images)
"Create a multipart/alternative with PLAIN and HTML alternatives. "Create a multipart/alternative with PLAIN and HTML alternatives.
If the html portion of the message includes IMAGES, wrap the html If the html portion of the message includes IMAGES, wrap the html
and images in a multipart/related part." and images in a multipart/related part."
(concat "<#multipart type=alternative><#part type=text/plain>" (concat "<#multipart type=alternative><#part type=text/plain>"
plain plain
(when images "<#multipart type=related>") (when images "<#multipart type=related>")
"<#part type=text/html>" "<#part type=text/html>"
html html
images images
(when images "<#/multipart>\n") (when images "<#/multipart>\n")
"<#/multipart>\n")) "<#/multipart>\n"))
(defun org~mu4e-mime-replace-images (str current-file) (defun org~mu4e-mime-replace-images (str current-file)
"Replace images in html files STR in CURRENT-FILE with cid links." "Replace images in html files STR in CURRENT-FILE with cid links."
@ -90,7 +90,7 @@ and images in a multipart/related part."
(ext (file-name-extension path)) (ext (file-name-extension path))
(id (replace-regexp-in-string "[\/\\\\]" "_" path))) (id (replace-regexp-in-string "[\/\\\\]" "_" path)))
(cl-pushnew (org~mu4e-mime-file (cl-pushnew (org~mu4e-mime-file
(concat "image/" ext) path id) (concat "image/" ext) path id)
html-images html-images
:test 'equal) :test 'equal)
id))) id)))
@ -102,36 +102,36 @@ and images in a multipart/related part."
(unless (fboundp 'org-export-string-as) (unless (fboundp 'org-export-string-as)
(mu4e-error "Required function 'org-export-string-as not found")) (mu4e-error "Required function 'org-export-string-as not found"))
(let* ((begin (let* ((begin
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(search-forward mail-header-separator))) (search-forward mail-header-separator)))
(end (point-max)) (end (point-max))
(raw-body (buffer-substring begin end)) (raw-body (buffer-substring begin end))
(tmp-file (make-temp-name (expand-file-name "mail" (tmp-file (make-temp-name (expand-file-name "mail"
temporary-file-directory))) temporary-file-directory)))
;; because we probably don't want to skip part of our mail ;; because we probably don't want to skip part of our mail
(org-export-skip-text-before-1st-heading nil) (org-export-skip-text-before-1st-heading nil)
;; because we probably don't want to export a huge style file ;; because we probably don't want to export a huge style file
(org-export-htmlize-output-type 'inline-css) (org-export-htmlize-output-type 'inline-css)
;; makes the replies with ">"s look nicer ;; makes the replies with ">"s look nicer
(org-export-preserve-breaks t) (org-export-preserve-breaks t)
;; dvipng for inline latex because MathJax doesn't work in mail ;; dvipng for inline latex because MathJax doesn't work in mail
(org-export-with-LaTeX-fragments (org-export-with-LaTeX-fragments
(if (executable-find "dvipng") 'dvipng (if (executable-find "dvipng") 'dvipng
(mu4e-message "Cannot find dvipng, ignore inline LaTeX") nil)) (mu4e-message "Cannot find dvipng, ignore inline LaTeX") nil))
;; to hold attachments for inline html images ;; to hold attachments for inline html images
(html-and-images (html-and-images
(org~mu4e-mime-replace-images (org~mu4e-mime-replace-images
(org-export-string-as raw-body 'html t) (org-export-string-as raw-body 'html t)
tmp-file)) tmp-file))
(html-images (cdr html-and-images)) (html-images (cdr html-and-images))
(html (car html-and-images))) (html (car html-and-images)))
(delete-region begin end) (delete-region begin end)
(save-excursion (save-excursion
(goto-char begin) (goto-char begin)
(newline) (newline)
(insert (org~mu4e-mime-multipart (insert (org~mu4e-mime-multipart
raw-body html (mapconcat 'identity html-images "\n")))))) raw-body html (mapconcat 'identity html-images "\n"))))))
;; next some functions to make the org/mu4e-compose-mode switch as smooth as ;; next some functions to make the org/mu4e-compose-mode switch as smooth as
;; possible. ;; possible.
@ -140,10 +140,10 @@ and images in a multipart/related part."
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(let* ((eoh (when (search-forward mail-header-separator) (let* ((eoh (when (search-forward mail-header-separator)
(match-end 0))) (match-end 0)))
(olay (make-overlay (point-min) eoh))) (olay (make-overlay (point-min) eoh)))
(when olay (when olay
(overlay-put olay 'face 'font-lock-comment-face))))) (overlay-put olay 'face 'font-lock-comment-face)))))
(defun org~mu4e-mime-undecorate-headers () (defun org~mu4e-mime-undecorate-headers ()
"Don't make the headers visually distinctive. "Don't make the headers visually distinctive.
@ -151,7 +151,7 @@ and images in a multipart/related part."
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(let* ((eoh (when (search-forward mail-header-separator) (let* ((eoh (when (search-forward mail-header-separator)
(match-end 0)))) (match-end 0))))
(remove-overlays (point-min) eoh)))) (remove-overlays (point-min) eoh))))
(defvar org-mu4e-convert-to-html nil (defvar org-mu4e-convert-to-html nil
@ -171,34 +171,34 @@ rich-text version of what is assumed to be an org mode body."
or org-mode (when in the body)." or org-mode (when in the body)."
(interactive) (interactive)
(let* ((sepapoint (let* ((sepapoint
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(search-forward-regexp mail-header-separator nil t)))) (search-forward-regexp mail-header-separator nil t))))
;; only do stuff when the sepapoint exist; note that after sending the ;; only do stuff when the sepapoint exist; note that after sending the
;; message, this function maybe called on a message with the sepapoint ;; message, this function maybe called on a message with the sepapoint
;; stripped. This is why we don't use `message-point-in-header'. ;; stripped. This is why we don't use `message-point-in-header'.
(when sepapoint (when sepapoint
(cond (cond
;; we're in the body, but in mu4e-compose-mode? ;; we're in the body, but in mu4e-compose-mode?
;; if so, switch to org-mode ;; if so, switch to org-mode
((and (> (point) sepapoint) (eq major-mode 'mu4e-compose-mode)) ((and (> (point) sepapoint) (eq major-mode 'mu4e-compose-mode))
(org-mode) (org-mode)
(add-hook 'before-save-hook (add-hook 'before-save-hook
(lambda () (lambda ()
(mu4e-error "Switch to mu4e-compose-mode (M-m) before saving")) (mu4e-error "Switch to mu4e-compose-mode (M-m) before saving"))
nil t) nil t)
(org~mu4e-mime-decorate-headers) (org~mu4e-mime-decorate-headers)
(local-set-key (kbd "M-m") (local-set-key (kbd "M-m")
(lambda (keyseq) (lambda (keyseq)
(interactive "kEnter mu4e-compose-mode key sequence: ") (interactive "kEnter mu4e-compose-mode key sequence: ")
(let ((func (lookup-key mu4e-compose-mode-map keyseq))) (let ((func (lookup-key mu4e-compose-mode-map keyseq)))
(if func (funcall func) (insert keyseq)))))) (if func (funcall func) (insert keyseq))))))
;; we're in the headers, but in org-mode? ;; we're in the headers, but in org-mode?
;; if so, switch to mu4e-compose-mode ;; if so, switch to mu4e-compose-mode
((and (<= (point) sepapoint) (eq major-mode 'org-mode)) ((and (<= (point) sepapoint) (eq major-mode 'org-mode))
(org~mu4e-mime-undecorate-headers) (org~mu4e-mime-undecorate-headers)
(mu4e-compose-mode) (mu4e-compose-mode)
(add-hook 'message-send-hook 'org~mu4e-mime-convert-to-html-maybe nil t))) (add-hook 'message-send-hook 'org~mu4e-mime-convert-to-html-maybe nil t)))
;; and add the hook ;; and add the hook
(add-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t t)))) (add-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t t))))
@ -212,12 +212,12 @@ Edit the message body using org mode. DEPRECATED."
;; post-command-hook is set; hackish...but a buffer-local variable does not ;; post-command-hook is set; hackish...but a buffer-local variable does not
;; seem to survive buffer switching ;; seem to survive buffer switching
(if (not (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)) (if (not (member 'org~mu4e-mime-switch-headers-or-body post-command-hook))
(progn (progn
(org~mu4e-mime-switch-headers-or-body) (org~mu4e-mime-switch-headers-or-body)
(mu4e-message (mu4e-message
(concat (concat
"org-mu4e-compose-org-mode enabled; " "org-mu4e-compose-org-mode enabled; "
"press M-m before issuing message-mode commands"))) "press M-m before issuing message-mode commands")))
(progn ;; otherwise, remove crap (progn ;; otherwise, remove crap
(remove-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t) (remove-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t)
(org~mu4e-mime-undecorate-headers) ;; shut off org-mode stuff (org~mu4e-mime-undecorate-headers) ;; shut off org-mode stuff