diff --git a/mu4e/mu4e-actions.el b/mu4e/mu4e-actions.el index 8e682528..9a63c326 100644 --- a/mu4e/mu4e-actions.el +++ b/mu4e/mu4e-actions.el @@ -1,6 +1,6 @@ ;;; mu4e-actions.el -- part of mu4e, the mu mail user agent ;; -;; Copyright (C) 2011-2017 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -37,7 +37,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-action-count-lines (msg) - "Count the number of lines in the e-mail message. + "Count the number of lines in the e-mail MSG. Works for headers view and message-view." (message "Number of lines: %s" (shell-command-to-string @@ -54,17 +54,17 @@ Works for headers view and message-view." "Path to the msg2pdf toy.") (defun mu4e-action-view-as-pdf (msg) - "Convert the message to pdf, then show it. + "Convert MSG to pdf, then show it. Works for the message view." (unless (file-executable-p mu4e-msg2pdf) - (mu4e-error "msg2pdf not found; please set `mu4e-msg2pdf'")) + (mu4e-error "Program msg2pdf not found; please set `mu4e-msg2pdf'")) (let* ((pdf - (shell-command-to-string - (concat mu4e-msg2pdf " " - (shell-quote-argument (mu4e-message-field msg :path)) - " 2> /dev/null"))) - (pdf (and pdf (> (length pdf) 5) - (substring pdf 0 -1)))) ;; chop \n + (shell-command-to-string + (concat mu4e-msg2pdf " " + (shell-quote-argument (mu4e-message-field msg :path)) + " 2> /dev/null"))) + (pdf (and pdf (> (length pdf) 5) + (substring pdf 0 -1)))) ;; chop \n (unless (and pdf (file-exists-p pdf)) (mu4e-warn "Failed to create PDF file")) (find-file pdf))) @@ -72,15 +72,15 @@ Works for the message view." (defun mu4e~write-body-to-html (msg) - "Write the body (either html or text) to a temporary file; + "Write MSG's body (either html or text) to a temporary file; return the filename." (let* ((html (mu4e-message-field msg :body-html)) - (txt (mu4e-message-field msg :body-txt)) - (tmpfile (mu4e-make-temp-file "html")) - (attachments (cl-remove-if (lambda (part) - (or (null (plist-get part :attachment)) - (null (plist-get part :cid)))) - (mu4e-message-field msg :parts)))) + (txt (mu4e-message-field msg :body-txt)) + (tmpfile (mu4e-make-temp-file "html")) + (attachments (cl-remove-if (lambda (part) + (or (null (plist-get part :attachment)) + (null (plist-get part :cid)))) + (mu4e-message-field msg :parts)))) (unless (or html txt) (mu4e-error "No body part for this message")) (with-temp-buffer @@ -96,37 +96,37 @@ return the filename." (write-file tmpfile) ;; rewrite attachment urls (mapc (lambda (attachment) - (goto-char (point-min)) - (while (re-search-forward (format "src=\"cid:%s\"" - (plist-get attachment :cid)) nil t) - (if (plist-get attachment :temp) - (replace-match (format "src=\"%s\"" - (plist-get attachment :temp))) - (replace-match (format "src=\"%s%s\"" temporary-file-directory - (plist-get attachment :name))) - (let ((tmp-attachment-name - (format "%s%s" temporary-file-directory - (plist-get attachment :name)))) - (mu4e~proc-extract 'save (mu4e-message-field msg :docid) - (plist-get attachment :index) - mu4e-decryption-policy tmp-attachment-name) - (mu4e-remove-file-later tmp-attachment-name))))) - attachments) + (goto-char (point-min)) + (while (re-search-forward (format "src=\"cid:%s\"" + (plist-get attachment :cid)) nil t) + (if (plist-get attachment :temp) + (replace-match (format "src=\"%s\"" + (plist-get attachment :temp))) + (replace-match (format "src=\"%s%s\"" temporary-file-directory + (plist-get attachment :name))) + (let ((tmp-attachment-name + (format "%s%s" temporary-file-directory + (plist-get attachment :name)))) + (mu4e~proc-extract 'save (mu4e-message-field msg :docid) + (plist-get attachment :index) + mu4e-decryption-policy tmp-attachment-name) + (mu4e-remove-file-later tmp-attachment-name))))) + attachments) (save-buffer) tmpfile))) (defun mu4e-action-view-in-browser (msg) - "View the body of the message in a browser. + "View the body of MSG in a web browser. You can influence the browser to use with the variable `browse-url-generic-program', and see the discussion of privacy aspects in `(mu4e) Displaying rich-text messages'." (browse-url (concat "file://" - (mu4e~write-body-to-html msg)))) + (mu4e~write-body-to-html msg)))) (defun mu4e-action-view-with-xwidget (msg) - "View the body of the message inside xwidget-webkit. This is -only available in emacs 25+; also see the discussion of privacy -aspects in `(mu4e) Displaying rich-text messages'." + "View the body of MSG inside xwidget-webkit. +This is only available in Emacs 25+; also see the discussion of +privacy aspects in `(mu4e) Displaying rich-text messages'." (unless (fboundp 'xwidget-webkit-browse-url) (mu4e-error "No xwidget support available")) (xwidget-webkit-browse-url @@ -138,10 +138,10 @@ aspects in `(mu4e) Displaying rich-text messages'." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defconst mu4e-text2speech-command "festival --tts" - "Program that speaks out text it receives on standard-input.") + "Program that speaks out text it receives on standard input.") (defun mu4e-action-message-to-speech (msg) - "Pronounce the message text using `mu4e-text2speech-command'." + "Pronounce MSG's body text using `mu4e-text2speech-command'." (unless (mu4e-message-field msg :body-txt) (mu4e-warn "No text body for this message")) (with-temp-buffer @@ -151,18 +151,15 @@ aspects in `(mu4e) Displaying rich-text messages'." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-captured-message nil - "The last-captured message (the s-expression).") + "The most recently captured message.") (defun mu4e-action-capture-message (msg) - "Remember MSG; we can create a an attachment based on this msg -with `mu4e-compose-attach-captured-message'." + "Remember MSG. +Later, we can create an attachment based on this message with +`mu4e-compose-attach-captured-message'." (setq mu4e-captured-message msg) (message "Message has been captured")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -171,13 +168,10 @@ with `mu4e-compose-attach-captured-message'." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-action-copy-message-file-path (msg) - "Save the full path the message file for the current message to -the kill-ring." + "Save the full path for the current MSG to the kill ring." (kill-new (mu4e-message-field msg :path))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-org-contacts-file nil @@ -189,32 +183,31 @@ Needed by `mu4e-action-add-org-contact'.") (defvar org-capture-templates nil))) (defun mu4e-action-add-org-contact (msg) - "Add an org-contact entry based on the From: address of the -current message (in headers or view). You need to set -`mu4e-org-contacts-file' to the full path to the file where you -store your org-contacts." + "Add an org-contact based on the sender ddress of the current MSG. +You need to set `mu4e-org-contacts-file' to the full path to the +file where you store your org-contacts." (unless (require 'org-capture nil 'noerror) - (mu4e-error "org-capture is not available.")) + (mu4e-error "Feature org-capture is not available")) (unless mu4e-org-contacts-file - (mu4e-error "`mu4e-org-contacts-file' is not defined.")) + (mu4e-error "Variable `mu4e-org-contacts-file' is nil")) (let* ((sender (car-safe (mu4e-message-field msg :from))) - (name (car-safe sender)) (email (cdr-safe sender)) - (blurb - (format - (concat - "* %%?%s\n" - ":PROPERTIES:\n" - ":EMAIL: %s\n" - ":NICK:\n" - ":BIRTHDAY:\n" - ":END:\n\n") - (or name email "") - (or email ""))) - (key "mu4e-add-org-contact-key") - (org-capture-templates - (append org-capture-templates - (list (list key "contacts" 'entry - (list 'file mu4e-org-contacts-file) blurb))))) + (name (car-safe sender)) (email (cdr-safe sender)) + (blurb + (format + (concat + "* %%?%s\n" + ":PROPERTIES:\n" + ":EMAIL: %s\n" + ":NICK:\n" + ":BIRTHDAY:\n" + ":END:\n\n") + (or name email "") + (or email ""))) + (key "mu4e-add-org-contact-key") + (org-capture-templates + (append org-capture-templates + (list (list key "contacts" 'entry + (list 'file mu4e-org-contacts-file) blurb))))) (message "%S" org-capture-templates) (when (fboundp 'org-capture) (org-capture nil key)))) @@ -234,16 +227,16 @@ store your org-contacts." (unless prompt (setq prompt "Target directory:")) (file-truename - (completing-read prompt 'read-file-name-internal #'file-directory-p - nil nil 'mu4e~patch-directory-history))) + (completing-read prompt 'read-file-name-internal #'file-directory-p + nil nil 'mu4e~patch-directory-history))) (defun mu4e-action-git-apply-patch (msg) "Apply `MSG' as a git patch." (let ((path (mu4e~read-patch-directory "Target directory: "))) (let ((default-directory path)) (shell-command - (format "git apply %s" - (shell-quote-argument (mu4e-message-field msg :path))))))) + (format "git apply %s" + (shell-quote-argument (mu4e-message-field msg :path))))))) (defun mu4e-action-git-apply-mbox (msg &optional signoff) "Apply `MSG' a git patch with optional `SIGNOFF'. @@ -252,15 +245,15 @@ If the `default-directory' matches the most recent history entry don't bother asking for the git tree again (useful for bulk actions)." (let ((cwd (substring-no-properties - (or (car mu4e~patch-directory-history) - "not-a-dir")))) + (or (car mu4e~patch-directory-history) + "not-a-dir")))) (unless (and (stringp cwd) (string= default-directory cwd)) (setq cwd (mu4e~read-patch-directory "Target directory: "))) (let ((default-directory cwd)) (shell-command - (format "git am %s %s" - (if signoff "--signoff" "") - (shell-quote-argument (mu4e-message-field msg :path))))))) + (format "git am %s %s" + (if signoff "--signoff" "") + (shell-quote-argument (mu4e-message-field msg :path))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -268,73 +261,70 @@ bother asking for the git tree again (useful for bulk actions)." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-action-tags-header "X-Keywords" - "Header where tags are stored. Used by `mu4e-action-retag-message'. - Make sure it is one of the headers mu recognizes for storing - tags: X-Keywords, X-Label, Keywords. Also note that changing - this setting on already tagged messages can lead to messages - with multiple tags headers.") + "Header where tags are stored. +Used by `mu4e-action-retag-message'. Make sure it is one of the +headers mu recognizes for storing tags: X-Keywords, X-Label, +Keywords. Also note that changing this setting on already tagged +messages can lead to messages with multiple tags headers.") (defvar mu4e-action-tags-completion-list '() - "List of tags to show for autocompletion in - `mu4e-action-retag-message'.") + "List of tags for completion in `mu4e-action-retag-message'.") (defun mu4e~contains-line-matching (regexp path) - "Determine whether the file at path contains a line matching - the given regexp." + "Does file at PATH contain a line matching the given REGEXP?" (with-temp-buffer (insert-file-contents path) (save-excursion (goto-char (point-min)) (if (re-search-forward regexp nil t) - t - nil)))) + t + nil)))) (defun mu4e~replace-first-line-matching (regexp to-string path) - "Replace the first line in the file at path that matches regexp - with the string replace." + "Replace first line matching REGEXP in PATH with TO-STRING." (with-temp-file path (insert-file-contents path) (save-excursion (goto-char (point-min)) (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) - "Change tags of a message. Accepts a comma-separated list of - additions and removals. + "Change tags of MSG with RETAG-ARG. - Example: +tag,+long tag,-oldtag +RETAG-ARG is a comma-separated list of additions and removals. - would add 'tag' and 'long tag', and remove 'oldtag'." +Example: +tag,+long tag,-oldtag +would add 'tag' and 'long tag', and remove 'oldtag'." (let* ( - (path (mu4e-message-field msg :path)) - (maildir (mu4e-message-field msg :maildir)) - (oldtags (mu4e-message-field msg :tags)) - (tags-completion - (append - mu4e-action-tags-completion-list - (mapcar (lambda (tag) (format "+%s" tag)) - mu4e-action-tags-completion-list) - (mapcar (lambda (tag) (format "-%s" tag)) - oldtags))) - (retag (if retag-arg - (split-string retag-arg ",") - (completing-read-multiple "Tags: " tags-completion))) - (header mu4e-action-tags-header) - (sep (cond ((string= header "Keywords") ", ") - ((string= header "X-Label") " ") - ((string= header "X-Keywords") ", ") - (t ", "))) - (taglist (if oldtags (copy-sequence oldtags) '())) - tagstr) + (path (mu4e-message-field msg :path)) + (maildir (mu4e-message-field msg :maildir)) + (oldtags (mu4e-message-field msg :tags)) + (tags-completion + (append + mu4e-action-tags-completion-list + (mapcar (lambda (tag) (format "+%s" tag)) + mu4e-action-tags-completion-list) + (mapcar (lambda (tag) (format "-%s" tag)) + oldtags))) + (retag (if retag-arg + (split-string retag-arg ",") + (completing-read-multiple "Tags: " tags-completion))) + (header mu4e-action-tags-header) + (sep (cond ((string= header "Keywords") ", ") + ((string= header "X-Label") " ") + ((string= header "X-Keywords") ", ") + (t ", "))) + (taglist (if oldtags (copy-sequence oldtags) '())) + tagstr) (dolist (tag retag taglist) (cond - ((string-match "^\\+\\(.+\\)" tag) - (setq taglist (push (match-string 1 tag) taglist))) - ((string-match "^\\-\\(.+\\)" tag) - (setq taglist (delete (match-string 1 tag) taglist))) - (t - (setq taglist (push tag taglist))))) + ((string-match "^\\+\\(.+\\)" tag) + (setq taglist (push (match-string 1 tag) taglist))) + ((string-match "^\\-\\(.+\\)" tag) + (setq taglist (delete (match-string 1 tag) taglist))) + (t + (setq taglist (push tag taglist))))) (setq taglist (sort (delete-dups taglist) 'string<)) (setq tagstr (mapconcat 'identity taglist sep)) @@ -345,32 +335,32 @@ bother asking for the git tree again (useful for bulk actions)." (if (not (mu4e~contains-line-matching (concat header ":.*") path)) ;; Add tags header just before the content (mu4e~replace-first-line-matching - "^$" (concat header ": " tagstr "\n") path) + "^$" (concat header ": " tagstr "\n") path) ;; replaces keywords, restricted to the header (mu4e~replace-first-line-matching - (concat header ":.*") - (concat header ": " tagstr) - path)) + (concat header ":.*") + (concat header ": " tagstr) + path)) (mu4e-message (concat "tagging: " (mapconcat 'identity taglist ", "))) (mu4e-refresh-message path maildir))) (defun mu4e-action-show-thread (msg) - "Show all messages that are in the same thread as the message -at point. Point remains on the message with the message-id where -the action was invoked. If invoked in view-mode, continue to -display the message." + "Show thread for message at point with point remaining on MSG. +I.e., point remains on the message with the message-id where the +action was invoked. If invoked in view mode, continue to display +the message." (let ((msgid (mu4e-message-field msg :message-id))) (when msgid (let ((mu4e-headers-show-threads t) - (mu4e-headers-include-related t)) - (mu4e-headers-search - (format "msgid:%s" msgid) - nil nil nil - msgid (and (eq major-mode 'mu4e-view-mode) - (not (eq mu4e-split-view 'single-window)))))))) + (mu4e-headers-include-related t)) + (mu4e-headers-search + (format "msgid:%s" msgid) + nil nil nil + msgid (and (eq major-mode 'mu4e-view-mode) + (not (eq mu4e-split-view 'single-window)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - (provide 'mu4e-actions) +;;; mu4e-actions.el ends here