diff options
Diffstat (limited to '_spacemacs.d/local')
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-actions.el | 316 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-compose.el | 839 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-context.el | 157 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-contrib.el | 165 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-draft.el | 495 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-headers.el | 1717 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-lists.el | 93 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-main.el | 225 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-mark.el | 466 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-message.el | 284 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-meta.el | 11 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-proc.el | 525 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-speedbar.el | 125 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-utils.el | 1281 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-vars.el | 927 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e-view.el | 1542 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/mu4e.el | 102 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/org-mu4e.el | 323 | ||||
-rw-r--r-- | _spacemacs.d/local/mu4e/org-old-mu4e.el | 289 |
19 files changed, 0 insertions, 9882 deletions
diff --git a/_spacemacs.d/local/mu4e/mu4e-actions.el b/_spacemacs.d/local/mu4e/mu4e-actions.el deleted file mode 100644 index 0128c52..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-actions.el +++ /dev/null @@ -1,316 +0,0 @@ -;;; mu4e-actions.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; Example actions for messages, attachments (see chapter 'Actions' in the -;; manual) - -;;; Code: -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) -(require 'cl) -(require 'ido) - -(require 'mu4e-utils) -(require 'mu4e-message) -(require 'mu4e-meta) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e-action-count-lines (msg) - "Count the number of lines in the e-mail message. -Works for headers view and message-view." - (message "Number of lines: %s" - (shell-command-to-string - (concat "wc -l < " (shell-quote-argument (mu4e-message-field msg :path)))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e-msg2pdf (concat mu4e-builddir "/toys/msg2pdf/msg2pdf") - "Path to the msg2pdf toy.") - -(defun mu4e-action-view-as-pdf (msg) - "Convert the message 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'")) - (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 - (unless (and pdf (file-exists-p pdf)) - (mu4e-warn "Failed to create PDF file")) - (find-file pdf))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defun mu4e~write-body-to-html (msg) - "Write the 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 (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 - (insert "<head><meta charset=\"UTF-8\"></head>\n") - (insert (or html (concat "<pre>" txt "</pre>"))) - (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))) - (mu4e~proc-extract 'save (mu4e-message-field msg :docid) (plist-get attachment :index) mu4e-decryption-policy temporary-file-directory) - (mu4e-remove-file-later (format "%s%s" temporary-file-directory (plist-get attachment :name)))))) - attachments) - (save-buffer) - tmpfile))) - -(defun mu4e-action-view-in-browser (msg) - "View the body of the message in a 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)))) - -(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'." - (unless (fboundp 'xwidget-webkit-browse-url) - (mu4e-error "No xwidget support available")) - (xwidget-webkit-browse-url - (concat "file://" (mu4e~write-body-to-html msg)) t)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defconst mu4e-text2speech-command "festival --tts" - "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'." - (unless (mu4e-message-field msg :body-txt) - (mu4e-warn "No text body for this message")) - (with-temp-buffer - (insert (mu4e-message-field msg :body-txt)) - (shell-command-on-region (point-min) (point-max) - mu4e-text2speech-command))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - - - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e-captured-message nil - "The last-captured message (the s-expression).") - -(defun mu4e-action-capture-message (msg) - "Remember MSG; we can create a an attachment based on this msg -with `mu4e-compose-attach-captured-message'." - (setq mu4e-captured-message msg) - (message "Message has been captured")) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e-org-contacts-file nil - "File to store contact information for org-contacts. -Needed by `mu4e-action-add-org-contact'.") - -(eval-when-compile ;; silence compiler warning about free variable - (unless (require 'org-capture nil 'noerror) - (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." - (unless (require 'org-capture nil 'noerror) - (mu4e-error "org-capture is not available.")) - (unless mu4e-org-contacts-file - (mu4e-error "`mu4e-org-contacts-file' is not defined.")) - (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))))) - (message "%S" org-capture-templates) - (when (fboundp 'org-capture) - (org-capture nil key)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e-action-git-apply-patch (msg) - "Apply the git [patch] message." - (let ((path (ido-read-directory-name "Target directory: " - (car ido-work-directory-list) - "~/" t))) - (setf ido-work-directory-list - (cons path (delete path ido-work-directory-list))) - (shell-command - (format "cd %s; git apply %s" - path - (mu4e-message-field msg :path))))) - -(defun mu4e-action-git-apply-mbox (msg) - "Apply and commit the git [patch] message." - (let ((path (ido-read-directory-name "Target directory: " - (car ido-work-directory-list) - "~/" t))) - (setf ido-work-directory-list - (cons path (delete path ido-work-directory-list))) - (shell-command - (format "cd %s; git am %s" - path - (mu4e-message-field msg :path))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(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.") - -(defun mu4e~contains-line-matching (regexp path) - "Determine whether the file at path contains 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)))) - -(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." - (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))))) - -(defun mu4e-action-retag-message (msg &optional retag-arg) - "Change tags of a message. Example: +tag \"+long tag\" -oldtag - adds 'tag' and 'long tag', and removes oldtag." - (let* ((retag (or retag-arg (read-string "Tags: "))) - (path (mu4e-message-field msg :path)) - (maildir (mu4e-message-field msg :maildir)) - (oldtags (mu4e-message-field msg :tags)) - (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 (split-string-and-unquote 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))))) - - (setq taglist (sort (delete-dups taglist) 'string<)) - (setq tagstr (mapconcat 'identity taglist sep)) - - (setq tagstr (replace-regexp-in-string "[\\&]" "\\\\\\&" tagstr)) - (setq tagstr (replace-regexp-in-string "[/]" "\\&" tagstr)) - - (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) - - ;; replaces keywords, restricted to the header - (mu4e~replace-first-line-matching - (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." - (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 (eq major-mode 'mu4e-view-mode)))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(provide 'mu4e-actions) diff --git a/_spacemacs.d/local/mu4e/mu4e-compose.el b/_spacemacs.d/local/mu4e/mu4e-compose.el deleted file mode 100644 index 32d238d..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-compose.el +++ /dev/null @@ -1,839 +0,0 @@ -;; -*-mode: emacs-lisp; tab-width: 8; indent-tabs-mode: t -*- -;; mu4e-compose.el -- part of mu4e, the mu mail user agent for emacs -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; In this file, various functions to compose/send messages, piggybacking on -;; gnus' message mode - - -;; Magic / Rupe Goldberg - -;; 1) When we reply/forward a message, we get it from the backend, ie: -;; we send to the backend (mu4e-compose): -;; compose type:reply docid:30935 -;; backend responds with: -;; (:compose reply :original ( .... <original message> )) - - -;; 2) When we compose a message, message and headers are separated by -;; `mail-header-separator', ie. '--text follows this line--. We use -;; before-save-hook and after-save-hook to remove/re-add this special line, so -;; it stays in the buffer, but never hits the disk. -;; see: -;; mu4e~compose-insert-mail-header-separator -;; mu4e~compose-remove-mail-header-separator -;; -;; (maybe we can get away with remove it only just before sending? what does -;; gnus do?) - -;; 3) When sending a message, we want to do a few things: -;; a) move the message from drafts to the sent folder (maybe; depends on -;; `mu4e-sent-messages-behavior') -;; b) if it's a reply, mark the replied-to message as "R", i.e. replied -;; if it's a forward, mark the forwarded message as "P", i.e. passed (forwarded) -;; c) kill all buffers looking at the sent message - -;; a) is dealt with by message-mode, but we need to tell it where to move the -;; sent message. We do this by adding an Fcc: header with the target folder, -;; see `mu4e~compose-setup-fcc-maybe'. Since message-mode does not natively -;; understand maildirs, we also need to tell it what to do, so we also set -;; `message-fcc-handler-function' there. Finally, we add the the message in -;; the sent-folder to the database. -;; -;; b) this is handled in `mu4e~compose-set-parent-flag' -;; -;; c) this is handled in our handler for the `sent'-message from the backend -;; (`mu4e-sent-handler') -;; - -;;; Code: - -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) -(require 'cl) - -(require 'message) -(require 'mail-parse) -(require 'smtpmail) -(require 'rfc2368) - -(require 'mu4e-utils) -(require 'mu4e-vars) -(require 'mu4e-proc) -(require 'mu4e-actions) -(require 'mu4e-message) -(require 'mu4e-draft) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Composing / Sending messages -(defgroup mu4e-compose nil - "Customizations for composing/sending messages." - :group 'mu4e) - -(defcustom mu4e-sent-messages-behavior 'sent - "Determines what mu4e does with sent messages. - -This is one of the symbols: -* `sent' move the sent message to the Sent-folder (`mu4e-sent-folder') -* `trash' move the sent message to the Trash-folder (`mu4e-trash-folder') -* `delete' delete the sent message. - -Note, when using GMail/IMAP, you should set this to either -`trash' or `delete', since GMail already takes care of keeping -copies in the sent folder. - -Alternatively, `mu4e-sent-messages-behavior' can be a function -which takes no arguments, and which should return on of the mentioned symbols, -for example: - - (setq mu4e-sent-messages-behavior (lambda () - (if (string= (message-sendmail-envelope-from) \"foo@example.com\") - 'delete 'sent))) - -The various `message-' functions from `message-mode' are available -for querying the message information." - :type '(choice (const :tag "move message to mu4e-sent-folder" sent) - (const :tag "move message to mu4e-trash-folder" trash) - (const :tag "delete message" delete)) - :safe 'symbolp - :group 'mu4e-compose) - - -(defcustom mu4e-compose-context-policy 'ask - "Policy for determining the context when composing a new message. - -If the value is `always-ask', ask the user unconditionally. - -In all other cases, if any context matches (using its match -function), this context is used. Otherwise, if none of the -contexts match, we have the following choices: - -- `pick-first': pick the first of the contexts available (ie. the default) -- `ask': ask the user -- `ask-if-none': ask if there is no context yet, otherwise leave it as it is -- nil: return nil; leaves the current context as is. - -Also see `mu4e-context-policy'." - :type '(choice - (const :tag "Always ask what context to use" 'always-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 "Pick the first context if none match" 'pick-first) - (const :tag "Don't change the context when none match" nil) - :safe 'symbolp - :group 'mu4e-compose)) - - -(defcustom mu4e-compose-crypto-reply-policy 'sign-and-encrypt - "Policy for signing/encrypting replies to encrypted messages. -We have the following choices: - -- `sign': sign the reply -- `sign-and-encrypt': sign and encrypt the repy -- `encrypt': encrypt the reply, but don't sign it. -- anything else: do nothing." - :type '(choice - (const :tag "Sign the reply" 'sign) - (const :tag "Sign and encrypt the reply" 'sign-and-encrypt) - (const :tag "Encrypt the reply" 'encrypt) - (const :tag "Don't do anything" nil) - :safe 'symbolp - :group 'mu4e-compose)) - -(defcustom mu4e-compose-format-flowed nil - "Whether to compose messages to be sent as format=flowed (or - with long lines if `use-hard-newlines' is set to nil). The - variable `fill-flowed-encode-column' lets you customize the - width beyond which format=flowed lines are wrapped." - :type 'boolean - :safe 'booleanp - :group 'mu4e-compose) - -(defcustom mu4e-compose-pre-hook nil - "Hook run just *before* message composition starts. -If the compose-type is either 'reply' or 'forward', the variable -`mu4e-compose-parent-message' points to the message replied to / -being forwarded / edited. - -Note that there is no draft message yet when this hook runs, it -is meant for influencing the how mu4e constructs the draft -message. If you want to do something with the draft messages after -it has been constructed, `mu4e-compose-mode-hook' would be the -place to do that." - :type 'hook - :group 'mu4e-compose) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defun mu4e-compose-attach-captured-message () - "Insert the last captured message file as an attachment. -Messages are captured with `mu4e-action-capture-message'." - (interactive) - (unless mu4e-captured-message - (mu4e-warn "No message has been captured")) - (let ((path (plist-get mu4e-captured-message :path))) - (unless (file-exists-p path) - (mu4e-warn "Captured message file not found")) - (mml-attach-file - path - "message/rfc822" - (or (plist-get mu4e-captured-message :subject) "No subject") - "attachment"))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;; 'fcc' refers to saving a copy of a sent message to a certain folder. that's -;; what these 'Sent mail' folders are for! -;; -;; We let message mode take care of this by adding a field - -;; Fcc: <full-path-to-message-in-target-folder> - -;; in the "message-send-hook" (ie., just before sending). message mode will -;; then take care of the saving when the message is actually sent. -;; -;; note, where and if you make this copy depends on the value of -;; `mu4e-sent-messages-behavior'. - -(defun mu4e~compose-setup-fcc-maybe () - "Maybe setup Fcc, based on `mu4e-sent-messages-behavior'. -If needed, set the Fcc header, and register the handler function." - (let* ((sent-behavior - ;; Note; we cannot simply use functionp here, since at least - ;; delete is a function, too... - (if (member mu4e-sent-messages-behavior '(delete trash sent)) - mu4e-sent-messages-behavior - (if (functionp mu4e-sent-messages-behavior) - (funcall mu4e-sent-messages-behavior) - mu4e-sent-messages-behavior))) - (mdir - (case sent-behavior - (delete nil) - (trash (mu4e-get-trash-folder mu4e-compose-parent-message)) - (sent (mu4e-get-sent-folder mu4e-compose-parent-message)) - (otherwise - (mu4e-error "unsupported value '%S' `mu4e-sent-messages-behavior'." - mu4e-sent-messages-behavior)))) - (fccfile (and mdir - (concat mu4e-maildir mdir "/cur/" - (mu4e~draft-message-filename-construct "S"))))) - ;; if there's an fcc header, add it to the file - (when fccfile - (message-add-header (concat "Fcc: " fccfile "\n")) - ;; sadly, we cannot define as 'buffer-local'... this will screw up gnus - ;; etc. if you run it after mu4e so, (hack hack) we reset it to the old - ;; handler after we've done our thing. - (setq message-fcc-handler-function - (lexical-let ((maildir mdir) (old-handler message-fcc-handler-function)) - (lambda (file) - (setq message-fcc-handler-function old-handler) ;; reset the fcc handler - (write-file file) ;; writing maildirs files is easy - (mu4e~proc-add file (or maildir "/")))))))) ;; update the database - -(defvar mu4e-compose-hidden-headers - `("^References:" "^Face:" "^X-Face:" - "^X-Draft-From:" "^User-agent:") - "Hidden headers when composing.") - -(defun mu4e~compose-hide-headers () - "Hide the headers as per `mu4e-compose-hidden-headers'." - (let ((message-hidden-headers mu4e-compose-hidden-headers)) - (message-hide-headers))) - -(defconst mu4e~compose-address-fields-regexp - "^\\(To\\|B?Cc\\|Reply-To\\|From\\):") - -(defun mu4e~compose-register-message-save-hooks () - "Just before saving, we remove the mail-header-separator; just -after saving we restore it; thus, the separator should never -appear on disk." - (add-hook 'before-save-hook - 'mu4e~draft-remove-mail-header-separator nil t) - (add-hook 'after-save-hook - (lambda () - (mu4e~compose-set-friendly-buffer-name) - (mu4e~draft-insert-mail-header-separator) - ;; hide some headers again - (mu4e~compose-hide-headers) - (set-buffer-modified-p nil) - (mu4e-message "Saved (%d lines)" (count-lines (point-min) (point-max))) - ;; update the file on disk -- ie., without the separator - (mu4e~proc-add (buffer-file-name) mu4e~draft-drafts-folder)) nil t)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; address completion; inspired by org-contacts.el and -;; https://github.com/nordlow/elisp/blob/master/mine/completion-styles-cycle.el -(defun mu4e~compose-complete-handler (str pred action) - (cond - ((eq action nil) - (try-completion str mu4e~contacts pred)) - ((eq action t) - (all-completions str mu4e~contacts pred)) - ((eq action 'metadata) - ;; our contacts are already sorted - just need to tell the - ;; completion machinery not to try to undo that... - '(metadata - (display-sort-function . mu4e~sort-contacts-for-completion) - (cycle-sort-function . mu4e~sort-contacts-for-completion))))) - -(defun mu4e~compose-complete-contact (&optional start) - "Complete the text at START with a contact. -Ie. either 'name <email>' or 'email')." - (interactive) - (let ((mail-abbrev-mode-regexp mu4e~compose-address-fields-regexp) - (eoh ;; end-of-headers - (save-excursion - (goto-char (point-min)) - (search-forward-regexp mail-header-separator nil t)))) - ;; try to complete only when we're in the headers area, - ;; looking at an address field. - (when (and eoh (> eoh (point)) (mail-abbrev-in-expansion-header-p)) - (let* ((end (point)) - (start - (or start - (save-excursion - (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*") - (goto-char (match-end 0)) - (point))))) - (list start end 'mu4e~compose-complete-handler))))) - -(defun mu4e~compose-setup-completion () - "Set up auto-completion of addresses." - (set (make-local-variable 'completion-ignore-case) t) - (set (make-local-variable 'completion-cycle-threshold) 7) - (add-to-list (make-local-variable 'completion-styles) 'substring) - (add-hook 'completion-at-point-functions - 'mu4e~compose-complete-contact nil t)) - -(defun mu4e~remove-refs-maybe () - "Remove the References: header if the In-Reply-To header is -missing. This allows the user to effectively start a new -message-thread by removing the In-Reply-To header." - (unless (message-fetch-field "in-reply-to") - (message-remove-header "References"))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e-compose-mode-map nil - "Keymap for \"*mu4e-compose*\" buffers.") -(unless mu4e-compose-mode-map - (setq mu4e-compose-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) - (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) - (define-key map (kbd "C-c C-k") 'mu4e-message-kill-buffer) - (define-key map (kbd "M-q") 'mu4e-fill-paragraph) - map))) - -(defun mu4e-fill-paragraph (&optional region) - "If `use-hard-newlines', takes a multi-line paragraph and makes -it into a single line of text. Assume paragraphs are separated -by blank lines. If `use-hard-newlines' is not enabled, this -simply executes `fill-paragraph'." - ;; Inspired by https://www.emacswiki.org/emacs/UnfillParagraph - (interactive (progn (barf-if-buffer-read-only) '(t))) - (if mu4e-compose-format-flowed - (let ((fill-column (point-max)) - (use-hard-newlines nil)); rfill "across" hard newlines - (fill-paragraph nil region)) - (fill-paragraph nil region))) - -(defun mu4e-toggle-use-hard-newlines () - (interactive) - (setq use-hard-newlines (not use-hard-newlines)) - (if use-hard-newlines - (turn-off-auto-fill) - (turn-on-auto-fill))) - -(defun mu4e~compose-remap-faces () - "Our parent `message-mode' uses font-locking for the compose -buffers; lets remap its faces so it uses the ones for mu4e." - ;; normal headers - (face-remap-add-relative 'message-header-name - '((:inherit mu4e-header-key-face))) - (face-remap-add-relative 'message-header-other - '((:inherit mu4e-header-value-face))) - ;; special headers - (face-remap-add-relative 'message-header-from - '((:inherit mu4e-contact-face))) - (face-remap-add-relative 'message-header-to - '((:inherit mu4e-contact-face))) - (face-remap-add-relative 'message-header-cc - '((:inherit mu4e-contact-face))) - (face-remap-add-relative 'message-header-bcc - '((:inherit mu4e-contact-face))) - (face-remap-add-relative 'message-header-subject - '((:inherit mu4e-special-header-value-face))) - ;; citation - (face-remap-add-relative 'message-cited-text - '((:inherit mu4e-cited-1-face)))) - - -(defvar mu4e-compose-mode-abbrev-table nil) -(define-derived-mode mu4e-compose-mode message-mode "mu4e:compose" - "Major mode for the mu4e message composition, derived from `message-mode'. -\\{message-mode-map}." - (progn - (use-local-map mu4e-compose-mode-map) - (set (make-local-variable 'global-mode-string) '(:eval (mu4e-context-label))) - (set (make-local-variable 'message-signature) mu4e-compose-signature) - ;; set this to allow mu4e to work when gnus-agent is unplugged in gnus - (set (make-local-variable 'message-send-mail-real-function) nil) - (make-local-variable 'message-default-charset) - ;; message-mode has font-locking, but uses its own faces. Let's - ;; use the mu4e-specific ones instead - (mu4e~compose-remap-faces) - ;; if the default charset is not set, use UTF-8 - (unless message-default-charset - (setq message-default-charset 'utf-8)) - ;; make sure mu4e is started in the background (ie. we don't want to error - ;; out when sending the message; better to do it now if there's a problem) - (mu4e~start) ;; start mu4e in background, if needed - (mu4e~compose-register-message-save-hooks) - ;; set the default directory to the user's home dir; this is probably more - ;; useful e.g. when finding an attachment file the directory the current - ;; mail files lives in... - (setq default-directory (expand-file-name "~/")) - ;; offer completion for e-mail addresses - (when mu4e-compose-complete-addresses - (mu4e~compose-setup-completion)) - - (when mu4e-compose-format-flowed - (turn-off-auto-fill) - (setq truncate-lines nil - word-wrap t - use-hard-newlines t) - ;; Set the marks in the fringes before activating visual-line-mode - (set (make-local-variable 'visual-line-fringe-indicators) - '(left-curly-arrow right-curly-arrow)) - (visual-line-mode t)) - - (when (lookup-key message-mode-map [menu-bar text]) - (define-key-after - (lookup-key message-mode-map [menu-bar text]) - [mu4e-hard-newlines] - '(menu-item "Format=flowed" mu4e-toggle-use-hard-newlines - :button (:toggle . use-hard-newlines) - :help "Toggle format=flowed" - :visible (eq major-mode 'mu4e-compose-mode) - :enable mu4e-compose-format-flowed) - 'sep)) - - (when (lookup-key mml-mode-map [menu-bar Attachments]) - (define-key-after - (lookup-key mml-mode-map [menu-bar Attachments]) - [mu4e-compose-attach-captured-message] - '(menu-item "Attach captured message" - mu4e-compose-attach-captured-message - :help "Attach message captured in Headers View (with 'a c')" - :visible (eq major-mode 'mu4e-compose-mode)) - (quote Attach\ External...))) - - ;; setup the fcc-stuff, if needed - (add-hook 'message-send-hook - (lambda () ;; mu4e~compose-save-before-sending - ;; when in-reply-to was removed, remove references as well. - (when (eq mu4e~compose-type 'reply) - (mu4e~remove-refs-maybe)) - (when use-hard-newlines - (mu4e-send-harden-newlines)) - ;; for safety, always save the draft before sending - (set-buffer-modified-p t) - (save-buffer) - (mu4e~compose-setup-fcc-maybe) - (widen)) nil t) - ;; when the message has been sent. - (add-hook 'message-sent-hook - (lambda () ;; mu4e~compose-mark-after-sending - (setq mu4e-sent-func 'mu4e-sent-handler) - (mu4e~proc-sent (buffer-file-name) mu4e~draft-drafts-folder)) nil t)) - ;; 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-mark-after-sending 'permanent-local-hook t)) - -(defun mu4e-send-harden-newlines () - "Set the hard property to all newlines." - (save-excursion - (goto-char (point-min)) - (while (search-forward "\n" nil t) - (put-text-property (1- (point)) (point) 'hard t)))) - -(defconst mu4e~compose-buffer-max-name-length 30 - "Maximum length of the mu4e-send-buffer-name.") - -(defvar mu4e~compose-type nil - "Compose-type for this buffer.") - -(defun mu4e~compose-set-friendly-buffer-name (&optional compose-type) - "Set some user-friendly buffer name based on the compose type." - (let* ((subj (message-field-value "subject")) - (subj (unless (and subj (string-match "^[:blank:]*$" subj)) subj)) - (str (or subj - (case compose-type - (reply "*reply*") - (forward "*forward*") - (otherwise "*draft*"))))) - (rename-buffer (generate-new-buffer-name - (truncate-string-to-width str - mu4e~compose-buffer-max-name-length - nil nil t))))) - - -(defun mu4e~compose-crypto-reply (parent compose-type) - "When composing a reply to an encrypted message, we can -automatically encrypt that reply." - (message "%S %S" parent compose-type) - (when (and (eq compose-type 'reply) - (and parent (member 'encrypted (mu4e-message-field parent :flags)))) - (case mu4e-compose-crypto-reply-policy - (sign (mml-secure-message-sign)) - (encrypt (mml-secure-message-encrypt)) - (sign-and-encrypt (mml-secure-message-sign-encrypt)) - (message "Do nothing")))) - - - -(defun* mu4e~compose-handler (compose-type &optional original-msg includes) - "Create a new draft message, or open an existing one. - -COMPOSE-TYPE determines the kind of message to compose and is a -symbol, either `reply', `forward', `edit', `resend' `new'. `edit' -is for editing existing (draft) messages. When COMPOSE-TYPE is -`reply' or `forward', MSG should be a message plist. If -COMPOSE-TYPE is `new', ORIGINAL-MSG should be nil. - -Optionally (when forwarding, replying) ORIGINAL-MSG is the original -message we will forward / reply to. - -Optionally (when forwarding) INCLUDES contains a list of - (:file-name <filename> :mime-type <mime-type> :disposition <disposition>) -for the attachements to include; file-name refers to -a file which our backend has conveniently saved for us (as a -tempfile)." - - ;; Run the hooks defined for `mu4e-compose-pre-hook'. If compose-type is - ;; `reply', `forward' or `edit', `mu4e-compose-parent-message' points to the - ;; message being forwarded or replied to, otherwise it is nil. - (set (make-local-variable 'mu4e-compose-parent-message) original-msg) - (put 'mu4e-compose-parent-message 'permanent-local t) - ;; maybe switch the context - (mu4e~context-autoswitch mu4e-compose-parent-message - mu4e-compose-context-policy) - (run-hooks 'mu4e-compose-pre-hook) - - ;; this opens (or re-opens) a messages with all the basic headers set. - (let ((winconf (current-window-configuration))) - (condition-case nil - (mu4e-draft-open compose-type original-msg) - (quit (set-window-configuration winconf) - (mu4e-message "Operation aborted") - (return-from mu4e~compose-handler)))) - ;; insert mail-header-separator, which is needed by message mode to separate - ;; headers and body. will be removed before saving to disk - (mu4e~draft-insert-mail-header-separator) - ;; maybe encrypt/sign replies - (mu4e~compose-crypto-reply original-msg compose-type) - ;; include files -- e.g. when forwarding a message with attachments, - ;; we take those from the original. - (save-excursion - (goto-char (point-max)) ;; put attachments at the end - (dolist (att includes) - (mml-attach-file - (plist-get att :file-name) (plist-get att :mime-type)))) - ;; buffer is not user-modified yet - (mu4e~compose-set-friendly-buffer-name compose-type) - (set-buffer-modified-p nil) - ;; now jump to some useful positions, and start writing that mail! - - (if (member compose-type '(new forward)) - (message-goto-to) - (message-goto-body)) - ;; bind to `mu4e-compose-parent-message' of compose buffer - (set (make-local-variable 'mu4e-compose-parent-message) original-msg) - (put 'mu4e-compose-parent-message 'permanent-local t) - ;; remember the compose-type - (set (make-local-variable 'mu4e~compose-type) compose-type) - (put 'mu4e~compose-type 'permanent-local t) - - ;; hide some headers - (mu4e~compose-hide-headers) - ;; switch on the mode - (mu4e-compose-mode) - (when mu4e-compose-in-new-frame - ;; make sure to close the frame when we're done with the message these are - ;; all buffer-local; - (push 'delete-frame message-exit-actions) - (push 'delete-frame message-postpone-actions))) - -(defun mu4e-sent-handler (docid path) - "Handler function, called with DOCID and PATH for the just-sent -message. For Forwarded ('Passed') and Replied messages, try to set -the appropriate flag at the message forwarded or replied-to." - (mu4e~compose-set-parent-flag path) - (when (file-exists-p path) ;; maybe the draft was not saved at all - (mu4e~proc-remove docid)) - ;; kill any remaining buffers for the draft file, or they will hang around... - ;; this seems a bit hamfisted... - (dolist (buf (buffer-list)) - (when (and (buffer-file-name buf) - (string= (buffer-file-name buf) path)) - (if message-kill-buffer-on-exit - (kill-buffer buf)))) - ;; now, try to go back to some previous buffer, in the order - ;; view->headers->main - (if (buffer-live-p mu4e~view-buffer) - (switch-to-buffer mu4e~view-buffer) - (if (buffer-live-p mu4e~headers-buffer) - (switch-to-buffer mu4e~headers-buffer) - ;; if all else fails, back to the main view - (when (fboundp 'mu4e) (mu4e)))) - (mu4e-message "Message sent")) - -(defun mu4e-message-kill-buffer () - "Wrapper around `message-kill-buffer'. -It restores mu4e window layout after killing the compose-buffer." - (interactive) - (let ((current-buffer (current-buffer))) - (message-kill-buffer) - ;; Compose buffer killed - (when (not (equal current-buffer (current-buffer))) - ;; Restore mu4e - (if mu4e-compose-in-new-frame - (delete-frame) - (if (buffer-live-p mu4e~view-buffer) - (switch-to-buffer mu4e~view-buffer) - (if (buffer-live-p mu4e~headers-buffer) - (switch-to-buffer mu4e~headers-buffer) - ;; if all else fails, back to the main view - (when (fboundp 'mu4e) (mu4e)))))))) - -(defun mu4e~compose-set-parent-flag (path) - "Set the 'replied' \"R\" flag on messages we replied to, and the -'passed' \"F\" flag on message we have forwarded. - -If a message has an 'in-reply-to' header, it is considered a reply -to the message with the corresponding message id. If it does not -have an 'in-reply-to' header, but does have a 'references' header, -it is considered to be a forward message for the message -corresponding with the /last/ message-id in the references header. - -Now, if the message has been determined to be either a forwarded -message or a reply, we instruct the server to update that message -with resp. the 'P' (passed) flag for a forwarded message, or the -'R' flag for a replied message. The original messages are also -marked as Seen. - -Function assumes that it's executed in the context of the message -buffer." - (let ((buf (find-file-noselect path))) - (when buf - (with-current-buffer buf - (message-narrow-to-headers-or-head) - (let ((in-reply-to (message-fetch-field "in-reply-to")) - (forwarded-from) - (references (message-fetch-field "references"))) - (unless in-reply-to - (when references - (with-temp-buffer ;; inspired by `message-shorten-references'. - (insert references) - (goto-char (point-min)) - (let ((refs)) - (while (re-search-forward "<[^ <]+@[^ <]+>" nil t) - (push (match-string 0) refs)) - ;; the last will be the first - (setq forwarded-from (first refs)))))) - ;; remove the <> - (when (and in-reply-to (string-match "<\\(.*\\)>" in-reply-to)) - (mu4e~proc-move (match-string 1 in-reply-to) nil "+R-N")) - (when (and forwarded-from (string-match "<\\(.*\\)>" forwarded-from)) - (mu4e~proc-move (match-string 1 forwarded-from) nil "+P-N"))))))) - -(defun mu4e-compose (compose-type) - "Start composing a message of COMPOSE-TYPE, where COMPOSE-TYPE -is a symbol, one of `reply', `forward', `edit', `resend' -`new'. All but `new' take the message at point as input. Symbol -`edit' is only allowed for draft messages." - (let ((msg (mu4e-message-at-point 'noerror))) - ;; some sanity checks - (unless (or msg (eq compose-type 'new)) - (mu4e-warn "No message at point")) - (unless (member compose-type '(reply forward edit resend new)) - (mu4e-error "Invalid compose type '%S'" compose-type)) - (when (and (eq compose-type 'edit) - (not (member 'draft (mu4e-message-field msg :flags)))) - (mu4e-warn "Editing is only allowed for draft messages")) - - ;; '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* - (if (eq compose-type 'new) - (mu4e~compose-handler 'new) - ;; otherwise, we need the doc-id - (let* ((docid (mu4e-message-field msg :docid)) - ;; decrypt (or not), based on `mu4e-decryption-policy'. - (decrypt - (and (member 'encrypted (mu4e-message-field msg :flags)) - (if (eq mu4e-decryption-policy 'ask) - (yes-or-no-p (mu4e-format "Decrypt message?")) - mu4e-decryption-policy)))) - ;; if there's a visible view window, select that before starting 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 it... - (let ((viewwin (get-buffer-window mu4e~view-buffer))) - (when (window-live-p viewwin) - (select-window viewwin))) - ;; talk to the backend - (mu4e~proc-compose compose-type decrypt docid))))) - -(defun mu4e-compose-reply () - "Compose a reply for the message at point in the headers buffer." - (interactive) - (mu4e-compose 'reply)) - -(defun mu4e-compose-forward () - "Forward the message at point in the headers buffer." - (interactive) - (mu4e-compose 'forward)) - -(defun mu4e-compose-edit () - "Edit the draft message at point in the headers buffer. -This is only possible if the message at point is, in fact, a -draft message." - (interactive) - (mu4e-compose 'edit)) - -(defun mu4e-compose-resend () - "Resend the message at point in the headers buffer." - (interactive) - (mu4e-compose 'resend)) - -(defun mu4e-compose-new () - "Start writing a new message." - (interactive) - (mu4e-compose 'new)) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; mu4e-compose-func and mu4e-send-func are wrappers so we can set ourselves -;; as default emacs mailer (define-mail-user-agent etc.) - -;;;###autoload -(defun mu4e~compose-mail (&optional to subject other-headers continue - switch-function yank-action send-actions return-action) - "This is mu4e's implementation of `compose-mail'." - - ;; create a new draft message 'resetting' (as below) is not actually needed in - ;; this case, but let's prepare for the re-edit case as well - (mu4e~compose-handler 'new) - - (when (message-goto-to) ;; reset to-address, if needed - (message-delete-line)) - (message-add-header (concat "To: " to "\n")) - - (when (message-goto-subject) ;; reset subject, if needed - (message-delete-line)) - (message-add-header (concat "Subject: " subject "\n")) - - ;; add any other headers specified - (when other-headers - (message-add-header other-headers)) - - ;; yank message - (if (bufferp yank-action) - (list 'insert-buffer yank-action) - yank-action) - - ;; try to put the user at some reasonable spot... - (if (not to) - (message-goto-to) - (if (not subject) - (message-goto-subject) - (message-goto-body)))) - -;; happily, we can re-use most things from message mode -;;;###autoload -(define-mail-user-agent 'mu4e-user-agent - 'mu4e~compose-mail - 'message-send-and-exit - 'message-kill-buffer - 'message-send-hook) -;; Without this `mail-user-agent' cannot be set to `mu4e-user-agent' -;; through customize, as the custom type expects a function. Not -;; sure whether this function is actually ever used; if it is then -;; returning the symbol is probably the correct thing to do, as other -;; such functions suggest. -(defun mu4e-user-agent () - 'mu4e-user-agent) - -(defun mu4e~compose-browse-url-mail (url &optional ignored) - "Adapter for `browse-url-mailto-function." - (let* ((headers (rfc2368-parse-mailto-url url)) - (to (cdr (assoc "To" headers))) - (subject (cdr (assoc "Subject" headers))) - (body (cdr (assoc "Body" headers)))) - (mu4e~compose-mail to subject) - (if body - (progn - (message-goto-body) - (insert body) - (if (not to) - (message-goto-to) - (if (not subject) - (message-goto-subject) - (message-goto-body))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun mu4e-compose-goto-top () - "Go to the beginning of the message or buffer. -Go to the beginning of the message or, if already there, go to the -beginning of the buffer." - (interactive) - (let ((old-position (point))) - (message-goto-body) - (when (equal (point) old-position) - (goto-char (point-min))))) - -(define-key mu4e-compose-mode-map - (vector 'remap 'beginning-of-buffer) 'mu4e-compose-goto-top) - -(defun mu4e-compose-goto-bottom () - "Go to the end of the message or buffer. -Go to the end of the message (before signature) or, if already there, go to the -end of the buffer." - (interactive) - (let ((old-position (point)) - (message-position (save-excursion (message-goto-body) (point)))) - (goto-char (point-max)) - (when (re-search-backward message-signature-separator message-position t) - (forward-line -1)) - (when (equal (point) old-position) - (goto-char (point-max))))) - -(define-key mu4e-compose-mode-map - (vector 'remap 'end-of-buffer) 'mu4e-compose-goto-bottom) - -(provide 'mu4e-compose) - -;; Load mu4e completely even when this file was loaded through -;; autoload. -(require 'mu4e) diff --git a/_spacemacs.d/local/mu4e/mu4e-context.el b/_spacemacs.d/local/mu4e/mu4e-context.el deleted file mode 100644 index f5f8fff..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-context.el +++ /dev/null @@ -1,157 +0,0 @@ -; mu4e-context.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2015-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; A mu4e 'context' is a a set of variable-settings and functions, which can be -;; used e.g. to switch between accounts. - -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) -(require 'cl) - -(require 'mu4e-utils) - -(defvar mu4e-contexts nil "The list of `mu4e-context' objects -describing mu4e's contexts.") - -(defvar mu4e~context-current nil - "The current context; for internal use. Use - `mu4e-context-switch' to change it.") - -(defun mu4e-context-current () - "Get the currently active context, or nil if there is none." - mu4e~context-current) - -(defun mu4e-context-label () - "Propertized string with the current context name, or \"\" if - there is none." - (if (mu4e-context-current) - (concat "[" (propertize (mu4e~quote-for-modeline - (mu4e-context-name (mu4e-context-current))) - 'face 'mu4e-title-face) "]") "")) - -(defstruct mu4e-context - "A mu4e context object with the following members: -- `name': the name of the context, eg. \"Work\" or \"Private\".' -- `enter-func': a parameterless function invoked when entering - this context, or nil -- `leave-func':a parameterless fuction invoked when leaving this - context, or nil -- `match-func': a function called when comnposing a new messages, - and takes a message plist -for the message replied to or forwarded, and nil -otherwise. Before composing a new message, `mu4e' switches to the -first context for which `match-func' return t." - name ;; name of the context, e.g. "work" - (enter-func nil) ;; function invoked when entering the context - (leave-func nil) ;; function invoked when leaving the context - (match-func nil) ;; function that takes a msg-proplist, and return t - ;; if it matches, nil otherwise - vars) ;; alist of variables. - -(defun mu4e~context-ask-user (prompt) - "Let user choose some context based on its name." - (when mu4e-contexts - (let* ((names (map 'list (lambda (context) - (cons (mu4e-context-name context) context)) - mu4e-contexts)) - (context (mu4e-read-option prompt names))) - (or context (mu4e-error "No such context"))))) - -(defun mu4e-context-switch (&optional force name) - "Switch context to a context with NAME which is part of -`mu4e-contexts'; if NAME is nil, query user. - -If the new context is the same and the current context, only -switch (run associated functions) when prefix argument FORCE is -non-nil." - (interactive "P") - (unless mu4e-contexts - (mu4e-error "No contexts defined")) - (let* ((names (map 'list (lambda (context) - (cons (mu4e-context-name context) context)) - mu4e-contexts)) - (context - (if name - (cdr-safe (assoc name names)) - (mu4e~context-ask-user "Switch to context: ")))) - (unless context (mu4e-error "No such context")) - ;; if new context is same as old one one switch with FORCE is set. - (when (or force (not (eq context (mu4e-context-current)))) - (when (and (mu4e-context-current) - (mu4e-context-leave-func mu4e~context-current)) - (funcall (mu4e-context-leave-func mu4e~context-current))) - ;; enter the new context - (when (mu4e-context-enter-func context) - (funcall (mu4e-context-enter-func context))) - (when (mu4e-context-vars context) - (mapc #'(lambda (cell) - (set (car cell) (cdr cell))) - (mu4e-context-vars context))) - (setq mu4e~context-current context) - (mu4e-message "Switched context to %s" (mu4e-context-name context))) - context)) - -(defun mu4e~context-autoswitch (&optional msg policy) - "When contexts are defined but there is no context yet, switch -to the first whose :match-func return non-nil. If none of them -match, return the first. For MSG and POLICY, see `mu4e-context-determine'." - (when mu4e-contexts - (let ((context (mu4e-context-determine msg policy))) - (when context (mu4e-context-switch - nil (mu4e-context-name context)))))) - -(defun mu4e-context-determine (msg &optional policy) - "Return the first context with a match-func that returns t. MSG -points to the plist for the message replied to or forwarded, or -nil if there is no such MSG; similar to what -`mu4e-compose-pre-hook' does. - -POLICY specifies how to do the determination. If POLICY is -'always-ask, we ask the user unconditionally. - -In all other cases, if any context matches (using its match -function), this context is returned. If none of the contexts -match, POLICY determines what to do: - -- pick-first: pick the first of the contexts available -- ask: ask the user -- ask-if-none: ask if there is no context yet -- otherwise, return nil. Effectively, this leaves the current context as it is." - (when mu4e-contexts - (if (eq policy 'always-ask) - (mu4e~context-ask-user "Select context: ") - (or ;; is there a matching one? - (find-if (lambda (context) - (when (mu4e-context-match-func context) - (funcall (mu4e-context-match-func context) msg))) - mu4e-contexts) - ;; no context found yet; consult policy - (case policy - (pick-first (car mu4e-contexts)) - (ask (mu4e~context-ask-user "Select context: ")) - (ask-if-none (or (mu4e-context-current) - (mu4e~context-ask-user "Select context: "))) - (otherwise nil)))))) - -(provide 'mu4e-context) - diff --git a/_spacemacs.d/local/mu4e/mu4e-contrib.el b/_spacemacs.d/local/mu4e/mu4e-contrib.el deleted file mode 100644 index 156f5b6..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-contrib.el +++ /dev/null @@ -1,165 +0,0 @@ -;;; mu4e-contrib.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2013-2016 Dirk-Jan C. Binnema - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; Some user-contributed functions for mu4e - -;; Contributed by sabof - -(require 'mu4e) - -(defun mu4e-headers-mark-all-unread-read () - "Put a ! \(read) mark on all visible unread messages." - (interactive) - (mu4e-headers-mark-for-each-if - (cons 'read nil) - (lambda (msg param) - (memq 'unread (mu4e-msg-field msg :flags))))) - -(defun mu4e-headers-flag-all-read () - "Flag all visible messages as \"read\"." - (interactive) - (mu4e-headers-mark-all-unread-read) - (mu4e-mark-execute-all t)) - -;;; - -(defun mu4e-headers-mark-all () - "Mark all messages within current query results and ask user to execute which action." - (interactive) - (mu4e-headers-mark-for-each-if - (cons 'something nil) - (lambda (msg param) t)) - (mu4e-mark-execute-all)) - -;;; - -;;; Bookmark handlers -;; -;; Allow bookmarking a mu4e buffer in regular emacs bookmarks. - -;; Probably this can be moved to mu4e-view.el. -(add-hook 'mu4e-view-mode-hook - #'(lambda () - (set (make-local-variable 'bookmark-make-record-function) - 'mu4e-view-bookmark-make-record))) -;; And this can be moved to mu4e-headers.el. -(add-hook 'mu4e-headers-mode-hook - #'(lambda () - (set (make-local-variable 'bookmark-make-record-function) - 'mu4e-view-bookmark-make-record))) - -(defun mu4e-view-bookmark-make-record () - "Make a bookmark entry for a mu4e buffer. Note that this is an -emacs bookmark, not to be confused with `mu4e-bookmarks'." - (let* ((msg (mu4e-message-at-point)) - (maildir (plist-get msg :maildir)) - (date (format-time-string "%Y%m%d" (plist-get msg :date))) - (query (format "maildir:%s date:%s" maildir date)) - (docid (plist-get msg :docid)) - (mode (symbol-name major-mode)) - (subject (or (plist-get msg :subject) "No subject"))) - `(,subject - ,@(bookmark-make-record-default 'no-file 'no-context) - (location . (,query . ,docid)) - (mode . ,mode) - (handler . mu4e-bookmark-jump)))) - -(defun mu4e-bookmark-jump (bookmark) - "Handler function for record returned by `mu4e-view-bookmark-make-record'. -BOOKMARK is a bookmark name or a bookmark record." - (let* ((path (bookmark-prop-get bookmark 'location)) - (mode (bookmark-prop-get bookmark 'mode)) - (docid (cdr path)) - (query (car path))) - (call-interactively 'mu4e) - (mu4e-headers-search query) - (sit-for 0.5) - (mu4e~headers-goto-docid docid) - (mu4e~headers-highlight docid) - (unless (string= mode "mu4e-headers-mode") - (call-interactively 'mu4e-headers-view-message) - (run-with-timer 0.1 nil - (lambda (bmk) - (bookmark-default-handler - `("" (buffer . ,(current-buffer)) . - ,(bookmark-get-bookmark-record bmk)))) - bookmark)))) - - - -;;; handling spam with Bogofilter with possibility to define it for SpamAssassin -;;; contributed by Gour - -;; to add the actions to the menu, you can use something like: - -;; (add-to-list 'mu4e-headers-actions -;; '("sMark as spam" . mu4e-register-msg-as-spam) t) -;; (add-to-list 'mu4e-headers-actions -;; '("hMark as ham" . mu4e-register-msg-as-ham) t) - -(defvar mu4e-register-as-spam-cmd nil - "Command for invoking spam processor to register message as spam, -for example for bogofilter, use \"/usr/bin/bogofilter -Ns < %s\" ") - -(defvar mu4e-register-as-ham-cmd nil - "Command for invoking spam processor to register message as ham. -For example for bogofile, use \"/usr/bin/bogofilter -Sn < %s\"") - -(defun mu4e-register-msg-as-spam (msg) - "Mark message as spam." - (interactive) - (let* ((path (shell-quote-argument (mu4e-message-field msg :path))) - (command (format mu4e-register-as-spam-cmd path))) ;; re-register msg as spam - (shell-command command)) -(mu4e-mark-at-point 'delete nil)) - -(defun mu4e-register-msg-as-ham (msg) - "Mark message as ham." - (interactive) - (let* ((path (shell-quote-argument(mu4e-message-field msg :path))) - (command (format mu4e-register-as-ham-cmd path))) ;; re-register msg as ham - (shell-command command)) -(mu4e-mark-at-point 'something nil)) - -;; (add-to-list 'mu4e-view-actions -;; '("sMark as spam" . mu4e-view-register-msg-as-spam) t) -;; (add-to-list 'mu4e-view-actions -;; '("hMark as ham" . mu4e-view-register-msg-as-ham) t) - -(defun mu4e-view-register-msg-as-spam (msg) - "Mark message as spam (view mode)." - (interactive) - (let* ((path (shell-quote-argument (mu4e-message-field msg :path))) - (command (format mu4e-register-as-spam-cmd path))) - (shell-command command)) - (mu4e-view-mark-for-delete)) - -(defun mu4e-view-register-msg-as-ham (msg) - "Mark message as ham (view mode)." - (interactive) - (let* ((path (shell-quote-argument(mu4e-message-field msg :path))) - (command (format mu4e-register-as-ham-cmd path))) - (shell-command command)) - (mu4e-view-mark-for-something)) - -;;; end of spam-filtering functions - -(provide 'mu4e-contrib) diff --git a/_spacemacs.d/local/mu4e/mu4e-draft.el b/_spacemacs.d/local/mu4e/mu4e-draft.el deleted file mode 100644 index d7df6e0..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-draft.el +++ /dev/null @@ -1,495 +0,0 @@ -;; mu4e-draft.el -- part of mu4e, the mu mail user agent for emacs -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; In this file, various functions to create draft messages - -;; Code - -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) -(require 'cl) - -(require 'mu4e-vars) -(require 'mu4e-utils) -(require 'mu4e-message) -(require 'message) ;; mail-header-separator -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defcustom mu4e-compose-dont-reply-to-self nil - "If non-nil, don't include self (that is, any member of -`mu4e-user-mail-address-list') in replies." - :type 'boolean - :group 'mu4e-compose) - -(defcustom mu4e-compose-cite-function - (or message-cite-function 'message-cite-original-without-signature) - "The function to use to cite message in replies and forwarded -messages. This is the mu4e-specific version of -`message-cite-function'." - :type 'function - :group 'mu4e-compose) - -(defcustom mu4e-compose-signature - (or message-signature "Sent with my mu4e") - "The message signature (i.e. the blob at the bottom of -messages). This is the mu4e-specific version of -`message-signature'." - :group 'mu4e-compose) - -(defcustom mu4e-compose-signature-auto-include t - "Whether to automatically include a message-signature in new -messages (if it is set)." - :type 'boolean - :group 'mu4e-compose) - -(defcustom mu4e-compose-auto-include-date nil - "Whether to include a date header when starting to draft a -message; if nil, only do so when sending the message." - :type 'boolean - :group 'mu4e-compose) - -(defcustom mu4e-compose-in-new-frame nil - "Whether to compose messages in a new frame instead of the -current window." - :type 'boolean - :group 'mu4e-compose) - -(defvar mu4e-user-agent-string - (format "mu4e %s; emacs %s" mu4e-mu-version emacs-version) - "The User-Agent string for mu4e.") - -(defun mu4e~draft-cite-original (msg) - "Return a cited version of the original message MSG as a plist. -This function uses `mu4e-compose-cite-function', and as such all -its settings apply." - (with-temp-buffer - (when (fboundp 'mu4e-view-message-text) ;; keep bytecompiler happy - (let ((mu4e-view-date-format "%Y-%m-%dT%T%z")) - (insert (mu4e-view-message-text msg))) - (message-yank-original) - (goto-char (point-min)) - (push-mark (point-max)) - ;; set the the signature separator to 'loose', since in the real world, - ;; many message don't follow the standard... - (let ((message-signature-separator "^-- *$") - (message-signature-insert-empty-line t)) - (funcall mu4e-compose-cite-function)) - (pop-mark) - (goto-char (point-min)) - (buffer-string)))) - -(defun mu4e~draft-header (hdr val) - "Return a header line of the form \"HDR: VAL\". -If VAL is nil, return nil." - ;; note: the propertize here is currently useless, since gnus sets its own - ;; later. - (when val (format "%s: %s\n" - (propertize hdr 'face 'mu4e-header-key-face) - (propertize val 'face 'mu4e-header-value-face)))) - -(defconst mu4e~max-reference-num 21 - "Maximum number of References:, as suggested by -`message-shorten-references'.") - -(defun mu4e~shorten-1 (list cut surplus) - "Cut SURPLUS elements out of LIST, beginning with CUTth -one. Code borrowed from `message-shorten-1'." - (setcdr (nthcdr (- cut 2) list) - (nthcdr (+ (- cut 2) surplus 1) list))) - -(defun mu4e~draft-references-construct (msg) - "Construct the value of the References: header based on MSG as a -comma-separated string. Normally, this the concatenation of the -existing References + In-Reply-To (which may be empty, an note -that :references includes the old in-reply-to as well) and the -message-id. If the message-id is empty, returns the old -References. If both are empty, return nil." - (let* ( ;; these are the ones from the message being replied to / forwarded - (refs (mu4e-message-field msg :references)) - (msgid (mu4e-message-field msg :message-id)) - ;; now, append in - (refs (if (and msgid (not (string= msgid ""))) - (append refs (list msgid)) refs)) - ;; no doubles - (refs (delete-duplicates refs :test #'equal)) - (refnum (length refs)) - (cut 2)) - ;; remove some refs when there are too many - (when (> refnum mu4e~max-reference-num) - (let ((surplus (- refnum mu4e~max-reference-num))) - (mu4e~shorten-1 refs cut surplus))) - (mapconcat (lambda (id) (format "<%s>" id)) refs " "))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; determine the recipient fields for new messages - -(defun mu4e~draft-recipients-list-to-string (lst) - "Convert a lst LST of address cells into a string with a list of -e-mail addresses. If LST is nil, returns nil." - (when lst - (mapconcat - (lambda (addrcell) - (let ((name (car addrcell)) - (email (cdr addrcell))) - (if name - (format "%s <%s>" (mu4e~rfc822-quoteit name) email) - (format "%s" email)))) - lst ", "))) - -(defun mu4e~draft-address-cell-equal (cell1 cell2) - "Return t if CELL1 and CELL2 have the same e-mail address. -The comparison is done case-insensitively. If the cells done -match return nil. CELL1 and CELL2 are cons cells of the -form (NAME . EMAIL)." - (string= - (downcase (or (cdr cell1) "")) - (downcase (or (cdr cell2) "")))) - - -(defun mu4e~draft-create-to-lst (origmsg) - "Create a list of address for the To: in a new message, based on -the original message ORIGMSG. If the Reply-To address is set, use -that, otherwise use the From address. Note, 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 of the original, we simple copy -the list form the original." - (let ((reply-to - (or (plist-get origmsg :reply-to) (plist-get origmsg :from)))) - (delete-duplicates reply-to :test #'mu4e~draft-address-cell-equal) - (if mu4e-compose-dont-reply-to-self - (delete-if - (lambda (to-cell) - (member-if - (lambda (addr) - (string= (downcase addr) (downcase (cdr to-cell)))) - mu4e-user-mail-address-list)) - reply-to) - reply-to))) - - -(defun mu4e~draft-create-cc-lst (origmsg reply-all) - "Create a list of address for the Cc: in a new message, based on -the original message ORIGMSG, and whether it's a reply-all." - (when reply-all - (let* ((cc-lst ;; get the cc-field from the original, remove dups - (delete-duplicates - (append - (plist-get origmsg :to) - (plist-get origmsg :cc)) - :test #'mu4e~draft-address-cell-equal)) - ;; now we have the basic list, but we must remove - ;; addresses also in the to list - (cc-lst - (delete-if - (lambda (cc-cell) - (find-if - (lambda (to-cell) - (mu4e~draft-address-cell-equal cc-cell to-cell)) - (mu4e~draft-create-to-lst origmsg))) - cc-lst)) - ;; finally, we need to remove ourselves from the cc-list - ;; unless mu4e-compose-keep-self-cc is non-nil - (cc-lst - (if (or mu4e-compose-keep-self-cc (null user-mail-address)) - cc-lst - (delete-if - (lambda (cc-cell) - (member-if - (lambda (addr) - (string= (downcase addr) (downcase (cdr cc-cell)))) - mu4e-user-mail-address-list)) - cc-lst)))) - cc-lst))) - -(defun mu4e~draft-recipients-construct (field origmsg &optional reply-all) - "Create value (a string) for the recipient field FIELD (a -symbol, :to or :cc), based on the original message ORIGMSG, -and (optionally) REPLY-ALL which indicates this is a reply-to-all -message. Return nil if there are no recipients for the particular field." - (mu4e~draft-recipients-list-to-string - (case field - (:to - (mu4e~draft-create-to-lst origmsg)) - (:cc - (mu4e~draft-create-cc-lst origmsg reply-all)) - (otherwise - (mu4e-error "Unsupported field"))))) - - -(defun mu4e~draft-from-construct () - "Construct a value for the From:-field of the reply to MSG, -based on `user-full-name' and `user-mail-address'; if the latter is -nil, function returns nil." - (when user-mail-address - (if user-full-name - (format "%s <%s>" (mu4e~rfc822-quoteit user-full-name) user-mail-address) - (format "%s" user-mail-address)))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e~draft-insert-mail-header-separator () - "Insert `mail-header-separator' in the first empty line of the message. -`message-mode' needs this line to know where the headers end and -the body starts. Note, in `mu4e-compose-mode', we use -`before-save-hook' and `after-save-hook' to ensure that this -separator is never written to the message file. Also see -`mu4e-remove-mail-header-separator'." - ;; we set this here explicitly, since (as it has happened) a wrong - ;; value for this (such as "") breaks address completion and other things - (set (make-local-variable 'mail-header-separator) "--text follows this line--") - (put 'mail-header-separator 'permanent-local t) - (save-excursion - ;; make sure there's not one already - (mu4e~draft-remove-mail-header-separator) - (let ((sepa (propertize mail-header-separator - 'intangible t - ;; don't make this read-only, message-mode - ;; seems to require it being writable in some cases - ;;'read-only "Can't touch this" - 'rear-nonsticky t - 'font-lock-face 'mu4e-compose-separator-face))) - (widen) - ;; search for the first empty line - (goto-char (point-min)) - (if (search-forward-regexp "^$" nil t) - (replace-match sepa) - (progn ;; no empty line? then prepend one - (goto-char (point-max)) - (insert "\n" sepa)))))) - -(defun mu4e~draft-remove-mail-header-separator () - "Remove `mail-header-separator; we do this before saving a -file (and restore it afterwards), to ensure that the separator -never hits the disk. Also see `mu4e~draft-insert-mail-header-separator." - (save-excursion - (widen) - (goto-char (point-min)) - ;; remove the --text follows this line-- separator - (when (search-forward-regexp (concat "^" mail-header-separator) nil t) - (let ((inhibit-read-only t)) - (replace-match ""))))) - - -(defun mu4e~draft-reply-all-p (origmsg) - "Ask user whether she wants to reply to *all* recipients. -If there is just one recipient of ORIGMSG do nothing." - (let* ((recipnum - (+ (length (mu4e~draft-create-to-lst origmsg)) - (length (mu4e~draft-create-cc-lst origmsg t)))) - (response - (if (= recipnum 1) - 'all ;; with one recipient, we can reply to 'all'.... - (mu4e-read-option - "Reply to " - `( (,(format "all %d recipients" recipnum) . all) - ("sender only" . sender-only)))))) - (eq response 'all))) - -(defun mu4e~draft-message-filename-construct (&optional flagstr) - "Construct a randomized name for a message file with flags FLAGSTR. -It looks something like - <time>-<random>.<hostname>:2, -You can append flags." - (let* ((sysname (if (fboundp 'system-name) - (system-name) - (with-no-warnings system-name))) - (hostname (downcase - (save-match-data - (substring sysname - (string-match "^[^.]+" sysname) - (match-end 0)))))) - (format "%s.%04x%04x%04x%04x.%s:2,%s" - (format-time-string "%s" (current-time)) - (random 65535) (random 65535) (random 65535) (random 65535) - hostname (or flagstr "")))) - -(defun mu4e~draft-common-construct () - "Construct the common headers for each message." - (concat - (mu4e~draft-header "User-agent" mu4e-user-agent-string) - (when mu4e-compose-auto-include-date - (mu4e~draft-header "Date" (message-make-date))))) - -(defconst mu4e~draft-reply-prefix "Re: " - "String to prefix replies with.") - -(defun mu4e~draft-reply-construct (origmsg) - "Create a draft message as a reply to original message -ORIGMSG. Replying-to-self is a special; in that case, the To and Cc -fields will be the same as in the original." - (let* ((reply-to-self (mu4e-message-contact-field-matches-me origmsg :from)) - (recipnum - (+ (length (mu4e~draft-create-to-lst origmsg)) - (length (mu4e~draft-create-cc-lst origmsg t)))) - ;; reply-to-self implies reply-all - (reply-all (or reply-to-self (mu4e~draft-reply-all-p origmsg))) - (old-msgid (plist-get origmsg :message-id)) - (subject - (concat mu4e~draft-reply-prefix - (message-strip-subject-re (or (plist-get origmsg :subject) ""))))) - (concat - (mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) - (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) - - (if reply-to-self - ;; When we're replying to ourselves, simply keep the same headers. - (concat - (mu4e~draft-header "To" (mu4e~draft-recipients-list-to-string - (mu4e-message-field origmsg :to))) - (mu4e~draft-header "Cc" (mu4e~draft-recipients-list-to-string - (mu4e-message-field origmsg :cc)))) - - ;; if there's no-one in To, copy the CC-list - (if (zerop (length (mu4e~draft-create-to-lst origmsg))) - (mu4e~draft-header "To" (mu4e~draft-recipients-construct - :cc origmsg reply-all)) - ;; otherwise... - (concat - (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 "Subject" subject) - (mu4e~draft-header "References" - (mu4e~draft-references-construct origmsg)) - (mu4e~draft-common-construct) - (when old-msgid - (mu4e~draft-header "In-reply-to" (format "<%s>" old-msgid))) - "\n\n" - (mu4e~draft-cite-original origmsg)))) - -(defconst mu4e~draft-forward-prefix "Fwd: " - "String to prefix replies with.") - -(defun mu4e~draft-forward-construct (origmsg) - "Create a draft forward message for original message ORIGMSG." - (let ((subject - (or (plist-get origmsg :subject) ""))) - (concat - (mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) - (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) - (mu4e~draft-header "To" "") - (mu4e~draft-common-construct) - (mu4e~draft-header "References" - (mu4e~draft-references-construct origmsg)) - (mu4e~draft-header "Subject" - (concat - ;; if there's no Fwd: yet, prepend it - (if (string-match "^Fwd:" subject) - "" - mu4e~draft-forward-prefix) - subject)) - "\n\n" - (mu4e~draft-cite-original origmsg)))) - -(defun mu4e~draft-newmsg-construct () - "Create a new message." - (concat - (mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) - (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) - (mu4e~draft-header "To" "") - (mu4e~draft-header "Subject" "") - (mu4e~draft-common-construct))) - -(defvar mu4e~draft-drafts-folder nil - "The drafts-folder for this compose buffer, based on -`mu4e-drafts-folder', which is evaluated once.") - -(defun mu4e~draft-open-file (path) - "Open the the draft file at PATH." - (if mu4e-compose-in-new-frame - (find-file-other-frame path) - (find-file path))) - -(defun mu4e~draft-determine-path (draft-dir) - "Determine the path for a new draft file." - (format "%s/%s/cur/%s" - mu4e-maildir draft-dir (mu4e~draft-message-filename-construct "DS"))) - - -(defun mu4e-draft-open (compose-type &optional msg) - "Open a draft file for a new message (when COMPOSE-TYPE is `reply', - `forward' or `new'), open an existing draft (when COMPOSE-TYPE -is `edit'), or re-send an existing message (when COMPOSE-TYPE is -`resend'). - -The name of the draft folder is constructed from the -concatenation of `mu4e-maildir' and `mu4e-drafts-folder' (the -latter will be evaluated). The message file name is a unique name -determined by `mu4e-send-draft-file-name'. The initial contents -will be created from either `mu4e~draft-reply-construct', or -`mu4e~draft-forward-construct' or `mu4e~draft-newmsg-construct'." - (unless mu4e-maildir (mu4e-error "mu4e-maildir not set")) - (let ((draft-dir nil)) - (case compose-type - - (edit - ;; 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 - ;; guess - (setq draft-dir (mu4e~guess-maildir (mu4e-message-field msg :path))) - (mu4e~draft-open-file (mu4e-message-field msg :path))) - - (resend - ;; case-2: copy some exisisting message to a draft message, then edit - ;; that. - (setq draft-dir (mu4e~guess-maildir (mu4e-message-field msg :path))) - (let ((draft-path (mu4e~draft-determine-path draft-dir))) - (copy-file (mu4e-message-field msg :path) draft-path) - (mu4e~draft-open-file draft-path))) - - ((reply forward new) - ;; case-3: creating a new message; in this case, we can determine - ;; mu4e-get-drafts-folder - (setq draft-dir (mu4e-get-drafts-folder msg)) - (let ((draft-path (mu4e~draft-determine-path draft-dir)) - (initial-contents - (case compose-type - (reply (mu4e~draft-reply-construct msg)) - (forward (mu4e~draft-forward-construct msg)) - (new (mu4e~draft-newmsg-construct))))) - (mu4e~draft-open-file draft-path) - (insert initial-contents) - (newline) - ;; include the message signature (if it's set) - (if (and mu4e-compose-signature-auto-include mu4e-compose-signature) - (let ((message-signature mu4e-compose-signature)) - (save-excursion - (message-insert-signature) - (mu4e~fontify-signature)))))) - (t (mu4e-error "unsupported compose-type %S" compose-type))) - ;; if we didn't find a draft folder yet, try some default - (unless draft-dir - (setq draft-dir (mu4e-get-drafts-folder msg))) - ;; evaluate mu4e~drafts-drafts-folder once, here, and use that value - ;; throughout. - (set (make-local-variable 'mu4e~draft-drafts-folder) draft-dir) - (put 'mu4e~draft-drafts-folder 'permanent-local t) - (unless mu4e~draft-drafts-folder - (mu4e-error "failed to determine drafts folder")))) - - -(provide 'mu4e-draft) diff --git a/_spacemacs.d/local/mu4e/mu4e-headers.el b/_spacemacs.d/local/mu4e/mu4e-headers.el deleted file mode 100644 index e137c02..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-headers.el +++ /dev/null @@ -1,1717 +0,0 @@ -;;; mu4e-headers.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; In this file are function related mu4e-headers-mode, to creating the list of -;; one-line descriptions of emails, aka 'headers' (not to be confused with -;; headers like 'To:' or 'Subject:') - -;; Code: -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) -(require 'cl) - -(require 'fringe) -(require 'hl-line) - -(require 'mu4e-utils) ;; utility functions -(require 'mu4e-proc) -(require 'mu4e-vars) -(require 'mu4e-mark) -(require 'mu4e-compose) -(require 'mu4e-actions) -(require 'mu4e-message) - -;; the headers view -(defgroup mu4e-headers nil - "Settings for the headers view." - :group 'mu4e) - -(defcustom mu4e-headers-fields - '( (:human-date . 12) - (:flags . 6) - (:mailing-list . 10) - (:from . 22) - (:subject . nil)) - "A list of header fields to show in the headers buffer. -Each element has the form (HEADER . WIDTH), where HEADER is one of -the available headers (see `mu4e-header-info') and WIDTH is the -respective width in characters. A width of `nil' means -'unrestricted', and this is best reserved for the rightmost (last) -field. Note that emacs may become very slow with excessively long -lines (1000s of characters), so if you regularly get such messages, -you want to avoid fields with `nil' altogether." - :type `(repeat (cons (choice ,@(mapcar (lambda (h) - (list 'const :tag - (plist-get (cdr h) :help) - (car h))) - mu4e-header-info)) - (choice (integer :tag "width") - (const :tag "unrestricted width" nil)))) - :group 'mu4e-headers) - -(defcustom mu4e-headers-date-format "%x" - "Date format to use in the headers view. -In the format of `format-time-string'." - :type 'string - :group 'mu4e-headers) - -(defcustom mu4e-headers-time-format "%X" - "Time format to use in the headers view. -In the format of `format-time-string'." - :type 'string - :group 'mu4e-headers) - -(defcustom mu4e-headers-long-date-format "%c" - "Date format to use in the headers view tooltip. -In the format of `format-time-string'." - :type 'string - :group 'mu4e-headers) - -(defcustom mu4e-headers-visible-lines 10 - "Number of lines to display in the header view when using the -horizontal split-view. This includes the header-line at the top, -and the mode-line." - :type 'integer - :group 'mu4e-headers) - -(defcustom mu4e-headers-visible-columns 30 - "Number of columns to display for the header view when using the -vertical split-view." - :type 'integer - :group 'mu4e-headers) - -(defcustom mu4e-headers-auto-update t - "Whether to automatically update the current headers buffer if an -indexing operation showed changes." - :type 'boolean - :group 'mu4e-headers) - -(defcustom mu4e-headers-results-limit 500 - "Maximum number of results to show; this affects performance -quite a bit, especially when `mu4e-headers-include-related' is -non-nil. Set to -1 for no limits, and you temporarily (for one -query) ignore the limit by pressing a C-u before invoking the -search." - :type '(choice (const :tag "Unlimited" -1) - (integer :tag "Limit")) - :group 'mu4e-headers) - -(make-obsolete-variable 'mu4e-search-results-limit - 'mu4e-headers-results-limit "0.9.9.5-dev6") - -(defcustom mu4e-headers-skip-duplicates nil - "With this option set to non-nil, show only one of duplicate -messages. This is useful when you have multiple copies of the same -message, which is a common occurence for example when using Gmail -and offlineimap." - :type 'boolean - :group 'mu4e-headers) - -(defcustom mu4e-headers-include-related nil - "With this option set to non-nil, not just return the matches for -a searches, but also messages that are related (through their -references) to these messages. This can be useful e.g. to include -sent messages into message threads." - :type 'boolean - :group 'mu4e-headers) - - -(defvar mu4e-headers-hide-predicate nil - "Predicate function applied to headers before they are shown; -if function is nil or evaluates to nil, show the header, -otherwise don't. function takes one parameter MSG, which is the -message plist for the message to be hidden or not. - -Example that hides all 'trashed' messages: - (setq mu4e-headers-hide-predicate - (lambda (msg) - (member 'trashed (mu4e-message-field msg :flags)))) - -Note that this is merely a display filter.") - - -(defcustom mu4e-headers-visible-flags - '(draft flagged new passed replied seen trashed attach encrypted signed unread) - "An ordered list of flags to show in the headers buffer. Each -element is a symbol in the list (DRAFT FLAGGED NEW PASSED -REPLIED SEEN TRASHED ATTACH ENCRYPTED SIGNED UNREAD)." - :type '(set - (const :tag "Draft" draft) - (const :tag "Flagged" flagged) - (const :tag "New" new) - (const :tag "Passed" passed) - (const :tag "Replied" replied) - (const :tag "Seen" seen) - (const :tag "Trashed" trashed) - (const :tag "Attach" attach) - (const :tag "Encrypted" encrypted) - (const :tag "Signed" signed) - (const :tag "Unread" unread)) - :group 'mu4e-headers) - -(defcustom mu4e-headers-found-hook nil - "Hook run just *after* all of the headers for the last search -query have been received and are displayed." - :type 'hook - :group 'mu4e-headers) - -(defcustom mu4e-headers-search-bookmark-hook nil - "Hook run just after we invoke a bookmarked search. This -function receives the query as its parameter. - -The reason to use this instead of `mu4e-headers-search-hook' -is if you only want to execute a hook when a search is entered -via a bookmark, e.g. if you'd like to treat the bookmarks as a -custom folder and change the options for the search, -e.g. `mu4e-headers-show-threads', `mu4e-headers-include-related', -`mu4e-headers-skip-duplicates` or `mu4e-headers-results-limit'." - :type 'hook - :group 'mu4e-headers) - -(defcustom mu4e-headers-search-hook nil - "Hook run just before executing a new search operation. This -function receives the query as its parameter. - -This is a more general hook facility than the -`mu4e-headers-search-bookmark-hook'. It gets called on every -executed search, not just those that are invoked via bookmarks, -but also manually invoked searches." - :type 'hook - :group 'mu4e-headers) - -(defvar mu4e-headers-sort-field :date - "Field to sort the headers by. -Field must be a symbol, one of: :date, :subject, :size, :prio, -:from, :to.") - -(defvar mu4e-headers-sort-direction 'descending - "Direction to sort by; a symbol either `descending' (sorting - Z->A) or `ascending' (sorting A->Z).") - -;; marks for headers of the form; each is a cons-cell (basic . fancy) -;; each of which is basic ascii char and something fancy, respectively -(defvar mu4e-headers-draft-mark '("D" . "âš’") "Draft.") -(defvar mu4e-headers-flagged-mark '("F" . "✚") "Flagged.") -(defvar mu4e-headers-new-mark '("N" . "✱") "New.") -(defvar mu4e-headers-passed-mark '("P" . "â¯") "Passed (fwd).") -(defvar mu4e-headers-replied-mark '("R" . "â®") "Replied.") -(defvar mu4e-headers-seen-mark '("S" . "✔") "Seen.") -(defvar mu4e-headers-trashed-mark '("T" . "âš") "Trashed.") -(defvar mu4e-headers-attach-mark '("a" . "âš“") "W/ attachments.") -(defvar mu4e-headers-encrypted-mark '("x" . "âš´") "Encrypted.") -(defvar mu4e-headers-signed-mark '("s" . "☡") "Signed.") -(defvar mu4e-headers-unread-mark '("u" . "⎕") "Unread.") - -;; thread prefix marks -(defvar mu4e-headers-has-child-prefix '("+" . "â—¼ ") "Parent.") -(defvar mu4e-headers-empty-parent-prefix '("-" . "â—½ ") "Orphan.") -(defvar mu4e-headers-first-child-prefix '("\\" . "â”—â–¶") "First child.") -(defvar mu4e-headers-duplicate-prefix '("=" . "≡ ") "Duplicate.") -(defvar mu4e-headers-default-prefix '("|" . "│ ") "Default.") - -(defvar mu4e-headers-actions - '( ("capture message" . mu4e-action-capture-message) - ("show this thread" . mu4e-action-show-thread)) - "List of actions to perform on messages in the headers list. -The actions are of the form (NAME SHORTCUT FUNC) where: -* NAME is the name of the action (e.g. \"Count lines\") -* SHORTCUT is a one-character shortcut to call this action -* FUNC is a function which receives a message plist as an argument.") - -(defvar mu4e-headers-custom-markers - '(("Older than" - (lambda (msg date) (time-less-p (mu4e-msg-field msg :date) date)) - (lambda () (mu4e-get-time-date "Match messages before: "))) - ("Newer than" - (lambda (msg date) (time-less-p date (mu4e-msg-field msg :date))) - (lambda () (mu4e-get-time-date "Match messages after: "))) - ("Bigger than" - (lambda (msg bytes) (> (mu4e-msg-field msg :size) (* 1024 bytes))) - (lambda () (read-number "Match messages bigger than (Kbytes): ")))) - "List of custom markers -- functions to mark message that match -some custom function. Each of the list members has the following format: - (NAME PREDICATE-FUNC PARAM-FUNC) -* NAME is the name of the predicate function, and the first character -is the shortcut (so keep those unique). -* PREDICATE-FUNC is a function that takes two parameters, MSG -and (optionally) PARAM, and should return non-nil when there's a -match. -* PARAM-FUNC is function that is evaluated once, and its value is then passed to -PREDICATE-FUNC as PARAM. This is useful for getting user-input.") - -(defvar mu4e-headers-show-threads t - "Whether to show threads in the headers list.") - -(defvar mu4e-headers-full-search nil - "Whether to show all results. -If this is nil show results up to `mu4e-search-results-limit')") - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;;; internal variables/constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; docid cookies -(defconst mu4e~headers-docid-pre "\376" - "Each header starts (invisibly) with the `mu4e~headers-docid-pre', -followed by the docid, followed by `mu4e~headers-docid-post'.") -(defconst mu4e~headers-docid-post "\377" - "Each header starts (invisibly) with the `mu4e~headers-docid-pre', -followed by the docid, followed by `mu4e~headers-docid-post'.") - -(defvar mu4e~headers-view-win nil - "The view window connected to this headers view.") - -(defvar mu4e~headers-sort-field-choices - '( ("date" . :date) - ("from" . :from) - ("prio" . :prio) - ("zsize" . :size) - ("subject" . :subject) - ("to" . :to)) - "List of cells describing the various sort-options. -In the format needed for `mu4e-read-option'.") -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun mu4e~headers-clear () - "Clear the header buffer and related data structures." - (when (buffer-live-p mu4e~headers-buffer) - (let ((inhibit-read-only t)) - (with-current-buffer mu4e~headers-buffer - (setq mu4e~view-msg nil) - (mu4e~mark-clear) - (erase-buffer))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; handler functions -;; -;; next are a bunch of handler functions; those will be called from mu4e~proc in -;; response to output from the server process - -(defun mu4e~headers-view-handler (msg) - "Handler function for displaying a message." - (mu4e-view msg mu4e~headers-buffer)) - -(defun mu4e~headers-view-this-message-p (docid) - "Is DOCID currently being viewed?" - (let ((viewbuf (get-buffer mu4e~view-buffer-name))) - (when (and viewbuf (buffer-live-p viewbuf)) - (with-current-buffer viewbuf - (eq docid (plist-get mu4e~view-msg :docid)))))) - -(defun mu4e~headers-update-handler (msg is-move) - "Update handler, will be called when a message has been updated -in the database. This function will update the current list of -headers." - (when (buffer-live-p mu4e~headers-buffer) - (with-current-buffer mu4e~headers-buffer - (let* ((docid (mu4e-message-field msg :docid)) - (initial-message-at-point (mu4e~headers-docid-at-point)) - (initial-column (current-column)) - (point (mu4e~headers-docid-pos docid))) - - (when point ;; is the message present in this list? - - ;; if it's marked, unmark it now - (when (mu4e-mark-docid-marked-p docid) - (mu4e-mark-set 'unmark)) - - ;; re-use the thread info from the old one; this is needed because - ;; *update* messages don't have thread info by themselves (unlike - ;; search results) - ;; since we still have the search results, re-use - ;; those - (plist-put msg :thread - (mu4e~headers-field-for-docid docid :thread)) - - ;; first, remove the old one (otherwise, we'd have two headers with - ;; the same docid... - (mu4e~headers-remove-handler docid) - - ;; if we're actually viewing this message (in mu4e-view mode), we - ;; update it; that way, the flags can be updated, as well as the path - ;; (which is useful for viewing the raw message) - (when (mu4e~headers-view-this-message-p docid) - (mu4e-view msg mu4e~headers-buffer)) - ;; now, if this update was about *moving* a message, we don't show it - ;; anymore (of course, we cannot be sure if the message really no - ;; longer matches the query, but this seem a good heuristic. if it - ;; was only a flag-change, show the message with its updated flags. - (unless is-move - (mu4e~headers-header-handler msg point)) - - (if (and initial-message-at-point - (mu4e~headers-goto-docid initial-message-at-point)) - (progn - (move-to-column initial-column) - (mu4e~headers-highlight initial-message-at-point)) - ;; attempt to highlight the corresponding line and make it visible - (mu4e~headers-highlight docid))))))) - -(defun mu4e~headers-remove-handler (docid) - "Remove handler, will be called when a message with DOCID has -been removed from the database. This function will hide the removed -message from the current list of headers. If the message is not -present, don't do anything." - (when (buffer-live-p mu4e~headers-buffer) - (with-current-buffer mu4e~headers-buffer - (mu4e~headers-remove-header docid t) - - ;; if we were viewing this message, close it now. - (when (and (mu4e~headers-view-this-message-p docid) - (buffer-live-p mu4e~view-buffer)) - (with-current-buffer mu4e~view-buffer - ;; XXX it seems this sometimes fails; investigate; - ;; for now, just ignore the error - (ignore-errors - (kill-buffer-and-window))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defsubst mu4e~headers-contact-str (contacts) - "Turn the list of contacts CONTACTS (with elements (NAME . EMAIL) -into a string." - (mapconcat - (lambda (ct) - (let ((name (car ct)) (email (cdr ct))) - (or name email "?"))) contacts ", ")) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defsubst mu4e~headers-thread-prefix (thread) - "Calculate the thread prefix based on thread info THREAD." - (when thread - (let ((get-prefix - (lambda (cell) (if mu4e-use-fancy-chars (cdr cell) (car cell))))) - (concat - (make-string (* (if (plist-get thread :empty-parent) 0 1) - (plist-get thread :level)) ?\s) - (cond - ((plist-get thread :has-child) - (funcall get-prefix mu4e-headers-has-child-prefix)) - ((plist-get thread :empty-parent) - (funcall get-prefix mu4e-headers-empty-parent-prefix)) - ((plist-get thread :first-child) - (funcall get-prefix mu4e-headers-first-child-prefix)) - ((plist-get thread :duplicate) - (funcall get-prefix mu4e-headers-duplicate-prefix)) - (t - (funcall get-prefix mu4e-headers-default-prefix))) - " ")))) - -(defsubst mu4e~headers-flags-str (flags) - "Get a display string for the flags. -Note that `mu4e-flags-to-string' is for internal use only; this -function is for display. (This difference is significant, since -internally, the Maildir spec determines what the flags look like, -while our display may be different)." - (let ((str "") - (get-prefix - (lambda (cell) (if mu4e-use-fancy-chars (cdr cell) (car cell))))) - (dolist (flag mu4e-headers-visible-flags) - (when (member flag flags) - (setq str - (concat str - (case flag - ('draft (funcall get-prefix mu4e-headers-draft-mark)) - ('flagged (funcall get-prefix mu4e-headers-flagged-mark)) - ('new (funcall get-prefix mu4e-headers-new-mark)) - ('passed (funcall get-prefix mu4e-headers-passed-mark)) - ('replied (funcall get-prefix mu4e-headers-replied-mark)) - ('seen (funcall get-prefix mu4e-headers-seen-mark)) - ('trashed (funcall get-prefix mu4e-headers-trashed-mark)) - ('attach (funcall get-prefix mu4e-headers-attach-mark)) - ('encrypted (funcall get-prefix mu4e-headers-encrypted-mark)) - ('signed (funcall get-prefix mu4e-headers-signed-mark)) - ('unread (funcall get-prefix mu4e-headers-unread-mark))))))) - str)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defconst mu4e-headers-from-or-to-prefix '("" . "To ") - "Prefix for the :from-or-to field. -It's a cons cell with the car element being the From: prefix, the -cdr element the To: prefix.") - -(defsubst mu4e~headers-from-or-to (msg) - "When the from address for message MSG is one of the the user's addresses, -\(as per `mu4e-user-mail-address-list'), show the To address; -otherwise ; show the from address; prefixed with the appropriate -`mu4e-headers-from-or-to-prefix'." - (let ((addr (cdr-safe (car-safe (mu4e-message-field msg :from))))) - (if (mu4e-user-mail-address-p addr) - (concat (cdr mu4e-headers-from-or-to-prefix) - (mu4e~headers-contact-str (mu4e-message-field msg :to))) - (concat (car mu4e-headers-from-or-to-prefix) - (mu4e~headers-contact-str (mu4e-message-field msg :from)))))) - -(defsubst mu4e~headers-human-date (msg) - "Show a 'human' date. -If the date is today, show the time, otherwise, show the -date. The formats used for date and time are -`mu4e-headers-date-format' and `mu4e-headers-time-format'." - (let ((date (mu4e-msg-field msg :date))) - (if (equal date '(0 0 0)) - "None" - (let ((day1 (decode-time date)) - (day2 (decode-time (current-time)))) - (if (and - (eq (nth 3 day1) (nth 3 day2)) ;; day - (eq (nth 4 day1) (nth 4 day2)) ;; month - (eq (nth 5 day1) (nth 5 day2))) ;; year - (format-time-string mu4e-headers-time-format date) - (format-time-string mu4e-headers-date-format date)))))) - - -(defsubst mu4e~headers-thread-subject (msg) - "Get the subject if it is the first one in a thread; otherwise, -return the thread-prefix without the subject-text. In other words, -show the subject of a thread only once, similar to e.g. 'mutt'." - (let* ((tinfo (mu4e-message-field msg :thread)) - (subj (mu4e-msg-field msg :subject))) - (concat ;; prefix subject with a thread indicator - (mu4e~headers-thread-prefix tinfo) - (if (or (not tinfo) (zerop (plist-get tinfo :level)) - (plist-get tinfo :empty-parent)) - (truncate-string-to-width subj 600) "")))) - - -(defsubst mu4e~headers-mailing-list (list) - "Get some identifier for the mailing list." - (if list - (propertize (mu4e-get-mailing-list-shortname list) 'help-echo list) - "")) - -(defun mu4e~headers-custom-field (msg field) - "Show some custom header field, or raise an error if it is not -found." - (let* ((item (or (assoc field mu4e-header-info-custom) - (mu4e-error "field %S not found" field))) - (func (or (plist-get (cdr-safe item) :function) - (mu4e-error "no :function defined for field %S %S" - field (cdr item))))) - (funcall func msg))) - -(defun mu4e~headers-field-apply-basic-properties (msg field val width) - (case field - (:subject - (concat ;; prefix subject with a thread indicator - (mu4e~headers-thread-prefix (mu4e-message-field msg :thread)) - ;; "["(plist-get (mu4e-message-field msg :thread) :path) "] " - ;; work-around: emacs' display gets really slow when lines are too long; - ;; so limit subject length to 600 - (truncate-string-to-width val 600))) - (:thread-subject (mu4e~headers-thread-subject msg)) - ((:maildir :path :message-id) val) - ((:to :from :cc :bcc) (mu4e~headers-contact-str val)) - ;; if we (ie. `user-mail-address' is the 'From', show - ;; 'To', otherwise show From - (:from-or-to (mu4e~headers-from-or-to msg)) - (:date (format-time-string mu4e-headers-date-format val)) - (:mailing-list (mu4e~headers-mailing-list val)) - (:human-date (propertize (mu4e~headers-human-date msg) - 'help-echo (format-time-string - mu4e-headers-long-date-format - (mu4e-msg-field msg :date)))) - (:flags (propertize (mu4e~headers-flags-str val) - 'help-echo (format "%S" val))) - (:tags (propertize (mapconcat 'identity val ", "))) - (:size (mu4e-display-size val)) - (t (mu4e~headers-custom-field msg field)))) - -(defun mu4e~headers-field-truncate-to-width (_msg _field val width) - "Truncate VAL to WIDTH." - (if width - (truncate-string-to-width val width 0 ?\s t) - val)) - -(defvar mu4e~headers-field-handler-functions - '(mu4e~headers-field-apply-basic-properties - mu4e~headers-field-truncate-to-width)) - -(defun mu4e~headers-field-handler (f-w msg) - "Create a description of the field of MSG described by F-W." - (let* ((field (car f-w)) - (width (cdr f-w)) - (val (mu4e-message-field msg (car f-w)))) - (dolist (func mu4e~headers-field-handler-functions) - (setq val (funcall func msg field val width))) - val)) - -(defvar mu4e~headers-line-handler-functions - '(mu4e~headers-line-apply-flag-face)) - -(defun mu4e~headers-line-apply-flag-face (msg line) - "Adjust LINE's face property based on FLAGS." - (let* ((flags (mu4e-message-field msg :flags)) - (face (cond - ((memq 'trashed flags) 'mu4e-trashed-face) - ((memq 'draft flags) 'mu4e-draft-face) - ((or (memq 'unread flags) (memq 'new flags)) - 'mu4e-unread-face) - ((memq 'flagged flags) 'mu4e-flagged-face) - ((memq 'replied flags) 'mu4e-replied-face) - ((memq 'passed flags) 'mu4e-forwarded-face) - (t 'mu4e-header-face)))) - ;; hmmm, this only works with emacs 24.4+ - (when (fboundp 'add-face-text-property) - (add-face-text-property 0 (length line) face t line)) - line)) - -(defun mu4e~headers-line-handler (msg line) - (dolist (func mu4e~headers-line-handler-functions) - (setq line (funcall func msg line))) - line) - -;; note: this function is very performance-sensitive -(defun mu4e~headers-header-handler (msg &optional point) - "Create a one line description of MSG in this buffer, at POINT, -if provided, or at the end of the buffer otherwise." - (unless (and mu4e-headers-hide-predicate - (funcall mu4e-headers-hide-predicate msg)) - (let ((docid (mu4e-message-field msg :docid)) - (line (mapconcat (lambda (f-w) - (mu4e~headers-field-handler f-w msg)) - mu4e-headers-fields " "))) - (setq line (mu4e~headers-line-handler msg line)) - (mu4e~headers-add-header line docid point msg)))) - -(defconst mu4e~no-matches "No matching messages found") -(defconst mu4e~end-of-results "End of search results") - -(defun mu4e~headers-found-handler (count) - "Create a one line description of the number of headers found -after the end of the search results." - (when (buffer-live-p mu4e~headers-buffer) - (with-current-buffer mu4e~headers-buffer - (save-excursion - (goto-char (point-max)) - (let ((inhibit-read-only t) - (str (if (zerop count) mu4e~no-matches mu4e~end-of-results))) - (insert (propertize str 'face 'mu4e-system-face 'intangible t)) - (unless (zerop count) - (mu4e-message "Found %d matching message%s" - count (if (= 1 count) "" "s"))))) - ;; if we need to jump to some specific message, do so now - (goto-char (point-min)) - (when mu4e~headers-msgid-target - (mu4e-headers-goto-message-id mu4e~headers-msgid-target)) - (when (and mu4e~headers-view-target (mu4e-message-at-point 'noerror)) - ;; view the message at point when there is one. - (mu4e-headers-view-message)) - (setq mu4e~headers-view-target nil - mu4e~headers-msgid-target nil)) - (when (mu4e~headers-docid-at-point) - (mu4e~headers-highlight (mu4e~headers-docid-at-point))) - ;; run-hooks - (run-hooks 'mu4e-headers-found-hook))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defmacro mu4e~headers-defun-mark-for (mark) - "Define a function mu4e~headers-mark-MARK." - (let ((funcname (intern (format "mu4e-headers-mark-for-%s" mark))) - (docstring (format "Mark header at point with %s." mark))) - `(progn - (defun ,funcname () ,docstring - (interactive) - (mu4e-headers-mark-and-next ',mark)) - (put ',funcname 'definition-name ',mark)))) - -(mu4e~headers-defun-mark-for refile) -(mu4e~headers-defun-mark-for something) -(mu4e~headers-defun-mark-for delete) -(mu4e~headers-defun-mark-for flag) -(mu4e~headers-defun-mark-for move) -(mu4e~headers-defun-mark-for read) -(mu4e~headers-defun-mark-for trash) -(mu4e~headers-defun-mark-for unflag) -(mu4e~headers-defun-mark-for untrash) -(mu4e~headers-defun-mark-for unmark) -(mu4e~headers-defun-mark-for unread) -(mu4e~headers-defun-mark-for action) - - -;;; headers-mode and mode-map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e-headers-mode-map nil - "Keymap for *mu4e-headers* buffers.") -(unless mu4e-headers-mode-map - (setq mu4e-headers-mode-map - (let ((map (make-sparse-keymap))) - - (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) - ;; for terminal users - (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) - - (define-key map "s" 'mu4e-headers-search) - (define-key map "S" 'mu4e-headers-search-edit) - - (define-key map "/" 'mu4e-headers-search-narrow) - - (define-key map "j" 'mu4e~headers-jump-to-maildir) - - (define-key map (kbd "<M-left>") 'mu4e-headers-query-prev) - (define-key map (kbd "\\") 'mu4e-headers-query-prev) - (define-key map (kbd "<M-right>") 'mu4e-headers-query-next) - - (define-key map "b" 'mu4e-headers-search-bookmark) - (define-key map "B" 'mu4e-headers-search-bookmark-edit) - - (define-key map "O" 'mu4e-headers-change-sorting) - (define-key map "P" 'mu4e-headers-toggle-threading) - (define-key map "Q" 'mu4e-headers-toggle-full-search) - (define-key map "W" 'mu4e-headers-toggle-include-related) - (define-key map "V" 'mu4e-headers-toggle-skip-duplicates) - - (define-key map "q" 'mu4e~headers-quit-buffer) - (define-key map "g" 'mu4e-headers-rerun-search) ;; for compatibility - - (define-key map "%" 'mu4e-headers-mark-pattern) - (define-key map "t" 'mu4e-headers-mark-subthread) - (define-key map "T" 'mu4e-headers-mark-thread) - - ;; navigation between messages - (define-key map "p" 'mu4e-headers-prev) - (define-key map "n" 'mu4e-headers-next) - (define-key map (kbd "<M-up>") 'mu4e-headers-prev) - (define-key map (kbd "<M-down>") 'mu4e-headers-next) - - (define-key map (kbd "[") 'mu4e-headers-prev-unread) - (define-key map (kbd "]") 'mu4e-headers-next-unread) - - ;; change the number of headers - (define-key map (kbd "C-+") 'mu4e-headers-split-view-grow) - (define-key map (kbd "C--") 'mu4e-headers-split-view-shrink) - (define-key map (kbd "<C-kp-add>") 'mu4e-headers-split-view-grow) - (define-key map (kbd "<C-kp-subtract>") 'mu4e-headers-split-view-shrink) - - (define-key map ";" 'mu4e-context-switch) - - ;; switching to view mode (if it's visible) - (define-key map "y" 'mu4e-select-other-view) - - ;; marking/unmarking ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - (define-key map (kbd "<backspace>") 'mu4e-headers-mark-for-trash) - (define-key map (kbd "d") 'mu4e-headers-mark-for-trash) - (define-key map (kbd "<delete>") 'mu4e-headers-mark-for-delete) - (define-key map (kbd "<deletechar>") 'mu4e-headers-mark-for-delete) - (define-key map (kbd "D") 'mu4e-headers-mark-for-delete) - (define-key map (kbd "m") 'mu4e-headers-mark-for-move) - (define-key map (kbd "r") 'mu4e-headers-mark-for-refile) - - (define-key map (kbd "?") 'mu4e-headers-mark-for-unread) - (define-key map (kbd "!") 'mu4e-headers-mark-for-read) - (define-key map (kbd "A") 'mu4e-headers-mark-for-action) - - (define-key map (kbd "u") 'mu4e-headers-mark-for-unmark) - (define-key map (kbd "+") 'mu4e-headers-mark-for-flag) - (define-key map (kbd "-") 'mu4e-headers-mark-for-unflag) - (define-key map (kbd "=") 'mu4e-headers-mark-for-untrash) - (define-key map (kbd "&") 'mu4e-headers-mark-custom) - - (define-key map (kbd "*") 'mu4e-headers-mark-for-something) - (define-key map (kbd "<kp-multiply>") 'mu4e-headers-mark-for-something) - (define-key map (kbd "<insertchar>") 'mu4e-headers-mark-for-something) - (define-key map (kbd "<insert>") 'mu4e-headers-mark-for-something) - - - (define-key map (kbd "#") 'mu4e-mark-resolve-deferred-marks) - - (define-key map "U" 'mu4e-mark-unmark-all) - (define-key map "x" 'mu4e-mark-execute-all) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - (define-key map "a" 'mu4e-headers-action) - - ;; message composition - (define-key map "R" 'mu4e-compose-reply) - (define-key map "F" 'mu4e-compose-forward) - (define-key map "C" 'mu4e-compose-new) - (define-key map "E" 'mu4e-compose-edit) - - (define-key map (kbd "RET") 'mu4e-headers-view-message) - (define-key map [mouse-2] 'mu4e-headers-view-message) - - (define-key map "$" 'mu4e-show-log) - (define-key map "H" 'mu4e-display-manual) - - ;; menu - (define-key map [menu-bar] (make-sparse-keymap)) - (let ((menumap (make-sparse-keymap "Headers"))) - (define-key map [menu-bar headers] (cons "Headers" menumap)) - - (define-key menumap [mu4e~headers-quit-buffer] - '("Quit view" . mu4e~headers-quit-buffer)) - (define-key menumap [display-help] '("Help" . mu4e-display-manual)) - - (define-key menumap [sepa0] '("--")) - - (define-key menumap [toggle-include-related] - '(menu-item "Toggle related messages" - mu4e-headers-toggle-include-related - :button (:toggle . - (and (boundp 'mu4e-headers-include-related) - mu4e-headers-include-related)))) - (define-key menumap [toggle-threading] - '(menu-item "Toggle threading" mu4e-headers-toggle-threading - :button (:toggle . - (and (boundp 'mu4e-headers-show-threads) - mu4e-headers-show-threads)))) - - (define-key menumap [sepa1] '("--")) - - (define-key menumap [execute-marks] '("Execute marks" - . mu4e-mark-execute-all)) - (define-key menumap [unmark-all] '("Unmark all" . mu4e-mark-unmark-all)) - (define-key menumap [unmark] - '("Unmark" . mu4e-headers-mark-for-unmark)) - - (define-key menumap [mark-pattern] '("Mark pattern" . - mu4e-headers-mark-pattern)) - (define-key menumap [mark-as-read] '("Mark as read" . - mu4e-headers-mark-for-read)) - (define-key menumap [mark-as-unread] - '("Mark as unread" . mu4e-headers-mark-for-unread)) - - (define-key menumap [mark-delete] - '("Mark for deletion" . mu4e-headers-mark-for-delete)) - (define-key menumap [mark-trash] - '("Mark for trash" . mu4e-headers-mark-for-trash)) - (define-key menumap [mark-move] - '("Mark for move" . mu4e-headers-mark-for-move)) - (define-key menumap [sepa2] '("--")) - - - (define-key menumap [resend] '("Resend" . mu4e-compose-resend)) - (define-key menumap [forward] '("Forward" . mu4e-compose-forward)) - (define-key menumap [reply] '("Reply" . mu4e-compose-reply)) - (define-key menumap [compose-new] '("Compose new" . mu4e-compose-new)) - - - (define-key menumap [sepa3] '("--")) - - (define-key menumap [query-next] - '("Next query" . mu4e-headers-query-next)) - (define-key menumap [query-prev] '("Previous query" . - mu4e-headers-query-prev)) - (define-key menumap [narrow-search] '("Narrow search" . - mu4e-headers-search-narrow)) - (define-key menumap [bookmark] '("Search bookmark" . - mu4e-headers-search-bookmark)) - (define-key menumap [jump] '("Jump to maildir" . - mu4e~headers-jump-to-maildir)) - (define-key menumap [refresh] '("Refresh" . mu4e-headers-rerun-search)) - (define-key menumap [search] '("Search" . mu4e-headers-search)) - - - (define-key menumap [sepa4] '("--")) - - (define-key menumap [view] '("View" . mu4e-headers-view-message)) - (define-key menumap [next] '("Next" . mu4e-headers-next)) - (define-key menumap [previous] '("Previous" . mu4e-headers-prev)) - (define-key menumap [sepa5] '("--"))) - map))) -(fset 'mu4e-headers-mode-map mu4e-headers-mode-map) - - -(defun mu4e~header-line-format () - "Get the format for the header line." - (let ((uparrow (if mu4e-use-fancy-chars " â–²" " ^")) - (downarrow (if mu4e-use-fancy-chars " â–¼" " V"))) - (cons - (make-string - (+ mu4e~mark-fringe-len (floor (fringe-columns 'left t))) ?\s) - (mapcar - (lambda (item) - (let* ((field (car item)) (width (cdr item)) - (info (cdr (assoc field - (append mu4e-header-info mu4e-header-info-custom)))) - (sortable (plist-get info :sortable)) - ;; if sortable, it is either t (when field is sortable itself) - ;; or a symbol (if another field is used for sorting) - (sortfield (when sortable (if (booleanp sortable) field sortable))) - (help (plist-get info :help)) - ;; triangle to mark the sorted-by column - (arrow - (when (and sortable (eq sortfield mu4e-headers-sort-field)) - (if (eq mu4e-headers-sort-direction 'descending) downarrow uparrow))) - (name (concat (plist-get info :shortname) arrow)) - (map (make-sparse-keymap))) - (when sortable - (define-key map [header-line mouse-1] - (lambda (&optional e) - ;; getting the field, inspired by `tabulated-list-col-sort' - (interactive "e") - (let* ((obj (posn-object (event-start e))) - (field - (and obj (get-text-property 0 'field (car obj))))) - ;; "t": if we're already sorted by field, the sort-order is - ;; changed - (mu4e-headers-change-sorting field t))))) - (concat - (propertize - (if width - (truncate-string-to-width name width 0 ?\s t) - name) - 'face (when arrow 'bold) - 'help-echo help - 'mouse-face (when sortable 'highlight) - 'keymap (when sortable map) - 'field field) " "))) - mu4e-headers-fields)))) - -(defvar mu4e-headers-mode-abbrev-table nil) - -(defun mu4e~headers-do-auto-update () - "Update the current headers buffer after indexing has brought -some changes, `mu4e-headers-auto-update' is non-nil and there is no -user-interaction ongoing." - (when (and mu4e-headers-auto-update ;; must be set - (zerop (mu4e-mark-marks-num)) ;; non active marks - (not (active-minibuffer-window))) ;; no user input - (with-current-buffer mu4e~headers-buffer - (mu4e-headers-rerun-search)))) - -(define-derived-mode mu4e-headers-mode special-mode - "mu4e:headers" - "Major mode for displaying mu4e search results. -\\{mu4e-headers-mode-map}." - (use-local-map mu4e-headers-mode-map) - (make-local-variable 'mu4e~headers-proc) - (make-local-variable 'mu4e~highlighted-docid) - (make-local-variable 'global-mode-string) - (set (make-local-variable 'hl-line-face) 'mu4e-header-highlight-face) - - ;; maybe update the current headers upon indexing changes - (add-hook 'mu4e-index-updated-hook 'mu4e~headers-do-auto-update nil t) - (setq - truncate-lines t - buffer-undo-list t ;; don't record undo information - overwrite-mode nil - header-line-format (mu4e~header-line-format)) - - (mu4e~mark-initialize) ;; initialize the marking subsystem - (hl-line-mode 1)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; highlighting -(defvar mu4e~highlighted-docid nil - "The highlighted docid") - -(defun mu4e~headers-highlight (docid) - "Highlight the header with DOCID, or do nothing if it's not found. -Also, unhighlight any previously highlighted headers." - (with-current-buffer mu4e~headers-buffer - (save-excursion - ;; first, unhighlight the previously highlighted docid, if any - (when (and docid mu4e~highlighted-docid - (mu4e~headers-goto-docid mu4e~highlighted-docid)) - (hl-line-unhighlight)) - ;; now, highlight the new one - (when (mu4e~headers-goto-docid docid) - (hl-line-highlight))) - (setq mu4e~highlighted-docid docid))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e~headers-select-window () - "When there is a visible window for the headers buffer, make sure -to select it. This is needed when adding new headers, otherwise -adding a lot of new headers looks really choppy." - (let ((win (get-buffer-window mu4e~headers-buffer))) - (when win (select-window win)))) - -;;;; headers in the buffer are prefixed by an invisible string with the docid -;;;; followed by an EOT ('end-of-transmission', \004, ^D) non-printable ascii -;;;; character. this string also has a text-property with the docid. the former -;;;; is used for quickly finding a certain header, the latter for retrieving the -;;;; docid at point without string matching etc. - -(defsubst mu4e~headers-docid-cookie (docid) - "Create an invisible string containing DOCID; this is to be used -at the beginning of lines to identify headers." - (propertize (format "%s%d%s" - mu4e~headers-docid-pre docid mu4e~headers-docid-post) - 'docid docid 'invisible t));; - -(defsubst mu4e~headers-docid-at-point (&optional point) - "Get the docid for the header at POINT, or at current (point) if -nil. Returns the docid, or nil if there is none." - (save-excursion - (when point - (goto-char point)) - (get-text-property (line-beginning-position) 'docid))) - -(defun mu4e~headers-goto-docid (docid &optional to-mark) - "Go to the beginning of the line with the header with docid -DOCID, or nil if it cannot be found. If the optional TO-MARK is -non-nil, go to the point directly *after* the docid-cookie instead -of the beginning of the line." - (let ((oldpoint (point)) (newpoint)) - (goto-char (point-min)) - (setq newpoint - (search-forward (mu4e~headers-docid-cookie docid) nil t)) - (unless to-mark - (if (null newpoint) - (goto-char oldpoint) ;; not found; restore old pos - (progn - (beginning-of-line) ;; found, move to beginning of line - (setq newpoint (point))))) - newpoint)) ;; return the point, or nil if not found - - -(defsubst mu4e~headers-docid-pos (docid) - "Return the pos of the beginning of the line with the header with -docid DOCID, or nil if it cannot be found." - (let ((pos)) - (save-excursion - (setq pos (mu4e~headers-goto-docid docid))) - pos)) - -(defsubst mu4e~headers-field-for-docid (docid field) - "Get FIELD (a symbol, see `mu4e-headers-names') for the message -with DOCID which must be present in the headers buffer." - (save-excursion - (when (mu4e~headers-goto-docid docid) - (mu4e-message-field (mu4e-message-at-point) field)))) - -(defun mu4e-headers-goto-message-id (msgid) - "Go to the next message with message-id MSGID. Return the -message plist, or nil if not found." - (mu4e-headers-find-if - (lambda (msg) - (let ((this-msgid (mu4e-message-field msg :message-id))) - (when (and this-msgid (string= msgid this-msgid)) - msg))))) - -;;;; markers mark headers for -(defun mu4e~headers-mark (docid mark) - "(Visually) mark the header for DOCID with character MARK." - (with-current-buffer mu4e~headers-buffer - (let ((inhibit-read-only t) (oldpoint (point))) - (unless (mu4e~headers-goto-docid docid) - (mu4e-error "Cannot find message with docid %S" docid)) - ;; now, we're at the beginning of the header, looking at - ;; <docid>\004 - ;; (which is invisible). jump past that… - (unless (re-search-forward mu4e~headers-docid-post nil t) - (mu4e-error "Cannot find the `mu4e~headers-docid-post' separator")) - - ;; clear old marks, and add the new ones. - (let ((msg (get-text-property (point) 'msg))) - (delete-char mu4e~mark-fringe-len) - (insert (propertize - (format mu4e~mark-fringe-format mark) - 'face 'mu4e-header-marks-face - 'docid docid - 'msg msg))) - (goto-char oldpoint)))) - - -(defsubst mu4e~headers-add-header (str docid point &optional msg) - "Add header STR with DOCID to the buffer at POINT if non-nil, or -at (point-max) otherwise. If MSG is not nil, add it as the -text-property `msg'." - (when (buffer-live-p mu4e~headers-buffer) - (with-current-buffer mu4e~headers-buffer - (let ((inhibit-read-only t) - (is-first-header (= (point-min) (point-max)))) - (save-excursion - (goto-char (if point point (point-max))) - (insert - (propertize - (concat - (mu4e~headers-docid-cookie docid) - mu4e~mark-fringe - str "\n") - 'docid docid 'msg msg))))))) - -(defun mu4e~headers-remove-header (docid &optional ignore-missing) - "Remove header with DOCID at point. -When IGNORE-MISSING is non-nill, don't raise an error when the -docid is not found." - (with-current-buffer mu4e~headers-buffer - (if (mu4e~headers-goto-docid docid) - (let ((inhibit-read-only t)) - (delete-region (line-beginning-position) (line-beginning-position 2))) - (unless ignore-missing - (mu4e-error "Cannot find message with docid %S" docid))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e~headers-search-execute (expr ignore-history) - "Search in the mu database for EXPR, and switch to the output -buffer for the results. If IGNORE-HISTORY is true, do *not* update -the query history stack." - ;; note: we don't want to update the history if this query comes from - ;; `mu4e~headers-query-next' or `mu4e~headers-query-prev'. - (mu4e-hide-other-mu4e-buffers) - (let* ((buf (get-buffer-create mu4e~headers-buffer-name)) - (inhibit-read-only t) - (maxnum (unless mu4e-headers-full-search mu4e-headers-results-limit))) - (with-current-buffer buf - (mu4e-headers-mode) - (unless ignore-history - ;; save the old present query to the history list - (when mu4e~headers-last-query - (mu4e~headers-push-query mu4e~headers-last-query 'past))) - (setq - mu4e~headers-buffer buf - mode-name "mu4e-headers" - mu4e~headers-last-query expr - global-mode-string - '(:eval - (concat - (propertize - (mu4e~quote-for-modeline mu4e~headers-last-query) - 'face 'mu4e-modeline-face) - " " - (mu4e-context-label))))) - - (switch-to-buffer buf) - (run-hook-with-args 'mu4e-headers-search-hook expr) - (mu4e~proc-find - expr - mu4e-headers-show-threads - mu4e-headers-sort-field - mu4e-headers-sort-direction - maxnum - mu4e-headers-skip-duplicates - mu4e-headers-include-related))) - -(defun mu4e~headers-redraw-get-view-window () - "Close all windows, redraw the headers buffer based on the value -of `mu4e-split-view', and return a window for the message view." - (mu4e-hide-other-mu4e-buffers) - (unless (buffer-live-p mu4e~headers-buffer) - (mu4e-error "No headers buffer available")) - (switch-to-buffer mu4e~headers-buffer) - ;; kill the existing view win - (when (buffer-live-p mu4e~view-buffer) - (kill-buffer mu4e~view-buffer)) - ;; get a new view window - (setq mu4e~headers-view-win - (let* ((new-win-func - (cond - ((eq mu4e-split-view 'horizontal) ;; split horizontally - '(split-window-vertically mu4e-headers-visible-lines)) - ((eq mu4e-split-view 'vertical) ;; split vertically - '(split-window-horizontally mu4e-headers-visible-columns))))) - (cond ((with-demoted-errors "Unable to split window: %S" - (eval new-win-func))) - (t ;; no splitting; just use the currently selected one - (selected-window))))) - mu4e~headers-view-win) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; search-based marking - -(defun mu4e-headers-for-each (func) - "Call FUNC for each header, moving point to the header. -FUNC receives one argument, the message s-expression for the -corresponding header." - (save-excursion - (goto-char (point-min)) - (while (search-forward mu4e~headers-docid-pre nil t) - ;; not really sure why we need to jump to bol; we do need to, otherwise we - ;; miss lines sometimes... - (let ((msg (get-text-property (line-beginning-position) 'msg))) - (when msg - (funcall func msg)))))) - -(defun mu4e-headers-find-if (func &optional backward) - "Move to the next header for which FUNC returns non-`nil', -starting from the current position. FUNC receives one argument, the -message s-expression for the corresponding header. If BACKWARD is -non-`nil', search backwards. Returns the new position, or `nil' if -nothing was found. If you want to exclude matches for the current -message, you can use `mu4e-headers-find-if-next'." - (let ((pos) - (search-func (if backward 'search-backward 'search-forward))) - (save-excursion - (while (and (null pos) - (funcall search-func mu4e~headers-docid-pre nil t)) - ;; not really sure why we need to jump to bol; we do need to, otherwise we - ;; miss lines sometimes... - (let ((msg (get-text-property (line-beginning-position) 'msg))) - (when (and msg (funcall func msg)) - (setq pos (point)))))) - (when pos - (goto-char pos)))) - -(defun mu4e-headers-find-if-next (func &optional backwards) - "Like `mu4e-headers-find-if', but do not match the current header. -Move to the next or (if BACKWARDS is non-`nil') header for which FUNC -returns non-`nil', starting from the current position." - (let ((pos)) - (save-excursion - (if backwards - (beginning-of-line) - (end-of-line)) - (setq pos (mu4e-headers-find-if func backwards))) - (when pos (goto-char pos)))) - -(defvar mu4e~headers-regexp-hist nil - "History list of regexps used.") - -(defun mu4e-headers-mark-for-each-if (markpair mark-pred &optional param) - "Mark all headers for which predicate function MARK-PRED returns -non-nil with MARKPAIR. MARK-PRED is function that receives two -arguments, MSG (the message at point) and PARAM (a user-specified -parameter). MARKPAIR is a cell (MARK . TARGET); see -`mu4e-mark-at-point' for details about marks." - (mu4e-headers-for-each - (lambda (msg) - (when (funcall mark-pred msg param) - (mu4e-mark-at-point (car markpair) (cdr markpair)))))) - -(defun mu4e-headers-mark-pattern () - "Ask user for a kind of mark (move, delete etc.), a field to -match and a regular expression to match with. Then, mark all -matching messages with that mark." - (interactive) - (let ((markpair (mu4e~mark-get-markpair "Mark matched messages with: " t)) - (field (mu4e-read-option "Field to match: " - '( ("subject" . :subject) - ("from" . :from) - ("to" . :to) - ("cc" . :cc) - ("bcc" . :bcc) - ("list" . :mailing-list)))) - (pattern (read-string - (mu4e-format "Regexp:") - nil 'mu4e~headers-regexp-hist))) - (mu4e-headers-mark-for-each-if - markpair - (lambda (msg param) - (let* ((do-mark) (value (mu4e-msg-field msg field))) - (setq do-mark - (if (member field '(:to :from :cc :bcc :reply-to)) - (find-if (lambda (contact) - (let ((name (car contact)) (email (cdr contact))) - (or (and name (string-match pattern name)) - (and email (string-match pattern email))))) value) - (string-match pattern (or value ""))))))))) - -(defun mu4e-headers-mark-custom () - "Mark messages based on a user-provided predicate function." - (interactive) - (let* ((pred (mu4e-read-option "Match function: " - mu4e-headers-custom-markers)) - (param (when (cdr pred) (eval (cdr pred)))) - (markpair (mu4e~mark-get-markpair "Mark matched messages with: " t))) - (mu4e-headers-mark-for-each-if markpair (car pred) param))) - -(defun mu4e~headers-get-thread-info (msg what) - "Get WHAT (a symbol, either path or thread-id) for MSG." - (let* ((thread (or (mu4e-message-field msg :thread) - (mu4e-error "No thread info found"))) - (path (or (plist-get thread :path) - (mu4e-error "No threadpath found")))) - (case what - (path path) - (thread-id - (save-match-data - ;; the thread id is the first segment of the thread path - (when (string-match "^\\([[:xdigit:]]+\\):?" path) - (match-string 1 path)))) - (otherwise (mu4e-error "Not supported"))))) - - -(defun mu4e-headers-mark-thread-using-markpair (markpair &optional subthread) - "Mark the thread at point using the given markpair. If SUBTHREAD is -non-nil, marking is limited to the message at point and its -descendants." - (let* ((mark (car markpair)) - (allowed-marks (mapcar 'car mu4e-marks))) - (unless (memq mark allowed-marks) - (mu4e-error "The mark (%s) has to be one of: %s" - mark allowed-marks))) - ;; note: the tread id is shared by all messages in a thread - (let* ((msg (mu4e-message-at-point)) - (thread-id (mu4e~headers-get-thread-info msg 'thread-id)) - (path (mu4e~headers-get-thread-info msg 'path)) - (last-marked-point)) - (mu4e-headers-for-each - (lambda (mymsg) - (let ((my-thread-id (mu4e~headers-get-thread-info mymsg 'thread-id))) - (if subthread - ;; subthread matching; mymsg's thread path should have path as its - ;; prefix - (when (string-match (concat "^" path) - (mu4e~headers-get-thread-info mymsg 'path)) - (mu4e-mark-at-point (car markpair) (cdr markpair)) - (setq last-marked-point (point))) - ;; nope; not looking for the subthread; looking for the whole thread - (when (string= thread-id - (mu4e~headers-get-thread-info mymsg 'thread-id)) - (mu4e-mark-at-point (car markpair) (cdr markpair)) - (setq last-marked-point (point))))))) - (when last-marked-point - (goto-char last-marked-point) - (mu4e-headers-next)))) - -(defun mu4e-headers-mark-thread (&optional subthread markpair) - "Like `mu4e-headers-mark-thread-using-markpair' but prompt for the markpair." - (interactive - (let* ((subthread current-prefix-arg)) - (list current-prefix-arg - ;; FIXME: e.g., for refiling we should evaluate this - ;; for each line separately - (mu4e~mark-get-markpair - (if subthread "Mark subthread with: " "Mark whole thread with: ") t)))) - (mu4e-headers-mark-thread-using-markpair markpair subthread)) - -(defun mu4e-headers-mark-subthread (&optional markpair) - "Like `mu4e-mark-thread', but only for a sub-thread." - (interactive) - (if markpair (mu4e-headers-mark-thread t markpair) - (let ((current-prefix-arg t)) - (call-interactively 'mu4e-headers-mark-thread)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;; the query past / present / future ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e~headers-query-past nil - "Stack of queries before the present one.") -(defvar mu4e~headers-query-future nil - "Stack of queries after the present one.") -(defvar mu4e~headers-query-stack-size 20 - "Maximum size for the query stacks.") - -(defun mu4e~headers-push-query (query where) - "Push QUERY to one of the query stacks. -WHERE is a symbol telling us where to push; it's a symbol, either -'future or 'past. Functional also removes duplicats, limits the -stack size." - (let ((stack - (case where - (past mu4e~headers-query-past) - (future mu4e~headers-query-future)))) - ;; only add if not the same item - (unless (and stack (string= (car stack) query)) - (push query stack) - ;; limit the stack to `mu4e~headers-query-stack-size' elements - (when (> (length stack) mu4e~headers-query-stack-size) - (setq stack (subseq stack 0 mu4e~headers-query-stack-size))) - ;; remove all duplicates of the new element - (remove-if (lambda (elm) (string= elm (car stack))) (cdr stack)) - ;; update the stacks - (case where - (past (setq mu4e~headers-query-past stack)) - (future (setq mu4e~headers-query-future stack)))))) - -(defun mu4e~headers-pop-query (whence) - "Pop a query from the stack. -WHENCE is a symbol telling us where to get it from, either `future' -or `past'." - (case whence - (past - (unless mu4e~headers-query-past - (mu4e-warn "No more previous queries")) - (pop mu4e~headers-query-past)) - (future - (unless mu4e~headers-query-future - (mu4e-warn "No more next queries")) - (pop mu4e~headers-query-future)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;; interactive functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e~headers-search-hist nil - "History list of searches.") - -(defvar mu4e~headers-msgid-target nil - "Message-id to jump to after the search has finished.") - -(defvar mu4e~headers-view-target nil - "Whether to automatically view (open) the target message (as - per `mu4e~headers-msgid-target').") - -(defun mu4e-headers-search (&optional expr prompt edit ignore-history msgid show) - "Search in the mu database for EXPR, and switch to the output -buffer for the results. This is an interactive function which ask -user for EXPR. PROMPT, if non-nil, is the prompt used by this -function (default is \"Search for:\"). If EDIT is non-nil, -instead of executing the query for EXPR, let the user edit the -query before executing it. If IGNORE-HISTORY is true, do *not* -update the query history stack. If MSGID is non-nil, attempt to -move point to the first message with that message-id after -searching. If SHOW is non-nil, show the message with MSGID." - ;; note: we don't want to update the history if this query comes from - ;; `mu4e~headers-query-next' or `mu4e~headers-query-prev'." - (interactive) - (let* ((prompt (mu4e-format (or prompt "Search for: "))) - (expr - (if edit - (read-string prompt expr) - (or expr - (read-string prompt nil 'mu4e~headers-search-hist))))) - (mu4e-mark-handle-when-leaving) - (mu4e~headers-search-execute expr ignore-history) - (setq mu4e~headers-msgid-target msgid - mu4e~headers-view-target show))) - -(defun mu4e-headers-search-edit () - "Edit the last search expression." - (interactive) - (mu4e-headers-search mu4e~headers-last-query nil t)) - -(defun mu4e-headers-search-bookmark (&optional expr edit) - "Search using some bookmarked query EXPR. -If EDIT is non-nil, let the user edit the bookmark before starting -the search." - (interactive) - (let ((expr - (or expr - (mu4e-ask-bookmark (if edit "Select bookmark: " "Bookmark: "))))) - (run-hook-with-args 'mu4e-headers-search-bookmark-hook expr) - (mu4e-headers-search expr (when edit "Edit bookmark: ") edit))) - -(defun mu4e-headers-search-bookmark-edit () - "Edit an existing bookmark before executing it." - (interactive) - (mu4e-headers-search-bookmark nil t)) - - -(defun mu4e-headers-search-narrow (filter ) - "Narrow the last search by appending search expression FILTER to -the last search expression. Note that you can go back to previous -query (effectively, 'widen' it), with `mu4e-headers-query-prev'." - (interactive - (let ((filter - (read-string (mu4e-format "Narrow down to: ") - nil 'mu4e~headers-search-hist nil t))) - (list filter))) - (unless mu4e~headers-last-query - (mu4e-warn "There's nothing to filter")) - (mu4e-headers-search - (format "(%s) AND (%s)" mu4e~headers-last-query filter))) - - -(defun mu4e-headers-change-sorting (&optional field dir) - "Change the sorting/threading parameters. -FIELD is the field to sort by; DIR is a symbol: either 'ascending, -'descending, 't (meaning: if FIELD is the same as the current -sortfield, change the sort-order) or nil (ask the user)." - (interactive) - (let* ((field - (or field - (mu4e-read-option "Sortfield: " mu4e~headers-sort-field-choices))) - ;; note: 'sortable' is either a boolean (meaning: if non-nil, this is - ;; sortable field), _or_ another field (meaning: sort by this other field). - (sortable (plist-get (cdr (assoc field mu4e-header-info)) :sortable)) - ;; error check - (sortable - (if sortable - sortable - (mu4e-error "Not a sortable field"))) - (sortfield (if (booleanp sortable) field sortable)) - (dir - (case dir - ((ascending descending) dir) - ;; change the sort order if field = curfield - (t - (if (eq sortfield mu4e-headers-sort-field) - (if (eq mu4e-headers-sort-direction 'ascending) - 'descending 'ascending) - 'descending)) - (mu4e-read-option "Direction: " - '(("ascending" . 'ascending) ("descending" . 'descending)))))) - (setq - mu4e-headers-sort-field sortfield - mu4e-headers-sort-direction dir) - (mu4e-message "Sorting by %s (%s)" - (symbol-name sortfield) - (symbol-name mu4e-headers-sort-direction)) - (mu4e-headers-rerun-search))) - -(defun mu4e~headers-toggle (name togglevar dont-refresh) - "Toggle variable TOGGLEVAR for feature NAME. Unless DONT-REFRESH is non-nil, -re-run the last search." - (set togglevar (not (symbol-value togglevar))) - (mu4e-message "%s turned %s%s" - name - (if (symbol-value togglevar) "on" "off") - (if dont-refresh - " (press 'g' to refresh)" "")) - (unless dont-refresh - (mu4e-headers-rerun-search))) - -(defun mu4e-headers-toggle-threading (&optional dont-refresh) - "Toggle `mu4e-headers-show-threads'. With prefix-argument, do -_not_ refresh the last search with the new setting for threading." - (interactive "P") - (mu4e~headers-toggle "Threading" 'mu4e-headers-show-threads dont-refresh)) - -(defun mu4e-headers-toggle-full-search (&optional dont-refresh) - "Toggle `mu4e-headers-full-search'. With prefix-argument, do -_not_ refresh the last search with the new setting for threading." - (interactive "P") - (mu4e~headers-toggle "Full-search" - 'mu4e-headers-full-search dont-refresh)) - -(defun mu4e-headers-toggle-include-related (&optional dont-refresh) - "Toggle `mu4e-headers-include-related'. With prefix-argument, do -_not_ refresh the last search with the new setting for threading." - (interactive "P") - (mu4e~headers-toggle "Include-related" - 'mu4e-headers-include-related dont-refresh)) - -(defun mu4e-headers-toggle-skip-duplicates (&optional dont-refresh) - "Toggle `mu4e-headers-skip-duplicates'. With prefix-argument, do -_not_ refresh the last search with the new setting for threading." - (interactive "P") - (mu4e~headers-toggle "Skip-duplicates" - 'mu4e-headers-skip-duplicates dont-refresh)) - -(defvar mu4e~headers-loading-buf nil - "A buffer for loading a message view.") - -(defun mu4e~headers-get-loading-buf () - "Get a buffer to give feedback while loading a message view." - (unless (buffer-live-p mu4e~headers-loading-buf) - (setq mu4e~headers-loading-buf - (get-buffer-create " *mu4e-loading*"))) - (with-current-buffer mu4e~headers-loading-buf - (let ((inhibit-read-only t)) - (erase-buffer) - (local-set-key (kbd "q") 'kill-buffer-and-window) - (insert (propertize "Waiting for message..." - 'face 'mu4e-system-face 'intangible t)))) - mu4e~headers-loading-buf) - -(defun mu4e-headers-view-message () - "View message at point. -If there's an existing window for the view, re-use that one. If -not, create a new one, depending on the value of -`mu4e-split-view': if it's a symbol `horizontal' or `vertical', -split the window accordingly; if it is nil, replace the current -window. " - (interactive) - (unless (eq major-mode 'mu4e-headers-mode) - (mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode)) - (let* ((msg (mu4e-message-at-point)) - (docid (or (mu4e-message-field msg :docid) - (mu4e-warn "No message at point"))) - ;; decrypt (or not), based on `mu4e-decryption-policy'. - (decrypt - (and (member 'encrypted (mu4e-message-field msg :flags)) - (if (eq mu4e-decryption-policy 'ask) - (yes-or-no-p (mu4e-format "Decrypt message?")) - mu4e-decryption-policy))) - (viewwin (mu4e~headers-redraw-get-view-window))) - (unless (window-live-p viewwin) - (mu4e-error "Cannot get a message view")) - (select-window viewwin) - (switch-to-buffer (mu4e~headers-get-loading-buf)) - (mu4e~proc-view docid mu4e-view-show-images decrypt))) - -(defun mu4e-headers-rerun-search () - "Rerun the search for the last search expression." - (interactive) - ;; if possible, try to return to the same message - (let* ((msg (mu4e-message-at-point)) - (msgid (and msg (mu4e-message-field msg :message-id)))) - (mu4e-headers-search mu4e~headers-last-query nil nil t msgid))) - -(defun mu4e~headers-query-navigate (whence) - "Execute the previous query from the query stacks. -WHENCE determines where the query is taken from and is a symbol, -either `future' or `past'." - (let ((query (mu4e~headers-pop-query whence)) - (where (if (eq whence 'future) 'past 'future))) - (when query - (mu4e~headers-push-query mu4e~headers-last-query where) - (mu4e-headers-search query nil nil t)))) - -(defun mu4e-headers-query-next () - "Execute the previous query from the query stacks." - (interactive) - (mu4e~headers-query-navigate 'future)) - -(defun mu4e-headers-query-prev () - "Execute the previous query from the query stacks." - (interactive) - (mu4e~headers-query-navigate 'past)) - -;; forget the past so we don't repeat it :/ -(defun mu4e-headers-forget-queries () - "Forget all the complete query history." - (interactive) - (setq ;; note: don't forget the present one - mu4e~headers-query-past nil - mu4e~headers-query-future nil) - (mu4e-message "Query history cleared")) - -(defun mu4e~headers-move (lines) - "Move point LINES lines forward (if LINES is positive) or -backward (if LINES is negative). If this succeeds, return the new -docid. Otherwise, return nil." - (unless (eq major-mode 'mu4e-headers-mode) - (mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode)) - (let ((succeeded (zerop (forward-line lines))) - (docid (mu4e~headers-docid-at-point))) - ;; move point, even if this function is called when this window is not - ;; visible - (when docid - ;; update all windows showing the headers buffer - (walk-windows - (lambda (win) - (when (eq (window-buffer win) mu4e~headers-buffer) - (set-window-point win (point)))) - nil t) - ;;(set-window-point (get-buffer-window mu4e~headers-buffer t) (point)) - ;; attempt to highlight the new line, display the message - (mu4e~headers-highlight docid) - ;; update message view if it was already showing - (when (and mu4e-split-view (window-live-p mu4e~headers-view-win)) - (mu4e-headers-view-message)) - docid))) - -(defun mu4e-headers-next (&optional n) - "Move point to the next message header. -If this succeeds, return the new docid. Otherwise, return nil. -Optionally, takes an integer N (prefix argument), to the Nth next -header." - (interactive "P") - (mu4e~headers-move (or n 1))) - -(defun mu4e-headers-prev (&optional n) - "Move point to the previous message header. -If this succeeds, return the new docid. Otherwise, return nil. -Optionally, takes an integer N (prefix argument), to the Nth -previous header." - (interactive "P") - (mu4e~headers-move (- (or n 1)))) - -(defun mu4e~headers-prev-or-next-unread (backwards) - "Move point to the next message that is unread (and -untrashed). If BACKWARDS is non-`nil', move backwards." - (interactive) - (or (mu4e-headers-find-if-next - (lambda (msg) - (let ((flags (mu4e-message-field msg :flags))) - (and (member 'unread flags) (not (member 'trashed flags))))) - backwards) - (mu4e-message (format "No %s unread message found" - (if backwards "previous" "next"))))) - -(defun mu4e-headers-prev-unread () - "Move point to the previous message that is unread (and -untrashed)." - (interactive) - (mu4e~headers-prev-or-next-unread t)) - -(defun mu4e-headers-next-unread () - "Move point to the next message that is unread (and -untrashed)." - (interactive) - (mu4e~headers-prev-or-next-unread nil)) - -(defun mu4e~headers-jump-to-maildir (maildir) - "Show the messages in maildir (user is prompted to ask what -maildir)." - (interactive - (let ((maildir (mu4e-ask-maildir "Jump to maildir: "))) - (list maildir))) - (when maildir - (mu4e-mark-handle-when-leaving) - (mu4e-headers-search - (format "maildir:\"%s\"" maildir)))) - -(defun mu4e-headers-split-view-grow (&optional n) - "In split-view, grow the headers window. -In horizontal split-view, increase the number of lines shown by N. -In vertical split-view, increase the number of columns shown by N. -If N is negative shrink the headers window. When not in split-view -do nothing." - (interactive "P") - (let ((n (or n 1)) - (hwin (get-buffer-window mu4e~headers-buffer))) - (when (and (buffer-live-p mu4e~view-buffer) (window-live-p hwin)) - (let ((n (or n 1))) - (case mu4e-split-view - ;; emacs has weird ideas about what horizontal, vertical means... - (horizontal - (window-resize hwin n nil) - (incf mu4e-headers-visible-lines n)) - (vertical - (window-resize hwin n t) - (incf mu4e-headers-visible-columns n))))))) - -(defun mu4e-headers-split-view-shrink (&optional n) - "In split-view, shrink the headers window. -In horizontal split-view, decrease the number of lines shown by N. -In vertical split-view, decrease the number of columns shown by N. -If N is negative grow the headers window. When not in split-view -do nothing." - (interactive "P") - (mu4e-headers-split-view-grow (- (or n 1)))) - -(defun mu4e-headers-action (&optional actionfunc) - "Ask user what to do with message-at-point, then do it. -The actions are specified in `mu4e-headers-actions'. Optionally, -pass ACTIONFUNC, which is a function that takes a msg-plist -argument." - (interactive) - (let ((msg (mu4e-message-at-point)) - (afunc (or actionfunc (mu4e-read-option "Action: " mu4e-headers-actions)))) - (funcall afunc msg))) - -(defun mu4e-headers-mark-and-next (mark) - "Set mark MARK on the message at point or on all messages in the -region if there is a region, then move to the next message." - (interactive) - (mu4e-mark-set mark) - (mu4e-headers-next)) - -(defun mu4e~headers-quit-buffer () - "Quit the mu4e-headers buffer. -This is a rather complex function, to ensure we don't disturb -other windows." - (interactive) - (unless (eq major-mode 'mu4e-headers-mode) - (mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode)) - (mu4e-mark-handle-when-leaving) - (let ((curbuf (current-buffer)) (curwin (selected-window)) - (headers-visible)) - (walk-windows - (lambda (win) - (with-selected-window win - ;; if we the view window connected to this one, kill it - (when (and (not (one-window-p win)) (eq mu4e~headers-view-win win)) - (delete-window win) - (setq mu4e~headers-view-win nil))) - ;; and kill any _other_ (non-selected) window that shows the current - ;; buffer - (when (and - (eq curbuf (window-buffer win)) ;; does win show curbuf? - (not (eq curwin win)) ;; it's not the curwin? - (not (one-window-p))) ;; and not the last one? - (delete-window win)))) ;; delete it! - ;; now, all *other* windows should be gone. kill ourselves, and return - ;; to the main view - (kill-buffer) - (mu4e~main-view))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(provide 'mu4e-headers) diff --git a/_spacemacs.d/local/mu4e/mu4e-lists.el b/_spacemacs.d/local/mu4e/mu4e-lists.el deleted file mode 100644 index 00884da..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-lists.el +++ /dev/null @@ -1,93 +0,0 @@ -;;; mu4e-lists.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; In this file, we create a table of list-id -> shortname for mailing lists. -;; The shortname (friendly) should a at most 8 characters, camel-case - - -(defvar mu4e~mailing-lists - '( ("bbdb-info.lists.sourceforge.net" . "BBDB") - ("boost-announce.lists.boost.org" . "BoostA") - ("boost-interest.lists.boost.org" . "BoostI") - ("conkeror.mozdev.org" . "Conkeror") - ("curl-library.cool.haxx.se" . "LibCurl") - ("crypto-gram-list.schneier.com " . "CryptoGr") - ("dbus.lists.freedesktop.org" . "DBus") - ("desktop-devel-list.gnome.org" . "GnomeDT") - ("emacs-devel.gnu.org" . "EmacsDev") - ("emacs-orgmode.gnu.org" . "Orgmode") - ("emms-help.gnu.org" . "Emms") - ("enlightenment-devel.lists.sourceforge.net" . "E-Dev") - ("erlang-questions.erlang.org" . "Erlang") - ("evolution-hackers.lists.ximian.com" . "EvoDev") - ("farsight-devel.lists.sourceforge.net" . "Farsight") - ("mailman.lists.freedesktop.org" . "FDeskTop") - ("gcc-help.gcc.gnu.org" . "Gcc") - ("gmime-devel-list.gnome.org" . "GMimeDev") - ("gnome-shell-list.gnome.org" . "GnomeSh") - ("gnu-emacs-sources.gnu.org" . "EmacsSrc") - ("gnupg-users.gnupg.org" . "GnupgU") - ("gpe.handhelds.org" . "GPE") - ("gstreamer-devel.lists.freedesktop.org" . "GstDev") - ("gstreamer-devel.lists.sourceforge.net" . "GstDev") - ("gstreamer-openmax.lists.sourceforge.net" . "GstOmx") - ("gtk-devel-list.gnome.org" . "GtkDev") - ("gtkmm-list.gnome.org" . "GtkmmDev") - ("guile-devel.gnu.org" . "GuileDev") - ("guile-user.gnu.org" . "GuileUsr") - ("help-gnu-emacs.gnu.org" . "EmacsUsr") - ("lggdh-algemeen.vvtp.tudelft.nl" . "LGGDH") - ("linux-bluetooth.vger.kernel.org" . "Bluez") - ("maemo-developers.maemo.org" . "MaemoDev") - ("maemo-users.maemo.org" . "MaemoUsr") - ("monit-general.nongnu.org" . "Monit") - ("mu-discuss.googlegroups.com" . "Mu") - ("nautilus-list.gnome.org" . "Nautilus") - ("notmuch.notmuchmail.org" . "Notmuch") - ("orbit-list.gnome.org" . "ORBit") - ("pulseaudio-discuss.lists.freedesktop.org" . "PulseA") - ("sqlite-announce.sqlite.org" . "SQliteAnn") - ("sqlite-dev.sqlite.org" . "SQLiteDev") - ("sup-talk.rubyforge.org" . "Sup") - ("sylpheed-claws-users.lists.sourceforge.net" . "Sylpheed") - ("tinymail-devel-list.gnome.org" . "Tinymail") - ("unicode.sarasvati.unicode.org" . "Unicode") - ("xapian-discuss.lists.xapian.org" . "Xapian") - ("xdg.lists.freedesktop.org" . "XDG") - ("wl-en.lists.airs.net" . "Wdrlust") - ("wl-en.ml.gentei.org" . "WdrLust") - ("xapian-devel.lists.xapian.org" . "Xapian") - ("zsh-users.zsh.org" . "ZshUsr")) - "AList of cells (MAILING-LIST-ID . SHORTNAME)") - -(defvar mu4e-user-mailing-lists nil - "An alist with cells (MAILING-LIST-ID . SHORTNAME); these are -used in addition to the built-in list `mu4e~mailing-lists'.") - -(defvar mu4e-mailing-list-patterns nil - "A list of regex patterns to capture a shortname out of a list -ID. For the first regex that matches, its first matchgroup will -be used as the shortname.") - -(provide 'mu4e-lists) diff --git a/_spacemacs.d/local/mu4e/mu4e-main.el b/_spacemacs.d/local/mu4e/mu4e-main.el deleted file mode 100644 index a647a2d..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-main.el +++ /dev/null @@ -1,225 +0,0 @@ -;;; mu4e-main.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;;; Code: -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(require 'smtpmail) ;; the queing stuff (silence elint) -(require 'mu4e-utils) ;; utility functions -(require 'mu4e-context) ;; the context - - -(defconst mu4e~main-buffer-name " *mu4e-main*" - "*internal* Name of the mu4e main view buffer.") - -(defvar mu4e-main-mode-map - (let ((map (make-sparse-keymap))) - - (define-key map "b" 'mu4e-headers-search-bookmark) - (define-key map "B" 'mu4e-headers-search-bookmark-edit) - - (define-key map "s" 'mu4e-headers-search) - (define-key map "q" 'mu4e-quit) - (define-key map "j" 'mu4e~headers-jump-to-maildir) - (define-key map "C" 'mu4e-compose-new) - - (define-key map "m" 'mu4e~main-toggle-mail-sending-mode) - (define-key map "f" 'smtpmail-send-queued-mail) - - ;; - (define-key map "U" 'mu4e-update-mail-and-index) - (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) - ;; for terminal users - (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) - - (define-key map "S" 'mu4e-interrupt-update-mail) - (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) - (define-key map ";" 'mu4e-context-switch) - - (define-key map "$" 'mu4e-show-log) - (define-key map "A" 'mu4e-about) - (define-key map "N" 'mu4e-news) - (define-key map "H" 'mu4e-display-manual) - map) - - "Keymap for the *mu4e-main* buffer.") -(fset 'mu4e-main-mode-map mu4e-main-mode-map) - -(defvar mu4e-main-mode-abbrev-table nil) -(define-derived-mode mu4e-main-mode special-mode "mu4e:main" - "Major mode for the mu4e main screen. -\\{mu4e-main-mode-map}." - (use-local-map mu4e-main-mode-map) - (setq - truncate-lines t - overwrite-mode 'overwrite-mode-binary) - - ;; show context in mode-string - (set (make-local-variable 'global-mode-string) '(:eval (mu4e-context-label))) - (set (make-local-variable 'revert-buffer-function) #'mu4e~main-view-real)) - - -(defun mu4e~main-action-str (str &optional func-or-shortcut) - "Highlight the first occurence of [.] in STR. -If FUNC-OR-SHORTCUT is non-nil and if it is a function, call it -when STR is clicked (using RET or mouse-2); if FUNC-OR-SHORTCUT is -a string, execute the corresponding keyboard action when it is -clicked." - (let ((newstr - (replace-regexp-in-string - "\\[\\(..?\\)\\]" - (lambda(m) - (format "[%s]" - (propertize (match-string 1 m) 'face 'mu4e-highlight-face))) - str)) - (map (make-sparse-keymap)) - (func (if (functionp func-or-shortcut) - func-or-shortcut - (if (stringp func-or-shortcut) - (lexical-let ((macro func-or-shortcut)) - (lambda()(interactive) - (execute-kbd-macro macro))))))) - (define-key map [mouse-2] func) - (define-key map (kbd "RET") func) - (put-text-property 0 (length newstr) 'keymap map newstr) - (put-text-property (string-match "\\[.+$" newstr) - (- (length newstr) 1) 'mouse-face 'highlight newstr) newstr)) - -;; NEW -;; This is the old `mu4e~main-view' function but without -;; buffer switching at the end. -(defun mu4e~main-view-real (ignore-auto noconfirm) - (let ((buf (get-buffer-create mu4e~main-buffer-name)) - (inhibit-read-only t)) - (with-current-buffer buf - (erase-buffer) - (insert - "* " - (propertize "mu4e - mu for emacs version " 'face 'mu4e-title-face) - (propertize mu4e-mu-version 'face 'mu4e-header-key-face) - - ;; show some server properties; in this case; a big C when there's - ;; crypto support, a big G when there's Guile support - " " - (propertize - (concat - (when (plist-get mu4e~server-props :crypto) "C") - (when (plist-get mu4e~server-props :guile) "G")) - 'face 'mu4e-title-face) - - "\n\n" - (propertize " Basics\n\n" 'face 'mu4e-title-face) - (mu4e~main-action-str "\t* [j]ump to some maildir\n" 'mu4e-jump-to-maildir) - (mu4e~main-action-str "\t* enter a [s]earch query\n" 'mu4e-search) - (mu4e~main-action-str "\t* [C]ompose a new message\n" 'mu4e-compose-new) - "\n" - (propertize " Bookmarks\n\n" 'face 'mu4e-title-face) - ;; TODO: it's a bit uncool to hard-code the "b" shortcut... - (mapconcat - (lambda (bm) - (mu4e~main-action-str - (concat "\t* [b" (make-string 1 (mu4e-bookmark-key bm)) "] " - (mu4e-bookmark-name bm)) - (concat "b" (make-string 1 (mu4e-bookmark-key bm))))) - (mu4e-bookmarks) "\n") - "\n\n" - (propertize " Misc\n\n" 'face 'mu4e-title-face) - - (mu4e~main-action-str "\t* [;]Switch focus\n" 'mu4e-context-switch) - - (mu4e~main-action-str "\t* [U]pdate email & database\n" - 'mu4e-update-mail-and-index) - - ;; show the queue functions if `smtpmail-queue-dir' is defined - (if (file-directory-p smtpmail-queue-dir) - (mu4e~main-view-queue) - "") - "\n" - (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* [H]elp\n" 'mu4e-display-manual) - (mu4e~main-action-str "\t* [q]uit\n" 'mu4e-quit)) - (mu4e-main-mode) - ))) - -(defun mu4e~main-view-queue () - "Display queue-related actions in the main view." - (concat - (mu4e~main-action-str "\t* toggle [m]ail sending mode " - 'mu4e~main-toggle-mail-sending-mode) - "(currently " - (propertize (if smtpmail-queue-mail "queued" "direct") - 'face 'mu4e-header-key-face) - ")\n" - (let ((queue-size (mu4e~main-queue-size))) - (if (zerop queue-size) - "" - (mu4e~main-action-str - (format "\t* [f]lush %s queued %s\n" - (propertize (int-to-string queue-size) - 'face 'mu4e-header-key-face) - (if (> queue-size 1) "mails" "mail")) - 'smtpmail-send-queued-mail))))) - -(defun mu4e~main-queue-size () - "Return, as an int, the number of emails in the queue." - (condition-case nil - (with-temp-buffer - (insert-file-contents (expand-file-name smtpmail-queue-index-file - smtpmail-queue-dir)) - (count-lines (point-min) (point-max))) - (error 0))) - -(defun mu4e~main-view () - "Create the mu4e main-view, and switch to it." - (mu4e~main-view-real nil nil) - (switch-to-buffer mu4e~main-buffer-name) - (goto-char (point-min)) - (setq global-mode-string '(:eval (mu4e-context-label)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Interactive functions -;; NEW -;; Toggle mail sending mode without switching -(defun mu4e~main-toggle-mail-sending-mode () - "Toggle sending mail mode, either queued or direct." - (interactive) - (let ((curpos (point))) - (unless (file-directory-p smtpmail-queue-dir) - (mu4e-error "`smtpmail-queue-dir' does not exist")) - (setq smtpmail-queue-mail (not smtpmail-queue-mail)) - (message - (concat "Outgoing mail will now be " - (if smtpmail-queue-mail "queued" "sent directly"))) - (mu4e~main-view-real nil nil) - (goto-char curpos))) - - -;; (progn -;; (define-key mu4e-compose-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode) -;; (define-key mu4e-view-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode) -;; (define-key mu4e-compose-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode) -;; (define-key mu4e-headers-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode) -;; ) - -(provide 'mu4e-main) diff --git a/_spacemacs.d/local/mu4e/mu4e-mark.el b/_spacemacs.d/local/mu4e/mu4e-mark.el deleted file mode 100644 index 4b17f34..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-mark.el +++ /dev/null @@ -1,466 +0,0 @@ -;; mu4e-mark.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; In this file are function related to marking messages; they assume we are -;; currently in the headers buffer. - -;; Code: -(require 'mu4e-proc) -(require 'mu4e-utils) -(require 'mu4e-message) - -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) - -;; keep byte-compiler happy -(declare-function mu4e~headers-mark "mu4e-headers") -(declare-function mu4e~headers-goto-docid "mu4e-headers") -(declare-function mu4e-headers-next "mu4e-headers") - - -(defcustom mu4e-headers-leave-behavior 'ask - "What to do when user leaves the headers view. -That is when he e.g. quits, refreshes or does a new search. -Value is one of the following symbols: -- `ask' ask user whether to ignore the marks -- `apply' automatically apply the marks before doing anything else -- `ignore' automatically ignore the marks without asking" - :type '(choice (const ask :tag "ask user whether to ignore marks") - (const apply :tag "apply marks without asking") - (const ignore :tag "ignore marks without asking")) - :group 'mu4e-headers) - -(defcustom mu4e-mark-execute-pre-hook nil - "Hook run just *before* a mark is applied to a message. The hook function -is called with two arguments, the mark being executed and the message itself.") - -(defvar mu4e-headers-show-target t - "Whether to show targets (such as '-> delete', '-> /archive') -when marking message. Normally, this is useful information for the -user, however, when you often mark large numbers (thousands) of -message, showing the target makes this quite a bit slower (showing -the target uses an emacs feature called 'overlays', which aren't -particularly fast).") - -;;; insert stuff;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e~mark-map nil - "Map (hash) of docid->markinfo; when a message is marked, the -information is added here. -markinfo is a cons cell consisting of the following: -\(mark . target) -where - MARK is the type of mark (move, trash, delete) - TARGET (optional) is the target directory (for 'move')") - -;; the mark-map is specific for the current header buffer -;; currently, there can't be more than one, but we never know what will -;; happen in the future - -;; the fringe is the space on the left of headers, where we put marks below some -;; handy definitions; only `mu4e-mark-fringe-len' should be change (if ever), -;; the others follow from that. -(defconst mu4e~mark-fringe-len 2 - "Width of the fringe for marks on the left.") -(defconst mu4e~mark-fringe (make-string mu4e~mark-fringe-len ?\s) - "The space on the left of message headers to put marks.") -(defconst mu4e~mark-fringe-format (format "%%-%ds" mu4e~mark-fringe-len) - "Format string to set a mark and leave remaining space.") - -(defun mu4e~mark-initialize () - "Initialize the marks subsystem." - (set (make-local-variable 'mu4e~mark-map) (make-hash-table))) - -(defun mu4e~mark-clear () - "Clear the marks subsystem." - (clrhash mu4e~mark-map)) - -(defun mu4e~mark-find-headers-buffer () - "Find the headers buffer, if any." - (find-if - (lambda (b) - (with-current-buffer b - (eq major-mode 'mu4e-headers-mode))) - (buffer-list))) - -(defmacro mu4e~mark-in-context (&rest body) - "Evaluate BODY in the context of the headers buffer in case this -is either a headers or view buffer." - `(cond - ((eq major-mode 'mu4e-headers-mode) ,@body) - ((eq major-mode 'mu4e-view-mode) - (when (buffer-live-p mu4e~view-headers-buffer) - (let* ((msg (mu4e-message-at-point)) - (docid (mu4e-message-field msg :docid))) - (with-current-buffer mu4e~view-headers-buffer - (if (mu4e~headers-goto-docid docid) - ,@body - (mu4e-error "cannot find message in headers buffer.")))))) - (t - ;; even in other modes (e.g. mu4e-main-mode we try to find - ;; the headers buffer - (let ((hbuf (mu4e~mark-find-headers-buffer))) - (if (buffer-live-p hbuf) - (with-current-buffer hbuf - ,@body) - (progn (mu4e-message "%S" major-mode) ,@body)))))) - -(defvar mu4e-marks - '((refile - :char ("r" . "â–¶") - :prompt "refile" - :dyn-target (lambda (target msg) (mu4e-get-refile-folder msg)) - :action (lambda (docid msg target) (mu4e~proc-move docid - (mu4e~mark-check-target target) "-N"))) - (delete - :char ("D" . "âŒ") - :prompt "Delete" - :show-target (lambda (target) "delete") - :action (lambda (docid msg target) (mu4e~proc-remove docid))) - (flag - :char ("+" . "✚") - :prompt "+flag" - :show-target (lambda (target) "flag") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "+F-u-N"))) - (move - :char ("m" . "â–·") - :prompt "move" - :ask-target mu4e~mark-get-move-target - :action (lambda (docid msg target) (mu4e~proc-move docid - (mu4e~mark-check-target target) "-N"))) - (read - :char ("!" . "â—¼") - :prompt "!read" - :show-target (lambda (target) "read") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N"))) - (trash - :char ("d" . "â–¼") - :prompt "dtrash" - :dyn-target (lambda (target msg) (mu4e-get-trash-folder msg)) - :action (lambda (docid msg target) (mu4e~proc-move docid - (mu4e~mark-check-target target) "+T-N"))) - (unflag - :char ("-" . "âž–") - :prompt "-unflag" - :show-target (lambda (target) "unflag") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N"))) - (untrash - :char ("=" . "â–²") - :prompt "=untrash" - :show-target (lambda (target) "untrash") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "-T"))) - (unread - :char ("?" . "â—»") - :prompt "?unread" - :show-target (lambda (target) "unread") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N"))) - (unmark - :char " " - :prompt "unmark" - :action (mu4e-error "No action for unmarking")) - (action - :char ( "a" . "â—¯") - :prompt "action" - :ask-target (lambda () (mu4e-read-option "Action: " mu4e-headers-actions)) - :action (lambda (docid msg actionfunc) - (save-excursion - (when (mu4e~headers-goto-docid docid) - (mu4e-headers-action actionfunc))))) - (something - :char ("*" . "✱") - :prompt "*something" - :action (mu4e-error "No action for deferred mark"))) - - "The list of all the possible marks. -This is an alist mapping mark symbols to their properties. The -properties are: - :char (string) or (basic . fancy) The character to display in - the headers view. Either a single-character string, or a - dotted-pair cons cell where the second item will be used if - `mu4e-use-fancy-chars' is `t', otherwise we'll use - the first one. It can also be a plain string for backwards - compatibility since we didn't always support - `mu4e-use-fancy-chars' here. - :prompt (string) The prompt to use when asking for marks (used for - example when marking a whole thread) - :ask-target (function returning a string) Get the target. This - function run once per bulk-operation, and thus is suitable - for user-interaction. If nil, the target is nil. - :dyn-target (function from (TARGET MSG) to string). Compute - the dynamic target. This is run once per message, which is - passed as MSG. The default is to just return the target. - :show-target (function from TARGET to string) How to display - the target. - :action (function taking (DOCID MSG TARGET)). The action to - apply on the message.") - - -(defun mu4e-mark-at-point (mark target) - "Mark (or unmark) message at point. -MARK specifies the mark-type. For `move'-marks and `trash'-marks -the TARGET argument is non-nil and specifies to which -maildir the message is to be moved/trashed. The function works in -both headers buffers and message buffers. - -The following marks are available, and the corresponding props: - - MARK TARGET description - ---------------------------------------------------------- - `refile' y mark this message for archiving - `something' n mark this message for *something* (decided later) - `delete' n remove the message - `flag' n mark this message for flagging - `move' y move the message to some folder - `read' n mark the message as read - `trash' y trash the message to some folder - `unflag' n mark this message for unflagging - `untrash' n remove the 'trashed' flag from a message - `unmark' n unmark this message - `unread' n mark the message as unread - `action' y mark the message for some action." - (interactive) - (let* ((msg (mu4e-message-at-point)) - (docid (mu4e-message-field msg :docid)) - ;; 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 - ;; info for the user. - (markdesc (cdr (or (assq mark mu4e-marks) (mu4e-error "Invalid mark %S" mark)))) - (get-markkar - (lambda (char) - (if (listp char) - (if mu4e-use-fancy-chars (cdr char) (car char)) - char))) - (markkar (funcall get-markkar (plist-get markdesc :char))) - (target (mu4e~mark-get-dyn-target mark target)) - (show-fct (plist-get markdesc :show-target)) - (shown-target (if show-fct - (funcall show-fct target) - (if target (format "%S" target))))) - (unless docid (mu4e-warn "No message on this line")) - (unless (eq major-mode 'mu4e-headers-mode) (mu4e-error "Not in headers-mode")) - (save-excursion - (when (mu4e~headers-mark docid markkar) - ;; update the hash -- remove everything current, and if add the new stuff, - ;; unless we're unmarking - (remhash docid mu4e~mark-map) - ;; remove possible overlays - (remove-overlays (line-beginning-position) (line-end-position)) - ;; now, let's set a mark (unless we were unmarking) - (unless (eql mark 'unmark) - (puthash docid (cons mark target) mu4e~mark-map) - ;; when we have a target (ie., when moving), show the target folder in - ;; an overlay - (when (and shown-target mu4e-headers-show-target) - (let* ((targetstr (propertize (concat "-> " shown-target " ") - 'face 'mu4e-system-face)) - ;; mu4e~headers-goto-docid docid t \will take us just after the - ;; docid cookie and then we skip the mu4e~mark-fringe - (start (+ (length mu4e~mark-fringe) - (mu4e~headers-goto-docid docid t))) - (overlay (make-overlay start (+ start (length targetstr))))) - (overlay-put overlay 'display targetstr) - docid))))))) - - -(defun mu4e~mark-get-move-target () - "Ask for a move target, and propose to create it if it does not exist." - (interactive) - ;; (mu4e-message-at-point) ;; raises error if there is none - (let* ((target (mu4e-ask-maildir "Move message to: ")) - (target (if (string= (substring target 0 1) "/") - target - (concat "/" target))) - (fulltarget (concat mu4e-maildir target))) - (when (or (file-directory-p fulltarget) - (and (yes-or-no-p - (format "%s does not exist. Create now?" fulltarget)) - (mu4e~proc-mkdir fulltarget))) - target))) - -(defun mu4e~mark-ask-target (mark) - "Ask the target for MARK, if the user should be asked the target." - (let ((getter (plist-get (cdr (assq mark mu4e-marks)) :ask-target))) - (and getter (funcall getter)))) - -(defun mu4e~mark-get-dyn-target (mark target) - "Get the dynamic target for MARK. The result may depend on the -message at point." - (let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target))) - (if getter - (funcall getter target (mu4e-message-at-point)) - target))) - - -(defun mu4e-mark-set (mark &optional target) - "Mark the header at point, or, if region is active, mark all -headers in the region. Optionally, provide TARGET (for moves)." - (unless target - (setq target (mu4e~mark-ask-target mark))) - (if (not (use-region-p)) - ;; single message - (mu4e-mark-at-point mark target) - ;; mark all messages in the region. - (save-excursion - (let ((cant-go-further) (eor (region-end))) - (goto-char (region-beginning)) - (while (and (<= (point) eor) (not cant-go-further)) - (mu4e-mark-at-point mark target) - (setq cant-go-further (not (mu4e-headers-next)))))))) - -(defun mu4e-mark-restore (docid) - "Restore the visual mark for the message with DOCID." - (let ((markcell (gethash docid mu4e~mark-map))) - (when markcell - (save-excursion - (when (mu4e~headers-goto-docid docid) - (mu4e-mark-at-point (car markcell) (cdr markcell))))))) - -(defun mu4e~mark-get-markpair (prompt &optional allow-something) - "Ask user for a mark; return (MARK . TARGET). -If ALLOW-SOMETHING is non-nil, allow the 'something' pseudo mark -as well." - (let* ((marks (mapcar (lambda (markdescr) - (cons (plist-get (cdr markdescr) :prompt) - (car markdescr))) - mu4e-marks)) - (marks - (if allow-something - marks (remove-if (lambda (m) (eq 'something (cdr m))) marks))) - (mark (mu4e-read-option prompt marks)) - (target (mu4e~mark-ask-target mark))) - (cons mark target))) - - -(defun mu4e-mark-resolve-deferred-marks () - "Check if there are any deferred ('something') marks. -If there are such marks, replace them with a _real_ mark (ask the -user which one)." - (interactive) - (mu4e~mark-in-context - (let ((markpair)) - (maphash - (lambda (docid val) - (let ((mark (car val)) (target (cdr val))) - (when (eql mark 'something) - (unless markpair - (setq markpair - (mu4e~mark-get-markpair "Set deferred mark(s) to: " nil))) - (save-excursion - (when (mu4e~headers-goto-docid docid) - (mu4e-mark-set (car markpair) (cdr markpair))))))) - mu4e~mark-map)))) - -(defun mu4e~mark-check-target (target) - "Check if the target exists; if not, offer to create it." - (let ((fulltarget (concat mu4e-maildir target))) - (if (not (mu4e-create-maildir-maybe fulltarget)) - (mu4e-error "Target dir %s does not exist " fulltarget) - target))) - -(defun mu4e-mark-execute-all (&optional no-confirmation) - "Execute the actions for all marked messages in this buffer. -After the actions have been executed succesfully, the affected -messages are *hidden* from the current header list. Since the -headers are the result of a search, we cannot be certain that the -messages no longer match the current one - to get that -certainty, we need to rerun the search, but we don't want to do -that automatically, as it may be too slow and/or break the user's -flow. Therefore, we hide the message, which in practice seems to -work well. - -If NO-CONFIRMATION is non-nil, don't ask user for confirmation." - (interactive) - (mu4e~mark-in-context - (let ((marknum (hash-table-count mu4e~mark-map))) - (if (zerop marknum) - (message "Nothing is marked") - (mu4e-mark-resolve-deferred-marks) - (when (or no-confirmation - (y-or-n-p - (format "Are you sure you want to execute %d mark%s?" - marknum (if (> marknum 1) "s" "")))) - (maphash - (lambda (docid val) - (let* ((mark (car val)) (target (cdr val)) - (markdescr (assq mark mu4e-marks)) - (msg (save-excursion - (mu4e~headers-goto-docid docid) - (mu4e-message-at-point)))) - ;; note: whenever you do something with the message, - ;; it looses its N (new) flag - (if markdescr - (progn - (run-hook-with-args - 'mu4e-mark-execute-pre-hook mark msg) - (funcall (plist-get (cdr markdescr) :action) docid msg target)) - (mu4e-error "Unrecognized mark %S" mark)))) - mu4e~mark-map)) - (mu4e-mark-unmark-all) - (message nil))))) - -(defun mu4e-mark-unmark-all () - "Unmark all marked messages." - (interactive) - (mu4e~mark-in-context - (when (or (null mu4e~mark-map) (zerop (hash-table-count mu4e~mark-map))) - (mu4e-warn "Nothing is marked")) - (maphash - (lambda (docid val) - (save-excursion - (when (mu4e~headers-goto-docid docid) - (mu4e-mark-set 'unmark)))) - mu4e~mark-map) - ;; in any case, clear the marks map - (mu4e~mark-clear))) - -(defun mu4e-mark-docid-marked-p (docid) - "Is the given docid marked?" - (when (gethash docid mu4e~mark-map) t)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun mu4e-mark-marks-num () - "Return the number of marks in the current buffer." - (if mu4e~mark-map (hash-table-count mu4e~mark-map) 0)) - - -(defun mu4e-mark-handle-when-leaving () - "If there are any marks in the current buffer, handle those -according to the value of `mu4e-headers-leave-behavior'. This -function is to be called before any further action (like searching, -quitting the buffer) is taken; returning t means 'take the following -action', return nil means 'don't do anything'." - (mu4e~mark-in-context - (let ((marknum (mu4e-mark-marks-num)) - (what mu4e-headers-leave-behavior)) - (unless (zerop marknum) ;; nothing to do? - (when (eq what 'ask) - (setq what (mu4e-read-option - (format "There are %d existing mark(s); should we: " marknum) - '( ("apply marks" . apply) - ("ignore marks?" . ignore))))) - ;; we determined what to do... now do it - (when (eq what 'apply) - (mu4e-mark-execute-all t)))))) - - -(provide 'mu4e-mark) -;; End of mu4e-mark.el diff --git a/_spacemacs.d/local/mu4e/mu4e-message.el b/_spacemacs.d/local/mu4e/mu4e-message.el deleted file mode 100644 index e339077..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-message.el +++ /dev/null @@ -1,284 +0,0 @@ -;;; mu4e-message.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2012-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; Functions to get data from mu4e-message plist structure - -;;; Code: -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) - -(require 'mu4e-vars) -(require 'mu4e-utils) - -(require 'cl) -(require 'html2text) - - -(defcustom mu4e-html2text-command - (if (fboundp 'shr-insert-document) 'mu4e-shr2text 'html2text) - - "Either a shell command or a function that converts from html to plain text. - -If it is a shell-command, the command reads html from standard -input and outputs plain text on standard output. If you use the -htmltext program, it's recommended you use \"html2text -utf8 --width 72\". Alternatives are the python-based html2markdown, w3m -and on MacOS you may want to use textutil. - -It can also be a function, which takes the current buffer in html -as input, and transforms it into html (like the `html2text' -function). - -In both cases, the output is expected to be in UTF-8 encoding. - -Newer emacs has the shr renderer, and when its available, -conversion defaults `mu4e-shr2text'; otherwise, the default is -emacs' built-in `html2text' function." - :type '(choice string function) - :group 'mu4e-view) - -(defcustom mu4e-view-prefer-html nil - "Whether to base the body display on the html-version. -If the e-mail message has no html-version the plain-text version -is always used." - :type 'boolean - :group 'mu4e-view) - -(defcustom mu4e-view-html-plaintext-ratio-heuristic 5 - "Ratio between the length of the html and the plain text part -below which mu4e will consider the plain text part to be 'This -messages requires html' text bodies." - :type 'integer - :group 'mu4e-view) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defsubst mu4e-message-field-raw (msg field) - "Retrieve FIELD from message plist MSG. -FIELD is one of :from, :to, :cc, :bcc, :subject, :data, -:message-id, :path, :maildir, :priority, :attachments, -:references, :in-reply-to, :body-txt, :body-html - -Returns `nil' if the field does not exist. - -A message plist looks something like: -\(:docid 32461 - :from ((\"Nikola Tesla\" . \"niko@example.com\")) - :to ((\"Thomas Edison\" . \"tom@example.com\")) - :cc ((\"Rupert The Monkey\" . \"rupert@example.com\")) - :subject \"RE: what about the 50K?\" - :date (20369 17624 0) - :size 4337 - :message-id \"6BDC23465F79238C8233AB82D81EE81AF0114E4E74@123213.mail.example.com\" - :path \"/home/tom/Maildir/INBOX/cur/133443243973_1.10027.atlas:2,S\" - :maildir \"/INBOX\" - :priority normal - :flags (seen) - :attachments - ((:index 2 :name \"photo.jpg\" :mime-type \"image/jpeg\" :size 147331) - (:index 3 :name \"book.pdf\" :mime-type \"application/pdf\" :size 192220)) - :references (\"6BDC23465F79238C8384574032D81EE81AF0114E4E74@123213.mail.example.com\" - \"6BDC23465F79238203498230942D81EE81AF0114E4E74@123213.mail.example.com\") - :in-reply-to \"6BDC23465F79238203498230942D81EE81AF0114E4E74@123213.mail.example.com\" - :body-txt \"Hi Tom, ...\" -\)). -Some notes on the format: -- The address fields are lists of pairs (NAME . EMAIL), where NAME can be nil. -- The date is in format emacs uses in `current-time' -- Attachments are a list of elements with fields :index (the number of - the MIME-part), :name (the file name, if any), :mime-type (the - MIME-type, if any) and :size (the size in bytes, if any). -- Messages in the Headers view come from the database and do not have - :attachments, :body-txt or :body-html fields. Message in the - Message view use the actual message file, and do include these fields." - ;; after all this documentation, the spectacular implementation - (if msg - (plist-get msg field) - (mu4e-error "message must be non-nil"))) - -(defsubst mu4e-message-field (msg field) - "Retrieve FIELD from message plist MSG. -Like `mu4e-message-field-nil', but will sanitize `nil' values: -- all string field except body-txt/body-html: nil -> \"\" -- numeric fields + dates : nil -> 0 -- all others : return the value -Thus, function will return nil for empty lists, non-existing body-txt or body-html." - (let ((val (mu4e-message-field-raw msg field))) - (cond - (val - val) ;; non-nil -> just return it - ((member field '(:subject :message-id :path :maildir :in-reply-to)) - "") ;; string fields except body-txt, body-html: nil -> "" - ((member field '(:body-html :body-txt)) - val) - ((member field '(:docid :size)) - 0) ;; numeric type: nil -> 0 - (t - val)))) ;; otherwise, just return nil - -(defsubst mu4e-message-has-field (msg field) - "Return t if MSG contains FIELD, nil otherwise." - (plist-member msg field)) - -(defsubst mu4e-message-at-point (&optional noerror) - "Get the message s-expression for the message at point in either -the headers buffer or the view buffer, or nil if there is no such -message. If optional NOERROR is non-nil, do not raise an error when -there is no message at point." - (let ((msg (or (get-text-property (point) 'msg) mu4e~view-msg))) - (if msg - msg - (unless noerror (mu4e-warn "No message at point"))))) - -(defsubst mu4e-message-field-at-point (field) - "Get the field FIELD from the message at point. -This is equivalent to: - (mu4e-message-field (mu4e-message-at-point) FIELD)." - (mu4e-message-field (mu4e-message-at-point) field)) - -(defun mu4e-message-body-text (msg) - "Get the body in text form for this message. -This is either :body-txt, or if not available, :body-html converted -to text, using `mu4e-html2text-command' is non-nil, it will use -that. Normally, thiss function prefers the text part, but this can -be changed by setting `mu4e-view-prefer-html'." - (let* ((txt (mu4e-message-field msg :body-txt)) - (html (mu4e-message-field msg :body-html)) - (body - (cond - ;; does it look like some text? ie., if the text part is more than - ;; mu4e-view-html-plaintext-ratio-heuristic times shorter than the - ;; html part, it should't be used - ;; This is an heuristic to guard against 'This messages requires - ;; html' text bodies. - ((and (> (* mu4e-view-html-plaintext-ratio-heuristic - (length txt)) (length html)) - ;; use html if it's prefered, unless there is no html - (or (not mu4e-view-prefer-html) (not html))) - txt) - ;; otherwise, it there some html? - (html - (with-temp-buffer - (insert html) - (cond - ((stringp mu4e-html2text-command) - (let* ((tmp-file (mu4e-make-temp-file "html"))) - (write-region (point-min) (point-max) tmp-file) - (erase-buffer) - (call-process-shell-command mu4e-html2text-command tmp-file t t) - (delete-file tmp-file))) - ((functionp mu4e-html2text-command) - (funcall mu4e-html2text-command)) - (t (mu4e-error "Invalid `mu4e-html2text-command'"))) - (buffer-string)) - ) - (t ;; otherwise, an empty body - "")))) - ;; and finally, remove some crap from the remaining string; it seems - ;; esp. outlook lies about its encoding (ie., it says 'iso-8859-1' but - ;; really it's 'windows-1252'), thus giving us these funky chars. here, we - ;; either remove them, or replace with 'what-was-meant' (heuristically) - (with-temp-buffer - (insert body) - (goto-char (point-min)) - (while (re-search-forward "[
’]" nil t) - (replace-match - (cond - ((string= (match-string 0) "’") "'") - (t "")))) - (buffer-string)))) - -(defun mu4e-message-contact-field-matches (msg cfield rx) - "Checks whether any of the of the contacts in field -CFIELD (either :to, :from, :cc or :bcc, or a list of those) of -msg MSG matches (with their name or e-mail address) regular -expressions RX. If there is a match, return 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." - (if (and cfield (listp cfield)) - (or (mu4e-message-contact-field-matches msg (car cfield) rx) - (mu4e-message-contact-field-matches msg (cdr cfield) rx)) - (when cfield - (if (listp rx) - ;; if rx is a list, try each one of them for a match - (find-if - (lambda (a-rx) (mu4e-message-contact-field-matches msg cfield a-rx)) - rx) - ;; not a list, check the rx - (find-if - (lambda (ct) - (let ((name (car ct)) (email (cdr ct))) - (or - (and name (string-match rx name)) - (and email (string-match rx email))))) - (mu4e-message-field msg cfield)))))) - -(defun mu4e-message-contact-field-matches-me (msg cfield) - "Checks whether any of the of the contacts in field -CFIELD (either :to, :from, :cc or :bcc) of msg MSG matches *me*, -that is, any of the e-mail address in -`mu4e-user-mail-address-list'. Returns the contact cell that -matched, or nil." - (find-if - (lambda (cc-cell) - (member-if - (lambda (addr) - (string= (downcase addr) (downcase (cdr cc-cell)))) - mu4e-user-mail-address-list)) - (mu4e-message-field msg cfield))) - -(defsubst mu4e-message-part-field (msgpart field) - "Get some field in a message part; a part would look something like: - (:index 2 :name \"photo.jpg\" :mime-type \"image/jpeg\" :size 147331)." - (plist-get msgpart field)) - -;; backward compatibility ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defalias 'mu4e-msg-field 'mu4e-message-field) -(defalias 'mu4e-body-text 'mu4e-message-body-text) ;; backward compatibility - -(defun mu4e-field-at-point (field) - "Get FIELD (a symbol, see `mu4e-header-info') for the message at -point in eiter the headers buffer or the view buffer." - (plist-get (mu4e-message-at-point) field)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defun mu4e-shr2text () - "Html to text using the shr engine; this can be used in -`mu4e-html2text-command' in a new enough emacs. Based on code by -Titus von der Malsburg." - (interactive) - (let ((dom (libxml-parse-html-region (point-min) (point-max))) - ;; When HTML emails contain references to remote images, - ;; retrieving these images leaks information. For example, - ;; the sender can see when I openend the email and from which - ;; computer (IP address). For this reason, it is preferrable - ;; to not retrieve images. - ;; See this discussion on mu-discuss: - ;; https://groups.google.com/forum/#!topic/mu-discuss/gr1cwNNZnXo - (shr-inhibit-images t)) - (erase-buffer) - (shr-insert-document dom) - (goto-char (point-min)))) - -(provide 'mu4e-message) diff --git a/_spacemacs.d/local/mu4e/mu4e-meta.el b/_spacemacs.d/local/mu4e/mu4e-meta.el deleted file mode 100644 index 255d6e9..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-meta.el +++ /dev/null @@ -1,11 +0,0 @@ -;; auto-generated -(defconst mu4e-mu-version "0.9.17" - "Required mu binary version; mu4e's version must agree with this.") - -(defconst mu4e-builddir "/home/aly/devel/mu" - "Top-level build directory.") - -(defconst mu4e-doc-dir "/home/aly/local/mu/share/doc/mu" - "Mu4e's data-dir.") - -(provide 'mu4e-meta) diff --git a/_spacemacs.d/local/mu4e/mu4e-proc.el b/_spacemacs.d/local/mu4e/mu4e-proc.el deleted file mode 100644 index d6d90e1..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-proc.el +++ /dev/null @@ -1,525 +0,0 @@ -;; mu4e-proc.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;;; Code: -(require 'mu4e-vars) -(require 'mu4e-utils) -(require 'mu4e-meta) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; internal vars - -(defvar mu4e~proc-buf nil - "Buffer (string) for data received from the backend.") -(defconst mu4e~proc-name " *mu4e-proc*" - "Name of the server process, buffer.") -(defvar mu4e~proc-process nil - "The mu-server process.") - -;; dealing with the length cookie that precedes expressions -(defconst mu4e~cookie-pre "\376" - "Each expression we get from the backend (mu server) starts with -a length cookie: - <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") -(defconst mu4e~cookie-post "\377" - "Each expression we get from the backend (mu server) starts with -a length cookie: - <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") -(defconst mu4e~cookie-matcher-rx - (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)" mu4e~cookie-post) - "Regular expression matching the length cookie. -Match 1 will be the length (in hex).") - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defsubst mu4e~proc-send-command (frm &rest args) - "Send as command to the mu server process. -Start the process if needed." - (unless (mu4e~proc-running-p) - (mu4e~proc-start)) - (let ((cmd (apply 'format frm args))) - (mu4e-log 'to-server "%s" cmd) - (process-send-string mu4e~proc-process (concat cmd "\n")))) - -(defun mu4e~proc-start () - "Start the mu server process." - (unless (file-executable-p mu4e-mu-binary) - (mu4e-error (format "`mu4e-mu-binary' (%S) not found" mu4e-mu-binary))) - (let* ((process-connection-type nil) ;; use a pipe - (args '("server")) - (args (append args (when mu4e-mu-home - (list (concat "--muhome=" mu4e-mu-home)))))) - (setq mu4e~proc-buf "") - (setq mu4e~proc-process (apply 'start-process - mu4e~proc-name mu4e~proc-name - mu4e-mu-binary args)) - ;; register a function for (:info ...) sexps - (unless mu4e~proc-process - (mu4e-error "Failed to start the mu4e backend")) - (set-process-query-on-exit-flag mu4e~proc-process nil) - (set-process-coding-system mu4e~proc-process 'binary 'utf-8-unix) - (set-process-filter mu4e~proc-process 'mu4e~proc-filter) - (set-process-sentinel mu4e~proc-process 'mu4e~proc-sentinel))) - -(defun mu4e~proc-kill () - "Kill the mu server process." - (let* ((buf (get-buffer mu4e~proc-name)) - (proc (and (buffer-live-p buf) (get-buffer-process buf)))) - (when proc - (let ((delete-exited-processes t)) - ;; the mu server signal handler will make it quit after 'quit' - (mu4e~proc-send-command "cmd:quit")) - ;; try sending SIGINT (C-c) to process, so it can exit gracefully - (ignore-errors - (signal-process proc 'SIGINT)))) - (setq - mu4e~proc-process nil - mu4e~proc-buf nil)) - -(defun mu4e~proc-running-p () - "Whether the mu process is running." - (when (and mu4e~proc-process - (memq (process-status mu4e~proc-process) - '(run open listen connect stop))) - t)) - -(defsubst mu4e~proc-eat-sexp-from-buf () - "'Eat' the next s-expression from `mu4e~proc-buf'. -Note: this is a string, not an emacs-buffer. `mu4e~proc-buf gets -its contents from the mu-servers in the following form: - <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'> -Function returns this sexp, or nil if there was none. -`mu4e~proc-buf' is updated as well, with all processed sexp data -removed." - (ignore-errors ;; the server may die in the middle... - ;; mu4e~cookie-matcher-rx: - ;; (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)]" mu4e~cookie-post) - (let ((b (string-match mu4e~cookie-matcher-rx mu4e~proc-buf)) - (sexp-len) (objcons)) - (when b - (setq sexp-len (string-to-number (match-string 1 mu4e~proc-buf) 16)) - ;; does mu4e~proc-buf contain the full sexp? - (when (>= (length mu4e~proc-buf) (+ sexp-len (match-end 0))) - ;; clear-up start - (setq mu4e~proc-buf (substring mu4e~proc-buf (match-end 0))) - ;; 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 - ;; it. - (setq objcons (read-from-string - (decode-coding-string - (substring mu4e~proc-buf 0 sexp-len) - 'utf-8 t))) - (when objcons - (setq mu4e~proc-buf (substring mu4e~proc-buf sexp-len)) - (car objcons))))))) - - -(defun mu4e~proc-filter (proc str) - "A process-filter for the 'mu server' output. -It accumulates the strings into valid sexps by checking of the -';;eox' end-of-sexp marker, and then evaluating them. - -The server output is as follows: - - 1. an error - (:error 2 :message \"unknown command\") - ;; eox - => this will be passed to `mu4e-error-func'. - - 2a. a message sexp looks something like: - \( - :docid 1585 - :from ((\"Donald Duck\" . \"donald@example.com\")) - :to ((\"Mickey Mouse\" . \"mickey@example.com\")) - :subject \"Wicked stuff\" - :date (20023 26572 0) - :size 15165 - :references (\"200208121222.g7CCMdb80690@msg.id\") - :in-reply-to \"200208121222.g7CCMdb80690@msg.id\" - :message-id \"foobar32423847ef23@pluto.net\" - :maildir: \"/archive\" - :path \"/home/mickey/Maildir/inbox/cur/1312254065_3.32282.pluto,4cd5bd4e9:2,\" - :priority high - :flags (new unread) - :attachments ((2 \"hello.jpg\" \"image/jpeg\") (3 \"laah.mp3\" \"audio/mp3\")) - :body-txt \" <message body>\" -\) -;; eox - => this will be passed to `mu4e-header-func'. - - 2b. After the list of message sexps has been returned (see 2a.), - we'll receive a sexp that looks like - (:found <n>) with n the number of messages found. The <n> will be - passed to `mu4e-found-func'. - - 3. a view looks like: - (:view <msg-sexp>) - => the <msg-sexp> (see 2.) will be passed to `mu4e-view-func'. - - 4. a database update looks like: - (:update <msg-sexp> :move <nil-or-t>) - - => the <msg-sexp> (see 2.) will be passed to - `mu4e-update-func', :move tells us whether this is a move to - another maildir, or merely a flag change. - - 5. a remove looks like: - (:remove <docid>) - => the docid will be passed to `mu4e-remove-func' - - 6. a compose looks like: - (:compose <reply|forward|edit|new> [:original<msg-sexp>] [:include <attach>]) - `mu4e-compose-func'." - (mu4e-log 'misc "* Received %d byte(s)" (length str)) - (setq mu4e~proc-buf (concat mu4e~proc-buf str)) ;; update our buffer - (let ((sexp (mu4e~proc-eat-sexp-from-buf))) - (with-local-quit - (while sexp - (mu4e-log 'from-server "%S" sexp) - (cond - ;; a header plist can be recognized by the existence of a :date field - ((plist-get sexp :date) - (funcall mu4e-header-func sexp)) - - ;; the found sexp, we receive after getting all the headers - ((plist-get sexp :found) - (funcall mu4e-found-func (plist-get sexp :found))) - - ;; viewing a specific message - ((plist-get sexp :view) - (funcall mu4e-view-func (plist-get sexp :view))) - - ;; receive an erase message - ((plist-get sexp :erase) - (funcall mu4e-erase-func)) - - ;; receive a :sent message - ((plist-get sexp :sent) - (funcall mu4e-sent-func - (plist-get sexp :docid) - (plist-get sexp :path))) - - ;; received a pong message - ((plist-get sexp :pong) - (funcall mu4e-pong-func - (plist-get sexp :props))) - - ;; received a contacts message - ;; note: we use 'member', to match (:contacts nil) - ((plist-member sexp :contacts) - (funcall mu4e-contacts-func - (plist-get sexp :contacts))) - - ;; something got moved/flags changed - ((plist-get sexp :update) - (funcall mu4e-update-func - (plist-get sexp :update) (plist-get sexp :move))) - - ;; a message got removed - ((plist-get sexp :remove) - (funcall mu4e-remove-func (plist-get sexp :remove))) - - ;; start composing a new message - ((plist-get sexp :compose) - (funcall mu4e-compose-func - (plist-get sexp :compose) - (plist-get sexp :original) - (plist-get sexp :include))) - - ;; do something with a temporary file - ((plist-get sexp :temp) - (funcall mu4e-temp-func - (plist-get sexp :temp) ;; name of the temp file - (plist-get sexp :what) ;; what to do with it - ;; (pipe|emacs|open-with...) - (plist-get sexp :docid) ;; docid of the message - (plist-get sexp :param)));; parameter for the action - - ;; get some info - ((plist-get sexp :info) - (funcall mu4e-info-func sexp)) - - ;; receive an error - ((plist-get sexp :error) - (funcall mu4e-error-func - (plist-get sexp :error) - (plist-get sexp :message))) - - (t (mu4e-message "Unexpected data from server [%S]" sexp))) - - (setq sexp (mu4e~proc-eat-sexp-from-buf)))))) - - -;; error codes are defined in src/mu-util -;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent") - -(defun mu4e~proc-sentinel (proc msg) - "Function that will be called when the mu-server process terminates." - (let ((status (process-status proc)) (code (process-exit-status proc))) - (setq mu4e~proc-process nil) - (setq mu4e~proc-buf "") ;; clear any half-received sexps - (cond - ((eq status 'signal) - (cond - ((eq code 9) (message nil)) - ;;(message "the mu server process has been stopped")) - (t (error (format "mu server process received signal %d" code))))) - ((eq status 'exit) - (cond - ((eq code 0) - (message nil)) ;; don't do anything - ((eq code 11) - (error "Database is locked by another process")) - ((eq code 15) - (error "Database needs upgrade; try `mu index --rebuild' from the command line")) - ((eq code 19) - (error "Database empty; try indexing some messages")) - (t (error "mu server process ended with exit code %d" code)))) - (t - (error "Something bad happened to the mu server process"))))) - -(defsubst mu4e~docid-msgid-param (docid-or-msgid) - "Construct a backend parameter based on DOCID-OR-MSGID." - (format - (if (stringp docid-or-msgid) - "msgid:\"%s\"" - "docid:%d") - docid-or-msgid)) - -(defun mu4e~proc-remove (docid) - "Remove message identified by docid. -The results are reporter through either (:update ... ) or (:error) -sexp, which are handled my `mu4e-error-func', respectively." - (mu4e~proc-send-command "cmd:remove docid:%d" docid)) - -(defun mu4e~proc-escape (str) - "Escape STRING for transport -- put it in quotes, and escape existing quotation. -In particular, backslashes and double-quotes." - (let ((esc (replace-regexp-in-string "\\\\" "\\\\\\\\" str))) - (format "\"%s\"" (replace-regexp-in-string "\"" "\\\\\"" esc)))) - -(defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups include-related) - "Start a database query for QUERY. -If THREADS is non-nil, show results in threaded fasion, SORTFIELD -is a symbol describing the field to sort by (or nil); see -`mu4e~headers-sortfield-choices'. If SORT is `descending', sort -Z->A, if it's `ascending', sort A->Z. MAXNUM determines the maximum -number of results to return, or nil for 'unlimited'. If SKIP-DUPS -is non-nil, show only one of duplicate messages (see -`mu4e-headers-skip-duplicates'). If INCLUDE-RELATED is non-nil, -include messages related to the messages matching the search -query (see `mu4e-headers-include-related'). - -For each -result found, a function is called, depending on the kind of -result. The variables `mu4e-error-func' contain the function that -will be called for, resp., a message (header row) or an error." - (mu4e~proc-send-command - (concat - "cmd:find query:%s threads:%s sortfield:%s reverse:%s maxnum:%d " - "skip-dups:%s include-related:%s") - (mu4e~proc-escape query) - (if threads "true" "false") - ;; sortfield is e.g. ':subject'; this removes the ':' - (if (null sortfield) "nil" (substring (symbol-name sortfield) 1)) - ;; TODO: use ascending/descending in backend too (it's clearer than 'reverse' - (if (eq sortdir 'descending) "true" "false") - (if maxnum maxnum -1) - (if skip-dups "true" "false") - (if include-related "true" "false"))) - -(defun mu4e~proc-move (docid-or-msgid &optional maildir flags) - "Move message identified by DOCID-OR-MSGID. -At least one of MAILDIR and FLAGS should be specified. Note, even -if MAILDIR is nil, this is still a move, since a change in flags -still implies a change in message filename. - -MAILDIR (), optionally -setting FLAGS (keyword argument :flags). optionally setting FLAGS -in the process. If MAILDIR is nil, message will be moved within the -same maildir. - -MAILDIR must be a maildir, that is, the part _without_ cur/ or new/ -or the root-maildir-prefix. E.g. \"/archive\". This directory must -already exist. - -The FLAGS parameter can have the following forms: - 1. a list of flags such as '(passed replied seen) - 2. a string containing the one-char versions of the flags, e.g. \"PRS\" - 3. a delta-string specifying the changes with +/- and the one-char flags, - e.g. \"+S-N\" to set Seen and remove New. - -The flags are any of `deleted', `flagged', `new', `passed', `replied' `seen' or -`trashed', or the corresponding \"DFNPRST\" as defined in [1]. See -`mu4e-string-to-flags' and `mu4e-flags-to-string'. -The server reports the results for the operation through -`mu4e-update-func'. - -If the variable `mu4e-change-filenames-when-moving' is -non-nil, moving to a different maildir generates new names for -the target files; this helps certain tools (such as mbsync). - -The results are reported through either (:update ... ) -or (:error ) sexp, which are handled my `mu4e-update-func' and -`mu4e-error-func', respectively." - (unless (or maildir flags) - (mu4e-error "At least one of maildir and flags must be specified")) - (unless (or (not maildir) (file-exists-p (concat mu4e-maildir "/" maildir "/"))) - (mu4e-error "Target dir does not exist")) - (let* ((idparam (mu4e~docid-msgid-param docid-or-msgid)) - (flagstr - (when flags - (concat " flags:" - (if (stringp flags) flags (mu4e-flags-to-string flags))))) - (path - (when maildir - (format " maildir:%s" (mu4e~proc-escape maildir)))) - (rename - (if (and maildir mu4e-change-filenames-when-moving) "true" "false"))) - (mu4e~proc-send-command "cmd:move %s %s %s %s" - idparam (or flagstr "") (or path "") - (format "newname:%s" rename)))) - -(defun mu4e~proc-index (path my-addresses cleanup lazy-check) - "Update the message database for filesystem PATH, which should -point to some maildir directory structure. MY-ADDRESSES is a list -of 'my' email addresses (see `mu4e-user-mail-address-list')." - (let ((path (mu4e~proc-escape path)) - (addrs (when my-addresses (mapconcat 'identity my-addresses ",")))) - (if addrs - (mu4e~proc-send-command "cmd:index path:%s my-addresses:%s cleanup:%s lazy-check:%s" - path addrs (if cleanup "true" : "false") (if lazy-check "true")) - (mu4e~proc-send-command "cmd:index path:%s" path)))) - -(defun mu4e~proc-add (path maildir) - "Add the message at PATH to the database. -With MAILDIR set to the maildir this message resides in, -e.g. '/drafts'; if this works, we will receive (:info add :path -<path> :docid <docid>) as well as (:update <msg-sexp>)." - (mu4e~proc-send-command "cmd:add path:%s %s" - (mu4e~proc-escape path) - (if maildir - (format "maildir:%s" (mu4e~proc-escape maildir)) - ""))) - -(defun mu4e~proc-sent (path maildir) - "Add the message at PATH to the database. -With MAILDIR set to the maildir this message resides in, -e.g. '/drafts'. - - if this works, we will receive (:info add :path <path> :docid -<docid> :fcc <path>)." - (mu4e~proc-send-command "cmd:sent path:%s maildir:%s" - (mu4e~proc-escape path) (mu4e~proc-escape maildir))) - - -(defun mu4e~proc-compose (type decrypt &optional docid) - "Start composing a message of certain TYPE (a symbol, either -`forward', `reply', `edit', `resend' or `new', based on an -original message (ie, replying to, forwarding, editing, -resending) with DOCID or nil for type `new'. - -The result will be delivered to the function registered as -`mu4e-compose-func'." - (unless (member type '(forward reply edit resend new)) - (mu4e-error "Unsupported compose-type %S" type)) - (unless (eq (null docid) (eq type 'new)) - (mu4e-error "`new' implies docid not-nil, and vice-versa")) - (mu4e~proc-send-command - "cmd:compose type:%s docid:%d extract-encrypted:%s use-agent:true" - (symbol-name type) docid (if decrypt "true" "false"))) - -(defun mu4e~proc-mkdir (path) - "Create a new maildir-directory at filesystem PATH." - (mu4e~proc-send-command "cmd:mkdir path:%s" (mu4e~proc-escape path))) - -(defun mu4e~proc-extract (action docid partidx decrypt &optional path what param) - "Extract an attachment with index PARTIDX from message with DOCID -and perform ACTION on it (as symbol, either `save', `open', `temp') which -mean: - * save: save the part to PARAM1 (a path) (non-optional for save)$ - * open: open the part with the default application registered for doing so - * temp: save to a temporary file, then respond with - (:temp <path> :what <what> :param <param>)." - (let ((cmd - (concat "cmd:extract " - (case action - (save - (format "action:save docid:%d index:%d path:%s extract-encrypted:%s use-agent:true" - docid partidx (mu4e~proc-escape path) (if decrypt "true" "false"))) - (open (format "action:open docid:%d index:%d extract-encrypted:%s use-agent:true" - docid partidx (if decrypt "true" "false"))) - (temp - (format "action:temp docid:%d index:%d what:%s%s extract-encrypted:%s use-agent:true" - docid partidx what - (if param - (if (stringp param) - (format " param:%s" (mu4e~proc-escape param)) - (format " param:%S" param)) "") (if decrypt "true" "false"))) - (otherwise (mu4e-error "Unsupported action %S" action)))) - )) - (mu4e~proc-send-command "%s" cmd))) - - -(defun mu4e~proc-ping () - "Sends a ping to the mu server, expecting a (:pong ...) in response." - (mu4e~proc-send-command "cmd:ping")) - -(defun mu4e~proc-contacts (personal after) - "Sends the contacts command to the mu server. -A (:contacts (<list>)) is expected in response. If PERSONAL is -non-nil, only get personal contacts, if AFTER is non-nil, get -only contacts seen AFTER (the time_t value)." - (mu4e~proc-send-command - "cmd:contacts personal:%s after:%d" - (if personal "true" "false") - (or after 0))) - -(defun mu4e~proc-view (docid-or-msgid &optional images decrypt) - "Get one particular message based on its DOCID-OR-MSGID. -Optionally, if IMAGES is non-nil, backend will any images -attached to the message, and return them as temp files. -The result will be delivered to the function registered as -`mu4e-view-func'." - (mu4e~proc-send-command - "cmd:view %s extract-images:%s extract-encrypted:%s use-agent:true" - (mu4e~docid-msgid-param docid-or-msgid) - (if images "true" "false") - (if decrypt "true" "false"))) - -(defun mu4e~proc-view-path (path &optional images decrypt) - "View message at PATH (keyword argument). -Optionally, if IMAGES is non-nil, backend will any images -attached to the message, and return them as temp files. The -result will be delivered to the function registered as -`mu4e-view-func'." - (mu4e~proc-send-command - "cmd:view path:%s extract-images:%s extract-encrypted:%s use-agent:true" - (mu4e~proc-escape path) - (if images "true" "false") - (if decrypt "true" "false"))) - - -(provide 'mu4e-proc) -;; End of mu4e-proc.el diff --git a/_spacemacs.d/local/mu4e/mu4e-speedbar.el b/_spacemacs.d/local/mu4e/mu4e-speedbar.el deleted file mode 100644 index 09a4c49..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-speedbar.el +++ /dev/null @@ -1,125 +0,0 @@ -;;; mu4e-speedbar --- Speedbar support for mu4e - -;; Copyright (C) 2012-2016 Antono Vasiljev, Dirk-Jan C. Binnema -;; -;; Author: Antono Vasiljev <self@antono.info> -;; Version: 0.1 -;; Keywords: file, tags, tools -;; -;; This file is not part of GNU Emacs. -;; -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. -;; -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: -;; -;; Speedbar provides a frame in which files, and locations in files -;; are displayed. These functions provide mu4e specific support, -;; showing maildir list in the side-bar. -;; -;; This file requires speedbar. - -;;; Code: - -(require 'speedbar) -(require 'mu4e-vars) -(require 'mu4e-headers) -(require 'mu4e-utils) - -(defvar mu4e-main-speedbar-key-map nil - "Keymap used when in mu4e display mode.") -(defvar mu4e-headers-speedbar-key-map nil - "Keymap used when in mu4e display mode.") -(defvar mu4e-view-speedbar-key-map nil - "Keymap used when in mu4e display mode.") - -(defvar mu4e-main-speedbar-menu-items nil - "Additional menu-items to add to speedbar frame.") -(defvar mu4e-headers-speedbar-menu-items nil - "Additional menu-items to add to speedbar frame.") -(defvar mu4e-view-speedbar-menu-items nil - "Additional menu-items to add to speedbar frame.") - - -(defun mu4e-speedbar-install-variables () - "Install those variables used by speedbar to enhance mu4e." - (dolist (keymap - '( mu4e-main-speedbar-key-map - mu4e-headers-speedbar-key-map - mu4e-view-speedbar-key-map)) - (unless keymap - (setq keymap (speedbar-make-specialized-keymap)) - (define-key keymap "RET" 'speedbar-edit-line) - (define-key keymap "e" 'speedbar-edit-line)))) - - -;; Make sure our special speedbar major mode is loaded -(if (featurep 'speedbar) - (mu4e-speedbar-install-variables) - (add-hook 'speedbar-load-hook 'mu4e-speedbar-install-variables)) - -(defun mu4e~speedbar-render-maildir-list () - "Insert the list of maildirs in the speedbar." - (interactive) - (mapcar (lambda (maildir-name) - (speedbar-insert-button - (concat " " maildir-name) - 'mu4e-highlight-face - 'highlight - 'mu4e~speedbar-maildir - maildir-name)) - (mu4e-get-maildirs))) - -(defun mu4e~speedbar-maildir (&optional text token ident) - "Jump to maildir TOKEN. TEXT and INDENT are not used." - (speedbar-with-attached-buffer - (mu4e-headers-search (concat "\"maildir:" token "\"") - current-prefix-arg))) - -(defun mu4e~speedbar-render-bookmark-list () - "Insert the list of bookmarks in the speedbar" - (interactive) - (mapcar (lambda (bookmark) - (speedbar-insert-button - (concat " " (mu4e-bookmark-name bookmark)) - 'mu4e-highlight-face - 'highlight - 'mu4e~speedbar-bookmark - (mu4e-bookmark-query bookmark))) - (mu4e-bookmarks))) - -(defun mu4e~speedbar-bookmark (&optional text token ident) - "Run bookmarked query TOKEN. TEXT and INDENT are not used." - (speedbar-with-attached-buffer - (mu4e-headers-search token current-prefix-arg))) - -;;;###autoload -(defun mu4e-speedbar-buttons (buffer) - "Create buttons for any mu4e BUFFER." - (interactive) - (erase-buffer) - (insert (propertize "* mu4e\n\n" 'face 'mu4e-title-face)) - - (insert (propertize " Bookmarks\n" 'face 'mu4e-title-face)) - (mu4e~speedbar-render-bookmark-list) - (insert "\n") - (insert (propertize " Maildirs\n" 'face 'mu4e-title-face)) - (mu4e~speedbar-render-maildir-list)) - -(defun mu4e-main-speedbar-buttons (buffer) (mu4e-speedbar-buttons buffer)) -(defun mu4e-headers-speedbar-buttons (buffer) (mu4e-speedbar-buttons buffer)) -(defun mu4e-view-speedbar-buttons (buffer) (mu4e-speedbar-buttons buffer)) - - -(provide 'mu4e-speedbar) -;;; mu4e-speedbar.el ends here diff --git a/_spacemacs.d/local/mu4e/mu4e-utils.el b/_spacemacs.d/local/mu4e/mu4e-utils.el deleted file mode 100644 index f8110b5..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-utils.el +++ /dev/null @@ -1,1281 +0,0 @@ -;;; mu4e-utils.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema -;; Copyright (C) 2013 Tibor Simko - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; Utility functions used in the mu4e - -;;; Code: -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) -(require 'cl) - -(eval-when-compile (require 'org nil 'noerror)) - -(require 'mu4e-vars) -(require 'mu4e-meta) -(require 'mu4e-lists) -(require 'doc-view) - -;; keep the byte-compiler happy -(declare-function mu4e~proc-mkdir "mu4e-proc") -(declare-function mu4e~proc-ping "mu4e-proc") -(declare-function mu4e~proc-contacts "mu4e-proc") -(declare-function mu4e~proc-kill "mu4e-proc") -(declare-function mu4e~proc-index "mu4e-proc") -(declare-function mu4e~proc-add "mu4e-proc") -(declare-function mu4e~proc-mkdir "mu4e-proc") -(declare-function mu4e~proc-running-p "mu4e-proc") - -(declare-function mu4e~context-autoswitch "mu4e-context") -(declare-function mu4e-context-determine "mu4e-context") -(declare-function mu4e-context-vars "mu4e-context") -(declare-function show-all "org") - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; the following is taken from org.el; we copy it here since we don't want to -;; depend on org-mode directly (it causes byte-compilation errors) TODO: a -;; cleaner solution.... -(defconst mu4e~ts-regexp0 - (concat - "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)" - "\\( +[^]+0-9>\r\n -]+\\)?\\( +\\([0-9]\\{1,2\\}\\):" - "\\([0-9]\\{2\\}\\)\\)?\\)") - "Regular expression matching time strings for analysis. -This one does not require the space after the date, so it can be -used on a string that terminates immediately after the date.") - -(defun mu4e-parse-time-string (s &optional nodefault) - "Parse the standard Org-mode time string. -This should be a lot faster than the normal `parse-time-string'. -If time is not given, defaults to 0:00. However, with optional -NODEFAULT, hour and minute fields will be nil if not given." - (if (string-match mu4e~ts-regexp0 s) - (list 0 - (if (or (match-beginning 8) (not nodefault)) - (string-to-number (or (match-string 8 s) "0"))) - (if (or (match-beginning 7) (not nodefault)) - (string-to-number (or (match-string 7 s) "0"))) - (string-to-number (match-string 4 s)) - (string-to-number (match-string 3 s)) - (string-to-number (match-string 2 s)) - nil nil nil) - (mu4e-error "Not a standard mu4e time string: %s" s))) - - -(defun mu4e-user-mail-address-p (addr) - "If ADDR is one of user's e-mail addresses return t, nil otherwise. -User's addresses are set in `mu4e-user-mail-address-list')." - (when (and addr mu4e-user-mail-address-list - (find addr mu4e-user-mail-address-list :test 'string=)) - t)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defmacro with~mu4e-context-vars (context &rest body) - "Evaluate BODY, with variables let-bound for CONTEXT (if any). -`funcall'." - (declare (indent 2)) - `(let* ((vars (and ,context (mu4e-context-vars ,context)))) - (progv ;; XXX: perhaps use eval's lexical environment instead of progv? - (mapcar (lambda(cell) (car cell)) vars) - (mapcar (lambda(cell) (cdr cell)) vars) - (eval ,@body)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; the standard folders can be functions too -(defun mu4e~get-folder (foldervar msg) - "Within the mu-context of MSG, get message folder FOLDERVAR. -If FOLDER is a string, return it, if it is a function, evaluate -this function with MSG as parameter (which may be `nil'), and -return the result." - (unless (member foldervar - '(mu4e-sent-folder mu4e-drafts-folder - mu4e-trash-folder mu4e-refile-folder)) - (mu4e-error "Folder must be one of mu4e-(sent|drafts|trash|refile)-folder")) - ;; get the value with the vars for the relevants context let-bound - (with~mu4e-context-vars (mu4e-context-determine msg nil) - (let* ((folder (symbol-value foldervar)) - (val - (cond - ((stringp folder) folder) - ((functionp folder) (funcall folder msg)) - (t (mu4e-error "unsupported type for %S" folder))))) - (or val (mu4e-error "%S evaluates to nil" foldervar))))) - -(defun mu4e-get-drafts-folder (&optional msg) - "Get the sent folder. See `mu4e-drafts-folder'." - (mu4e~get-folder 'mu4e-drafts-folder msg)) - -(defun mu4e-get-refile-folder (&optional msg) - "Get the folder for refiling. See `mu4e-refile-folder'." - (mu4e~get-folder 'mu4e-refile-folder msg)) - -(defun mu4e-get-sent-folder (&optional msg) - "Get the sent folder. See `mu4e-sent-folder'." - (mu4e~get-folder 'mu4e-sent-folder msg)) - -(defun mu4e-get-trash-folder (&optional msg) - "Get the sent folder. See `mu4e-trash-folder'." - (mu4e~get-folder 'mu4e-trash-folder msg)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e-remove-file-later (filename) - "Remove FILENAME in a few seconds." - (lexical-let ((filename filename)) - (run-at-time "10 sec" nil - (lambda () (ignore-errors (delete-file filename)))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e-make-temp-file (ext) - "Create a temporary file with extension EXT. The file will -self-destruct in a few seconds, enough to open it in another -program." - (let ((tmpfile (make-temp-file "mu4e-" nil (concat "." ext)))) - (mu4e-remove-file-later tmpfile) - tmpfile)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; mu4e-attachment-dir is either a string or a function that takes a filename -;; and the mime-type as argument, either (or both) which can be nil -(defun mu4e~get-attachment-dir (&optional fname mimetype) - "Get the directory for saving attachments from -`mu4e-attachment-dir' (which can be either a string or a function, -see its docstring)." - (let - ((dir - (cond - ((stringp mu4e-attachment-dir) - mu4e-attachment-dir) - ((functionp mu4e-attachment-dir) - (funcall mu4e-attachment-dir fname mimetype)) - (t - (mu4e-error "unsupported type for mu4e-attachment-dir" ))))) - (if dir - (expand-file-name dir) - (mu4e-error (mu4e-error "mu4e-attachment-dir evaluates to nil"))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e~guess-maildir (path) - "Guess the maildir for some path, or nil if cannot find it." - (let ((idx (string-match mu4e-maildir path))) - (when (and idx (zerop idx)) - (replace-regexp-in-string - mu4e-maildir - "" - (expand-file-name - (concat path "/../..")))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e-create-maildir-maybe (dir) - "Offer to create maildir DIR if it does not exist yet. -Return t if the dir already existed, or an attempt has been made to -create it -- we cannot be sure creation succeeded here, since this -is done asynchronously. Otherwise, return nil. NOte, DIR has to be -an absolute path." - (if (and (file-exists-p dir) (not (file-directory-p dir))) - (mu4e-error "%s exists, but is not a directory." dir)) - (cond - ((file-directory-p dir) t) - ((yes-or-no-p (mu4e-format "%s does not exist yet. Create now?" dir)) - (mu4e~proc-mkdir dir) t) - (t nil))) - -(defun mu4e-format (frm &rest args) - "Create [mu4e]-prefixed string based on format FRM and ARGS." - (concat - "[" (propertize "mu4e" 'face 'mu4e-title-face) "] " - (apply 'format frm args))) - -(defun mu4e-message (frm &rest args) - "Like `message', but prefixed with mu4e. -If we're waiting for user-input or if there's some message in the -echo area, don't show anything." - (unless (or (active-minibuffer-window)) - (message "%s" (apply 'mu4e-format frm args)))) - -(defun mu4e-index-message (frm &rest args) - "Like `mu4e-message', but specifically for -index-messages. Doesn't display anything if -`mu4e-hide-index-messages' is non-nil. " - (unless mu4e-hide-index-messages - (apply 'mu4e-message frm args))) - -(defun mu4e-error (frm &rest args) - "Create [mu4e]-prefixed error based on format FRM and ARGS. -Does a local-exit and does not return, and raises a -debuggable (backtrace) error." - (mu4e-log 'error (apply 'mu4e-format frm args)) - (error "%s" (apply 'mu4e-format frm args))) - -;; the user-error function is only available in emacs-trunk -(unless (fboundp 'user-error) - (defalias 'user-error 'error)) - -(defun mu4e-warn (frm &rest args) - "Create [mu4e]-prefixed warning based on format FRM and ARGS. -Does a local-exit and does not return. In emacs versions below -24.2, the functions is the same as `mu4e-error'." - (mu4e-log 'error (apply 'mu4e-format frm args)) - (user-error "%s" (apply 'mu4e-format frm args))) - -(defun mu4e~read-char-choice (prompt choices) - "Read and return one of CHOICES, prompting for PROMPT. -Any input that is not one of CHOICES is ignored. This mu4e's -version of `read-char-choice', that becomes case-insentive after -trying an exact match." - (let ((choice) (chosen) (inhibit-quit nil)) - (while (not chosen) - (message nil);; this seems needed... - (setq choice (read-char-exclusive prompt)) - (setq chosen (or (member choice choices) - (member (downcase choice) choices) - (member (upcase choice) choices)))) - (car chosen))) - -(defun mu4e-read-option (prompt options) - "Ask user for an option from a list on the input area. -PROMPT describes a multiple-choice question to the user. -OPTIONS describe the options, and is a list of cells describing -particular options. Cells have the following structure: - - (OPTIONSTRING . RESULT) - -where OPTIONSTRING is a non-empty string describing the -option. The first character of OPTIONSTRING is used as the -shortcut, and obviously all shortcuts must be different, so you -can prefix the string with an uniquifying character. - -The options are provided as a list for the user to choose from; -user can then choose by typing CHAR. Example: - (mu4e-read-option \"Choose an animal: \" - '((\"Monkey\" . monkey) (\"Gnu\" . gnu) (\"xMoose\" . moose))) - -User now will be presented with a list: \"Choose an animal: - [M]onkey, [G]nu, [x]Moose\". - -Function will return the cdr of the list element." - (let* ((prompt (mu4e-format "%s" prompt)) - (chosen) - (optionsstr - (mapconcat - (lambda (option) - ;; try to detect old-style options, and warn - (when (characterp (car-safe (cdr-safe option))) - (mu4e-error - (concat "Please use the new format for options/actions; " - "see the manual"))) - (let* ((kar (substring (car option) 0 1)) - (val (cdr option))) - (concat - "[" (propertize kar 'face 'mu4e-highlight-face) "]" - (substring (car option) 1)))) - options ", ")) - (response - (mu4e~read-char-choice - (concat prompt optionsstr - " [" (propertize "C-g" 'face 'mu4e-highlight-face) - " to cancel]") - ;; the allowable chars - (map 'list (lambda(elm) (string-to-char (car elm))) options))) - (chosen - (find-if - (lambda (option) (eq response (string-to-char (car option)))) - options))) - (if chosen - (cdr chosen) - (mu4e-warn "Unknown shortcut '%c'" response)))) - -(defun mu4e~get-maildirs-1 (path mdir) - "Get maildirs under path, recursively, as a list of relative paths." - (let ((dirs) - (dentries - (ignore-errors - (directory-files-and-attributes - (concat path mdir) nil - "^[^.]\\|\\.[^.][^.]" t)))) - (dolist (dentry dentries) - (when (and (booleanp (cadr dentry)) (cadr dentry)) - (if (file-accessible-directory-p - (concat mu4e-maildir "/" mdir "/" (car dentry) "/cur")) - (setq dirs (cons (concat mdir (car dentry)) dirs))) - (unless (member (car dentry) '("cur" "new" "tmp")) - (setq dirs (append dirs (mu4e~get-maildirs-1 path - (concat mdir (car dentry) "/"))))))) - dirs)) - -(defvar mu4e-cache-maildir-list nil - "Whether to cache the list of maildirs; set it to t if you find -that generating the list on the fly is too slow. If you do, you -can set `mu4e-maildir-list' to nil to force regenerating the -cache the next time `mu4e-get-maildirs' gets called.") - -(defvar mu4e-maildir-list nil - "Cached list of maildirs.") - -(defun mu4e-get-maildirs () - "Get maildirs under `mu4e-maildir', recursively, as a list of -relative paths (ie., /archive, /sent etc.). Most of the work is -done in `mu4e-get-maildirs-1'. Note, these results are /cached/, so -the list of maildirs will not change until you restart mu4e." - (unless mu4e-maildir (mu4e-error "`mu4e-maildir' is not defined")) - (unless (and mu4e-maildir-list mu4e-cache-maildir-list) - (setq mu4e-maildir-list - (sort - (append - (when (file-accessible-directory-p - (concat mu4e-maildir "/cur")) '("/")) - (mu4e~get-maildirs-1 mu4e-maildir "/")) - (lambda (s1 s2) (string< (downcase s1) (downcase s2)))))) - mu4e-maildir-list) - -(defun mu4e-ask-maildir (prompt) - "Ask the user for a shortcut (using PROMPT) as defined in -`mu4e-maildir-shortcuts', then return the corresponding folder -name. If the special shortcut 'o' (for _o_ther) is used, or if -`mu4e-maildir-shortcuts' is not defined, let user choose from all -maildirs under `mu4e-maildir'." - (let ((prompt (mu4e-format "%s" prompt))) - (if (not mu4e-maildir-shortcuts) - (funcall mu4e-completing-read-function prompt (mu4e-get-maildirs)) - (let* ((mlist (append mu4e-maildir-shortcuts '(("ther" . ?o)))) - (fnames - (mapconcat - (lambda (item) - (concat - "[" - (propertize (make-string 1 (cdr item)) - 'face 'mu4e-highlight-face) - "]" - (car item))) - mlist ", ")) - (kar (read-char (concat prompt fnames)))) - (if (member kar '(?/ ?o)) ;; user chose 'other'? - (funcall mu4e-completing-read-function prompt - (mu4e-get-maildirs) nil nil "/") - (or (car-safe - (find-if (lambda (item) (= kar (cdr item))) - mu4e-maildir-shortcuts)) - (mu4e-warn "Unknown shortcut '%c'" kar))))))) - - -(defun mu4e-ask-maildir-check-exists (prompt) - "Like `mu4e-ask-maildir', but check for existence of the maildir, -and offer to create it if it does not exist yet." - (let* ((mdir (mu4e-ask-maildir prompt)) - (fullpath (concat mu4e-maildir mdir))) - (unless (file-directory-p fullpath) - (and (yes-or-no-p - (mu4e-format "%s does not exist. Create now?" fullpath)) - (mu4e~proc-mkdir fullpath))) - mdir)) - - -(defstruct mu4e-bookmark - "A mu4e bookmarl object with the following members: -- `name': the user-visible name of the bookmark -- `key': a single key to search for this bookmark -- `query': the query for this bookmark. Either a literal string or a function - that evaluates to a string." - name ;; name/description of the bookmark - query ;; a query (a string or a function evaluation to string) - key ;; key to activate the bookmark - ) - -(defun mu4e-bookmarks () - "Get `mu4e-bookmarks' in the (new) format, converting from the old -format if needed." - (map 'list - (lambda (item) - (if (mu4e-bookmark-p item) - item ;; already in the right format - (if (and (listp item) (= (length item) 3)) - (make-mu4e-bookmark - :name (nth 1 item) - :query (nth 0 item) - :key (nth 2 item)) - (mu4e-error "Invalid bookmark in mu4e-bookmarks")))) - mu4e-bookmarks)) - - -(defun mu4e-ask-bookmark (prompt &optional kar) - "Ask the user for a bookmark (using PROMPT) as defined in -`mu4e-bookmarks', then return the corresponding query." - (unless (mu4e-bookmarks) (mu4e-error "No bookmarks defined")) - (let* ((prompt (mu4e-format "%s" prompt)) - (bmarks - (mapconcat - (lambda (bm) - (concat - "[" (propertize (make-string 1 (mu4e-bookmark-key bm)) - 'face 'mu4e-highlight-face) - "]" - (mu4e-bookmark-name bm))) (mu4e-bookmarks) ", ")) - (kar (read-char (concat prompt bmarks)))) - (mu4e-get-bookmark-query kar))) - -(defun mu4e-get-bookmark-query (kar) - "Get the corresponding bookmarked query for shortcut character -KAR, or raise an error if none is found." - (let* ((chosen-bm - (or (find-if - (lambda (bm) - (= kar (mu4e-bookmark-key bm))) - (mu4e-bookmarks)) - (mu4e-warn "Unknown shortcut '%c'" kar))) - (expr (mu4e-bookmark-query chosen-bm)) - (query (eval expr))) - (if (stringp query) - query - (mu4e-warn "Expression must evaluate to query string ('%S')" expr)))) - - -(defun mu4e-bookmark-define (query name key) - "Define a bookmark for QUERY with name NAME and -shortcut-character KEY in the list of `mu4e-bookmarks'. This -replaces any existing bookmark with KEY." - (setq mu4e-bookmarks - (remove-if - (lambda (bm) - (= (mu4e-bookmark-key bm) key)) - (mu4e-bookmarks))) - (add-to-list 'mu4e-bookmarks - (make-mu4e-bookmark - :name name - :query query - :key key) t)) - - -;;; converting flags->string and vice-versa ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e~flags-to-string-raw (flags) - "Convert a list of flags into a string as seen in Maildir -message files; flags are symbols draft, flagged, new, passed, -replied, seen, trashed and the string is the concatenation of the -uppercased first letters of these flags, as per [1]. Other flags -than the ones listed here are ignored. -Also see `mu4e-flags-to-string'. -\[1\]: http://cr.yp.to/proto/maildir.html" - (when flags - (let ((kar (case (car flags) - ('draft ?D) - ('flagged ?F) - ('new ?N) - ('passed ?P) - ('replied ?R) - ('seen ?S) - ('trashed ?T) - ('attach ?a) - ('encrypted ?x) - ('signed ?s) - ('unread ?u)))) - (concat (and kar (string kar)) - (mu4e~flags-to-string-raw (cdr flags)))))) - -(defun mu4e-flags-to-string (flags) - "Remove duplicates and sort the output of `mu4e~flags-to-string-raw'." - (concat - (sort (remove-duplicates - (append (mu4e~flags-to-string-raw flags) nil)) '>))) - -(defun mu4e~string-to-flags-1 (str) - "Convert a string with message flags as seen in Maildir -messages into a list of flags in; flags are symbols draft, -flagged, new, passed, replied, seen, trashed and the string is -the concatenation of the uppercased first letters of these flags, -as per [1]. Other letters than the ones listed here are ignored. -Also see `mu4e-flags-to-string'. -\[1\]: http://cr.yp.to/proto/maildir.html." - (when (/= 0 (length str)) - (let ((flag - (case (string-to-char str) - (?D 'draft) - (?F 'flagged) - (?P 'passed) - (?R 'replied) - (?S 'seen) - (?T 'trashed)))) - (append (when flag (list flag)) - (mu4e~string-to-flags-1 (substring str 1)))))) - -(defun mu4e-string-to-flags (str) - "Convert a string with message flags as seen in Maildir messages -into a list of flags in; flags are symbols draft, flagged, new, -passed, replied, seen, trashed and the string is the concatenation -of the uppercased first letters of these flags, as per [1]. Other -letters than the ones listed here are ignored. Also see -`mu4e-flags-to-string'. \[1\]: -http://cr.yp.to/proto/maildir.html " - ;; "Remove duplicates from the output of `mu4e~string-to-flags-1'" - (remove-duplicates (mu4e~string-to-flags-1 str))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defun mu4e-display-size (size) - "Get a string representation of SIZE (in bytes)." - (cond - ((>= size 1000000) (format "%2.1fM" (/ size 1000000.0))) - ((and (>= size 1000) (< size 1000000)) - (format "%2.1fK" (/ size 1000.0))) - ((< size 1000) (format "%d" size)) - (t (propertize "?" 'face 'mu4e-system-face)))) - - -(defun mu4e-display-manual () - "Display the mu4e manual page for the current mode. -Or go to the top level if there is none." - (interactive) - (info (case major-mode - ('mu4e-main-mode "(mu4e)Main view") - ('mu4e-headers-mode "(mu4e)Headers view") - ('mu4e-view-mode "(mu4e)Message view") - (t "mu4e")))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e-last-query () - "Get the most recent query or nil if there is none." - (when (buffer-live-p mu4e~headers-buffer) - (with-current-buffer mu4e~headers-buffer - mu4e~headers-last-query))) - -(defun mu4e-select-other-view () - "When the headers view is selected, select the message view (if -that has a live window), and vice versa." - (interactive) - (let* ((other-buf - (cond - ((eq major-mode 'mu4e-headers-mode) - mu4e~view-buffer) - ((eq major-mode 'mu4e-view-mode) - mu4e~headers-buffer))) - (other-win (and other-buf (get-buffer-window other-buf)))) - (if (window-live-p other-win) - (select-window other-win) - (mu4e-message "No window to switch to")))) - - -(defconst mu4e-output-buffer-name "*mu4e-output*" - "*internal* Name of the mu4e output buffer.") - -(defun mu4e-process-file-through-pipe (path pipecmd) - "Process file at PATH through a pipe with PIPECMD." - (let ((buf (get-buffer-create mu4e-output-buffer-name))) - (with-current-buffer buf - (let ((inhibit-read-only t)) - (erase-buffer) - (call-process-shell-command pipecmd path t t) - (view-mode))) - (switch-to-buffer buf))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar mu4e~lists-hash nil - "Hashtable of mailing-list-id => shortname, based on - `mu4e~mailing-lists' and `mu4e-user-mailing-lists'.") - -(defun mu4e-get-mailing-list-shortname (list-id) - "Get the shortname for a mailing-list with list-id LIST-ID. based -on `mu4e~mailing-lists', `mu4e-user-mailing-lists', and -`mu4e-mailing-list-patterns'." - (unless mu4e~lists-hash - (setq mu4e~lists-hash (make-hash-table :test 'equal)) - (dolist (cell mu4e~mailing-lists) - (puthash (car cell) (cdr cell) mu4e~lists-hash)) - (dolist (cell mu4e-user-mailing-lists) - (puthash (car cell) (cdr cell) mu4e~lists-hash))) - (or - (gethash list-id mu4e~lists-hash) - (and (boundp 'mu4e-mailing-list-patterns) - (cl-member-if - (lambda (pattern) - (string-match pattern list-id)) - mu4e-mailing-list-patterns) - (match-string 1 list-id)) - ;; if it's not in the db, take the part until the first dot if there is one; - ;; otherwise just return the whole thing - (if (string-match "\\([^.]*\\)\\." list-id) - (match-string 1 list-id) - list-id))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defvar mu4e-index-updated-hook nil - "Hook run when the indexing process had one or more updated messages. -This can be used as a simple way to invoke some action when new -messages appear, but note that an update in the index does not -necessarily mean a new message.") - -;; some handler functions for server messages -;; -(defun mu4e-info-handler (info) - "Handler function for (:info ...) sexps received from the server -process." - (let ((type (plist-get info :info))) - (cond - ((eq type 'add) t) ;; do nothing - ((eq type 'index) - (if (eq (plist-get info :status) 'running) - (mu4e-index-message "Indexing... processed %d, updated %d" - (plist-get info :processed) (plist-get info :updated)) - (progn - (mu4e-index-message - "Indexing completed; processed %d, updated %d, cleaned-up %d" - (plist-get info :processed) (plist-get info :updated) - (plist-get info :cleaned-up)) - (unless (zerop (plist-get info :updated)) - (run-hooks 'mu4e-index-updated-hook))))) - ((plist-get info :message) - (mu4e-index-message "%s" (plist-get info :message)))))) - -(defun mu4e-error-handler (errcode errmsg) - "Handler function for showing an error." - ;; don't use mu4e-error here; it's running in the process filter context - (case errcode - (4 (user-error "No matches for this search query.")) - (t (error "Error %d: %s" errcode errmsg)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; RFC2822 handling of phrases in mail-addresses -;;; The optional display-name contains a phrase, it sits before the angle-addr -;;; as specified in RFC2822 for email-addresses in header fields. -;;; contributed by jhelberg - -(defun mu4e~rfc822-phrase-type (ph) - "Return either atom, quoted-string, a corner-case or nil. This - checks for empty string first. Then quotes around the phrase - (returning 'rfc822-quoted-string). Then whether there is a quote - inside the phrase (returning 'rfc822-containing-quote). - The reverse of the RFC atext definition is then tested. - If it matches, nil is returned, if not, it is an 'rfc822-atom, which - is returned." - (cond - ((= (length ph) 0) 'rfc822-empty) - ((= (aref ph 0) ?\") - (if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph) - 'rfc822-quoted-string - 'rfc822-containing-quote)) ; starts with quote, but doesn't end with one - ((string-match-p "[\"]" ph) 'rfc822-containing-quote) - ((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil) - (t 'rfc822-atom))) - -(defun mu4e~rfc822-quoteit (ph) - "Quote RFC822 phrase only if necessary. - Atoms and quoted strings don't need quotes. The rest do. In - case a phrase contains a quote, it will be escaped." - (let ((type (mu4e~rfc822-phrase-type ph))) - (cond - ((eq type 'rfc822-atom) ph) - ((eq type 'rfc822-quoted-string) ph) - ((eq type 'rfc822-containing-quote) - (format "\"%s\"" - (replace-regexp-in-string "\"" "\\\\\"" ph))) - (t (format "\"%s\"" ph))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defsubst mu4e~process-contact (contact) - "Process CONTACT, and either return nil when it should not be included, -or (rfc822-string . CONTACT) otherwise." - (when mu4e-contact-rewrite-function - (setq contact (funcall mu4e-contact-rewrite-function contact))) - (when contact - (let ((name (plist-get contact :name)) - (mail (plist-get contact :mail)) - (ignore-rx (or mu4e-compose-complete-ignore-address-regexp "$^"))) - (when (and mail (not (string-match ignore-rx mail))) - (cons - (if name (format "%s <%s>" (mu4e~rfc822-quoteit name) mail) mail) - contact))))) - - -(defun mu4e~sort-contacts (contacts) - "Destructively sort contacts (only for cycling) in order of - 'mostly likely contact'.t See the code for the detail" - (let* ((now (+ (float-time) 3600)) ;; allow for clock diffs - (recent (- (float-time) (* 15 24 3600)))) - (sort* contacts - (lambda (c1 c2) - (let* ( (c1 (cdr c1)) (c2 (cdr c2)) - (personal1 (plist-get c1 :personal)) - (personal2 (plist-get c2 :personal)) - ;; note: freq, tstamp can only be missing if the rewrite - ;; function removed them. If the rewrite function changed the - ;; contact somehow, we guess it's important. - (freq1 (or (plist-get c1 :freq) 500)) - (freq2 (or (plist-get c2 :freq) 500)) - (tstamp1 (or (plist-get c1 :tstamp) now)) - (tstamp2 (or (plist-get c2 :tstamp) now))) - ;; only one is personal? if so, that one comes first - (if (not (equal personal1 personal2)) - (if personal1 t nil) - ;; only one is recent? that one comes first - (if (not (equal (> tstamp1 recent) (> tstamp2 recent))) - (> tstamp1 tstamp2) - ;; otherwise, use the frequency - (> freq1 freq2)))))))) - -(defun mu4e~sort-contacts-for-completion (contacts) - "Takes CONTACTS, which is a list of RFC-822 addresses, and sort them based -on the ranking in `mu4e~contacts.'" - (sort* contacts - (lambda (c1 c2) - (let ((rank1 (gethash c1 mu4e~contacts)) - (rank2 (gethash c2 mu4e~contacts))) - (< rank1 rank2))))) - -;; start and stopping -(defun mu4e~fill-contacts (contact-data) - "We receive a list of contacts, which each contact of the form - (:me NAME :mail EMAIL :tstamp TIMESTAMP :freq FREQUENCY) -and fill the hash `mu4e~contacts-for-completion' with it, with -each contact mapped to an integer for their ranking. - -This is used by the completion function in mu4e-compose." - (let ((contacts) (rank 0)) - (dolist (contact contact-data) - (let ((contact-maybe (mu4e~process-contact contact))) - ;; note, this gives cells (rfc822-address . contact) - (when contact-maybe (push contact-maybe contacts)))) - (setq contacts (mu4e~sort-contacts contacts)) - ;; now, we have our nicely sorted list, map them to a list - ;; of increasing integers. We use that map in the composer - ;; to sort them there. It would have been so much easier if emacs - ;; allowed us to use the sorted-list as-is, but no such luck. - (setq mu4e~contacts (make-hash-table :test 'equal :weakness nil - :size (length contacts))) - (dolist (contact contacts) - (puthash (car contact) rank mu4e~contacts) - (incf rank)) - (mu4e-index-message "Contacts received: %d" - (hash-table-count mu4e~contacts)))) - -(defun mu4e~check-requirements () - "Check for the settings required for running mu4e." - (unless (>= emacs-major-version 23) - (mu4e-error "Emacs >= 23.x is required for mu4e")) - (when mu4e~server-props - (let ((version (plist-get mu4e~server-props :version))) - (unless (string= version mu4e-mu-version) - (mu4e-error "mu server has version %s, but we need %s" - version mu4e-mu-version)))) - (unless (and mu4e-mu-binary (file-executable-p mu4e-mu-binary)) - (mu4e-error "Please set `mu4e-mu-binary' to the full path to the mu - binary.")) - (unless mu4e-maildir - (mu4e-error "Please set `mu4e-maildir' to the full path to your - Maildir directory.")) - ;; expand mu4e-maildir, mu4e-attachment-dir - (setq mu4e-maildir (expand-file-name mu4e-maildir)) - (unless (mu4e-create-maildir-maybe mu4e-maildir) - (mu4e-error "%s is not a valid maildir directory" mu4e-maildir)) - (dolist (var '(mu4e-sent-folder mu4e-drafts-folder - mu4e-trash-folder)) - (unless (and (boundp var) (symbol-value var)) - (mu4e-error "Please set %S" var)) - (unless (functionp (symbol-value var)) ;; functions are okay, too - (let* ((dir (symbol-value var)) - (path (concat mu4e-maildir dir))) - (unless (string= (substring dir 0 1) "/") - (mu4e-error "%S must start with a '/'" dir)) - (unless (mu4e-create-maildir-maybe path) - (mu4e-error "%s (%S) does not exist" path var)))))) - - -(defun mu4e-running-p () - "Whether mu4e is running. -Checks whether the server process is live." - (mu4e~proc-running-p)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; starting / getting mail / updating the index -;; -;; -(defvar mu4e~update-timer nil - "The mu4e update timer.") -(defconst mu4e~update-name "*mu4e-update*" - "Name of the process and buffer to update mail.") -(defconst mu4e~update-buffer-height 8 - "Height of the mu4e message retrieval/update buffer.") - -(defvar mu4e~get-mail-ask-password "mu4e get-mail: Enter password: " - "Query string for `mu4e-get-mail-command' password.") -(defvar mu4e~get-mail-password-regexp "^Remote: Enter password: $" - "Regexp to match a password query in the `mu4e-get-mail-command' output.") - -(defun mu4e~request-contacts () - "If `mu4e-compose-complete-addresses' is non-nil, get/update the -list of contacts we use for autocompletion; otherwise, do nothing." - (when mu4e-compose-complete-addresses - (setq mu4e-contacts-func 'mu4e~fill-contacts) - (mu4e~proc-contacts - mu4e-compose-complete-only-personal - (when mu4e-compose-complete-only-after - (float-time - (apply 'encode-time - (mu4e-parse-time-string mu4e-compose-complete-only-after))))))) - -(defun mu4e~start (&optional func) - "If `mu4e-contexts' have been defined, but we don't have a -context yet, switch to the matching one, or none matches, the -first. -If mu4e is already running, execute function FUNC (if non-nil). -Otherwise, check various requirements, then start mu4e. When -successful, call FUNC (if non-nil) afterwards." - ;; if we're already running, simply go to the main view - (if (mu4e-running-p) ;; already running? - (when func (funcall func)) ;; yes! run func if defined - (progn - ;; no! try to set a context, do some checks, set up pong handler and ping - ;; the server maybe switch the context - (mu4e~context-autoswitch nil mu4e-context-policy) - (lexical-let ((func func)) - (mu4e~check-requirements) - ;; set up the 'pong' handler func - (setq mu4e-pong-func - (lambda (props) - (setq mu4e~server-props props) ;; save props from the server - (let ((version (plist-get props :version)) - (doccount (plist-get props :doccount))) - (mu4e~check-requirements) - (when func (funcall func)) - (when (and mu4e-update-interval (null mu4e~update-timer)) - (setq mu4e~update-timer - (run-at-time - 0 mu4e-update-interval - (lambda () (mu4e-update-mail-and-index - mu4e-index-update-in-background))))) - (mu4e-message "Started mu4e with %d message%s in store" - doccount (if (= doccount 1) "" "s")))))) - ;; wake up server - (mu4e~proc-ping) - ;; maybe request the list of contacts, automatically refresh after - ;; reindexing - (mu4e~request-contacts) - (add-hook 'mu4e-index-updated-hook 'mu4e~request-contacts)))) - -(defun mu4e-clear-caches () - "Clear any cached resources." - (setq - mu4e-maildir-list nil - mu4e~contacts nil)) - -(defun mu4e~stop () - "Stop the mu4e session." - (when mu4e~update-timer - (cancel-timer mu4e~update-timer) - (setq mu4e~update-timer nil)) - (mu4e-clear-caches) - (mu4e~proc-kill) - ;; kill all main/view/headers buffer - (mapcar - (lambda (buf) - (with-current-buffer buf - (when (member major-mode - '(mu4e-headers-mode mu4e-view-mode mu4e-main-mode)) - (kill-buffer)))) - (buffer-list))) - - - -(defvar mu4e~progress-reporter nil - "Internal, the progress reporter object.") - -(defun mu4e~get-mail-process-filter (proc msg) - "Filter the output of `mu4e-get-mail-command'. -Currently the filter only checks if the command asks for a password -by matching the output against `mu4e~get-mail-password-regexp'. -The messages are inserted into the process buffer. - -Also scrolls to the final line, and update the progress throbber." - (when mu4e~progress-reporter - (progress-reporter-update mu4e~progress-reporter)) - - (when (string-match mu4e~get-mail-password-regexp msg) - (if (process-get proc 'x-interactive) - (process-send-string proc - (concat (read-passwd mu4e~get-mail-ask-password) - "\n")) - ;; TODO kill process? - (mu4e-error "Unrecognized password request"))) - (when (process-buffer proc) - (let ((inhibit-read-only t) - (procwin (get-buffer-window (process-buffer proc)))) - ;; Insert at end of buffer. Leave point alone. - (with-current-buffer (process-buffer proc) - (goto-char (point-max)) - (if (string-match ".*\r\\(.*\\)" msg) - (progn - (kill-line 0) - (insert (match-string 1 msg))) - (insert msg))) - ;; Auto-scroll unless user is interacting with the window. - (when (and (window-live-p procwin) - (not (eq (selected-window) procwin))) - (with-selected-window procwin - (goto-char (point-max))))))) - -(defun mu4e-update-index () - "Update the mu4e index." - (interactive) - (unless mu4e-maildir - (mu4e-error "`mu4e-maildir' is not defined")) - (mu4e~proc-index mu4e-maildir - mu4e-user-mail-address-list - mu4e-index-cleanup - mu4e-index-lazy-check)) - -(defvar mu4e~update-buffer nil - "Internal, store the buffer of the update process when - updating.") - -(define-derived-mode mu4e~update-mail-mode special-mode "mu4e:update" - "Major mode used for retrieving new e-mail messages in `mu4e'.") - -(define-key mu4e~update-mail-mode-map (kbd "q") 'mu4e-interrupt-update-mail) - -(defun mu4e~temp-window (buf height) - "Create a temporary window with HEIGHT at the bottom of the -frame to display buffer BUF." - (let ((win - (split-window - (frame-root-window) - (- (window-height (frame-root-window)) height)))) - (set-window-buffer win buf) - (set-window-dedicated-p win t) - win)) - -(defun mu4e~update-sentinel-func (proc msg) - "Sentinel function for the update process." - (when mu4e~progress-reporter - (progress-reporter-done mu4e~progress-reporter) - (setq mu4e~progress-reporter nil)) - (let* ((status (process-status proc)) - (code (process-exit-status proc)) - (maybe-error (or (not (eq status 'exit)) (/= code 0))) - (buf (and (buffer-live-p mu4e~update-buffer) mu4e~update-buffer)) - (win (and buf (get-buffer-window buf)))) - (message nil) - (if maybe-error - (progn - (when mu4e-index-update-error-warning - (mu4e-message "Update process returned with non-zero exit code") - (sit-for 5)) - (when mu4e-index-update-error-continue - (mu4e-update-index))) - (mu4e-update-index)) - (if (window-live-p win) - (with-selected-window win (kill-buffer-and-window)) - (when (buffer-live-p buf) (kill-buffer buf))))) - -;; complicated function, as it: -;; - needs to check for errors -;; - (optionally) pop-up a window -;; - (optionally) check password requests -(defun mu4e~update-mail-and-index-real (run-in-background) - "Get a new mail by running `mu4e-get-mail-command'. If -RUN-IN-BACKGROUND is non-nil (or called with prefix-argument), -run in the background; otherwise, pop up a window." - (let* ((process-connection-type t) - (proc (start-process-shell-command - "mu4e-update" " *mu4e-update*" - mu4e-get-mail-command)) - (buf (process-buffer proc)) - (win (or run-in-background - (mu4e~temp-window buf mu4e~update-buffer-height)))) - (setq mu4e~update-buffer buf) - (when (window-live-p win) - (with-selected-window win - ;; ;;(switch-to-buffer buf) - ;; (set-window-dedicated-p win t) - (erase-buffer) - (insert "\n") ;; FIXME -- needed so output start - (mu4e~update-mail-mode))) - (setq mu4e~progress-reporter - (unless mu4e-hide-index-messages - (make-progress-reporter - (mu4e-format "Retrieving mail...")))) - (set-process-sentinel proc 'mu4e~update-sentinel-func) - ;; if we're running in the foreground, handle password requests - (unless run-in-background - (process-put proc 'x-interactive (not run-in-background)) - (set-process-filter proc 'mu4e~get-mail-process-filter)))) - -(defun mu4e-update-mail-and-index (run-in-background) - "Get a new mail by running `mu4e-get-mail-command'. If -run-in-background is non-nil (or called with prefix-argument), run -in the background; otherwise, pop up a window." - (interactive "P") - (unless mu4e-get-mail-command - (mu4e-error "`mu4e-get-mail-command' is not defined")) - (if (and (buffer-live-p mu4e~update-buffer) - (process-live-p (get-buffer-process mu4e~update-buffer))) - (mu4e-message "Update process is already running") - (progn - (run-hooks 'mu4e-update-pre-hook) - (mu4e~update-mail-and-index-real run-in-background)))) - -(defun mu4e-interrupt-update-mail () - "Stop the update process by sending SIGINT to it." - (interactive) - (let* ((proc (and (buffer-live-p mu4e~update-buffer) - (get-buffer-process mu4e~update-buffer)))) - (when (process-live-p proc) - (interrupt-process proc t)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; logging / debugging -(defvar mu4e~log-max-lines 1200 - "*internal* Last <n> number of lines to keep around in the buffer.") -(defconst mu4e~log-buffer-name "*mu4e-log*" - "*internal* Name of the logging buffer.") - -(defun mu4e-log (type frm &rest args) - "Write a message of TYPE with format-string FRM and ARGS in -*mu4e-log* buffer, if the variable mu4e-debug is non-nil. Type is -either 'to-server, 'from-server or 'misc. This function is meant for debugging." - (when mu4e-debug - (with-current-buffer (get-buffer-create mu4e~log-buffer-name) - (view-mode) - (setq buffer-undo-list t) - (let* ((inhibit-read-only t) - (tstamp (propertize (format-time-string "%Y-%m-%d %T" - (current-time)) - 'face 'font-lock-string-face)) - (msg-face - (case type - (from-server 'font-lock-type-face) - (to-server 'font-lock-function-name-face) - (misc 'font-lock-variable-name-face) - (error 'font-lock-warning-face) - (otherwise (mu4e-error "Unsupported log type")))) - (msg (propertize (apply 'format frm args) 'face msg-face))) - (goto-char (point-max)) - (insert tstamp - (case type - (from-server " <- ") - (to-server " -> ") - (error " !! ") - (otherwise " ")) - msg "\n") - - ;; if `mu4e-log-max-lines is specified and exceeded, clearest the oldest - ;; lines - (when (numberp mu4e~log-max-lines) - (let ((lines (count-lines (point-min) (point-max)))) - (when (> lines mu4e~log-max-lines) - (goto-char (point-max)) - (forward-line (- mu4e~log-max-lines lines)) - (beginning-of-line) - (delete-region (point-min) (point))))))))) - -(defun mu4e-toggle-logging () - "Toggle between enabling/disabling debug-mode (in debug-mode, -mu4e logs some of its internal workings to a log-buffer. See -`mu4e-visit-log'." - (interactive) - (mu4e-log 'misc "logging disabled") - (setq mu4e-debug (not mu4e-debug)) - (mu4e-message "debug logging has been %s" - (if mu4e-debug "enabled" "disabled")) - (mu4e-log 'misc "logging enabled")) - -(defun mu4e-show-log () - "Visit the mu4e debug log." - (interactive) - (let ((buf (get-buffer mu4e~log-buffer-name))) - (unless (buffer-live-p buf) - (mu4e-warn "No debug log available")) - (switch-to-buffer buf))) - - -(defun mu4e-split-ranges-to-numbers (str n) - "Convert STR containing attachment numbers into a list of numbers. -STR is a string; N is the highest possible number in the list. -This includes expanding e.g. 3-5 into 3,4,5. If the letter -\"a\" ('all')) is given, that is expanded to a list with numbers [1..n]." - (let ((str-split (split-string str)) - beg end list) - (dolist (elem str-split list) - ;; special number "a" converts into all attachments 1-N. - (when (equal elem "a") - (setq elem (concat "1-" (int-to-string n)))) - (if (string-match "\\([0-9]+\\)-\\([0-9]+\\)" elem) - ;; we have found a range A-B, which needs converting - ;; into the numbers A, A+1, A+2, ... B. - (progn - (setq beg (string-to-number (match-string 1 elem)) - end (string-to-number (match-string 2 elem))) - (while (<= beg end) - (add-to-list 'list beg 'append) - (setq beg (1+ beg)))) - ;; else just a number - (add-to-list 'list (string-to-number elem) 'append))) - ;; Check that all numbers are valid. - (mapc - #'(lambda (x) - (cond - ((> x n) - (mu4e-warn "Attachment %d bigger than maximum (%d)" x n)) - ((< x 1) - (mu4e-warn "Attachment number must be greater than 0 (%d)" x)))) - list))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defvar mu4e-imagemagick-identify "identify" - "Name/path of the Imagemagick 'identify' program.") - -(defun mu4e-display-image (imgpath &optional maxwidth maxheight) - "Display image IMG at point; optionally specify MAXWIDTH and -MAXHEIGHT. Function tries to use imagemagick if available (ie., -emacs was compiled with inmagemagick support); otherwise MAXWIDTH -and MAXHEIGHT are ignored." - (let* ((have-im (and (fboundp 'imagemagick-types) - (imagemagick-types))) ;; hmm, should check for specific type - (identify (and have-im maxwidth - (executable-find mu4e-imagemagick-identify))) - (props (and identify (shell-command-to-string - (format "%s -format '%%w' %s" - identify (shell-quote-argument imgpath))))) - (width (and props (string-to-number props))) - (img (if have-im - (if (> (or width 0) (or maxwidth 0)) - (create-image imgpath 'imagemagick nil :width maxwidth) - (create-image imgpath 'imagemagick)) - (create-image imgpath)))) - (when img - (save-excursion - (insert "\n") - (let ((size (image-size img))) ;; inspired by gnus.. - (insert-char ?\n - (max 0 (round (- (window-height) (or maxheight (cdr size)) 1) 2))) - (insert-char ?\. - (max 0 (round (- (window-width) (or maxwidth (car size))) 2))) - (insert-image img)))))) - - -(defun mu4e-hide-other-mu4e-buffers () - "Bury mu4e-buffers (main, headers, view) (and delete all windows -displaying it). Do _not_ bury the current buffer, though." - (interactive) - (let ((curbuf (current-buffer))) - ;; note: 'walk-windows' does not seem to work correctly when modifying - ;; windows; therefore, the doloops here - (dolist (frame (frame-list)) - (dolist (win (window-list frame nil)) - (with-current-buffer (window-buffer win) - (unless (eq curbuf (current-buffer)) - (when (member major-mode '(mu4e-headers-mode mu4e-view-mode)) - (when (eq t (window-deletable-p win)) - (delete-window win))))))) t)) - - -(defun mu4e-get-time-date (prompt) - "Determine the emacs time value for the time/date entered by user - after PROMPT. Formats are all that are accepted by - `parse-time-string'." - (let ((timestr (read-string (mu4e-format "%s" prompt)))) - (apply 'encode-time (mu4e-parse-time-string timestr)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define-derived-mode mu4e-org-mode org-mode "mu4e:org" - "Major mode for mu4e documents, derived from - `org-mode'.") - -(defun mu4e-info (path) - "Show a buffer with the information (an org-file) at PATH." - (interactive) - (unless (file-exists-p path) - (mu4e-error "Cannot find %s" path)) - (lexical-let ((curbuf (current-buffer))) - (find-file path) - (mu4e-org-mode) - (setq buffer-read-only t) - (define-key mu4e-org-mode-map (kbd "q") - (lambda () - (interactive) - (bury-buffer) - (switch-to-buffer curbuf))))) - -(defun mu4e-about () - "Show the mu4e 'about' page." - (interactive) - (mu4e-info (concat mu4e-doc-dir "/mu4e-about.org"))) - -(defun mu4e-news () - "Show the mu4e 'about' page." - (interactive) - (mu4e-info (concat mu4e-doc-dir "/NEWS.org"))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun mu4e-refresh-message (path maildir) - "Re-parse message at PATH and MAILDIR; if this works, we will -receive (:info add :path <path> :docid <docid>) as well as (:update -<msg-sexp>)." - (mu4e~proc-add path maildir)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mu4e~fontify-cited () - "Colorize message content based on the citation level. This is -used in the view and compose modes." - (save-excursion - (goto-char (point-min)) - (when (search-forward-regexp "^\n" nil t) ;; search the first empty line - (while (re-search-forward mu4e-cited-regexp nil t) - (let* ((level (string-width (replace-regexp-in-string - "[^>]" "" (match-string 0)))) - (face (unless (zerop level) - (intern-soft (format "mu4e-cited-%d-face" level))))) - (when face - (add-text-properties (line-beginning-position 1) - (line-end-position 1) `(face ,face)))))))) - -(defun mu4e~fontify-signature () - "Give the message signatures a distinctive color. This is used in -the view and compose modes." - (let ((inhibit-read-only t)) - (save-excursion - ;; give the footer a different color... - (goto-char (point-min)) - (let ((p (search-forward "^-- *$" nil t))) - (when p - (add-text-properties p (point-max) '(face mu4e-footer-face))))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun mu4e~quote-for-modeline (str) - "Quote a string to be used literally in the modeline." - (replace-regexp-in-string "%" "%%" str t t)) - - -(provide 'mu4e-utils) -;;; End of mu4e-utils.el diff --git a/_spacemacs.d/local/mu4e/mu4e-vars.el b/_spacemacs.d/local/mu4e/mu4e-vars.el deleted file mode 100644 index e07b196..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-vars.el +++ /dev/null @@ -1,927 +0,0 @@ -;;; mu4e-vars.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;;; Code: - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Customization -(require 'mu4e-meta) -(require 'message) - -(defgroup mu4e nil - "mu4e - mu for emacs" - :group 'mail) - -(defcustom mu4e-mu-home nil - "Location of the mu homedir, or nil for the default." - :group 'mu4e - :type '(choice (const :tag "Default location" nil) - (directory :tag "Specify location")) - :safe 'stringp) - -(defcustom mu4e-mu-binary (executable-find "mu") - "Name of the mu-binary to use. -If it cannot be found in your PATH, you can specify the full -path." - :type 'file - :group 'mu4e - :safe 'stringp) - -(defcustom mu4e-maildir (expand-file-name "~/Maildir") - "The file system path to your Maildir. Must not be a symbolic -link." - :type 'directory - :safe 'stringp - :group 'mu4e) - -(defcustom mu4e-get-mail-command "true" - "Shell command to run to retrieve new mail. -Common values are \"offlineimap\", \"fetchmail\" or \"mbsync\", but -arbitrary shell-commands can be used. - -When set to the literal string \"true\" (the default), the -command simply finishes succesfully (running the 'true' command) -without retrieving any mail. This can be useful when mail is -already retrieved in another way." - :type 'string - :group 'mu4e - :safe 'stringp) - -(defcustom mu4e-index-update-error-warning t - "Whether to display warnings when we the retrieval process (as - per `mu4e-get-mail-command') finished with a non-zero exit code." - :type 'boolean - :group 'mu4e - :safe 'booleanp) - -(defcustom mu4e-index-update-error-continue t - "Whether to continue with indexing when we the retrieval - process (as per `mu4e-get-mail-command') finished with a non-zero - exit code." - :type 'boolean - :group 'mu4e - :safe 'booleanp) - -(defcustom mu4e-index-update-in-background t - "Whether to run the automatic mail retrieval in the -background." - :type 'boolean - :group 'mu4e - :safe 'booleanp) - -(defcustom mu4e-index-cleanup t - "Whether to run a cleanup face after indexing -- that is, see -if the is a message in the filesystem for each file in the -message store. Having this option as `t' ensures that no -non-existing mesages are shown but can also be quite slow with -large message stores." - :type 'boolean - :group 'mu4e - :safe 'booleanp) - -(defcustom mu4e-index-lazy-check nil - "Whether to run do a 'lazy check' for deciding whether to -indexing a message. When this is set to `t', mu only uses the -directory timestamps to decide on whether it needs to check the -messages beneath it, which would miss messages that are modified -outside mu. On the other hand, it's significantly faster." - :type 'boolean - :group 'mu4e - :safe 'booleanp) - - -(defcustom mu4e-update-interval nil - "Number of seconds between automatic calls to retrieve mail and -update the database. If nil, don't update automatically. Note, -changes in `mu4e-update-interval' only take effect after restarting -mu4e." - :type '(choice (const :tag "No automatic update" nil) - (integer :tag "Seconds")) - :group 'mu4e - :safe 'integerp) - -(defvar mu4e-update-pre-hook nil - "Hook run just *before* the mail-retrieval / database updating process starts. - You can use this hook for example to `mu4e-get-mail-command' with - some specific setting.") - -(defcustom mu4e-hide-index-messages nil - "Whether to hide the \"Indexing...\" messages, or any messages -relating to updated contacts." - :type 'boolean - :group 'mu4e) - -(defcustom mu4e-change-filenames-when-moving nil - "When moving messages to different folders, normally mu/mu4e keep -the base filename the same (the flags-part of the filename may -change still). With this option set to non-nil, mu4e instead -changes the filename. This latter behavior works better with some -IMAP-synchronization programs such as mbsync; the default works -better with e.g. offlineimap." - :type 'boolean - :group 'mu4e - :safe 'booleanp) - -(defcustom mu4e-attachment-dir (expand-file-name "~/") - "Default directory for saving attachments. -This can be either a string (a file system path), or a function -that takes a filename and the mime-type as arguments, and returns -the attachment dir. See Info node `(mu4e) Attachments' for details." - :type 'directory - :group 'mu4e - :safe 'stringp) - -(defcustom mu4e-user-mail-address-list `(,user-mail-address) - "List of e-mail addresses to consider 'my email addresses'. -I.e. addresses whose presence in an email imply that it is a -personal message. Note that e-mail addresses are case-sensitive, -as per RFC531." - :type '(repeat (string :tag "Address")) - :group 'mu4e) - -;; don't use the older vars anymore -(make-obsolete-variable 'mu4e-user-mail-address-regexp - 'mu4e-user-mail-address-list "0.9.9.x") - -(make-obsolete-variable 'mu4e-my-email-addresses - 'mu4e-user-mail-address-list "0.9.9.x") - -(defcustom mu4e-use-fancy-chars nil - "Whether to use fancy (Unicode) characters for marks and -threads. You can customize the exact fancy characters used with -`mu4e-marks' and various `mu4e-headers-..-mark' and -`mu4e-headers..-prefix' variables." - :type 'boolean - :group 'mu4e) - -(defcustom mu4e-date-format-long "%c" - "Date format to use in the message view, in the format of - `format-time-string'." - :type 'string - :group 'mu4e) - -(defvar mu4e-debug nil - "When set to non-nil, log debug information to the *mu4e-log* buffer.") - -(defcustom mu4e-bookmarks - '( ("flag:unread AND NOT flag:trashed" "Unread messages" ?u) - ("date:today..now" "Today's messages" ?t) - ("date:7d..now" "Last 7 days" ?w) - ("mime:image/*" "Messages with images" ?p)) - "A list of pre-defined queries. -These will show up in the main screen. Each of the list elements -is a three-element list of the form (QUERY DESCRIPTION KEY), -where QUERY is a string with a mu query, DESCRIPTION is a short -description of the query (this will show up in the UI), and KEY -is a shortcut key for the query." - :type '(repeat (list (string :tag "Query") - (string :tag "Description") - character)) - :group 'mu4e) - -(defvar mu4e-bookmarks - `( ,(make-mu4e-bookmark - :name "Unread messages" - :query "flag:unread AND NOT flag:trashed" - :key ?u) - ,(make-mu4e-bookmark - :name "Today's messages" - :query "date:today..now" - :key ?t) - ,(make-mu4e-bookmark - :name "Last 7 days" - :query "date:7d..now" - :key ?w) - ,(make-mu4e-bookmark - :name "Messages with images" - :query "mime:image/*" - :key ?p)) - "A list of pre-defined queries. Each query is represented by a -mu4e-bookmark structure with parameters @t{:name} with the name -of the bookmark, @t{:query} with the query expression (a query -string or an s-expression that evaluates to query string) and a -@t{:key}, which is the shortcut-key for the query. - -An older form of bookmark, a 3-item list with (QUERY DESCRIPTION -KEY) is still recognized as well, for backward-compatibility.") - - -(defcustom mu4e-split-view 'horizontal - "How to show messages / headers. -A symbol which is either: - * `horizontal': split horizontally (headers on top) - * `vertical': split vertically (headers on the left). - * anything else: don't split (show either headers or messages, - not both) -Also see `mu4e-headers-visible-lines' -and `mu4e-headers-visible-columns'." - :type '(choice (const :tag "Split horizontally" horizontal) - (const :tag "Split vertically" vertical) - (const :tag "Don't split" nil)) - :group 'mu4e-headers) - -(defcustom mu4e-view-show-images nil - "Whether to automatically display attached images in the message -view buffer." - :type 'boolean - :group 'mu4e-view) - -(make-obsolete-variable 'mu4e-show-images - 'mu4e-view-show-images "0.9.9.x") - -(defcustom mu4e-confirm-quit t - "Whether to confirm to quit mu4e." - :type 'boolean - :group 'mu4e) - -(defcustom mu4e-cited-regexp "^\\(\\([[:alpha:]]+\\)\\|\\( *\\)\\)\\(\\(>+ ?\\)+\\)" - "Regular expression that determines whether a line is a - citation. This recognizes lines starting with numbers of '>' - and spaces as well as citations of the type \"John> ... \"." - :type 'string - :group 'mu4e) - -(defcustom mu4e-completing-read-function 'ido-completing-read - "Function to be used to receive input from the user with -completion. This is used to receive the name of the maildir -to switch to via `mu4e~headers-jump-to-maildir'. - -Suggested possible values are: - * `completing-read': built-in completion method - * `ido-completing-read': dynamic completion within the minibuffer." - :type 'function - :options '(completing-read ido-completing-read) - :group 'mu4e) - -(defcustom mu4e-context-policy 'ask-if-none - "The policy to determine the context when entering the mu4e main view. - -If the value is `always-ask', ask the user unconditionally. - -In all other cases, if any context matches (using its match -function), this context is used. Otherwise, if none of the -contexts match, we have the following choices: - -- `pick-first': pick the first of the contexts available (ie. the default) -- `ask': ask the user -- `ask-if-none': ask if there is no context yet, otherwise leave it as it is -- nil: return nil; leaves the current context as is. - -Also see `mu4e-compose-context-policy'." - :type '(choice - (const :tag "Always ask what context to use, even if one matches" - 'always-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 "Pick the first context if none match" 'pick-first) - (const :tag "Don't change the context when none match" nil) - :safe 'symbolp - :group 'mu4e)) - - -;; crypto -(defgroup mu4e-crypto nil - "Crypto-related settings." - :group 'mu4e) - -(defcustom mu4e-auto-retrieve-keys nil - "Attempt to automatically retrieve public keys when needed." - :type 'boolean - :group 'mu4e-crypto) - -(defcustom mu4e-decryption-policy t - "Policy for dealing with encrypted parts. -The setting is a symbol: - * t: try to decrypt automatically - * `ask': ask before decrypting anything - * nil: don't try to decrypt anything." - :type '(choice (const :tag "Try to decrypt automatically" t) - (const :tag "Ask before decrypting anything" ask) - (const :tag "Don't try to decrypt anything" nil)) - :group 'mu4e-crypto) - -;; completion; we put them here rather than in mu4e-compose, as mu4e-utils needs -;; the variables. - -(defgroup mu4e-compose nil - "Message-composition related settings." - :group 'mu4e) - -;; address completion -(defcustom mu4e-compose-complete-addresses t - "Whether to do auto-completion of e-mail addresses." - :type 'boolean - :group 'mu4e-compose) - -(defcustom mu4e-compose-complete-only-personal nil - "Whether to consider only 'personal' e-mail addresses, -i.e. addresses from messages where user was explicitly in one of -the address fields (this excludes mailing list messages). See -`mu4e-user-mail-address-list' and the mu-index manpage for details for -details (in particular, how to define your own e-mail addresses)." - :type 'boolean - :group 'mu4e-compose) - -(defcustom mu4e-compose-complete-only-after "2010-01-01" - "Consider only contacts last seen after this date. -Date must be a string, in a format parseable by -`org-parse-time-string'. This excludes really old contacts. -Set to nil to not have any time-based restriction." - :type 'string - :group 'mu4e-compose) - - -;;; names and mail-addresses can be mapped onto their canonical -;;; counterpart. use the customizeable function -;;; mu4e-canonical-contact-function to do that. below the identity -;;; function for mapping a contact onto the canonical one. -(defun mu4e-contact-identity (contact) - "This returns the name and the mail-address of a contact. -It is used as the identity function for converting contacts to -their canonical counterpart; useful as an example." - (let ((name (plist-get contact :name)) - (mail (plist-get contact :mail))) - (list :name name :mail mail))) - -(defcustom mu4e-contact-rewrite-function nil - "Either nil or a function to be used for when processing -contacts and rewrite them or remove them altogether. - -If the function receives the contact as a list of the form - (:name NAME :mail EMAIL ... other properties ... ) -(other properties may be there as well) - -The function should return either: - - nil: remove this contact, or -- the rewritten cell, or -- the existing cell as-is - -For rewriting, it is recommended to use `plist-put' to set the -changed parameters, so the other properties stay in place. Those -are needed for sorting the contacts." - :type 'function - :group 'mu4e-compose) - - -(defcustom mu4e-compose-complete-ignore-address-regexp "no-?reply" - "Ignore any e-mail addresses for completion if they match this regexp." - :type 'string - :group 'mu4e-compose) - -(defcustom mu4e-compose-reply-to-address nil - "The Reply-To address (if this, for some reason, is not equal to -the From: address.)" - :type 'string - :group 'mu4e-compose) - - -;; backward compatibility -(make-obsolete-variable 'mu4e-reply-to-address 'mu4e-compose-reply-to-address - "v0.9.9") - -(defcustom mu4e-compose-keep-self-cc nil - "Non-nil means your e-mail address is kept on the CC list when -replying to messages." - :type 'boolean - :group 'mu4e-compose) - -(defvar mu4e-compose-parent-message nil - "The parent message plist. -This is the message being replied to, forwarded or edited; used -in `mu4e-compose-pre-hook'. For new messages, it is nil.") - -;; Folders -(defgroup mu4e-folders nil - "Special folders." - :group 'mu4e) - -(defcustom mu4e-drafts-folder "/drafts" - "Your folder for draft messages, relative to `mu4e-maildir'. -e.g. \"/drafts\". Instead of a string, may also be a function that -takes a message (a msg plist, see `mu4e-message-get-field'), and -returns a folder. Note, the message parameter refers to the -original message being replied to / being forwarded / re-edited and -is nil otherwise. `mu4e-drafts-folder' is only evaluated once." - :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) - :group 'mu4e-folders) - -(defcustom mu4e-refile-folder "/archive" - "Your folder for refiling messages, relative to `mu4e-maildir', -e.g. \"/Archive\". Instead of a string, may also be a function that -takes a message (a msg plist, see `mu4e-message-get-field'), and -returns a folder. Note that the message parameter refers to the -message-at-point." - :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) - :group 'mu4e-folders) - -(defcustom mu4e-sent-folder "/sent" - "Your folder for sent messages, relative to `mu4e-maildir', -e.g. \"/Sent Items\". Instead of a string, may also be a function -that takes a message (a msg plist, see `mu4e-message-get-field'), -and returns a folder. Note that the message parameter refers to -the original message being replied to / being forwarded / -re-edited, and is nil otherwise." - :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) - :group 'mu4e-folders) - -(defcustom mu4e-trash-folder "/trash" - "Your folder for trashed messages, relative to `mu4e-maildir', -e.g. \"/trash\". Instead of a string, may also be a function that -takes a message (a msg plist, see `mu4e-message-get-field'), and -returns a folder. When using `mu4e-trash-folder' in the headers -view (when marking messages for trash). Note that the message -parameter refers to the message-at-point. When using it when -composing a message (see `mu4e-sent-messages-behavior'), this -refers to the original message being replied to / being forwarded / -re-edited, and is nil otherwise." - :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) - :group 'mu4e-folders) - - -(defcustom mu4e-maildir-shortcuts nil - "A list of maildir shortcuts. This makes it possible to quickly -go to a particular maildir (folder), or quickly moving messages to -them (e.g., for archiving or refiling). The list contains elements -of the form (maildir . shortcut), where MAILDIR is a maildir (such -as \"/archive/\"), and shortcut is a single character. - -You can use these shortcuts in the headers and view buffers, for -example with `mu4e-mark-for-move-quick' (or 'm', by default) or -`mu4e-jump-to-maildir' (or 'j', by default), followed by the -designated shortcut character for the maildir. - -Unlike in search queries, folder names with spaces in them must NOT -be quoted, since mu4e does this automatically for you." - :type '(repeat (cons (string :tag "Maildir") character)) - :group 'mu4e-folders) - -;; Faces -(defgroup mu4e-faces nil - "Type faces (fonts) used in mu4e." - :group 'mu4e - :group 'faces) - -(defface mu4e-unread-face - '((t :inherit font-lock-keyword-face :bold t)) - "Face for an unread message header." - :group 'mu4e-faces) - -(defface mu4e-moved-face - '((t :inherit font-lock-comment-face :slant italic)) - "Face for a message header that has been moved to some folder. -\(It's still visible in the search results, since we cannot -be sure it no longer matches)." - :group 'mu4e-faces) - -(defface mu4e-trashed-face - '((t :inherit font-lock-comment-face :strike-through t)) - "Face for an message header in the trash folder." - :group 'mu4e-faces) - -(defface mu4e-draft-face - '((t :inherit font-lock-string-face)) - "Face for a draft message header -I.e. a message with the draft flag set." - :group 'mu4e-faces) - -(defface mu4e-flagged-face - '((t :inherit font-lock-constant-face :bold t)) - "Face for a flagged message header." - :group 'mu4e-faces) - -(defface mu4e-replied-face - '((t :inherit font-lock-builtin-face :bold nil)) - "Face for a replied message header." - :group 'mu4e-faces) - -(defface mu4e-forwarded-face - '((t :inherit font-lock-builtin-face :bold nil)) - "Face for a passed (forwarded) message header." - :group 'mu4e-faces) - -(defface mu4e-header-face - '((t :inherit default)) - "Face for a header without any special flags." - :group 'mu4e-faces) - -(defface mu4e-header-title-face - '((t :inherit font-lock-type-face)) - "Face for a header title in the headers view." - :group 'mu4e-faces) - -(defface mu4e-header-highlight-face - '((t :inherit region :weight bold :underline t)) - "Face for the header at point." - :group 'mu4e-faces) - -(defface mu4e-header-marks-face - '((t :inherit font-lock-preprocessor-face)) - "Face for the mark in the headers list." - :group 'mu4e-faces) - -(defface mu4e-header-key-face - '((t :inherit message-header-name :bold t)) - "Face for a header key (such as \"Foo\" in \"Subject:\ Foo\")." - :group 'mu4e-faces) - -(defface mu4e-header-value-face - '((t :inherit font-lock-type-face)) - "Face for a header value (such as \"Re: Hello!\")." - :group 'mu4e-faces) - -(defface mu4e-special-header-value-face - '((t :inherit font-lock-builtin-face)) - "Face for special header values." - :group 'mu4e-faces) - -(defface mu4e-link-face - '((t :inherit link)) - "Face for showing URLs and attachments in the message view." - :group 'mu4e-faces) - -(defface mu4e-contact-face - '((t :inherit font-lock-variable-name-face)) - "Face for showing URLs and attachments in the message view." - :group 'mu4e-faces) - -(defface mu4e-highlight-face - '((t :inherit highlight)) - "Face for highlighting things." - :group 'mu4e-faces) - -(defface mu4e-title-face - '((t :inherit font-lock-type-face :bold t)) - "Face for a header title in the headers view." - :group 'mu4e-faces) - -(defface mu4e-modeline-face - '((t :inherit font-lock-string-face :bold t)) - "Face for the query in the mode-line." - :group 'mu4e-faces) - -(defface mu4e-view-body-face - '((t :inherit default)) - "Face for the body in the message-view." - :group 'mu4e-faces) - -(defface mu4e-footer-face - '((t :inherit font-lock-comment-face)) - "Face for message footers (signatures)." - :group 'mu4e-faces) - -(defface mu4e-url-number-face - '((t :inherit font-lock-constant-face :bold t)) - "Face for the number tags for URLs." - :group 'mu4e-faces) - -(defface mu4e-attach-number-face - '((t :inherit font-lock-variable-name-face :bold t)) - "Face for the number tags for attachments." - :group 'mu4e-faces) - -(defface mu4e-cited-1-face - '((t :inherit font-lock-builtin-face :bold nil :italic t)) - "Face for cited message parts (level 1)." - :group 'mu4e-faces) - -(defface mu4e-cited-2-face - '((t :inherit font-lock-preprocessor-face :bold nil :italic t)) - "Face for cited message parts (level 2)." - :group 'mu4e-faces) - -(defface mu4e-cited-3-face - '((t :inherit font-lock-variable-name-face :bold nil :italic t)) - "Face for cited message parts (level 3)." - :group 'mu4e-faces) - -(defface mu4e-cited-4-face - '((t :inherit font-lock-keyword-face :bold nil :italic t)) - "Face for cited message parts (level 4)." - :group 'mu4e-faces) - -(defface mu4e-cited-5-face - '((t :inherit font-lock-comment-face :bold nil :italic t)) - "Face for cited message parts (level 5)." - :group 'mu4e-faces) - -(defface mu4e-cited-6-face - '((t :inherit font-lock-comment-delimiter-face :bold nil :italic t)) - "Face for cited message parts (level 6)." - :group 'mu4e-faces) - -(defface mu4e-cited-7-face - '((t :inherit font-lock-type-face :bold nil :italic t)) - "Face for cited message parts (level 7)." - :group 'mu4e-faces) - -(defface mu4e-system-face - '((t :inherit font-lock-comment-face :slant italic)) - "Face for system message (such as the footers for message headers)." - :group 'mu4e-faces) - -(defface mu4e-ok-face - '((t :inherit font-lock-comment-face :bold t :slant normal)) - "Face for things that are okay." - :group 'mu4e-faces) - -(defface mu4e-warning-face - '((t :inherit font-lock-warning-face :bold t :slant normal)) - "Face for warnings / error." - :group 'mu4e-faces) - -(defface mu4e-compose-separator-face - '((t :inherit message-separator :slant italic)) - "Face for the separator between headers / message in -mu4e-compose-mode." - :group 'mu4e-faces) - -(defface mu4e-compose-header-face - '((t :inherit message-separator :slant italic)) - "Face for the separator between headers / message in -mu4e-compose-mode." - :group 'mu4e-faces) - -(defface mu4e-region-code - '((t (:background "DarkSlateGray"))) - "Face for highlighting marked region in mu4e-view buffer." - :group 'mu4e-faces) - -;; headers info -(defconst mu4e-header-info - '( (:attachments . - ( :name "Attachments" - :shortname "Atts" - :help "Message attachments" - :sortable nil)) - (:bcc . - ( :name "Bcc" - :shortname "Bcc" - :help "Blind Carbon-Copy recipients for the message" - :sortable t)) - (:cc . - ( :name "Cc" - :shortname "Cc" - :help "Carbon-Copy recipients for the message" - :sortable t)) - (:date . - ( :name "Date" - :shortname "Date" - :help "Date/time when the message was written" - :sortable t)) - (:human-date . - ( :name "Date" - :shortname "Date" - :help "Date/time when the message was written." - :sortable :date)) - (:flags . - ( :name "Flags" - :shortname "Flgs" - :help "Flags for the message" - :sortable nil)) - (:from . - ( :name "From" - :shortname "From" - :help "The sender of the message" - :sortable t)) - (:from-or-to . - ( :name "From/To" - :shortname "From/To" - :help "Sender of the message if it's not me; otherwise the recipient" - :sortable nil)) - (:maildir . - ( :name "Maildir" - :shortname "Maildir" - :help "Maildir for this message" - :sortable t)) - (:mailing-list . - ( :name "List" - :shortname "List" - :help "Mailing list for this message" - :sortable nil)) - (:message-id . - ( :name "Message-Id" - :shortname "MsgID" - :help "Message-Id for this message" - :sortable nil)) - (:path . - ( :name "Path" - :shortname "Path" - :help "Full filesystem path to the message" - :sortable t)) - (:signature . - ( :name "Signature" - :shortname "Sgn" - :help "Check for the cryptographic signature" - :sortable nil)) - (:decryption . - ( :name "Decryption" - :shortname "Dec" - :help "Check the cryptographic decryption status" - :sortable nil)) - (:size . - ( :name "Size" - :shortname "Size" - :help "Size of the message" - :sortable t)) - (:subject . - ( :name "Subject" - :shortname "Subject" - :help "Subject of the message" - :sortable t)) - (:tags . - ( :name "Tags" - :shortname "Tags" - :help "Tags for the message" - :sortable nil)) - (:thread-subject . - ( :name "Subject" - :shortname "Subject" - :help "Subject of the thread" - :sortable :subject)) - (:to . - ( :name "To" - :shortname "To" - :help "Recipient of the message" - :sortable t)) - (:user-agent . - ( :name "User-Agent" - :shortname "UA" - :help "Program used for writing this message" - :sortable t))) - "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 -the fields the message view). - -Most fields should be self-explanatory. A special one is -`:from-or-to', which is equal to `:from' unless `:from' matches one -of the addresses in `mu4e-user-mail-address-list', in which case it -will be equal to `:to'. - -Furthermore, the property `:sortable' determines whether we can -sort by this field. This can be either a boolean (nil or t), or a -symbol for /another/ field. For example, the `:human-date' field -uses `:date' for that. - -Note, `:sortable' does not work for custom header fields.") - - -(defvar mu4e-header-info-custom - '( (:recipnum . - ( :name "Number of recipients" - :shortname "Recip#" - :help "Number of recipients for this message" - :function - (lambda (msg) - (format "%d" - (+ (length (mu4e-message-field msg :to)) - (length (mu4e-message-field msg :cc)))))))) -"A list of custom (user-defined) headers. The format is similar -to `mu4e-header-info', but adds a :function property, which -should point to a function that takes a message p-list as -argument, and returns a string. See the default value of -`mu4e-header-info-custom for an example.") - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; run-time vars used in multiple places - -;; headers -(defconst mu4e~headers-buffer-name "*mu4e-headers*" - "Name of the buffer for message headers.") -(defvar mu4e~headers-buffer nil "Buffer for message headers.") -; view -(defconst mu4e~view-buffer-name "*mu4e-view*" - "Name for the message view buffer.") - -(defconst mu4e~view-embedded-buffer-name " *mu4e-embedded-view*" - "Name for the embedded message view buffer.") - -(defvar mu4e~view-buffer nil "The view buffer.") - -(defvar mu4e~view-msg nil "The message being viewed in view mode.") - -(defvar mu4e~view-headers-buffer nil - "The headers buffer connected to this view.") - -(defvar mu4e~contacts nil - "Hash of that maps contacts (ie. 'name <e-mail>') to an integer -with their sort order. We need to keep this information around to -quickly re-sort subsets of the contacts in the completions function in -mu4e-compose.") - -(defvar mu4e~server-props nil - "Properties we receive from the mu4e server process. -\(in the 'pong-handler').") - -(defvar mu4e~headers-last-query nil - "The present (most recent) query.") - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; our handlers funcs -;; these handler funcs define what happens when we receive a certain message -;; from the server -(defun mu4e~default-handler (&rest args) - "*internal* Dummy handler function." - (error "Not handled: %S" args)) - -(defvar mu4e-error-func 'mu4e~default-handler - "A function called for each error returned from the server -process; the function is passed an error plist as argument. See -`mu4e~proc-filter' for the format.") - -(defvar mu4e-update-func 'mu4e~default-handler - "A function called for each :update sexp returned from the server -process; the function is passed a msg sexp as argument. See -`mu4e~proc-filter' for the format.") - -(defvar mu4e-remove-func 'mu4e~default-handler - "A function called for each :remove sexp returned from the server -process, when some message has been deleted. The function is passed -the docid of the removed message.") - -(defvar mu4e-sent-func 'mu4e~default-handler - "A function called for each :sent sexp returned from the server -process, when some message has been sent. The function is passed -the docid and the draft-path of the sent message.") - -(defvar mu4e-view-func 'mu4e~default-handler - "A function called for each single message sexp returned from the -server process. The function is passed a message sexp as -argument. See `mu4e~proc-filter' for the format.") - -(defvar mu4e-header-func 'mu4e~default-handler - "A function called for each message returned from the server -process; the function is passed a msg plist as argument. See -`mu4e~proc-filter' for the format.") - -(defvar mu4e-found-func 'mu4e~default-handler - "A function called for when we received a :found sexp after the -headers have returns, to report on the number of matches. See -`mu4e~proc-filter' for the format.") - -(defvar mu4e-erase-func 'mu4e~default-handler - "A function called for when we received an :erase sexp after the -headers have returns, to clear the current headers buffer. See -`mu4e~proc-filter' for the format.") - -(defvar mu4e-compose-func 'mu4e~default-handler - "A function called for each message returned from the server -process that is used as basis for composing a new message (ie., -either a reply or a forward); the function is passed msg and a -symbol (either reply or forward). See `mu4e~proc-filter' for the -format of <msg-plist>.") - -(defvar mu4e-info-func 'mu4e~default-handler - "A function called for each (:info type ....) sexp received from -the server process.") - -(defvar mu4e-pong-func 'mu4e~default-handler - "A function called for each (:pong type ....) sexp received from -the server process.") - -(defvar mu4e-contacts-func 'mu4e~default-handler - "A function called for each (:contacts (<list-of-contacts>) sexp -received from the server process.") - -(defvar mu4e-temp-func 'mu4e~default-handler - "A function called for each (:temp <file> <cookie>) sexp received -from the server process.") -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(provide 'mu4e-vars) -;;; End of mu4e-vars.el diff --git a/_spacemacs.d/local/mu4e/mu4e-view.el b/_spacemacs.d/local/mu4e/mu4e-view.el deleted file mode 100644 index 564835d..0000000 --- a/_spacemacs.d/local/mu4e/mu4e-view.el +++ /dev/null @@ -1,1542 +0,0 @@ -;;; mu4e-view.el -- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; In this file we define mu4e-view-mode (+ helper functions), which is used for -;; viewing e-mail messages - -;;; Code: -(require 'mu4e-utils) ;; utility functions -(require 'mu4e-vars) -(require 'mu4e-mark) -(require 'mu4e-proc) -(require 'mu4e-compose) -(require 'mu4e-actions) -(require 'mu4e-message) - -(require 'comint) -(require 'browse-url) -(require 'button) -(require 'epa) -(require 'epg) -(require 'thingatpt) -(require 'calendar) - -(eval-when-compile (byte-compile-disable-warning 'cl-functions)) -(require 'cl) - - -;; the message view -(defgroup mu4e-view nil - "Settings for the message view." - :group 'mu4e) - -(defcustom mu4e-view-fields - '(:from :to :cc :subject :flags :date :maildir :mailing-list :tags - :attachments :signature :decryption) - "Header fields to display in the message view buffer. -For the complete list of available headers, see `mu4e-header-info'." - :type (list 'symbol) - :group 'mu4e-view) - - -(defcustom mu4e-view-show-addresses nil - "Whether to initially show full e-mail addresses for contacts in -address fields, rather than only their names." - :type 'boolean - :group 'mu4e-view) - -(make-obsolete-variable 'mu4e-view-wrap-lines nil "0.9.9-dev7") -(make-obsolete-variable 'mu4e-view-hide-cited nil "0.9.9-dev7") - -(defcustom mu4e-view-date-format "%c" - "Date format to use in the message view. -In the format of `format-time-string'." - :type 'string - :group 'mu4e-view) - -(defcustom mu4e-view-image-max-width 800 - "The maximum width for images to display. -This is only effective if you're using an emacs with Imagemagick -support, and `mu4e-view-show-images' is non-nil." - :group 'mu4e-view) - -(defcustom mu4e-view-image-max-height 600 - "The maximum height for images to display. -This is only effective if you're using an emacs with Imagemagick -support, and `mu4e-view-show-images' is non-nil." - :group 'mu4e-view) - -(defcustom mu4e-view-scroll-to-next t - "If non-nil, move to the next message when calling -`mu4e-view-scroll-up-or-next' (typically bound to SPC) when at the -end of a message. Otherwise, don't move to the next message.") - -(defcustom mu4e-save-multiple-attachments-without-asking nil - "If non-nil, saving multiple attachments asks once for a -directory and saves all attachments in the chosen directory." - :type 'boolean - :group 'mu4e-view) - -(defvar mu4e-view-actions - '( ("capture message" . mu4e-action-capture-message) - ("view as pdf" . mu4e-action-view-as-pdf) - ("show this thread" . mu4e-action-show-thread)) - "List of actions to perform on messages in view mode. -The actions are of the form: - (NAME FUNC) -where: -* NAME is the name of the action (e.g. \"Count lines\") -* FUNC is a function which receives a message plist as an argument. - -The first letter of NAME is used as a shortcut character.") - -(defvar mu4e-view-attachment-actions - '( ("wopen-with" . mu4e-view-open-attachment-with) - ("ein-emacs" . mu4e-view-open-attachment-emacs) - ("dimport-in-diary" . mu4e-view-import-attachment-diary) - ("|pipe" . mu4e-view-pipe-attachment)) - "List of actions to perform on message attachments. -The actions are cons-cells of the form: - (NAME . FUNC) -where: -* NAME is the name of the action (e.g. \"Count lines\") -* FUNC is a function which receives two arguments: the message - plist and the attachment number. -The first letter of NAME is used as a shortcut character.") - -(defvar mu4e-view-fill-headers t - "If non-nil, automatically fill the headers when viewing them.") - -(defvar mu4e-view-contacts-header-keymap - (let ((map (make-sparse-keymap))) - (define-key map [mouse-2] 'mu4e~view-compose-contact) - (define-key map "C" 'mu4e~view-compose-contact) - (define-key map "c" 'mu4e~view-copy-contact) - map) - "Keymap used for the contacts in the header fields.") - -(defvar mu4e-view-clickable-urls-keymap - (let ((map (make-sparse-keymap))) - (define-key map [mouse-1] 'mu4e~view-browse-url-from-binding) - (define-key map [?\M-\r] 'mu4e~view-browse-url-from-binding) - map) - "Keymap used for the urls inside the body.") - -(defvar mu4e-view-attachments-header-keymap - (let ((map (make-sparse-keymap))) - (define-key map [mouse-1] 'mu4e~view-open-attach-from-binding) - (define-key map [?\M-\r] 'mu4e~view-open-attach-from-binding) - (define-key map [mouse-2] 'mu4e~view-save-attach-from-binding) - (define-key map (kbd "<S-return>") 'mu4e~view-save-attach-from-binding) - map) - "Keymap used in the \"Attachements\" header field.") - -(defcustom mu4e-view-auto-mark-as-read t - "Automatically mark messages are 'read' when you read -them. This is typically the expected behavior, but can be turned -off, for example when using a read-only file-system." - :type 'boolean - :group 'mu4e-view) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defvar mu4e~view-cited-hidden nil "Whether cited lines are hidden.") -(defvar mu4e~view-link-map nil - "A map of some number->url so we can jump to url by number.") - -(defvar mu4e~path-parent-docid-map (make-hash-table :test 'equal) - "A map of msg paths --> parent-docids. -This is to determine what is the parent docid for embedded -message extracted at some path.") - -(defvar mu4e~view-attach-map nil - "A mapping of user-visible attachment number to the actual part index.") -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun mu4e-view-message-with-message-id (msgid) - "View message with message-id MSGID. This (re)creates a -headers-buffer with a search for MSGID, then open a view for that -message." - (mu4e-headers-search (concat "msgid:" msgid) nil nil t msgid t)) - -(define-obsolete-function-alias 'mu4e-view-message-with-msgid - 'mu4e-view-message-with-message-id "0.9.17") - -(defun mu4e~view-custom-field (msg field) - "Show some custom header field, or raise an error if it is not -found." - (let* ((item (or (assoc field mu4e-header-info-custom) - (mu4e-error "field %S not found" field))) - (func (or (plist-get (cdr-safe item) :function) - (mu4e-error "no :function defined for field %S %S" - field (cdr item))))) - (funcall func msg))) - - -(defun mu4e-view-message-text (msg) - "Return the message to display (as a string), based on the MSG plist." - (concat - (mapconcat - (lambda (field) - (let ((fieldval (mu4e-message-field msg field))) - (case field - (:subject (mu4e~view-construct-header field fieldval)) - (:path (mu4e~view-construct-header field fieldval)) - (:maildir (mu4e~view-construct-header field fieldval)) - (:user-agent (mu4e~view-construct-header field fieldval)) - ((:flags :tags) (mu4e~view-construct-flags-tags-header - field fieldval)) - - ;; contact fields - (:to (mu4e~view-construct-contacts-header msg field)) - (:from (mu4e~view-construct-contacts-header msg field)) - (:cc (mu4e~view-construct-contacts-header msg field)) - (:bcc (mu4e~view-construct-contacts-header msg field)) - - ;; if we (`user-mail-address' are the From, show To, otherwise, - ;; show From - (:from-or-to - (let* ((from (mu4e-message-field msg :from)) - (from (and from (cdar from)))) - (if (mu4e-user-mail-address-p from) - (mu4e~view-construct-contacts-header msg :to) - (mu4e~view-construct-contacts-header msg :from)))) - ;; date - (:date - (let ((datestr - (when fieldval (format-time-string mu4e-view-date-format - fieldval)))) - (if datestr (mu4e~view-construct-header field datestr) ""))) - ;; size - (:size - (mu4e~view-construct-header field (mu4e-display-size fieldval))) - (:mailing-list - (mu4e~view-construct-header field fieldval)) - (:message-id - (mu4e~view-construct-header field fieldval)) - ;; attachments - (:attachments (mu4e~view-construct-attachments-header msg)) - ;; pgp-signatures - (:signature (mu4e~view-construct-signature-header msg)) - ;; pgp-decryption - (:decryption (mu4e~view-construct-decryption-header msg)) - (t (mu4e~view-construct-header field - (mu4e~view-custom-field msg field)))))) - mu4e-view-fields "") - "\n" - (let ((body (mu4e-message-body-text msg))) - (when (fboundp 'add-face-text-property) - (add-face-text-property 0 (length body) 'mu4e-view-body-face t body)) - body))) - -(defun mu4e~view-embedded-winbuf () - "Get a buffer (shown in a window) for the embedded message." - (let* ((buf (get-buffer-create mu4e~view-embedded-buffer-name)) - (win (or (get-buffer-window buf) (split-window-vertically)))) - (select-window win) - (switch-to-buffer buf))) - -(defun mu4e~delete-all-overlays () - "`delete-all-overlays' with compatibility fallback." - (if (functionp 'delete-all-overlays) - (delete-all-overlays) - (remove-overlays))) - - -(defun mu4e-view (msg headersbuf) - "Display the message MSG in a new buffer, and keep in sync with HDRSBUF. -'In sync' here means that moving to the next/previous message in -the the message view affects HDRSBUF, as does marking etc. - -As a side-effect, a message that is being viewed loses its 'unread' -marking if it still had that." - (let* ((embedded ;; is it as an embedded msg (ie. message/rfc822 att)? - (when (gethash (mu4e-message-field msg :path) - mu4e~path-parent-docid-map) t)) - (buf - (if embedded - (mu4e~view-embedded-winbuf) - (get-buffer-create mu4e~view-buffer-name)))) - ;; note: mu4e~view-mark-as-read-maybe will pseudo-recursively call mu4e-view - ;; again by triggering mu4e~view again as it marks the message as read - (with-current-buffer buf - (switch-to-buffer buf) - (setq mu4e~view-msg msg) - (when (or embedded (not (mu4e~view-mark-as-read-maybe msg))) - (let ((inhibit-read-only t)) - (erase-buffer) - (mu4e~delete-all-overlays) - (insert (mu4e-view-message-text msg)) - (goto-char (point-min)) - (mu4e~fontify-cited) - (mu4e~fontify-signature) - (mu4e~view-make-urls-clickable) - (mu4e~view-show-images-maybe msg) - (setq - mu4e~view-buffer buf - mu4e~view-headers-buffer headersbuf) - (when embedded (local-set-key "q" 'kill-buffer-and-window)) - (mu4e-view-mode)))))) - -(defun mu4e~view-get-property-from-event (prop) - "Get the property PROP at point, or the location of the mouse. -The action is chosen based on the `last-command-event'. -Meant to be evoked from interactive commands." - (if (and (eventp last-command-event) - (mouse-event-p last-command-event)) - (let ((posn (event-end last-command-event))) - (when (numberp (posn-point posn)) - (get-text-property - (posn-point posn) - prop - (window-buffer (posn-window posn))) - )) - (get-text-property (point) prop))) - -(defun mu4e~view-construct-header (field val &optional dont-propertize-val) - "Return header field FIELD (as in `mu4e-header-info') with value -VAL if VAL is non-nil. If DONT-PROPERTIZE-VAL is non-nil, do not -add text-properties to VAL." - (let* ((info (cdr (assoc field - (append mu4e-header-info mu4e-header-info-custom)))) - (key (plist-get info :name)) - (help (plist-get info :help))) - (if (and val (> (length val) 0)) - (with-temp-buffer - (insert (propertize (concat key ":") - 'face 'mu4e-header-key-face - 'help-echo help) " " - (if dont-propertize-val - val - (propertize val 'face 'mu4e-header-value-face)) "\n") - (when mu4e-view-fill-headers - ;; temporarily set the fill column <margin> positions to the right, so - ;; we can indent the following lines correctly - (let* ((margin 1) - (fill-column (max (- fill-column margin) 0))) - (fill-region (point-min) (point-max)) - (goto-char (point-min)) - (while (and (zerop (forward-line 1)) (not (looking-at "^$"))) - (indent-to-column margin)))) - (buffer-string)) - ""))) - -(defun mu4e~view-compose-contact (&optional point) - "Compose a message for the address at point." - (interactive) - (unless (get-text-property (or point (point)) 'email) - (mu4e-error "No address at point")) - (mu4e~compose-mail (get-text-property (or point (point)) 'long))) - -(defun mu4e~view-copy-contact (&optional full) - "Compose a message for the address at (point)." - (interactive "P") - (let ((email (get-text-property (point) 'email)) - (long (get-text-property (point) 'long))) - (unless email (mu4e-error "No address at point")) - (kill-new (if full long email)) - (mu4e-message "Address copied."))) - -(defun mu4e~view-construct-contacts-header (msg field) - "Add a header for a contact field (ie., :to, :from, :cc, :bcc)." - (mu4e~view-construct-header field - (mapconcat - (lambda(c) - (let* ((name (when (car c) - (replace-regexp-in-string "[[:cntrl:]]" "" (car c)))) - (email (when (cdr c) - (replace-regexp-in-string "[[:cntrl:]]" "" (cdr c)))) - (short (or name email)) ;; name may be nil - (long (if name (format "%s <%s>" name email) email))) - (propertize - (if mu4e-view-show-addresses long short) - 'long long - 'short short - 'email email - 'keymap mu4e-view-contacts-header-keymap - 'face 'mu4e-contact-face - 'mouse-face 'highlight - 'help-echo (format "<%s>\n%s" email - "[mouse-2] or C to compose a mail for this recipient")))) - (mu4e-message-field msg field) ", ") t)) - - -(defun mu4e~view-construct-flags-tags-header (field val) - "Construct a Flags: header." - (mu4e~view-construct-header - field - (mapconcat - (lambda (flag) - (propertize - (if (symbolp flag) - (symbol-name flag) - flag) - 'face 'mu4e-special-header-value-face)) - val - (propertize ", " 'face 'mu4e-header-value-face)) t)) - -(defun mu4e~view-construct-signature-header (msg) - "Construct a Signature: header, if there are any signed parts." - (let* ((parts (mu4e-message-field msg :parts)) - (verdicts - (remove-if 'null - (mapcar (lambda (part) (mu4e-message-part-field part :signature)) - parts))) - (val (when verdicts - (mapconcat - (lambda (v) - (propertize (symbol-name v) - 'face (if (eq v 'verified) - 'mu4e-ok-face 'mu4e-warning-face))) - verdicts ", "))) - (btn (when val - (with-temp-buffer - (insert-text-button "Details" - 'action (lambda (b) - (mu4e-view-verify-msg-popup - (button-get b 'msg)))) - (buffer-string)))) - (val (when val (concat val " (" btn ")")))) - (mu4e~view-construct-header :signature val t))) - -(defun mu4e~view-construct-decryption-header (msg) - "Construct a Decryption: header, if there are any encrypted parts." - (let* ((parts (mu4e-message-field msg :parts)) - (verdicts - (remove-if 'null - (mapcar (lambda (part) - (mu4e-message-part-field part :decryption)) - parts))) - (succeeded (remove-if (lambda (v) (eq v 'failed)) verdicts)) - (failed (remove-if (lambda (v) (eq v 'succeeded)) verdicts)) - (succ (when succeeded - (propertize - (concat (number-to-string (length succeeded)) - " part(s) decrypted") - 'face 'mu4e-ok-face))) - (fail (when failed - (propertize - (concat (number-to-string (length failed)) - " part(s) failed") - 'face 'mu4e-warning-face))) - (val (concat succ fail))) - (mu4e~view-construct-header :decryption val t))) - -(defun mu4e~view-open-attach-from-binding () - "Open the attachement at point, or click location." - (interactive) - (let* (( msg (mu4e~view-get-property-from-event 'mu4e-msg)) - ( attnum (mu4e~view-get-property-from-event 'mu4e-attnum))) - (when (and msg attnum) - (mu4e-view-open-attachment msg attnum)))) - -(defun mu4e~view-save-attach-from-binding () - "Save the attachement at point, or click location." - (interactive) - (let* (( msg (mu4e~view-get-property-from-event 'mu4e-msg)) - ( attnum (mu4e~view-get-property-from-event 'mu4e-attnum))) - (when (and msg attnum) - (mu4e-view-save-attachment-single msg attnum)))) - -(defun mu4e~view-construct-attachments-header (msg) - "Display attachment information; the field looks like something like: - :parts ((:index 1 :name \"1.part\" :mime-type \"text/plain\" - :type (leaf) :attachment nil :size 228) - (:index 2 :name \"analysis.doc\" - :mime-type \"application/msword\" - :type (leaf attachment) :attachment nil :size 605196))" - (setq mu4e~view-attach-map ;; buffer local - (make-hash-table :size 64 :weakness nil)) - (let* ((id 0) - (attachments - ;; we only list parts that look like attachments, ie. that have a - ;; non-nil :attachment property; we record a mapping between - ;; user-visible numbers and the part indices - (remove-if-not - (lambda (part) - (let* ((mtype (or (mu4e-message-part-field part :mime-type) - "application/octet-stream")) - (attachtype (mu4e-message-part-field part :type)) - (isattach - (or ;; we consider parts marked either - ;; "attachment" or "inline" as attachment. - (member 'attachment attachtype) - ;; list inline parts as attachment (so they can be - ;; saved), unless they are text/plain, which are - ;; usually just message footers in mailing lists - (and (member 'inline attachtype) - (not (string-match "^text/plain" mtype)))))) - (or ;; remove if it's not an attach *or* if it's an - ;; image/audio/application type (but not a signature) - isattach - (string-match "^\\(image\\|audio\\)" mtype) - (string= "message/rfc822" mtype) - (string= "text/calendar" mtype) - (and (string-match "^application" mtype) - (not (string-match "signature" mtype)))))) - (mu4e-message-field msg :parts))) - (attstr - (mapconcat - (lambda (part) - (let ((index (mu4e-message-part-field part :index)) - (name (mu4e-message-part-field part :name)) - (size (mu4e-message-part-field part :size))) - (incf id) - (puthash id index mu4e~view-attach-map) - - (concat - (propertize (format "[%d]" id) - 'face 'mu4e-attach-number-face) - (propertize name 'face 'mu4e-link-face - 'keymap mu4e-view-attachments-header-keymap - 'mouse-face 'highlight - 'help-echo (concat - "[mouse-1] or [M-RET] opens the attachment\n" - "[mouse-2] or [S-RET] offers to save it") - 'mu4e-msg msg - 'mu4e-attnum id - ) - (when (and size (> size 0)) - (propertize (format "(%s)" (mu4e-display-size size)) - 'face 'mu4e-header-key-face))))) - attachments ", "))) - (when attachments - (mu4e~view-construct-header :attachments attstr t)))) - -(defun mu4e-view-for-each-part (msg func) - "Apply FUNC to each part in MSG. -FUNC should be a function taking two arguments: - 1. the message MSG, and - 2. a plist describing the attachment. The plist looks like: - (:index 1 :name \"test123.doc\" - :mime-type \"application/msword\" :attachment t :size 1234)." - (dolist (part (mu4e-msg-field msg :parts)) - (funcall func msg part))) - -(defvar mu4e-view-mode-map nil - "Keymap for \"*mu4e-view*\" buffers.") -(unless mu4e-view-mode-map - (setq mu4e-view-mode-map - (let ((map (make-sparse-keymap))) - - (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) - (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) - - (define-key map "q" 'mu4e~view-quit-buffer) - - ;; note, 'z' is by-default bound to 'bury-buffer' - ;; but that's not very useful in this case - (define-key map "z" 'ignore) - - (define-key map "s" 'mu4e-headers-search) - (define-key map "S" 'mu4e-view-search-edit) - (define-key map "/" 'mu4e-view-search-narrow) - - (define-key map (kbd "<M-left>") 'mu4e-headers-query-prev) - (define-key map (kbd "<M-right>") 'mu4e-headers-query-next) - - (define-key map "b" 'mu4e-headers-search-bookmark) - (define-key map "B" 'mu4e-headers-search-bookmark-edit) - - (define-key map "%" 'mu4e-view-mark-pattern) - (define-key map "t" 'mu4e-view-mark-subthread) - (define-key map "T" 'mu4e-view-mark-thread) - - (define-key map "v" 'mu4e-view-verify-msg-popup) - - (define-key map "j" 'mu4e~headers-jump-to-maildir) - - (define-key map "g" 'mu4e-view-go-to-url) - (define-key map "k" 'mu4e-view-save-url) - (define-key map "f" 'mu4e-view-fetch-url) - - (define-key map "F" 'mu4e-compose-forward) - (define-key map "R" 'mu4e-compose-reply) - (define-key map "C" 'mu4e-compose-new) - (define-key map "E" 'mu4e-compose-edit) - - (define-key map "." 'mu4e-view-raw-message) - (define-key map "|" 'mu4e-view-pipe) - (define-key map "a" 'mu4e-view-action) - - (define-key map ";" 'mu4e-context-switch) - - ;; toggle header settings - (define-key map "O" 'mu4e-headers-change-sorting) - (define-key map "P" 'mu4e-headers-toggle-threading) - (define-key map "Q" 'mu4e-headers-toggle-full-search) - (define-key map "W" 'mu4e-headers-toggle-include-related) - - ;; change the number of headers - (define-key map (kbd "C-+") 'mu4e-headers-split-view-grow) - (define-key map (kbd "C--") 'mu4e-headers-split-view-shrink) - (define-key map (kbd "<C-kp-add>") 'mu4e-headers-split-view-grow) - (define-key map (kbd "<C-kp-subtract>") 'mu4e-headers-split-view-shrink) - - ;; intra-message navigation - (define-key map (kbd "SPC") 'mu4e-view-scroll-up-or-next) - (define-key map (kbd "<home>") 'beginning-of-buffer) - (define-key map (kbd "<end>") 'end-of-buffer) - (define-key map (kbd "RET") 'mu4e-scroll-up) - (define-key map (kbd "<backspace>") 'mu4e-scroll-down) - - ;; navigation between messages - (define-key map "p" 'mu4e-view-headers-prev) - (define-key map "n" 'mu4e-view-headers-next) - ;; the same - (define-key map (kbd "<M-down>") 'mu4e-view-headers-next) - (define-key map (kbd "<M-up>") 'mu4e-view-headers-prev) - - (define-key map (kbd "[") 'mu4e-view-headers-prev-unread) - (define-key map (kbd "]") 'mu4e-view-headers-next-unread) - - ;; switching to view mode (if it's visible) - (define-key map "y" 'mu4e-select-other-view) - - ;; attachments - (define-key map "e" 'mu4e-view-save-attachment) - (define-key map "o" 'mu4e-view-open-attachment) - (define-key map "A" 'mu4e-view-attachment-action) - - ;; marking/unmarking - (define-key map "d" 'mu4e-view-mark-for-trash) - (define-key map (kbd "<delete>") 'mu4e-view-mark-for-delete) - (define-key map (kbd "<deletechar>") 'mu4e-view-mark-for-delete) - (define-key map (kbd "D") 'mu4e-view-mark-for-delete) - (define-key map (kbd "m") 'mu4e-view-mark-for-move) - (define-key map (kbd "r") 'mu4e-view-mark-for-refile) - - (define-key map (kbd "?") 'mu4e-view-mark-for-unread) - (define-key map (kbd "!") 'mu4e-view-mark-for-read) - - (define-key map (kbd "+") 'mu4e-view-mark-for-flag) - (define-key map (kbd "-") 'mu4e-view-mark-for-unflag) - (define-key map (kbd "=") 'mu4e-view-mark-for-untrash) - (define-key map (kbd "&") 'mu4e-view-mark-custom) - - (define-key map (kbd "*") 'mu4e-view-mark-for-something) - (define-key map (kbd "<kp-multiply>") 'mu4e-view-mark-for-something) - (define-key map (kbd "<insert>") 'mu4e-view-mark-for-something) - (define-key map (kbd "<insertchar>") 'mu4e-view-mark-for-something) - - (define-key map (kbd "#") 'mu4e-mark-resolve-deferred-marks) - - ;; misc - (define-key map "w" 'visual-line-mode) - (define-key map "#" 'mu4e-view-toggle-hide-cited) - (define-key map "h" 'mu4e-view-toggle-html) - (define-key map (kbd "M-q") 'mu4e-view-fill-long-lines) - - ;; next 3 only warn user when attempt in the message view - (define-key map "u" 'mu4e-view-unmark) - (define-key map "U" 'mu4e-view-unmark-all) - (define-key map "x" 'mu4e-view-marked-execute) - - (define-key map "$" 'mu4e-show-log) - (define-key map "H" 'mu4e-display-manual) - - ;; menu - (define-key map [menu-bar] (make-sparse-keymap)) - (let ((menumap (make-sparse-keymap "View"))) - (define-key map [menu-bar headers] (cons "View" menumap)) - - (define-key menumap [quit-buffer] - '("Quit view" . mu4e~view-quit-buffer)) - (define-key menumap [display-help] '("Help" . mu4e-display-manual)) - - (define-key menumap [sepa0] '("--")) - (define-key menumap [wrap-lines] - '("Toggle wrap lines" . visual-line-mode)) - (define-key menumap [toggle-html] - '("Toggle view-html" . mu4e-view-toggle-html)) - (define-key menumap [hide-cited] - '("Toggle hide cited" . mu4e-view-toggle-hide-cited)) - (define-key menumap [raw-view] - '("View raw message" . mu4e-view-raw-message)) - (define-key menumap [pipe] - '("Pipe through shell" . mu4e-view-pipe)) - ;; (define-key menumap [inspect] - ;; '("Inspect with guile" . mu4e-inspect-message)) - - (define-key menumap [sepa8] '("--")) - (define-key menumap [open-att] - '("Open attachment" . mu4e-view-open-attachment)) - (define-key menumap [extract-att] - '("Extract attachment" . mu4e-view-save-attachment)) - - (define-key menumap [save-url] - '("Save URL to kill-ring" . mu4e-view-save-url)) - (define-key menumap [fetch-url] - '("Fetch URL" . mu4e-view-fetch-url)) - (define-key menumap [goto-url] - '("Visit URL" . mu4e-view-go-to-url)) - - (define-key menumap [sepa1] '("--")) - (define-key menumap [mark-delete] - '("Mark for deletion" . mu4e-view-mark-for-delete)) - (define-key menumap [mark-trash] - '("Mark for trash" . mu4e-view-mark-for-trash)) - (define-key menumap [mark-move] - '("Mark for move" . mu4e-view-mark-for-move)) - - (define-key menumap [sepa2] '("--")) - (define-key menumap [resend] '("Resend" . mu4e-compose-resend)) - (define-key menumap [forward] '("Forward" . mu4e-compose-forward)) - (define-key menumap [reply] '("Reply" . mu4e-compose-reply)) - (define-key menumap [compose-new] '("Compose new" . mu4e-compose-new)) - (define-key menumap [sepa3] '("--")) - - (define-key menumap [query-next] - '("Next query" . mu4e-headers-query-next)) - (define-key menumap [query-prev] - '("Previous query" . mu4e-headers-query-prev)) - (define-key menumap [narrow-search] - '("Narrow search" . mu4e-headers-search-narrow)) - (define-key menumap [bookmark] - '("Search bookmark" . mu4e-headers-search-bookmark)) - (define-key menumap [jump] - '("Jump to maildir" . mu4e~headers-jump-to-maildir)) - (define-key menumap [search] - '("Search" . mu4e-headers-search)) - - (define-key menumap [sepa4] '("--")) - (define-key menumap [next] '("Next" . mu4e-view-headers-next)) - (define-key menumap [previous] '("Previous" . mu4e-view-headers-prev))) - map))) - -(fset 'mu4e-view-mode-map mu4e-view-mode-map) - -(defcustom mu4e-view-mode-hook nil - "Hook run when entering Mu4e-View mode." - :options '(turn-on-visual-line-mode) - :type 'hook - :group 'mu4e-view) - -(defvar mu4e-view-mode-abbrev-table nil) -(define-derived-mode mu4e-view-mode special-mode "mu4e:view" - "Major mode for viewing an e-mail message in mu4e. -\\{mu4e-view-mode-map}." - (use-local-map mu4e-view-mode-map) - - (make-local-variable 'mu4e~view-headers-buffer) - (make-local-variable 'mu4e~view-msg) - (make-local-variable 'mu4e~view-link-map) - (make-local-variable 'mu4e~view-attach-map) - (make-local-variable 'mu4e~view-cited-hidden) - - ;; show context in mode-string - (set (make-local-variable 'global-mode-string) '(:eval (mu4e-context-label))) - - (setq buffer-undo-list t);; don't record undo info - - ;; autopair mode gives error when pressing RET - ;; turn it off - (when (boundp 'autopair-dont-activate) - (setq autopair-dont-activate t))) - -(defun mu4e~view-mark-as-read-maybe (msg) - "Clear the message MSG New/Unread status and set it to Seen. -If the message is not New/Unread, do nothing. Evaluates to t if it -triggers any changes, nil otherwise. If this function does any -changes, it triggers a refresh." - (when (and mu4e-view-auto-mark-as-read msg) - (let ((flags (mu4e-message-field msg :flags)) - (msgid (mu4e-message-field msg :message-id)) - (docid (mu4e-message-field msg :docid))) - ;; attached (embedded) messages don't have docids; leave them alone if it is a new message - (when (and docid (or (member 'unread flags) (member 'new flags))) - ;; mark /all/ messages with this message-id as read, so all copies of - ;; this message will be marked as read. - (mu4e~proc-move msgid nil "+S-u-N") - t)))) - -(defun mu4e~view-browse-url-func (url) - "Return a function that executes `browse-url' with URL. -What browser is called is depending on -`browse-url-browser-function' and `browse-url-mailto-function'." - (save-match-data - (if (string-match "^mailto:" url) - (lexical-let ((url url)) - (lambda () - (interactive) - (mu4e~compose-browse-url-mail url))) - (lexical-let ((url url)) - (lambda () - (interactive) - (browse-url url)))))) - -(defun mu4e~view-browse-url-from-binding (&optional url) - "View in browser the url at point, or click location. -If the optional argument URL is provided, browse that instead. -If the url is mailto link, start writing an email to that address." - (interactive) - (let* (( url (or url (mu4e~view-get-property-from-event 'mu4e-url)))) - (when url - (if (string-match-p "^mailto:" url) - (mu4e~compose-browse-url-mail url) - (browse-url url))))) - -(defun mu4e~view-show-images-maybe (msg) - "Show attached images, if `mu4e-show-images' is non-nil." - (when (and (display-images-p) mu4e-view-show-images) - (mu4e-view-for-each-part msg - (lambda (msg part) - (when (string-match "^image/" - (or (mu4e-message-part-field part :mime-type) - "application/object-stream")) - (let ((imgfile (mu4e-message-part-field part :temp))) - (when (and imgfile (file-exists-p imgfile)) - (save-excursion - (goto-char (point-max)) - (mu4e-display-image imgfile - mu4e-view-image-max-width - mu4e-view-image-max-height))))))))) - - -(defvar mu4e~view-beginning-of-url-regexp - "https?\\://\\|mailto:" - "Regexp that matches the beginning of http:/https:/mailto: -URLs; match-string 1 will contain the matched URL, if any.") - -;; this is fairly simplistic... -(defun mu4e~view-make-urls-clickable () - "Turn things that look like URLs into clickable things. -Also number them so they can be opened using `mu4e-view-go-to-url'." - (let ((num 0)) - (save-excursion - (setq mu4e~view-link-map ;; buffer local - (make-hash-table :size 32 :weakness nil)) - (goto-char (point-min)) - (while (re-search-forward mu4e~view-beginning-of-url-regexp nil t) - (let ((bounds (thing-at-point-bounds-of-url-at-point))) - (when bounds - (let* ((url (thing-at-point-url-at-point)) - (ov (make-overlay (car bounds) (cdr bounds)))) - (puthash (incf num) url mu4e~view-link-map) - (add-text-properties - (car bounds) - (cdr bounds) - `(face mu4e-link-face - mouse-face highlight - mu4e-url ,url - keymap ,mu4e-view-clickable-urls-keymap - help-echo - "[mouse-1] or [M-RET] to open the link")) - (overlay-put ov 'after-string - (propertize (format "[%d]" num) - 'face 'mu4e-url-number-face))))))))) - - -(defun mu4e~view-hide-cited () - "Toggle hiding of cited lines in the message body." - (save-excursion - (let ((inhibit-read-only t)) - (goto-char (point-min)) - (flush-lines mu4e-cited-regexp) - (setq mu4e~view-cited-hidden t)))) - -(defmacro mu4e~view-in-headers-context (&rest body) - "Evaluate BODY in the context of the headers buffer connected to -this view." - `(progn - (unless (buffer-live-p mu4e~view-headers-buffer) - (mu4e-error "no headers-buffer connected")) - (let* ((msg (mu4e-message-at-point)) - (docid (mu4e-message-field msg :docid)) - (curwin (selected-window))) - (unless docid - (mu4e-error "message without docid: action is not possible.")) - (with-current-buffer mu4e~view-headers-buffer - (when (get-buffer-window) - (select-window (get-buffer-window))) - (if (mu4e~headers-goto-docid docid) - ,@body - (mu4e-error "cannot find message in headers buffer.")))))) - -(defun mu4e-view-headers-next (&optional n) - "Move point to the next message header in the headers buffer -connected with this message view. If this succeeds, return the new -docid. Otherwise, return nil. Optionally, takes an integer -N (prefix argument), to the Nth next header." - (interactive "P") - (mu4e~view-in-headers-context - (mu4e~headers-move (or n 1)))) - -(defun mu4e-view-headers-prev (&optional n) - "Move point to the previous message header in the headers buffer -connected with this message view. If this succeeds, return the new -docid. Otherwise, return nil. Optionally, takes an integer -N (prefix argument), to the Nth previous header." - (interactive "P") - (mu4e~view-in-headers-context - (mu4e~headers-move (- (or n 1))))) - -(defun mu4e~view-prev-or-next-unread (backwards) - "Move point to the next or previous (when BACKWARDS is non-`nil') -unread message header in the headers buffer connected with this -message view. If this succeeds, return the new docid. Otherwise, -return nil." - (mu4e~view-in-headers-context - (mu4e~headers-prev-or-next-unread backwards)) - (mu4e-select-other-view) - (mu4e-headers-view-message)) - -(defun mu4e-view-headers-prev-unread () -"Move point to the previous unread message header in the headers -buffer connected with this message view. If this succeeds, return -the new docid. Otherwise, return nil." - (interactive) - (mu4e~view-prev-or-next-unread t)) - -(defun mu4e-view-headers-next-unread () - "Move point to the next unread message header in the headers -buffer connected with this message view. If this succeeds, return -the new docid. Otherwise, return nil." - (interactive) - (mu4e~view-prev-or-next-unread nil)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Interactive functions - -(defun mu4e-view-toggle-hide-cited () - "Toggle hiding of cited lines in the message body." - (interactive) - (if mu4e~view-cited-hidden - (mu4e-view-refresh) - (mu4e~view-hide-cited))) - -(defun mu4e-view-toggle-html () - "Toggle html-display of the message body (if any)." - (interactive) - (setq mu4e-view-prefer-html (not mu4e-view-prefer-html)) - (mu4e-view-refresh)) - -(defun mu4e-view-refresh () - "Redisplay the current message." - (interactive) - (mu4e-view mu4e~view-msg mu4e~view-headers-buffer) - (setq mu4e~view-cited-hidden nil)) - -(defun mu4e-view-action (&optional msg) - "Ask user for some action to apply on MSG, then do it. -If MSG is nil apply action to message returned -bymessage-at-point. The actions are specified in -`mu4e-view-actions'." - (interactive) - (let* ((msg (or msg (mu4e-message-at-point))) - (actionfunc (mu4e-read-option "Action: " mu4e-view-actions))) - (funcall actionfunc msg))) - -(defun mu4e-view-mark-pattern () - "Ask user for a kind of mark (move, delete etc.), a field to -match and a regular expression to match with. Then, mark all -matching messages with that mark." - (interactive) - (mu4e~view-in-headers-context (mu4e-headers-mark-pattern))) - -(defun mu4e-view-mark-thread (&optional markpair) - "Ask user for a kind of mark (move, delete etc.), and apply it -to all messages in the thread at point in the headers view. The -optional MARKPAIR can also be used to provide the mark -selection." - (interactive) - (mu4e~view-in-headers-context - (if markpair (mu4e-headers-mark-thread nil markpair) - (call-interactively 'mu4e-headers-mark-thread)))) - -(defun mu4e-view-mark-subthread (&optional markpair) - "Ask user for a kind of mark (move, delete etc.), and apply it -to all messages in the subthread at point in the headers view. -The optional MARKPAIR can also be used to provide the mark -selection." - (interactive) - (mu4e~view-in-headers-context - (if markpair (mu4e-headers-mark-subthread markpair) - (mu4e-headers-mark-subthread)))) - -(defun mu4e-view-search-narrow () - "Run `mu4e-headers-search-narrow' in the headers buffer." - (interactive) - (mu4e~view-in-headers-context - (call-interactively 'mu4e-headers-search-narrow))) - -(defun mu4e-view-search-edit () - "Run `mu4e-headers-search-edit' in the headers buffer." - (interactive) - (mu4e~view-in-headers-context (mu4e-headers-search-edit))) - -(defun mu4e-mark-region-code () - "Highlight region marked with `message-mark-inserted-region'. -Add this function to `mu4e-view-mode-hook' to enable this feature." - (require 'message) - (let (beg end ov-beg ov-end ov-inv) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward - (concat "^" message-mark-insert-begin) nil t) - (setq ov-beg (match-beginning 0) - ov-end (match-end 0) - ov-inv (make-overlay ov-beg ov-end) - beg ov-end) - (overlay-put ov-inv 'invisible t) - (when (re-search-forward - (concat "^" message-mark-insert-end) nil t) - (setq ov-beg (match-beginning 0) - ov-end (match-end 0) - ov-inv (make-overlay ov-beg ov-end) - end ov-beg) - (overlay-put ov-inv 'invisible t)) - (when (and beg end) - (let ((ov (make-overlay beg end))) - (overlay-put ov 'face 'mu4e-region-code)) - (setq beg nil end nil)))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Wash functions -(defun mu4e-view-fill-long-lines () - "Fill lines that are wider than the window width or `fill-column'." - (interactive) - (with-current-buffer mu4e~view-buffer - (save-excursion - (let ((inhibit-read-only t) - (width (window-width (get-buffer-window (current-buffer))))) - (save-restriction - (message-goto-body) - (while (not (eobp)) - (end-of-line) - (when (>= (current-column) (min fill-column width)) - (narrow-to-region (min (1+ (point)) (point-max)) - (point-at-bol)) - (let ((goback (point-marker))) - (fill-paragraph nil) - (goto-char (marker-position goback))) - (widen)) - (forward-line 1))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; attachment handling -(defun mu4e~view-get-attach-num (prompt msg &optional multi) - "Ask the user with PROMPT for an attachment number for MSG, and -ensure it is valid. The number is [1..n] for attachments -\[0..(n-1)] in the message. If MULTI is nil, return the number for -the attachment; otherwise (MULTI is non-nil), accept ranges of -attachment numbers, as per `mu4e-split-ranges-to-numbers', and -return the corresponding string." - (let* ((count (hash-table-count mu4e~view-attach-map)) (def)) - (when (zerop count) (mu4e-error "No attachments for this message")) - (if (not multi) - (if (= count 1) - (read-number (mu4e-format "%s: " prompt) 1) - (read-number (mu4e-format "%s (1-%d): " prompt count))) - (progn - (setq def (if (= count 1) "1" (format "1-%d" count))) - (read-string (mu4e-format "%s (default %s): " prompt def) - nil nil def))))) - -(defun mu4e~view-get-attach (msg attnum) - "Return the attachment plist in MSG corresponding to attachment -number ATTNUM." - (let* ((partid (gethash attnum mu4e~view-attach-map)) - (attach - (find-if - (lambda (part) - (eq (mu4e-message-part-field part :index) partid)) - (mu4e-message-field msg :parts)))) - (or attach (mu4e-error "Not a valid attachment")))) - - -(defun mu4e~view-request-attachment-path (fname path) - "Ask the user where to save FNAME (default is PATH/FNAME)." - (let ((fpath (expand-file-name - (read-file-name - (mu4e-format "Save as ") - path nil nil fname) path))) - (if (file-directory-p fpath) - (expand-file-name fname fpath) - fpath))) - -(defun mu4e~view-request-attachments-dir (path) - "Ask the user where to save multiple attachments (default is PATH)." - (let ((fpath (expand-file-name - (read-directory-name - (mu4e-format "Save in directory ") - path nil nil nil) path))) - (if (file-directory-p fpath) - fpath))) - -(defun mu4e-view-save-attachment-single (&optional msg attnum) - "Save attachment number ATTNUM from MSG. -If MSG is nil use the message returned by `message-at-point'. -If ATTNUM is nil ask for the attachment number." - (interactive) - (let* ((msg (or msg (mu4e-message-at-point))) - (attnum (or attnum - (mu4e~view-get-attach-num "Attachment to save" msg))) - (att (mu4e~view-get-attach msg attnum)) - (fname (plist-get att :name)) - (mtype (plist-get att :mime-type)) - (path (concat - (mu4e~get-attachment-dir fname mtype) "/")) - (index (plist-get att :index)) - (retry t) (fpath)) - (while retry - (setq fpath (mu4e~view-request-attachment-path fname path)) - (setq retry - (and (file-exists-p fpath) - (not (y-or-n-p (mu4e-format "Overwrite '%s'?" fpath)))))) - (mu4e~proc-extract - 'save (mu4e-message-field msg :docid) - index mu4e-decryption-policy fpath))) - - -(defun mu4e-view-save-attachment-multi (&optional msg) - "Offer to save multiple email attachments from the current message. -Default is to save all messages, [1..n], where n is the number of -attachments. You can type multiple values separated by space, e.g. - 1 3-6 8 -will save attachments 1,3,4,5,6 and 8. - -Furthermore, there is a shortcut \"a\" which so means all -attachments, but as this is the default, you may not need it." - (interactive) - (let* ((msg (or msg (mu4e-message-at-point))) - (attachstr (mu4e~view-get-attach-num - "Attachment number range (or 'a' for 'all')" msg t)) - (count (hash-table-count mu4e~view-attach-map)) - (attachnums (mu4e-split-ranges-to-numbers attachstr count))) - (if mu4e-save-multiple-attachments-without-asking - (let* ((path (concat (mu4e~get-attachment-dir) "/")) - (attachdir (mu4e~view-request-attachments-dir path))) - (dolist (num attachnums) - (let* ((att (mu4e~view-get-attach msg num)) - (fname (plist-get att :name)) - (index (plist-get att :index)) - (retry t) - fpath) - (while retry - (setq fpath (expand-file-name (concat attachdir fname) path)) - (setq retry - (and (file-exists-p fpath) - (not (y-or-n-p - (mu4e-format "Overwrite '%s'?" fpath)))))) - (mu4e~proc-extract - 'save (mu4e-message-field msg :docid) - index mu4e-decryption-policy fpath)))) - (dolist (num attachnums) - (mu4e-view-save-attachment-single msg num))))) - -(defun mu4e-view-save-attachment (&optional multi) - "Offer to save attachment(s). -If MULTI (prefix-argument) is nil, save a single one, otherwise, -offer to save a range of attachments." - (interactive "P") - (if multi - (mu4e-view-save-attachment-multi) - (mu4e-view-save-attachment-single))) - -(defun mu4e-view-open-attachment (&optional msg attnum) - "Open attachment number ATTNUM from MSG. -If MSG is nil use the message returned by `message-at-point'. -If ATTNUM is nil ask for the attachment number." - (interactive) - (let* ((msg (or msg (mu4e-message-at-point))) - (attnum (or attnum - (mu4e~view-get-attach-num "Attachment to open" msg))) - (att (or (mu4e~view-get-attach msg attnum))) - (index (plist-get att :index)) - (docid (mu4e-message-field msg :docid)) - (mimetype (plist-get att :mime-type))) - (if (and mimetype (string= mimetype "message/rfc822")) - ;; special handling for message-attachments; we open them in mu4e. we also - ;; send the docid as parameter (4th arg); we'll get this back from the - ;; server, and use it to determine the parent message (ie., the current - ;; message) when showing the embedded message/rfc822, and return to the - ;; current message when quitting that one. - (mu4e~view-temp-action docid index "mu4e" docid) - ;; otherwise, open with the default program (handled in mu-server - (mu4e~proc-extract 'open docid index mu4e-decryption-policy)))) - - -(defun mu4e~view-temp-action (docid index what &optional param) - "Open attachment INDEX for message with DOCID, and invoke ACTION." - (interactive) - (mu4e~proc-extract 'temp docid index mu4e-decryption-policy nil what param )) - -(defvar mu4e~view-open-with-hist nil "History list for the open-with argument.") - -(defun mu4e-view-open-attachment-with (msg attachnum &optional cmd) - "Open MSG's attachment ATTACHNUM with CMD. -If CMD is nil, ask user for it." - (interactive) - (let* ((att (mu4e~view-get-attach msg attachnum)) - (cmd (or cmd - (read-string - (mu4e-format "Shell command to open it with: ") - nil 'mu4e~view-open-with-hist))) - (index (plist-get att :index))) - (mu4e~view-temp-action - (mu4e-message-field msg :docid) index "open-with" cmd))) - -(defvar mu4e~view-pipe-hist nil - "History list for the pipe argument.") - -(defun mu4e-view-pipe-attachment (msg attachnum &optional pipecmd) - "Feed MSG's attachment ATTACHNUM through pipe PIPECMD. -If PIPECMD is nil, ask user for it." - (interactive) - (let* ((att (mu4e~view-get-attach msg attachnum)) - (pipecmd (or pipecmd - (read-string - (mu4e-format "Pipe: ") - nil - 'mu4e~view-pipe-hist))) - (index (plist-get att :index))) - (mu4e~view-temp-action - (mu4e-message-field msg :docid) index "pipe" pipecmd))) - - -(defun mu4e-view-open-attachment-emacs (msg attachnum) - "Open MSG's attachment ATTACHNUM in the current emacs instance." - (interactive) - (let* ((att (mu4e~view-get-attach msg attachnum)) - (index (plist-get att :index))) - (mu4e~view-temp-action (mu4e-message-field msg :docid) index "emacs"))) - -(defun mu4e-view-import-attachment-diary (msg attachnum) - "Open MSG's attachment ATTACHNUM in the current emacs instance." - (interactive) - (let* ((att (mu4e~view-get-attach msg attachnum)) - (index (plist-get att :index))) - (mu4e~view-temp-action (mu4e-message-field msg :docid) index "diary"))) - -(defun mu4e-view-attachment-action (&optional msg) - "Ask user what to do with attachments in MSG -If MSG is nil use the message returned by `message-at-point'. -The actions are specified in `mu4e-view-attachment-actions'." - (interactive) - (let* ((msg (or msg (mu4e-message-at-point))) - (actionfunc (mu4e-read-option - "Action on attachment: " - mu4e-view-attachment-actions)) - (attnum (mu4e~view-get-attach-num "Which attachment" msg))) - (when (and actionfunc attnum) - (funcall actionfunc msg attnum)))) - -;; handler-function to handle the response we get from the server when we -;; want to do something with one of the attachments. -(defun mu4e~view-temp-handler (path what docid param) - "Handler function for doing things with temp files (ie., -attachments) in response to a (mu4e~proc-extract 'temp ... )." - (cond - ((string= what "open-with") - ;; 'param' will be the program to open-with - (start-process "*mu4e-open-with-proc*" "*mu4e-open-with*" param path)) - ((string= what "pipe") - ;; 'param' will be the pipe command, path the infile for this - (mu4e-process-file-through-pipe path param)) - ;; if it's mu4e, it's some embedded message; 'param' may contain the docid - ;; of the parent message. - ((string= what "mu4e") - ;; remember the mapping path->docid, which maps the path of the embedded - ;; message to the docid of its parent - (puthash path docid mu4e~path-parent-docid-map) - (mu4e~proc-view-path path mu4e-view-show-images mu4e-decryption-policy)) - ((string= what "emacs") - (find-file path) - ;; make the buffer read-only since it usually does not make - ;; sense to edit the temp buffer; use C-x C-q if you insist... - (setq buffer-read-only t)) - ((string= what "diary") - (icalendar-import-file path diary-file)) - (t (mu4e-error "Unsupported action %S" what)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun mu4e-view-mark-custom () - "Run some custom mark function." - (mu4e~view-in-headers-context - (mu4e-headers-mark-custom))) - -(defun mu4e~view-split-view-p () - "Return t if we're in split-view, nil otherwise." - (member mu4e-split-view '(horizontal vertical))) - -(defun mu4e-view-scroll-up-or-next () - "Scroll-up the current message. -If `mu4e-view-scroll-to-next' is non-nil, and we can't scroll-up -anymore, go the next message." - (interactive) - (condition-case nil - (scroll-up) - (error - (when mu4e-view-scroll-to-next - (mu4e-view-headers-next))))) - -(defun mu4e-scroll-up () - "Scroll text of selected window up one line." - (interactive) - (scroll-up 1)) - -(defun mu4e-scroll-down () - "Scroll text of selected window down one line." - (interactive) - (scroll-down 1)) - -(defun mu4e-view-unmark-all () - "If we're in split-view, unmark all messages. -Otherwise, warn user that unmarking only works in the header -list." - (interactive) - (if (mu4e~view-split-view-p) - (mu4e~view-in-headers-context (mu4e-mark-unmark-all)) - (mu4e-message "Unmarking needs to be done in the header list view"))) - -(defun mu4e-view-unmark () - "If we're in split-view, unmark message at point. -Otherwise, warn user that unmarking only works in the header -list." - (interactive) - (if (mu4e~view-split-view-p) - (mu4e-view-mark-for-unmark) - (mu4e-message "Unmarking needs to be done in the header list view"))) - - -(defmacro mu4e~view-defun-mark-for (mark) - "Define a function mu4e-view-mark-for-MARK." - (let ((funcname (intern (format "mu4e-view-mark-for-%s" mark))) - (docstring (format "Mark the current message for %s." mark))) - `(progn - (defun ,funcname () ,docstring - (interactive) - (mu4e~view-in-headers-context - (mu4e-headers-mark-and-next ',mark))) - (put ',funcname 'definition-name ',mark)))) - -(mu4e~view-defun-mark-for move) -(mu4e~view-defun-mark-for trash) -(mu4e~view-defun-mark-for refile) -(mu4e~view-defun-mark-for delete) -(mu4e~view-defun-mark-for flag) -(mu4e~view-defun-mark-for unflag) -(mu4e~view-defun-mark-for unmark) -(mu4e~view-defun-mark-for something) -(mu4e~view-defun-mark-for read) -(mu4e~view-defun-mark-for unread) - -(defun mu4e-view-marked-execute () - "Execute the marks." - (interactive) - (mu4e~view-in-headers-context - (mu4e-mark-execute-all))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; URL handling -(defun mu4e~view-get-urls-num (prompt &optional multi) - "Ask the user with PROMPT for an URL number for MSG, and ensure -it is valid. The number is [1..n] for URLs \[0..(n-1)] in the -message. If MULTI is nil, return the number for the URL; -otherwise (MULTI is non-nil), accept ranges of URL numbers, as -per `mu4e-split-ranges-to-numbers', and return the corresponding -string." - (let* ((count (hash-table-count mu4e~view-link-map)) (def)) - (when (zerop count) (mu4e-error "No links for this message")) - (if (not multi) - (if (= count 1) - (read-number (mu4e-format "%s: " prompt) 1) - (read-number (mu4e-format "%s (1-%d): " prompt count))) - (progn - (setq def (if (= count 1) "1" (format "1-%d" count))) - (read-string (mu4e-format "%s (default %s): " prompt def) - nil nil def))))) - -(defun mu4e-view-go-to-url (&optional multi) - "Offer to go to url(s). If MULTI (prefix-argument) is nil, go to -a single one, otherwise, offer to go to a range of urls." - (interactive "P") - (mu4e~view-handle-urls "URL to visit" - multi (lambda (url) (mu4e~view-browse-url-from-binding url)))) - -(defun mu4e-view-save-url (&optional multi) - "Offer to save urls(s) to the kill-ring. If -MULTI (prefix-argument) is nil, save a single one, otherwise, offer -to save a range of URLs." - (interactive "P") - (mu4e~view-handle-urls "URL to save" multi - (lambda (url) - (kill-new url) - (mu4e-message "Saved %s to the kill-ring" url)))) - -(defun mu4e-view-fetch-url (&optional multi) - "Offer to fetch (download) urls(s). If MULTI (prefix-argument) is nil, -download a single one, otherwise, offer to fetch a range of -URLs. The urls are fetched to `mu4e-attachment-dir'." - (interactive "P") - (mu4e~view-handle-urls "URL to fetch" multi - (lambda (url) - (let ((target (concat (mu4e~get-attachment-dir url) "/" - (file-name-nondirectory url)))) - (url-copy-file url target) - (mu4e-message "Fetched %s -> %s" url target))))) - -(defun mu4e~view-handle-urls (prompt multi urlfunc) - "If MULTI is nil, apply URLFUNC to a single uri, otherwise, apply -it to a range of uris. PROMPT is the query to present to the user." - (interactive "P") - (if multi - (mu4e~view-handle-multi-urls prompt urlfunc) - (mu4e~view-handle-single-url prompt urlfunc))) - -(defun mu4e~view-handle-single-url (prompt urlfunc &optional num) - "Apply URLFUNC to url NUM in the current message, prompting the -user with PROMPT." - (interactive) - (let* ((num (or num (mu4e~view-get-urls-num prompt))) - (url (gethash num mu4e~view-link-map))) - (unless url (mu4e-warn "Invalid number for URL")) - (funcall urlfunc url))) - -(defun mu4e~view-handle-multi-urls (prompt urlfunc) - "Apply URLFUNC to a a range of urls in the current message, -prompting the user with PROMPT. - -Default is to aplly it to all URLs, [1..n], where n is the number -of urls. You can type multiple values separated by space, e.g. 1 -3-6 8 will visit urls 1,3,4,5,6 and 8. - -Furthermore, there is a shortcut \"a\" which means all urls, but as -this is the default, you may not need it." - (interactive) - (let* ((linkstr (mu4e~view-get-urls-num - "URL number range (or 'a' for 'all')" t)) - (count (hash-table-count mu4e~view-link-map)) - (linknums (mu4e-split-ranges-to-numbers linkstr count))) - (dolist (num linknums) - (mu4e~view-handle-single-url prompt urlfunc num)))) - -(defun mu4e-view-for-each-uri (func) - "Execute FUNC (which receives a uri) for each uri in the current - message." - (maphash (lambda (num uri) (funcall func uri)) mu4e~view-link-map)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defconst mu4e~view-raw-buffer-name " *mu4e-raw-view*" - "*internal* Name for the raw message view buffer") - -(defun mu4e-view-raw-message () - "Display the raw contents of message at point in a new buffer." - (interactive) - (let ((path (mu4e-message-field-at-point :path)) - (buf (get-buffer-create mu4e~view-raw-buffer-name))) - (unless (and path (file-readable-p path)) - (mu4e-error "Not a readable file: %S" path)) - (with-current-buffer buf - (let ((inhibit-read-only t)) - (erase-buffer) - (insert-file-contents path) - (view-mode) - (goto-char (point-min)))) - (switch-to-buffer buf))) - -(defun mu4e-view-pipe (cmd) - "Pipe the message at point through shell command CMD, and display -the results." - (interactive "sShell command: ") - (let ((path (mu4e-message-field (mu4e-message-at-point) :path))) - (mu4e-process-file-through-pipe path cmd))) - -(defconst mu4e~verify-buffer-name " *mu4e-verify*") - -(defun mu4e-view-verify-msg-popup (&optional msg) - "Pop-up a little signature verification window for (optional) MSG -or message-at-point." - (interactive) - (let* ((msg (or msg (mu4e-message-at-point))) - (path (mu4e-message-field msg :path)) - (cmd (format "%s verify --verbose %s %s" - mu4e-mu-binary - (shell-quote-argument path) - (if mu4e-decryption-policy - "--decrypt --use-agent" - ""))) - (output (shell-command-to-string cmd)) - ;; create a new one - (buf (get-buffer-create mu4e~verify-buffer-name)) - (win (or (get-buffer-window buf) - (split-window-vertically (- (window-height) 6))))) - (with-selected-window win - (let ((inhibit-read-only t)) - ;; (set-window-dedicated-p win t) - (switch-to-buffer buf) - (erase-buffer) - (insert output) - (goto-char (point-min)) - (local-set-key "q" 'kill-buffer-and-window)) - (setq buffer-read-only t)) - (select-window win))) - - -(defun mu4e~view-quit-buffer () - "Quit the mu4e-view buffer. -This is a rather complex function, to ensure we don't disturb -other windows." - (interactive) - (unless (eq major-mode 'mu4e-view-mode) - (mu4e-error "Must be in mu4e-view-mode (%S)" major-mode)) - (let ((curbuf (current-buffer)) (curwin (selected-window)) - (headers-win)) - (walk-windows - (lambda (win) - ;; check whether the headers buffer window is visible - (when (eq mu4e~view-headers-buffer (window-buffer win)) - (setq headers-win win)) - ;; and kill any _other_ (non-selected) window that shows the current - ;; buffer - (when - (and - (eq curbuf (window-buffer win)) ;; does win show curbuf? - (not (eq curwin win)) ;; but it's not the curwin? - (not (one-window-p))) ;; and not the last one on the frame? - (delete-window win)))) ;; delete it! - ;; now, all *other* windows should be gone. - ;; if the headers view is also visible, kill ourselves + window; otherwise - ;; switch to the headers view - (if (window-live-p headers-win) - ;; headers are visible - (progn - (kill-buffer-and-window) ;; kill the view win - (setq mu4e~headers-view-win nil) - (select-window headers-win)) ;; and switch to the headers win... - ;; headers are not visible... - (progn - (kill-buffer) - (setq mu4e~headers-view-win nil) - (when (buffer-live-p mu4e~view-headers-buffer) - (switch-to-buffer mu4e~view-headers-buffer)))))) - -(provide 'mu4e-view) -;; end of mu4e-view diff --git a/_spacemacs.d/local/mu4e/mu4e.el b/_spacemacs.d/local/mu4e/mu4e.el deleted file mode 100644 index b605cb9..0000000 --- a/_spacemacs.d/local/mu4e/mu4e.el +++ /dev/null @@ -1,102 +0,0 @@ -;;; mu4e.el --- part of mu4e, the mu mail user agent -;; -;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Keywords: email -;; Version: 0.0 - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;;; Code: - -(eval-when-compile (require 'cl)) - -(require 'mu4e-meta) ;; autogenerated file with metadata (version etc.) -(require 'mu4e-headers) ;; headers view -(require 'mu4e-view) ;; message view -(require 'mu4e-main) ;; main screen -(require 'mu4e-compose) ;; message composition / sending -(require 'mu4e-proc) ;; communication with backend -(require 'mu4e-utils) ;; utility functions -(require 'mu4e-context) ;; support for contexts -(require 'mu4e-speedbar) ;; support for speedbar - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; we can't properly use compose buffers that are revived using -;; desktop-save-mode; so let's turn that off -(require 'desktop) -(add-to-list 'desktop-modes-not-to-save 'mu4e-compose-mode) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; register our handler functions; these connect server messages to functions -;; to handle them. -;; -;; -;; these are all defined in mu4e-headers -(setq mu4e-update-func 'mu4e~headers-update-handler) -(setq mu4e-header-func 'mu4e~headers-header-handler) -(setq mu4e-found-func 'mu4e~headers-found-handler) -(setq mu4e-view-func 'mu4e~headers-view-handler) -(setq mu4e-remove-func 'mu4e~headers-remove-handler) -(setq mu4e-erase-func 'mu4e~headers-clear) - -;; these ones are defined in mu4e-utils -(setq mu4e-info-func 'mu4e-info-handler) -(setq mu4e-error-func 'mu4e-error-handler) -;; note: mu4e-utils also dynamically (temporarily) -;; registers mu4e-pong func - -;; this one is defined in mu4e-compose -(setq mu4e-compose-func 'mu4e~compose-handler) -;; note: mu4e-compose.el dynamically registers mu4e-sent-func -;; we don't do that here, because it's only a local (temporary) -;; handler - -;; this one is defined in mu4e-view -(setq mu4e-temp-func 'mu4e~view-temp-handler) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;###autoload -(defun mu4e (&optional background) - "If mu4e is not running yet, start it. Then, show the main -window, unless BACKGROUND (prefix-argument) is non-nil." - (interactive "P") - ;; start mu4e, then show the main view - (mu4e~start (unless background 'mu4e~main-view))) - -(defun mu4e-quit() - "Quit the mu4e session." - (interactive) - (if mu4e-confirm-quit - (when (y-or-n-p (mu4e-format "Are you sure you want to quit?")) - (mu4e~stop)) - (mu4e~stop))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'mu4e) - -;;; mu4e.el ends here diff --git a/_spacemacs.d/local/mu4e/org-mu4e.el b/_spacemacs.d/local/mu4e/org-mu4e.el deleted file mode 100644 index 545f566..0000000 --- a/_spacemacs.d/local/mu4e/org-mu4e.el +++ /dev/null @@ -1,323 +0,0 @@ -;;; org-mu4e -- Support for links to mu4e messages/queries from within -;;; org-mode, and for writing message in org-mode, sending them as -;;; rich-text -;; -;; Copyright (C) 2012-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Keywords: outlines, hypermedia, calendar, mail -;; Version: 0.0 - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of 1the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;;; Code: - - -;; The expect version here is org 8.x - - -;; the 'noerror is just to make sure bytecompilations does not break... -;; FIXME: find a better solution -(require 'org nil 'noerror) -(require 'org-exp nil 'noerror) - -(eval-when-compile (require 'cl)) -(require 'mu4e) - -(defgroup org-mu4e nil - "Settings for the org-mode related functionality in mu4e." - :group 'mu4e - :group 'org) - -(defvar org-mu4e-link-query-in-headers-mode nil - "If non-nil, `org-store-link' in `mu4e-headers-mode' links to the -the current query; otherwise, it links to the message at point.") - -(defcustom org-mu4e-link-desc-func - (lambda (msg) (or (plist-get msg :subject) "No subject")) - "Function that takes a msg and returns a string for the -description part of an org-mode link. - -Example usage: - - (defun my-link-descr (msg) - (let ((subject (or (plist-get msg :subject) - \"No subject\")) - (date (or (format-time-string mu4e-headers-date-format - (mu4e-msg-field msg :date)) - \"No date\"))) - (concat subject \" \" date))) - - (setq org-mu4e-link-desc-func 'my-link-descr)" - :type 'function - :group 'org-mu4e) - -(defun org-mu4e-store-link () - "Store a link to a mu4e query or message." - (when (member major-mode '(mu4e-headers-mode mu4e-view-mode)) - (if (and (eq major-mode 'mu4e-headers-mode) - org-mu4e-link-query-in-headers-mode) - ;; storing links to queries - (let* ((query (mu4e-last-query)) - desc link) - (org-store-link-props :type "mu4e" :query query) - (setq - desc (concat "mu4e:query:" query) - link desc) - (org-add-link-props :link link :description desc) - link) - ;; storing links to messages - (let* ((msg (mu4e-message-at-point)) - (msgid (or (plist-get msg :message-id) "<none>")) - (from (or (plist-get msg :from) '(("none" . "none")))) - (fromname (car (car from))) - (fromaddress (cdr (car from))) - (to (or (plist-get msg :to) '(("none" . "none")))) - (toname (car (car to))) - (toaddress (cdr (car to))) - (fromto (if (mu4e-user-mail-address-p fromaddress) - (format "to %s <%s>" toname toaddress) - (format "from %s <%s>" fromname fromaddress))) - (date (plist-get msg :date)) - (date-ts (format-time-string (org-time-stamp-format t) date)) - (date-ts-ia (format-time-string (org-time-stamp-format t t) date)) - (subject (or (plist-get msg :subject) "<none>")) - link) - (org-store-link-props :type "mu4e" :link link - :message-id msgid) - (setq link (concat "mu4e:msgid:" msgid)) - (org-add-link-props :link link - :to (format "%s <%s>" toname toaddress) - :toname toname - :toaddress toaddress - :from (format "%s <%s>" fromname fromaddress) - :fromname fromname - :fromaddress fromaddress - :fromto fromto - :date date-ts-ia - :date-timestamp date-ts - :date-timestamp-inactive date-ts-ia - :subject subject - :description (funcall org-mu4e-link-desc-func msg)) - link)))) - -(org-add-link-type "mu4e" 'org-mu4e-open) -(add-hook 'org-store-link-functions 'org-mu4e-store-link) - -(defun org-mu4e-open (path) - "Open the mu4e message (for paths starting with 'msgid:') or run -the query (for paths starting with 'query:')." - (cond - ((string-match "^msgid:\\(.+\\)" path) - (mu4e-view-message-with-message-id (match-string 1 path))) - ((string-match "^query:\\(.+\\)" path) - (mu4e-headers-search (match-string 1 path) current-prefix-arg)) - (t (mu4e-error "mu4e: unrecognized link type '%s'" path)))) - -(defun org-mu4e-store-and-capture () - "Store a link to the current message or query (depending on -`org-mu4e-link-query-in-headers-mode', and capture it with -org-mode)." - (interactive) - (org-mu4e-store-link) - (org-capture)) - - - -;;; editing with org-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; below, some functions for the org->html conversion -;; based on / inspired by Eric Schulte's org-mime.el -;; Homepage: http://orgmode.org/worg/org-contrib/org-mime.php -;; -;; EXPERIMENTAL -(defun org~mu4e-mime-file (ext path id) - "Create a file for an attachment." - (format (concat "<#part type=\"%s\" filename=\"%s\" " - "disposition=inline id=\"<%s>\">\n<#/part>\n") - ext path id)) - -(defun org~mu4e-mime-multipart (plain html &optional images) - "Create a multipart/alternative with text/plain and text/html alternatives. -If the html portion of the message includes images, wrap the html -and images in a multipart/related part." - (concat "<#multipart type=alternative><#part type=text/plain>" - plain - (when images "<#multipart type=related>") - "<#part type=text/html>" - html - images - (when images "<#/multipart>\n") - "<#/multipart>\n")) - -(defun org~mu4e-mime-replace-images (str current-file) - "Replace images in html files with cid links." - (let (html-images) - (cons - (replace-regexp-in-string ;; replace images in html - "src=\"\\([^\"]+\\)\"" - (lambda (text) - (format - "src=\"cid:%s\"" - (let* ((url (and (string-match "src=\"\\([^\"]+\\)\"" text) - (match-string 1 text))) - (path (expand-file-name - url (file-name-directory current-file))) - (ext (file-name-extension path)) - (id (replace-regexp-in-string "[\/\\\\]" "_" path))) - (add-to-list 'html-images - (org~mu4e-mime-file - (concat "image/" ext) path id)) - id))) - str) - html-images))) - -(defun org~mu4e-mime-convert-to-html () - "Convert the current body to html." - (unless (fboundp 'org-export-string-as) - (mu4e-error "require function 'org-export-string-as not found.")) - (let* ((begin - (save-excursion - (goto-char (point-min)) - (search-forward mail-header-separator))) - (end (point-max)) - (raw-body (buffer-substring begin end)) - (tmp-file (make-temp-name (expand-file-name "mail" - temporary-file-directory))) - ;; because we probably don't want to skip part of our mail - (org-export-skip-text-before-1st-heading nil) - ;; because we probably don't want to export a huge style file - (org-export-htmlize-output-type 'inline-css) - ;; makes the replies with ">"s look nicer - (org-export-preserve-breaks t) - ;; dvipng for inline latex because MathJax doesn't work in mail - (org-export-with-LaTeX-fragments - (if (executable-find "dvipng") 'dvipng - (mu4e-message "Cannot find dvipng, ignore inline LaTeX") nil)) - ;; to hold attachments for inline html images - (html-and-images - (org~mu4e-mime-replace-images - (org-export-string-as raw-body 'html t) - tmp-file)) - (html-images (cdr html-and-images)) - (html (car html-and-images))) - (delete-region begin end) - (save-excursion - (goto-char begin) - (newline) - (insert (org~mu4e-mime-multipart - raw-body html (mapconcat 'identity html-images "\n")))))) - -;; next some functions to make the org/mu4e-compose-mode switch as smooth as -;; possible. -(defun org~mu4e-mime-decorate-headers () - "Make the headers visually distinctive (org-mode)." - (save-excursion - (goto-char (point-min)) - (let* ((eoh (when (search-forward mail-header-separator) - (match-end 0))) - (olay (make-overlay (point-min) eoh))) - (when olay - (overlay-put olay 'face 'font-lock-comment-face))))) - -(defun org~mu4e-mime-undecorate-headers () - "Don't make the headers visually distinctive. -\(well, mu4e-compose-mode will take care of that)." - (save-excursion - (goto-char (point-min)) - (let* ((eoh (when (search-forward mail-header-separator) - (match-end 0)))) - (remove-overlays (point-min) eoh)))) - -(defvar org-mu4e-convert-to-html nil - "Whether to do automatic org-mode => html conversion when sending messages.") - -(defun org~mu4e-mime-convert-to-html-maybe () - "Convert to html if `org-mu4e-convert-to-html' is non-nil. -This function is called when sending a message (from -`message-send-hook') and, if non-nil, will send the message as -the rich-text version of the what is assumed to be an org-mode -body." - (when org-mu4e-convert-to-html - (mu4e-message "Converting to html") - (org~mu4e-mime-convert-to-html))) - -(defun org~mu4e-mime-switch-headers-or-body () - "Switch the buffer to either mu4e-compose-mode (when in headers) -or org-mode (when in the body)." - (interactive) - (let* ((sepapoint - (save-excursion - (goto-char (point-min)) - (search-forward-regexp mail-header-separator nil t)))) - ;; only do stuff when the sepapoint exist; note that after sending the - ;; message, this function maybe called on a message with the sepapoint - ;; stripped. This is why we don't use `message-point-in-header'. - (when sepapoint - (cond - ;; we're in the body, but in mu4e-compose-mode? - ;; if so, switch to org-mode - ((and (> (point) sepapoint) (eq major-mode 'mu4e-compose-mode)) - (org-mode) - (add-hook 'before-save-hook - (lambda () - (mu4e-error "Switch to mu4e-compose-mode (M-m) before saving.")) - nil t) - (org~mu4e-mime-decorate-headers) - (local-set-key (kbd "M-m") - (lambda (keyseq) - (interactive "kEnter mu4e-compose-mode key sequence: ") - (let ((func (lookup-key mu4e-compose-mode-map keyseq))) - (if func (funcall func) (insert keyseq)))))) - ;; we're in the headers, but in org-mode? - ;; if so, switch to mu4e-compose-mode - ((and (<= (point) sepapoint) (eq major-mode 'org-mode)) - (org~mu4e-mime-undecorate-headers) - (mu4e-compose-mode) - (add-hook 'message-send-hook 'org~mu4e-mime-convert-to-html-maybe nil t))) - ;; and add the hook - (add-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t t)))) - - -(defun org-mu4e-compose-org-mode () - "Pseudo-Minor mode for mu4e-compose-mode, to edit the message -body using org-mode." - (interactive) - (unless (member major-mode '(org-mode mu4e-compose-mode)) - (mu4e-error "Need org-mode or mu4e-compose-mode")) - ;; we can check if we're already in org-mu4e-compose-mode by checking if the - ;; post-command-hook is set; hackish...but a buffer-local variable does not - ;; seem to survive buffer switching - (if (not (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)) - (progn - (org~mu4e-mime-switch-headers-or-body) - (mu4e-message - (concat - "org-mu4e-compose-org-mode enabled; " - "press M-m before issuing message-mode commands"))) - (progn ;; otherwise, remove crap - (remove-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t) - (org~mu4e-mime-undecorate-headers) ;; shut off org-mode stuff - (mu4e-compose-mode) - (message "org-mu4e-compose-org-mode disabled")))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(provide 'org-mu4e) - -;;; org-mu4e.el ends here diff --git a/_spacemacs.d/local/mu4e/org-old-mu4e.el b/_spacemacs.d/local/mu4e/org-old-mu4e.el deleted file mode 100644 index d73a780..0000000 --- a/_spacemacs.d/local/mu4e/org-old-mu4e.el +++ /dev/null @@ -1,289 +0,0 @@ -;;; org-mu4e -- Support for links to mu4e messages/queries from within org-mode, -;;; and for writing message in org-mode, sending them as rich-text -;; -;; Copyright (C) 2012-2016 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> -;; Keywords: outlines, hypermedia, calendar, mail -;; Version: 0.0 - -;; This file is not part of GNU Emacs. -;; -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;;; Code: - -;; This should support org-mode versions 6.x. 7.x - -;; the 'noerror is just to make sure bytecompilations does not break... -;; FIXME: find a better solution -(require 'org nil 'noerror) -(require 'org-exp nil 'noerror) - -(eval-when-compile (require 'cl)) -(require 'mu4e) - -(defgroup org-mu4e nil - "Settings for the org-mode related functionality in mu4e." - :group 'mu4e - :group 'org) - -(defcustom org-mu4e-link-desc-func - (lambda (msg) (or (plist-get msg :subject) "No subject")) - "Function that takes a msg and returns a string for the -description part of an org-mode link. - -Example usage: - - (defun my-link-descr (msg) - (let ((subject (or (plist-get msg :subject) - \"No subject\")) - (date (or (format-time-string mu4e-headers-date-format - (mu4e-msg-field msg :date)) - \"No date\"))) - (concat subject \" \" date))) - - (setq org-mu4e-link-desc-func 'my-link-descr)" - :type 'function - :group 'org-mu4e) - -(defun org-mu4e-store-link () - "Store a link to a mu4e query or message." - (cond - ;; storing links to queries - ((eq major-mode 'mu4e-headers-mode) - (let* ((query (mu4e-last-query)) - desc link) - (org-store-link-props :type "mu4e" :query query) - (setq - desc (concat "mu4e:query:" query) - link desc) - (org-add-link-props :link link :description desc) - link)) - ;; storing links to messages - ((eq major-mode 'mu4e-view-mode) - (let* ((msg (mu4e-message-at-point)) - (msgid (or (plist-get msg :message-id) "<none>")) - link) - (org-store-link-props :type "mu4e" :link link - :message-id msgid) - (setq link (concat "mu4e:msgid:" msgid)) - (org-add-link-props :link link - :description (funcall org-mu4e-link-desc-func msg)) - link)))) - -(org-add-link-type "mu4e" 'org-mu4e-open) -(add-hook 'org-store-link-functions 'org-mu4e-store-link) - -(defun org-mu4e-open (path) - "Open the mu4e message (for paths starting with 'msgid:') or run -the query (for paths starting with 'query:')." - (require 'mu4e) - (cond - ((string-match "^msgid:\\(.+\\)" path) - (mu4e-view-message-with-message-id (match-string 1 path))) - ((string-match "^query:\\(.+\\)" path) - (mu4e-headers-search (match-string 1 path) current-prefix-arg)) - (t (mu4e-error "mu4e: unrecognized link type '%s'" path)))) - - - - -;;; editing with org-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; below, some functions for the org->html conversion -;; based on / inspired by Eric Schulte's org-mime.el -;; Homepage: http://orgmode.org/worg/org-contrib/org-mime.php -;; -;; EXPERIMENTAL -(defun org~mu4e-mime-file (ext path id) - "Create a file for an attachment." - (format (concat "<#part type=\"%s\" filename=\"%s\" " - "disposition=inline id=\"<%s>\">\n<#/part>\n") - ext path id)) - -(defun org~mu4e-mime-multipart (plain html &optional images) - "Create a multipart/alternative with text/plain and text/html alternatives. -If the html portion of the message includes images, wrap the html -and images in a multipart/related part." - (concat "<#multipart type=alternative><#part type=text/plain>" - plain - (when images "<#multipart type=related>") - "<#part type=text/html>" - html - images - (when images "<#/multipart>\n") - "<#/multipart>\n")) - -(defun org~mu4e-mime-replace-images (str current-file) - "Replace images in html files with cid links." - (let (html-images) - (cons - (replace-regexp-in-string ;; replace images in html - "src=\"\\([^\"]+\\)\"" - (lambda (text) - (format - "src=\"cid:%s\"" - (let* ((url (and (string-match "src=\"\\([^\"]+\\)\"" text) - (match-string 1 text))) - (path (expand-file-name - url (file-name-directory current-file))) - (ext (file-name-extension path)) - (id (replace-regexp-in-string "[\/\\\\]" "_" path))) - (add-to-list 'html-images - (org~mu4e-mime-file - (concat "image/" ext) path id)) - id))) - str) - html-images))) - -(defun org~mu4e-mime-convert-to-html () - "Convert the current body to html." - (unless (fboundp 'org-export-string) - (mu4e-error "require function 'org-export-string not found.")) - (unless (executable-find "dvipng") - (mu4e-error "Required program dvipng not found")) - (let* ((begin - (save-excursion - (goto-char (point-min)) - (search-forward mail-header-separator))) - (end (point-max)) - (raw-body (buffer-substring begin end)) - (tmp-file (make-temp-name (expand-file-name "mail" - temporary-file-directory))) - (body (org-export-string raw-body 'org - (file-name-directory tmp-file))) - ;; because we probably don't want to skip part of our mail - (org-export-skip-text-before-1st-heading nil) - ;; because we probably don't want to export a huge style file - (org-export-htmlize-output-type 'inline-css) - ;; makes the replies with ">"s look nicer - (org-export-preserve-breaks t) - ;; dvipng for inline latex because MathJax doesn't work in mail - (org-export-with-LaTeX-fragments 'dvipng) - ;; to hold attachments for inline html images - (html-and-images - (org~mu4e-mime-replace-images - (org-export-string raw-body 'html - (file-name-directory tmp-file)) - tmp-file)) - (html-images (cdr html-and-images)) - (html (car html-and-images))) - (delete-region begin end) - (save-excursion - (goto-char begin) - (newline) - (insert (org~mu4e-mime-multipart - body html (mapconcat 'identity html-images "\n")))))) - -;; next some functions to make the org/mu4e-compose-mode switch as smooth as -;; possible. -(defun org~mu4e-mime-decorate-headers () - "Make the headers visually distinctive (org-mode)." - (save-excursion - (goto-char (point-min)) - (let* ((eoh (when (search-forward mail-header-separator) - (match-end 0))) - (olay (make-overlay (point-min) eoh))) - (when olay - (overlay-put olay 'face 'font-lock-comment-face))))) - -(defun org~mu4e-mime-undecorate-headers () - "Don't make the headers visually distinctive. -\(well, mu4e-compose-mode will take care of that)." - (save-excursion - (goto-char (point-min)) - (let* ((eoh (when (search-forward mail-header-separator) - (match-end 0)))) - (remove-overlays (point-min) eoh)))) - -(defvar org-mu4e-convert-to-html nil - "Whether to do automatic org-mode => html conversion when sending messages.") - -(defun org~mu4e-mime-convert-to-html-maybe () - "Convert to html if `org-mu4e-convert-to-html' is non-nil. -This function is called when sending a message (from -`message-send-hook') and, if non-nil, will send the message as -the rich-text version of the what is assumed to be an org-mode -body." - (when org-mu4e-convert-to-html - (mu4e-message "Converting to html") - (org~mu4e-mime-convert-to-html))) - -(defun org~mu4e-mime-switch-headers-or-body () - "Switch the buffer to either mu4e-compose-mode (when in headers) -or org-mode (when in the body)." - (interactive) - (let* ((sepapoint - (save-excursion - (goto-char (point-min)) - (search-forward-regexp mail-header-separator nil t)))) - ;; only do stuff when the sepapoint exist; note that after sending the - ;; message, this function maybe called on a message with the sepapoint - ;; stripped. This is why we don't use `message-point-in-header'. - (when sepapoint - (cond - ;; we're in the body, but in mu4e-compose-mode? - ;; if so, switch to org-mode - ((and (> (point) sepapoint) (eq major-mode 'mu4e-compose-mode)) - (org-mode) - (add-hook 'before-save-hook - (lambda () - (mu4e-error "Switch to mu4e-compose-mode (M-m) before saving.")) - nil t) - (org~mu4e-mime-decorate-headers) - (local-set-key (kbd "M-m") - (lambda (keyseq) - (interactive "kEnter mu4e-compose-mode key sequence: ") - (let ((func (lookup-key mu4e-compose-mode-map keyseq))) - (if func (funcall func) (insert keyseq)))))) - ;; we're in the headers, but in org-mode? - ;; if so, switch to mu4e-compose-mode - ((and (<= (point) sepapoint) (eq major-mode 'org-mode)) - (org~mu4e-mime-undecorate-headers) - (mu4e-compose-mode) - (add-hook 'message-send-hook 'org~mu4e-mime-convert-to-html-maybe nil t))) - ;; and add the hook - (add-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t t)))) - - -(defun org-mu4e-compose-org-mode () - "Pseudo-Minor mode for mu4e-compose-mode, to edit the message -body using org-mode." - (interactive) - (unless (member major-mode '(org-mode mu4e-compose-mode)) - (mu4e-error "Need org-mode or mu4e-compose-mode")) - ;; we can check if we're already in org-mu4e-compose-mode by checking if the - ;; post-command-hook is set; hackish...but a buffer-local variable does not - ;; seem to survive buffer switching - (if (not (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)) - (progn - (org~mu4e-mime-switch-headers-or-body) - (mu4e-message - (concat - "org-mu4e-compose-org-mode enabled; " - "press M-m before issuing message-mode commands"))) - (progn ;; otherwise, remove crap - (remove-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t) - (org~mu4e-mime-undecorate-headers) ;; shut off org-mode stuff - (mu4e-compose-mode) - (message "org-mu4e-compose-org-mode disabled")))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(provide 'org-mu4e) - -;;; org-mu4e.el ends here |