From e1e26d1da2fab06309ec436ab5dd65bb526cbaa8 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Thu, 6 Feb 2020 20:28:24 +0200 Subject: [PATCH] mu4e: update to use server maildir/database/addresses Mkae mu4e-maildir and mu4e-personal-addresses obsolete, we get those from the server. --- mu/mu-cmd.c | 3 +- mu4e/mu4e-actions.el | 3 +- mu4e/mu4e-compose.el | 12 +- mu4e/mu4e-context.el | 18 +- mu4e/mu4e-contrib.el | 7 +- mu4e/mu4e-draft.el | 13 +- mu4e/mu4e-headers.el | 1038 ++++++++++++++++++++-------------------- mu4e/mu4e-icalendar.el | 95 ++-- mu4e/mu4e-main.el | 34 +- mu4e/mu4e-mark.el | 6 +- mu4e/mu4e-message.el | 12 +- mu4e/mu4e-org.el | 2 +- mu4e/mu4e-proc.el | 45 +- mu4e/mu4e-utils.el | 66 +-- mu4e/mu4e-vars.el | 58 ++- mu4e/mu4e-view.el | 4 +- mu4e/mu4e.texi | 264 ++++------ 17 files changed, 791 insertions(+), 889 deletions(-) diff --git a/mu/mu-cmd.c b/mu/mu-cmd.c index 26878fde..6727fb20 100644 --- a/mu/mu-cmd.c +++ b/mu/mu-cmd.c @@ -577,7 +577,8 @@ cmd_init (MuConfig *opts, GError **err) if (!opts->quiet) { mu_store_print_info (store, opts->nocolor); g_print ("\nstore created.\n" - "now you can use the index command to index some messages.\n" + "use 'mu index' to fill the database " + "with your messsages.\n" "see mu-index(1) for details\n"); } diff --git a/mu4e/mu4e-actions.el b/mu4e/mu4e-actions.el index 282c3937..2082c4d3 100644 --- a/mu4e/mu4e-actions.el +++ b/mu4e/mu4e-actions.el @@ -323,7 +323,6 @@ Example: +tag,+long tag,-oldtag would add 'tag' and 'long tag', and remove 'oldtag'." (let* ( (path (mu4e-message-field msg :path)) - (maildir (mu4e-message-field msg :maildir)) (oldtags (mu4e-message-field msg :tags)) (tags-completion (append @@ -369,7 +368,7 @@ would add 'tag' and 'long tag', and remove 'oldtag'." path)) (mu4e-message (concat "tagging: " (mapconcat 'identity taglist ", "))) - (mu4e-refresh-message path maildir))) + (mu4e-refresh-message path))) (defun mu4e-action-show-thread (msg) "Show thread for message at point with point remaining on MSG. diff --git a/mu4e/mu4e-compose.el b/mu4e/mu4e-compose.el index 972591db..ab79113a 100644 --- a/mu4e/mu4e-compose.el +++ b/mu4e/mu4e-compose.el @@ -1,6 +1,6 @@ ;; mu4e-compose.el -- part of mu4e, the mu mail user agent for emacs -*- lexical-binding: t -*- ;; -;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2020 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -260,8 +260,8 @@ If needed, set the Fcc header, and register the handler function." `mu4e-sent-messages-behavior'" mu4e-sent-messages-behavior)))) (fccfile (and mdir - (concat mu4e-maildir mdir "/cur/" - (mu4e~draft-message-filename-construct "S"))))) + (concat (mu4e-root-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")) @@ -273,7 +273,7 @@ If needed, set the Fcc header, and register the handler function." (old-handler message-fcc-handler-function)) (lambda (file) (setq message-fcc-handler-function old-handler) ;; reset the fcc handler - (let ((mdir-path (concat mu4e-maildir maildir))) + (let ((mdir-path (concat (mu4e-root-maildir) maildir))) ;; Create the full maildir structure for the sent folder if it doesn't exist. ;; `mu4e~proc-mkdir` runs asynchronously but no matter whether it runs before or after ;; `write-file`, the sent maildir ends up in the correct state. @@ -817,8 +817,8 @@ draft message." ;; 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) +(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'. Quoting its docstring: Start composing a mail message to send. diff --git a/mu4e/mu4e-context.el b/mu4e/mu4e-context.el index e314e78d..cd9a1473 100644 --- a/mu4e/mu4e-context.el +++ b/mu4e/mu4e-context.el @@ -1,6 +1,6 @@ ; mu4e-context.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;; -;; Copyright (C) 2015-2016 Dirk-Jan C. Binnema +;; Copyright (C) 2015-2020 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -31,14 +31,11 @@ (defvar smtpmail-smtp-user) (defvar mu4e-view-date-format) -(defcustom mu4e-contexts nil "The list of `mu4e-context' objects -describing mu4e's contexts." - :group 'mu4e) +(defvar mu4e-contexts nil "The list of `mu4e-context' objects +describing mu4e's contexts.") -(defcustom mu4e-context-changed-hook nil - "Hook run just *after* the context changed." - :type 'hook - :group 'mu4e-headers) +(defvar mu4e-context-changed-hook nil + "Hook run just *after* the context changed.") (defvar mu4e~context-current nil "The current context; for internal use. Use @@ -238,8 +235,7 @@ non-nil." (set (car cell) (cdr cell))) (mu4e-context-vars context))) (setq mu4e~context-current context) - (unless (eq mu4e-split-view 'single-window) - (mu4e~main-view-real nil nil)) + (run-hooks 'mu4e-context-changed-hook) (mu4e-message "Switched context to %s" (mu4e-context-name context))) context)) @@ -248,7 +244,7 @@ non-nil." "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 + (when (and mu4e-contexts (not mu4e~context-current)) (let ((context (mu4e-context-determine msg policy))) (when context (mu4e-context-switch nil (mu4e-context-name context)))))) diff --git a/mu4e/mu4e-contrib.el b/mu4e/mu4e-contrib.el index ed611acf..d102d338 100644 --- a/mu4e/mu4e-contrib.el +++ b/mu4e/mu4e-contrib.el @@ -1,6 +1,6 @@ ;;; mu4e-contrib.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;; -;; Copyright (C) 2013-2016 Dirk-Jan C. Binnema +;; Copyright (C) 2013-2020 Dirk-Jan C. Binnema ;; This file is not part of GNU Emacs. ;; @@ -20,7 +20,10 @@ ;;; Commentary: ;; Some user-contributed functions for mu4e - +(require 'mu4e-headers) +(require 'mu4e-view) +(require 'bookmark) +(require 'eshell) ;; Contributed by sabof (defvar bookmark-make-record-function) diff --git a/mu4e/mu4e-draft.el b/mu4e/mu4e-draft.el index f66e88c0..1856f968 100644 --- a/mu4e/mu4e-draft.el +++ b/mu4e/mu4e-draft.el @@ -1,6 +1,6 @@ ;; mu4e-draft.el -- part of mu4e, the mu mail user agent for emacs -*- lexical-binding: t -*- ;; -;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2020 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -38,7 +38,7 @@ (defcustom mu4e-compose-dont-reply-to-self nil "If non-nil, don't include self. -\(that is, member of `mu4e-user-mail-address-list') in replies." +\(that is, member of `(mu4e-personal-addresses)') in replies." :type 'boolean :group 'mu4e-compose) @@ -189,7 +189,7 @@ of the original, we simple copy the list form the original." (cl-member-if (lambda (addr) (string= (downcase addr) (downcase (cdr to-cell)))) - mu4e-user-mail-address-list)) + (mu4e-personal-addresses))) reply-to) reply-to))) @@ -253,7 +253,7 @@ REPLY-ALL." (cl-member-if (lambda (addr) (string= (downcase addr) (downcase (cdr cc-cell)))) - mu4e-user-mail-address-list)) + (mu4e-personal-addresses))) cc-lst)))) cc-lst))) @@ -540,7 +540,7 @@ This is based on `mu4e-drafts-folder', which is evaluated once.") (defun mu4e~draft-determine-path (draft-dir) "Determines the path for a new draft file in DRAFT-DIR." (format "%s/%s/cur/%s" - mu4e-maildir draft-dir (mu4e~draft-message-filename-construct "DS"))) + (mu4e-root-maildir) draft-dir (mu4e~draft-message-filename-construct "DS"))) (defun mu4e-draft-open (compose-type &optional msg) @@ -550,12 +550,11 @@ In case of a new message (when COMPOSE-TYPE is `reply', `forward' 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 +concatenation of `(mu4e-root-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 "Variable mu4e-maildir not set")) (let ((draft-dir nil)) (cl-case compose-type diff --git a/mu4e/mu4e-headers.el b/mu4e/mu4e-headers.el index d827951a..a35c6ea5 100644 --- a/mu4e/mu4e-headers.el +++ b/mu4e/mu4e-headers.el @@ -59,12 +59,12 @@ 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)))) + (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" @@ -119,7 +119,7 @@ matches. We then limit this second result as well, favoring the messages that were found in the first set (the \"leaders\"). " :type '(choice (const :tag "Unlimited" -1) - (integer :tag "Limit")) + (integer :tag "Limit")) :group 'mu4e-headers) (make-obsolete-variable 'mu4e-search-results-limit @@ -166,17 +166,17 @@ Note that this is merely a display filter.") 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)) + (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 @@ -332,11 +332,11 @@ In the format needed for `mu4e-read-option'.") (when (buffer-live-p (mu4e-get-headers-buffer)) (let ((inhibit-read-only t)) (with-current-buffer (mu4e-get-headers-buffer) - (mu4e~mark-clear) - (erase-buffer) - (when msg - (goto-char (point-min)) - (insert (propertize msg 'face 'mu4e-system-face 'intangible t))))))) + (mu4e~mark-clear) + (erase-buffer) + (when msg + (goto-char (point-min)) + (insert (propertize msg 'face 'mu4e-system-face 'intangible t))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; handler functions @@ -361,48 +361,48 @@ headers." (when (buffer-live-p (mu4e-get-headers-buffer)) (with-current-buffer (mu4e-get-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))) + (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? + (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)) + ;; 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)) + ;; 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-header docid t) + ;; first, remove the old one (otherwise, we'd have two headers with + ;; the same docid... + (mu4e~headers-remove-header docid t) - ;; 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 (and maybe-view (mu4e~headers-view-this-message-p docid)) - (mu4e-view msg)) - ;; 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 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 (and maybe-view (mu4e~headers-view-this-message-p docid)) + (mu4e-view msg)) + ;; 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)) - (run-hooks 'mu4e-message-changed-hook)))))) + (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)) + (run-hooks 'mu4e-message-changed-hook)))))) (defun mu4e~headers-remove-handler (docid &optional skip-hook) "Remove handler, will be called when a message with DOCID has @@ -415,10 +415,10 @@ If SKIP-HOOK is absent or nil, `mu4e-message-changed-hook' will be invoked." (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-get-view-buffer))) + (buffer-live-p (mu4e-get-view-buffer))) (unless (eq mu4e-split-view 'single-window) (mapc #'delete-window (get-buffer-window-list - (mu4e-get-view-buffer) nil t))) + (mu4e-get-view-buffer) nil t))) (kill-buffer (mu4e-get-view-buffer))) (unless skip-hook (run-hooks 'mu4e-message-changed-hook))) @@ -431,14 +431,14 @@ into a string." (mapconcat (lambda (ct) (let ((name (car ct)) (email (cdr ct))) - (or name email "?"))) contacts ", ")) + (or name email "?"))) contacts ", ")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defsubst mu4e~headers-thread-prefix-map (type) "Return the thread prefix based on the symbol TYPE." (let ((get-prefix - (lambda (cell) - (if mu4e-use-fancy-chars (cdr cell) (car cell))))) + (lambda (cell) + (if mu4e-use-fancy-chars (cdr cell) (car cell))))) (cl-case type ('child (funcall get-prefix mu4e-headers-thread-child-prefix)) ('last-child (funcall get-prefix mu4e-headers-thread-last-child-prefix)) @@ -459,54 +459,54 @@ into a string." "Calculate the thread prefix based on thread info THREAD." (when thread (let ((prefix "") - (level (plist-get thread :level)) - (has-child (plist-get thread :has-child)) - (empty-parent (plist-get thread :empty-parent)) - (first-child (plist-get thread :first-child)) - (last-child (plist-get thread :last-child)) - (duplicate (plist-get thread :duplicate))) + (level (plist-get thread :level)) + (has-child (plist-get thread :has-child)) + (empty-parent (plist-get thread :empty-parent)) + (first-child (plist-get thread :first-child)) + (last-child (plist-get thread :last-child)) + (duplicate (plist-get thread :duplicate))) ;; Do not prefix root messages. (if (= level 0) - (setq mu4e~headers-thread-state '())) + (setq mu4e~headers-thread-state '())) (if (> level 0) - (let* ((length (length mu4e~headers-thread-state)) - (padding (make-list (max 0 (- level length)) nil))) - ;; Trim and pad the state to ensure a message will - ;; always be shown with the correct indentation, even if - ;; a broken thread is returned. It's trimmed to level-1 - ;; because the current level has always an connection - ;; and it used a special formatting. - (setq mu4e~headers-thread-state - (cl-subseq (append mu4e~headers-thread-state padding) - 0 (- level 1))) - ;; Prepare the thread prefix. - (setq prefix - (concat - ;; Current mu4e~headers-thread-state, composed by - ;; connections or blanks. - (mapconcat - (lambda (s) - (mu4e~headers-thread-prefix-map - (if s 'connection 'blank))) - mu4e~headers-thread-state "") - ;; Current entry. - (mu4e~headers-thread-prefix-map - (if (and empty-parent first-child) - (if last-child 'single-orphan 'orphan) - (if last-child 'last-child 'child))))))) + (let* ((length (length mu4e~headers-thread-state)) + (padding (make-list (max 0 (- level length)) nil))) + ;; Trim and pad the state to ensure a message will + ;; always be shown with the correct indentation, even if + ;; a broken thread is returned. It's trimmed to level-1 + ;; because the current level has always an connection + ;; and it used a special formatting. + (setq mu4e~headers-thread-state + (cl-subseq (append mu4e~headers-thread-state padding) + 0 (- level 1))) + ;; Prepare the thread prefix. + (setq prefix + (concat + ;; Current mu4e~headers-thread-state, composed by + ;; connections or blanks. + (mapconcat + (lambda (s) + (mu4e~headers-thread-prefix-map + (if s 'connection 'blank))) + mu4e~headers-thread-state "") + ;; Current entry. + (mu4e~headers-thread-prefix-map + (if (and empty-parent first-child) + (if last-child 'single-orphan 'orphan) + (if last-child 'last-child 'child))))))) ;; If a new sub-thread will follow (has-child) and the current ;; one is still not done (not last-child), then a new ;; connection needs to be added to the tree-state. It's not ;; necessary to a blank (nil), because padding will handle ;; that. (if (and has-child (not last-child)) - (setq mu4e~headers-thread-state - (append mu4e~headers-thread-state '(t)))) + (setq mu4e~headers-thread-state + (append mu4e~headers-thread-state '(t)))) ;; Return the thread prefix. (format "%s%s" prefix (if duplicate - (mu4e~headers-thread-prefix-map 'duplicate) ""))))) + (mu4e~headers-thread-prefix-map 'duplicate) ""))))) (defsubst mu4e~headers-flags-str (flags) "Get a display string for the flags. @@ -515,24 +515,24 @@ 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))))) + (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 - (cl-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))))))) + (setq str + (concat str + (cl-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)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -549,9 +549,9 @@ otherwise ; show the from address; prefixed with the appropriate (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))) + (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)))))) + (mu4e~headers-contact-str (mu4e-message-field msg :from)))))) (defsubst mu4e~headers-human-date (msg) "Show a 'human' date. @@ -562,25 +562,25 @@ date. The formats used for date and time are (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)))))) + (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))) + (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) "")))) + (plist-get tinfo :empty-parent)) + (truncate-string-to-width subj 600) "")))) (defsubst mu4e~headers-mailing-list (list) "Get some identifier for the mailing list." @@ -592,21 +592,21 @@ show the subject of a thread only once, similar to e.g. 'mutt'." "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))))) + (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) +(defun mu4e~headers-field-apply-basic-properties (msg field val _width) (cl-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))) + (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)) @@ -616,11 +616,11 @@ found." (: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)))) + '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))) + 'help-echo (format "%S" val))) (:tags (propertize (mapconcat 'identity val ", "))) (:size (mu4e-display-size val)) (t (mu4e~headers-custom-field msg field)))) @@ -628,18 +628,18 @@ found." (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) + (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)) + 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)))) + (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)) @@ -650,15 +650,15 @@ found." (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)))) + (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)) @@ -676,50 +676,58 @@ if provided, or at the end of the buffer otherwise." (when (buffer-live-p (mu4e-get-headers-buffer)) (with-current-buffer (mu4e-get-headers-buffer) (let ((line (mu4e~message-header-description msg))) - (when line - (mu4e~headers-add-header line (mu4e-message-field msg :docid) - point msg)))))) + (when line + (mu4e~headers-add-header line (mu4e-message-field msg :docid) + point msg)))))) (defun mu4e~message-header-description (msg) "Return a propertized description of MSG suitable for displaying in the header view." (unless (and mu4e-headers-hide-predicate - (funcall mu4e-headers-hide-predicate msg)) + (funcall mu4e-headers-hide-predicate msg)) (let ((line (mapconcat - (lambda (f-w) (mu4e~headers-field-handler f-w msg)) - mu4e-headers-fields " "))) + (lambda (f-w) (mu4e~headers-field-handler f-w msg)) + mu4e-headers-fields " "))) (mu4e~headers-line-handler msg line)))) (defconst mu4e~searching "Searching...") (defconst mu4e~no-matches "No matching messages found") (defconst mu4e~end-of-results "End of search results") +(defvar mu4e~headers-view-target nil + "Whether to automatically view (open) the target message (as + per `mu4e~headers-msgid-target').") + +(defvar mu4e~headers-msgid-target nil + "Message-id to jump to after the search has finished.") + + (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-get-headers-buffer)) (with-current-buffer (mu4e-get-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"))))) + (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 (if (eq (current-buffer) (window-buffer)) - (mu4e-headers-goto-message-id mu4e~headers-msgid-target) + (mu4e-headers-goto-message-id mu4e~headers-msgid-target) (let* ((pos (mu4e-headers-goto-message-id mu4e~headers-msgid-target))) (when pos (set-window-point (get-buffer-window) pos))))) (when (and mu4e~headers-view-target (mu4e-message-at-point 'noerror)) - ;; view the message at point when there is one. - (mu4e-headers-view-message)) + ;; view the message at point when there is one. + (mu4e-headers-view-message)) (setq mu4e~headers-view-target nil - mu4e~headers-msgid-target nil) + mu4e~headers-msgid-target nil) (when (mu4e~headers-docid-at-point) (mu4e~headers-highlight (mu4e~headers-docid-at-point)))) @@ -731,11 +739,11 @@ after the end of the search results." (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))) + (docstring (format "Mark header at point with %s." mark))) `(progn (defun ,funcname () ,docstring - (interactive) - (mu4e-headers-mark-and-next ',mark)) + (interactive) + (mu4e-headers-mark-and-next ',mark)) (put ',funcname 'definition-name ',mark)))) (mu4e~headers-defun-mark-for refile) @@ -767,10 +775,10 @@ Also see `mu4e-view-mark-or-move-to-trash'." (let ((msg-dir (mu4e-message-field (mu4e-message-at-point) :maildir))) (if (not (seq-filter (lambda (re) (string-match re msg-dir)) - mu4e-move-to-trash-patterns)) - (mu4e-headers-mark-for-trash) + mu4e-move-to-trash-patterns)) + (mu4e-headers-mark-for-trash) (mu4e-mark-set 'move (if (functionp mu4e-trash-folder) - (funcall mu4e-trash-folder (mu4e-message-at-point)) + (funcall mu4e-trash-folder (mu4e-message-at-point)) mu4e-trash-folder)) (mu4e-headers-next)))) @@ -879,128 +887,128 @@ Also see `mu4e-view-mark-or-move-to-trash'." ;; 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 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 [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 [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 [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 [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 [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-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-untrash] - '("Mark for untrash" . mu4e-headers-mark-for-untrash)) - (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 [mark-delete] + '("Mark for deletion" . mu4e-headers-mark-for-delete)) + (define-key menumap [mark-untrash] + '("Mark for untrash" . mu4e-headers-mark-for-untrash)) + (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 [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 [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 [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 [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] '("--"))) + (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"))) + (downarrow (if mu4e-use-fancy-chars " â–¼" " V"))) (cons (make-string - (+ mu4e~mark-fringe-len (floor (fringe-columns 'left t))) ?\s) + (+ 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)))) - (require-full (plist-get info :require-full)) - (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 require-full - (mu4e-error "Field %S is not supported in mu4e-headers-mode" field)) - (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)))) + (lambda (item) + (let* ((field (car item)) (width (cdr item)) + (info (cdr (assoc field + (append mu4e-header-info mu4e-header-info-custom)))) + (require-full (plist-get info :require-full)) + (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 require-full + (mu4e-error "Field %S is not supported in mu4e-headers-mode" field)) + (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) @@ -1009,16 +1017,16 @@ Also see `mu4e-view-mark-or-move-to-trash'." 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 only + (zerop (mu4e-mark-marks-num)) ;; non active marks + (not (active-minibuffer-window))) ;; no user input only ;; rerun search if there's a live window with search results; ;; otherwise we'd trigger a headers view from out of nowhere. (when (and (buffer-live-p (mu4e-get-headers-buffer)) - (window-live-p (get-buffer-window (mu4e-get-headers-buffer) t))) + (window-live-p (get-buffer-window (mu4e-get-headers-buffer) t))) (mu4e-headers-rerun-search)))) (define-derived-mode mu4e-headers-mode special-mode - "mu4e:headers" + "mu4e:headers" "Major mode for displaying mu4e search results. \\{mu4e-headers-mode-map}." (use-local-map mu4e-headers-mode-map) @@ -1052,11 +1060,11 @@ Also, unhighlight any previously highlighted headers." (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)) + (mu4e~headers-goto-docid mu4e~highlighted-docid)) + (hl-line-unhighlight)) ;; now, highlight the new one (when (mu4e~headers-goto-docid docid) - (hl-line-highlight))) + (hl-line-highlight))) (setq mu4e~highlighted-docid docid))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1077,16 +1085,16 @@ adding a lot of new headers looks really choppy." "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) + 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))) + (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 @@ -1099,10 +1107,10 @@ of the beginning of the line." (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))))) + (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) @@ -1126,8 +1134,8 @@ 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))))) + (when (and this-msgid (string= msgid this-msgid)) + msg))))) ;;;; markers mark headers for (defun mu4e~headers-mark (docid mark) @@ -1135,21 +1143,21 @@ message plist, or nil if not found." (with-current-buffer (mu4e-get-headers-buffer) (let ((inhibit-read-only t) (oldpoint (point))) (unless (mu4e~headers-goto-docid docid) - (mu4e-error "Cannot find message with docid %S" docid)) + (mu4e-error "Cannot find message with docid %S" docid)) ;; now, we're at the beginning of the header, looking at ;; \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")) + (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))) + (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) @@ -1159,16 +1167,16 @@ text-property `msg'." (when (buffer-live-p (mu4e-get-headers-buffer)) (with-current-buffer (mu4e-get-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))))))) + (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. @@ -1177,9 +1185,9 @@ docid is not found." (with-current-buffer (mu4e-get-headers-buffer) (if (mu4e~headers-goto-docid docid) (let ((inhibit-read-only t)) - (delete-region (line-beginning-position) (line-beginning-position 2))) + (delete-region (line-beginning-position) (line-beginning-position 2))) (unless ignore-missing - (mu4e-error "Cannot find message with docid %S" docid))))) + (mu4e-error "Cannot find message with docid %S" docid))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defcustom mu4e-query-rewrite-function 'identity @@ -1210,32 +1218,32 @@ the query history stack." ;; `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) - (rewritten-expr (funcall mu4e-query-rewrite-function expr)) - (maxnum (unless mu4e-headers-full-search mu4e-headers-results-limit))) + (inhibit-read-only t) + (rewritten-expr (funcall mu4e-query-rewrite-function expr)) + (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))) + ;; save the old present query to the history list + (when mu4e~headers-last-query + (mu4e~headers-push-query mu4e~headers-last-query 'past))) (setq - mode-name "mu4e-headers" - mu4e~headers-last-query rewritten-expr) + mode-name "mu4e-headers" + mu4e~headers-last-query rewritten-expr) (add-to-list 'global-mode-string - '(:eval - (concat - (propertize - (mu4e~quote-for-modeline mu4e~headers-last-query) - 'face 'mu4e-modeline-face) - " " - (mu4e-context-label) - (if (and mu4e-display-update-status-in-modeline - (buffer-live-p mu4e~update-buffer) - (process-live-p (get-buffer-process - mu4e~update-buffer))) - (propertize " (updating)" 'face 'mu4e-modeline-face) - ""))))) + '(:eval + (concat + (propertize + (mu4e~quote-for-modeline mu4e~headers-last-query) + 'face 'mu4e-modeline-face) + " " + (mu4e-context-label) + (if (and mu4e-display-update-status-in-modeline + (buffer-live-p mu4e~update-buffer) + (process-live-p (get-buffer-process + mu4e~update-buffer))) + (propertize " (updating)" 'face 'mu4e-modeline-face) + ""))))) ;; when the buffer is already visible, select it; otherwise, ;; switch to it. @@ -1256,9 +1264,9 @@ the query history stack." "Close all windows, redraw the headers buffer based on the value of `mu4e-split-view', and return a window for the message view." (if (eq mu4e-split-view 'single-window) - (or (and (buffer-live-p (mu4e-get-view-buffer)) - (get-buffer-window (mu4e-get-view-buffer))) - (selected-window)) + (or (and (buffer-live-p (mu4e-get-view-buffer)) + (get-buffer-window (mu4e-get-view-buffer))) + (selected-window)) (mu4e-hide-other-mu4e-buffers) (unless (buffer-live-p (mu4e-get-headers-buffer)) (mu4e-error "No headers buffer available")) @@ -1268,16 +1276,16 @@ of `mu4e-split-view', and return a window for the message view." (kill-buffer (mu4e-get-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))))))) + (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))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; search-based marking @@ -1292,8 +1300,8 @@ corresponding header." ;; 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)))))) + (when msg + (funcall func msg)))))) (defun mu4e-headers-find-if (func &optional backward) "Move to the next header for which FUNC returns non-`nil', @@ -1303,15 +1311,15 @@ 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))) + (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)))))) + (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)))) @@ -1322,8 +1330,8 @@ returns non-`nil', starting from the current position." (let ((pos)) (save-excursion (if backwards - (beginning-of-line) - (end-of-line)) + (beginning-of-line) + (end-of-line)) (setq pos (mu4e-headers-find-if func backwards))) (when pos (goto-char pos)))) @@ -1339,7 +1347,7 @@ parameter). MARKPAIR is a cell (MARK . TARGET); see (mu4e-headers-for-each (lambda (msg) (when (funcall mark-pred msg param) - (mu4e-mark-at-point (car markpair) (cdr markpair)))))) + (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 @@ -1347,50 +1355,50 @@ 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))) + (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)) - (cl-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 ""))))))))) + (let* ((do-mark) (value (mu4e-msg-field msg field))) + (setq do-mark + (if (member field '(:to :from :cc :bcc :reply-to)) + (cl-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-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")))) + (mu4e-error "No thread info found"))) + (path (or (plist-get thread :path) + (mu4e-error "No threadpath found")))) (cl-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)))) + (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) @@ -1398,30 +1406,29 @@ matching messages with that mark." non-nil, marking is limited to the message at point and its descendants." (let* ((mark (car markpair)) - (allowed-marks (mapcar 'car mu4e-marks))) + (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))) + 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)) + (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))))))) + (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 my-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)))) @@ -1429,13 +1436,13 @@ descendants." (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)))) + (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) @@ -1461,21 +1468,21 @@ WHERE is a symbol telling us where to push; it's a symbol, either 'future or 'past. Functional also removes duplicates, limits the stack size." (let ((stack - (cl-case where - (past mu4e~headers-query-past) - (future mu4e~headers-query-future)))) - ;; only add if not the same item + (cl-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 (cl-subseq stack 0 mu4e~headers-query-stack-size))) + (setq stack (cl-subseq stack 0 mu4e~headers-query-stack-size))) ;; remove all duplicates of the new element (cl-remove-if (lambda (elm) (string= elm (car stack))) (cdr stack)) ;; update the stacks (cl-case where - (past (setq mu4e~headers-query-past stack)) - (future (setq mu4e~headers-query-future stack)))))) + (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. @@ -1484,11 +1491,11 @@ or `past'." (cl-case whence (past (unless mu4e~headers-query-past - (mu4e-warn "No more previous queries")) + (mu4e-warn "No more previous queries")) (pop mu4e~headers-query-past)) (future (unless mu4e~headers-query-future - (mu4e-warn "No more next queries")) + (mu4e-warn "No more next queries")) (pop mu4e~headers-query-future)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1497,15 +1504,8 @@ or `past'." (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) + 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 @@ -1519,11 +1519,11 @@ searching. If SHOW is non-nil, show the message with MSGID." ;; `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))))) + (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 @@ -1540,8 +1540,8 @@ 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: "))))) + (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))) @@ -1556,8 +1556,8 @@ 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))) + (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")) @@ -1571,28 +1571,28 @@ FIELD is the field to sort by; DIR is a symbol: either 'ascending, 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 - (cl-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)))))) + (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 + (cl-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) @@ -1638,7 +1638,7 @@ _not_ refresh the last search with the new setting for threading." _not_ refresh the last search with the new setting for threading." (interactive "P") (mu4e~headers-toggle "Skip-duplicates" - 'mu4e-headers-skip-duplicates dont-refresh)) + 'mu4e-headers-skip-duplicates dont-refresh)) (defvar mu4e~headers-loading-buf nil "A buffer for loading a message view.") @@ -1653,11 +1653,11 @@ _not_ refresh the last search with the new setting for threading." (let ((inhibit-read-only t)) (erase-buffer) (local-set-key (kbd "q") - (if (eq mu4e-split-view 'single-window) - 'kill-buffer - 'kill-buffer-and-window)) + (if (eq mu4e-split-view 'single-window) + 'kill-buffer + 'kill-buffer-and-window)) (insert (propertize "Waiting for message..." - 'face 'mu4e-system-face 'intangible t)))) + 'face 'mu4e-system-face 'intangible t)))) mu4e~headers-loading-buf) (defun mu4e~decrypt-p (msg) @@ -1678,10 +1678,10 @@ window. " (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 (mu4e~decrypt-p msg)) - (viewwin (mu4e~headers-redraw-get-view-window))) + (docid (or (mu4e-message-field msg :docid) + (mu4e-warn "No message at point"))) + (decrypt (mu4e~decrypt-p msg)) + (viewwin (mu4e~headers-redraw-get-view-window))) (unless (window-live-p viewwin) (mu4e-error "Cannot get a message view")) (select-window viewwin) @@ -1693,7 +1693,7 @@ window. " (interactive) ;; if possible, try to return to the same message (let* ((msg (mu4e-message-at-point t)) - (msgid (and msg (mu4e-message-field msg :message-id)))) + (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) @@ -1701,7 +1701,7 @@ window. " 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))) + (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)))) @@ -1732,22 +1732,22 @@ 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))) + (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-get-headers-buffer)) - (set-window-point win (point)))) - nil t) + (lambda (win) + (when (eq (window-buffer win) (mu4e-get-headers-buffer)) + (set-window-point win (point)))) + nil t) (if (eq mu4e-split-view 'single-window) - (when (eq (window-buffer) (mu4e-get-view-buffer)) - (mu4e-headers-view-message)) - ;; update message view if it was already showing - (when (and mu4e-split-view (window-live-p mu4e~headers-view-win)) - (mu4e-headers-view-message))) + (when (eq (window-buffer) (mu4e-get-view-buffer)) + (mu4e-headers-view-message)) + ;; update message view if it was already showing + (when (and mu4e-split-view (window-live-p mu4e~headers-view-win)) + (mu4e-headers-view-message))) ;; attempt to highlight the new line, display the message (mu4e~headers-highlight docid) docid))) @@ -1773,12 +1773,12 @@ previous header." 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) + (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"))))) + (if backwards "previous" "next"))))) (defun mu4e-headers-prev-unread () "Move point to the previous message that is unread (and @@ -1810,17 +1810,17 @@ 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-get-headers-buffer)))) - (when (and (buffer-live-p (mu4e-get-view-buffer)) (window-live-p hwin)) - (let ((n (or n 1))) - (cl-case mu4e-split-view - ;; emacs has weird ideas about what horizontal, vertical means... - (horizontal - (window-resize hwin n nil) - (cl-incf mu4e-headers-visible-lines n)) - (vertical - (window-resize hwin n t) - (cl-incf mu4e-headers-visible-columns n))))))) + (hwin (get-buffer-window (mu4e-get-headers-buffer)))) + (when (and (buffer-live-p (mu4e-get-view-buffer)) (window-live-p hwin)) + (let ((n (or n 1))) + (cl-case mu4e-split-view + ;; emacs has weird ideas about what horizontal, vertical means... + (horizontal + (window-resize hwin n nil) + (cl-incf mu4e-headers-visible-lines n)) + (vertical + (window-resize hwin n t) + (cl-incf mu4e-headers-visible-columns n))))))) (defun mu4e-headers-split-view-shrink (&optional n) "In split-view, shrink the headers window. @@ -1838,7 +1838,7 @@ 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)))) + (afunc (or actionfunc (mu4e-read-option "Action: " mu4e-headers-actions)))) (funcall afunc msg))) (defun mu4e-headers-mark-and-next (mark) @@ -1854,28 +1854,28 @@ This is a rather complex function, to ensure we don't disturb other windows." (interactive) (if (eq mu4e-split-view 'single-window) - (progn (mu4e-mark-handle-when-leaving) - (kill-buffer)) + (progn (mu4e-mark-handle-when-leaving) + (kill-buffer)) (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)) + (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! + (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) diff --git a/mu4e/mu4e-icalendar.el b/mu4e/mu4e-icalendar.el index d50eaae5..8b1dce91 100644 --- a/mu4e/mu4e-icalendar.el +++ b/mu4e/mu4e-icalendar.el @@ -42,6 +42,9 @@ (require 'gnus-icalendar) (require 'cl-lib) +(eval-when-compile (require 'mu4e-mark)) +(eval-when-compile (require 'mu4e-vars)) + ;;;###autoload (defun mu4e-icalendar-setup () "Perform the necessary initialization to use mu4e-icalendar." @@ -49,34 +52,34 @@ (cl-defmethod gnus-icalendar-event:inline-reply-buttons :around ((event gnus-icalendar-event) handle) (if (and (boundp 'mu4e~view-rendering) - (gnus-icalendar-event:rsvp event)) - (let ((method (gnus-icalendar-event:method event))) - (when (or (string= method "REQUEST") (string= method "PUBLISH")) - `(("Accept" mu4e-icalendar-reply (,handle accepted ,event)) - ("Tentative" mu4e-icalendar-reply (,handle tentative ,event)) - ("Decline" mu4e-icalendar-reply (,handle declined ,event))))) + (gnus-icalendar-event:rsvp event)) + (let ((method (gnus-icalendar-event:method event))) + (when (or (string= method "REQUEST") (string= method "PUBLISH")) + `(("Accept" mu4e-icalendar-reply (,handle accepted ,event)) + ("Tentative" mu4e-icalendar-reply (,handle tentative ,event)) + ("Decline" mu4e-icalendar-reply (,handle declined ,event))))) (cl-call-next-method event handle)))) (defun mu4e-icalendar-reply (data) "Reply to the text/calendar event present in DATA." ;; Based on `gnus-icalendar-reply'. (let* ((handle (car data)) - (status (cadr data)) - (event (caddr data)) - (gnus-icalendar-additional-identities mu4e-user-mail-address-list) - (reply (gnus-icalendar-with-decoded-handle handle - (gnus-icalendar-event-reply-from-buffer - (current-buffer) status (gnus-icalendar-identities)))) - (msg (mu4e-message-at-point 'noerror)) - (charset (cdr (assoc 'charset (mm-handle-type handle))))) + (status (cadr data)) + (event (caddr data)) + (gnus-icalendar-additional-identities (mu4e-personal-addresses)) + (reply (gnus-icalendar-with-decoded-handle handle + (gnus-icalendar-event-reply-from-buffer + (current-buffer) status (gnus-icalendar-identities)))) + (msg (mu4e-message-at-point 'noerror)) + (charset (cdr (assoc 'charset (mm-handle-type handle))))) (when reply (cl-labels - ((fold-icalendar-buffer - () - (goto-char (point-min)) - (while (re-search-forward "^\\(.\\{72\\}\\)\\(.+\\)$" nil t) - (replace-match "\\1\n \\2") - (goto-char (line-beginning-position))))) + ((fold-icalendar-buffer + () + (goto-char (point-min)) + (while (re-search-forward "^\\(.\\{72\\}\\)\\(.+\\)$" nil t) + (replace-match "\\1\n \\2") + (goto-char (line-beginning-position))))) (with-current-buffer (get-buffer-create gnus-icalendar-reply-bufname) (delete-region (point-min) (point-max)) @@ -92,7 +95,7 @@ (gnus-icalendar--update-org-event event status)) (when mu4e-icalendar-diary-file (mu4e~icalendar-insert-diary event status - mu4e-icalendar-diary-file)))))) + mu4e-icalendar-diary-file)))))) (defun mu4e~icalendar-delete-citation () "Function passed to `mu4e-compose-cite-function' to remove the citation." @@ -104,17 +107,17 @@ "See `mu4e-sent-handler' for DOCID and PATH." (mu4e-sent-handler docid path) (let* ((docid (mu4e-message-field original-msg :docid)) - (markdescr (assq 'trash mu4e-marks)) - (action (plist-get (cdr markdescr) :action)) - (target (mu4e-get-trash-folder original-msg))) + (markdescr (assq 'trash mu4e-marks)) + (action (plist-get (cdr markdescr) :action)) + (target (mu4e-get-trash-folder original-msg))) (with-current-buffer (mu4e-get-headers-buffer) (run-hook-with-args 'mu4e-mark-execute-pre-hook 'trash original-msg) (funcall action docid original-msg target)) (when (and (mu4e~headers-view-this-message-p docid) - (buffer-live-p (mu4e-get-view-buffer))) + (buffer-live-p (mu4e-get-view-buffer))) (switch-to-buffer (mu4e-get-view-buffer)) (or (mu4e-view-headers-next) - (kill-buffer-and-window)))))) + (kill-buffer-and-window)))))) (defun mu4e-icalendar-reply-ical (original-msg event status buffer-name) "Reply to ORIGINAL-MSG containing invitation EVENT with STATUS. @@ -123,8 +126,8 @@ STATUS values. BUFFER-NAME is the name of the buffer holding the response in icalendar format." (let ((message-signature nil)) (let ((mu4e-compose-cite-function #'mu4e~icalendar-delete-citation) - (mu4e-sent-messages-behavior 'delete) - (mu4e-compose-reply-recipients 'sender)) + (mu4e-sent-messages-behavior 'delete) + (mu4e-compose-reply-recipients 'sender)) (mu4e~compose-handler 'reply original-msg)) ;; Make sure the recipient is the organizer (let ((organizer (gnus-icalendar-event:organizer event))) @@ -136,23 +139,23 @@ response in icalendar format." (mml-insert-multipart "alternative") (mml-insert-part "text/plain") (let ((reply-event (gnus-icalendar-event-from-buffer - buffer-name mu4e-user-mail-address-list))) + buffer-name (mu4e-personal-addresses)))) (insert (gnus-icalendar-event->gnus-calendar reply-event status))) (forward-line 1); move past closing tag (mml-attach-buffer buffer-name "text/calendar; method=REPLY; charset=utf-8") (message-remove-header "Subject") (message-goto-subject) (insert (capitalize (symbol-name status)) - ": " (gnus-icalendar-event:summary event)) + ": " (gnus-icalendar-event:summary event)) (set-buffer-modified-p nil); not yet modified by user (when mu4e-icalendar-trash-after-reply ;; Override `mu4e-sent-handler' set by `mu4e-compose-mode' to ;; also trash the message (thus must be appended to hooks). (add-hook - 'message-sent-hook - (lambda () (setq mu4e-sent-func - (mu4e~icalendar-trash-message original-msg))) - t t)))) + 'message-sent-hook + (lambda () (setq mu4e-sent-func + (mu4e~icalendar-trash-message original-msg))) + t t)))) (defun mu4e~icalendar-insert-diary (event reply-status filename) @@ -161,20 +164,20 @@ REPLY-STATUS is the status of the reply. The possible values are given in the doc of `gnus-icalendar-event-reply-from-buffer'." ;; FIXME: handle recurring events (let* ((beg (gnus-icalendar-event:start-time event)) - (beg-date (format-time-string "%d/%m/%Y" beg)) - (beg-time (format-time-string "%H:%M" beg)) - (end (gnus-icalendar-event:end-time event)) - (end-date (format-time-string "%d/%m/%Y" end)) - (end-time (format-time-string "%H:%M" end)) - (summary (gnus-icalendar-event:summary event)) - (location (gnus-icalendar-event:location event)) - (status (capitalize (symbol-name reply-status))) - (txt (if location - (format "%s (%s)\n %s " summary status location) - (format "%s (%s)" summary status)))) + (beg-date (format-time-string "%d/%m/%Y" beg)) + (beg-time (format-time-string "%H:%M" beg)) + (end (gnus-icalendar-event:end-time event)) + (end-date (format-time-string "%d/%m/%Y" end)) + (end-time (format-time-string "%H:%M" end)) + (summary (gnus-icalendar-event:summary event)) + (location (gnus-icalendar-event:location event)) + (status (capitalize (symbol-name reply-status))) + (txt (if location + (format "%s (%s)\n %s " summary status location) + (format "%s (%s)" summary status)))) (with-temp-buffer (if (string= beg-date end-date) - (insert beg-date " " beg-time "-" end-time " " txt "\n") + (insert beg-date " " beg-time "-" end-time " " txt "\n") (insert beg-date " " beg-time " Start of: " txt "\n") (insert beg-date " " end-time " End of: " txt "\n")) (write-region (point-min) (point-max) filename t)))) diff --git a/mu4e/mu4e-main.el b/mu4e/mu4e-main.el index ba52b08c..dfeaa606 100644 --- a/mu4e/mu4e-main.el +++ b/mu4e/mu4e-main.el @@ -27,6 +27,7 @@ (require 'smtpmail) ;; the queueing stuff (silence elint) (require 'mu4e-utils) ;; utility functions (require 'mu4e-context) ;; the context +(require 'mu4e-vars) ;; the context (require 'cl-lib) (defconst mu4e~main-buffer-name " *mu4e-main*" @@ -140,22 +141,31 @@ clicked." "\n"))) +(defun mu4e~key-val (key val &optional unit) + "Return a key / value pair." + (concat + " * " + (propertize (format "%-20s" key) 'face 'mu4e-header-title-face) + ": " + (propertize val 'face 'mu4e-header-key-face) + (if unit + (propertize (concat " " unit) 'face 'mu4e-header-title-face) + "") + "\n")) + ;; NEW ;; This is the old `mu4e~main-view' function but without ;; buffer switching at the end. -(defun mu4e~main-view-real (ignore-auto noconfirm) +(defun mu4e~main-view-real (_ignore-auto _noconfirm) (let ((buf (get-buffer-create mu4e~main-buffer-name)) (inhibit-read-only t)) (with-current-buffer buf (erase-buffer) (insert "* " - (propertize "mu4e - mu for emacs version " 'face 'mu4e-title-face) + (propertize "mu4e" 'face 'mu4e-header-key-face) + (propertize " - mu for emacs version " 'face 'mu4e-title-face) (propertize mu4e-mu-version 'face 'mu4e-header-key-face) - (propertize "; (in store: " 'face 'mu4e-title-face) - (propertize (format "%s" (plist-get mu4e~server-props :doccount)) 'face 'mu4e-header-key-face) - (propertize " messages)" 'face 'mu4e-title-face) - "\n\n" (propertize " Basics\n\n" 'face 'mu4e-title-face) (mu4e~main-action-str @@ -183,9 +193,15 @@ clicked." (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) - ))) + (mu4e~main-action-str "\t* [q]uit\n" 'mu4e-quit) + + "\n" + (propertize " Info\n\n" 'face 'mu4e-title-face) + (mu4e~key-val "database-path" (mu4e-database-path)) + (mu4e~key-val "maildir" (mu4e-root-maildir)) + (mu4e~key-val "in store" + (format "%d" (plist-get mu4e~server-props :doccount)) "messages")) + (mu4e-main-mode)))) (defun mu4e~main-view-queue () "Display queue-related actions in the main view." diff --git a/mu4e/mu4e-mark.el b/mu4e/mu4e-mark.el index e094e2dc..6a2086ee 100644 --- a/mu4e/mu4e-mark.el +++ b/mu4e/mu4e-mark.el @@ -1,6 +1,6 @@ ;; mu4e-mark.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;; -;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2020 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -288,7 +288,7 @@ The following marks are available, and the corresponding props: (target (if (string= (substring target 0 1) "/") target (concat "/" target))) - (fulltarget (concat mu4e-maildir target))) + (fulltarget (concat (mu4e-root-maildir) target))) (when (or (file-directory-p fulltarget) (and (yes-or-no-p (format "%s does not exist. Create now?" fulltarget)) @@ -368,7 +368,7 @@ user which one)." (defun mu4e~mark-check-target (target) "Check if TARGET exists; if not, offer to create it." - (let ((fulltarget (concat mu4e-maildir target))) + (let ((fulltarget (concat (mu4e-root-maildir) target))) (if (not (mu4e-create-maildir-maybe fulltarget)) (mu4e-error "Target dir %s does not exist " fulltarget) target))) diff --git a/mu4e/mu4e-message.el b/mu4e/mu4e-message.el index 3d4ea2bf..84923001 100644 --- a/mu4e/mu4e-message.el +++ b/mu4e/mu4e-message.el @@ -1,6 +1,6 @@ ;;; mu4e-message.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;; -;; Copyright (C) 2012-2018 Dirk-Jan C. Binnema +;; Copyright (C) 2012-2020 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -243,11 +243,11 @@ replace with." (with-temp-buffer (insert body) (goto-char (point-min)) - (while (re-search-forward "[  ’]" nil t) + (while (re-search-forward "[  ’]" nil t) (replace-match (cond - ((string= (match-string 0) "’") "'") - ((string= (match-string 0) " ") " ") + ((string= (match-string 0) "Â’") "'") + ((string= (match-string 0) " ") " ") (t "")))) (buffer-string))) @@ -282,14 +282,14 @@ expressions, in which case any of those are tried for a match." 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 +`(mu4e-personal-addresses)'. Returns the contact cell that matched, or nil." (cl-find-if (lambda (cc-cell) (cl-member-if (lambda (addr) (string= (downcase addr) (downcase (cdr cc-cell)))) - mu4e-user-mail-address-list)) + (mu4e-personal-addresses))) (mu4e-message-field msg cfield))) (defsubst mu4e-message-part-field (msgpart field) diff --git a/mu4e/mu4e-org.el b/mu4e/mu4e-org.el index fb571f56..1c0c4b10 100644 --- a/mu4e/mu4e-org.el +++ b/mu4e/mu4e-org.el @@ -1,5 +1,5 @@ ;;; mu4e-org -- Org-links to mu4e messages/queries -*- lexical-binding: t -*- -;; Copyright (C) 2012-2019 Dirk-Jan C. Binnema +;; Copyright (C) 2012-2020 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema diff --git a/mu4e/mu4e-proc.el b/mu4e/mu4e-proc.el index 357d6451..cdc780b4 100644 --- a/mu4e/mu4e-proc.el +++ b/mu4e/mu4e-proc.el @@ -180,8 +180,7 @@ The server output is as follows: ;; received a pong message ((plist-get sexp :pong) - (funcall mu4e-pong-func - (plist-get sexp :props))) + (funcall mu4e-pong-func sexp)) ;; received a contacts message ;; note: we use 'member', to match (:contacts nil) @@ -253,11 +252,7 @@ Start the process if needed." (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))))) - (args (append args (mapcar (lambda(addr) - (format "--my-address=%s" addr)) - mu4e-user-mail-address-list)))) + (args '("server"))) (setq mu4e~proc-buf "") (setq mu4e~proc-process (apply 'start-process mu4e~proc-name mu4e~proc-name @@ -395,15 +390,12 @@ or an error." :skip-dups ,skip-dups :include-related ,include-related))) -(defun mu4e~proc-index (path my-addresses cleanup lazy-check) - "Index messages on PATH with possible CLEANUP and LAZY-CHECK. +(defun mu4e~proc-index (&optional cleanup lazy-check) + "Index messages with possible CLEANUP and LAZY-CHECK. PATH should point to some maildir directory structure. MY-ADDRESSES is a list of 'my' email addresses (see `mu4e-user-mail-address-list')." - (mu4e~call-mu `(index - :my-addresses ,my-addresses - :cleanup ,cleanup - :lazy-check ,lazy-check))) + (mu4e~call-mu `(index :cleanup ,cleanup :lazy-check ,lazy-check))) (defun mu4e~proc-mkdir (path) "Create a new maildir-directory at filesystem PATH." @@ -447,26 +439,15 @@ Returns either (:update ... ) or (:error ) sexp, which are handled my (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 "/"))) + (file-exists-p (concat (mu4e-root-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~escape maildir)))) - (rename - (if (and maildir mu4e-change-filenames-when-moving) - "true" "false"))) - (mu4e~call-mu `(move - :docid ,(if (stringp docid-or-msgid) nil docid-or-msgid) - :msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil) - :flags ,(or flags nil) - :maildir ,(or maildir nil) - :rename ,(and maildir mu4e-change-filenames-when-moving) - :noview ,no-view)))) + (mu4e~call-mu `(move + :docid ,(if (stringp docid-or-msgid) nil docid-or-msgid) + :msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil) + :flags ,(or flags nil) + :maildir ,(or maildir nil) + :rename ,(and maildir mu4e-change-filenames-when-moving) + :noview ,no-view))) (defun mu4e~proc-ping (&optional queries) "Sends a ping to the mu server, expecting a (:pong ...) in response. diff --git a/mu4e/mu4e-utils.el b/mu4e/mu4e-utils.el index 491274bf..7191e0d9 100644 --- a/mu4e/mu4e-utils.el +++ b/mu4e/mu4e-utils.el @@ -83,10 +83,10 @@ NODEFAULT, hour and minute fields will be nil if not given." (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'. Case +User's addresses are set in `(mu4e-personal-addresses)'. Case insensitive comparison is used." - (when (and addr mu4e-user-mail-address-list - (cl-find addr mu4e-user-mail-address-list + (when (and addr (mu4e-personal-addresses) + (cl-find addr (mu4e-personal-addresses) :test (lambda (s1 s2) (eq t (compare-strings s1 nil nil s2 nil nil t))))) t)) @@ -182,10 +182,10 @@ see its docstring)." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~guess-maildir (path) "Guess the maildir for some path, or nil if cannot find it." - (let ((idx (string-match mu4e-maildir path))) + (let ((idx (string-match (mu4e-root-maildir) path))) (when (and idx (zerop idx)) (replace-regexp-in-string - mu4e-maildir + (mu4e-root-maildir) "" (expand-file-name (concat path "/../..")))))) @@ -322,7 +322,7 @@ Function will return the cdr of the list element." (dolist (dentry dentries) (when (and (booleanp (cadr dentry)) (cadr dentry)) (if (file-accessible-directory-p - (concat mu4e-maildir "/" mdir "/" (car dentry) "/cur")) + (concat (mu4e-root-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 @@ -332,7 +332,7 @@ Function will return the cdr of the list element." (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 +can set `(mu4e-root-maildir)-list' to nil to force regenerating the cache the next time `mu4e-get-maildirs' gets called.") (defvar mu4e-maildir-list nil @@ -344,14 +344,13 @@ relative paths (ie., /archive, /sent etc.). Most of the work is done in `mu4e~get-maildirs-1'. Note, these results are /cached/ if `mu4e-cache-maildir-list' is customized to non-nil. In that case, 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 "/")) + (concat (mu4e-root-maildir) "/cur")) '("/")) + (mu4e~get-maildirs-1 (mu4e-root-maildir) "/")) (lambda (s1 s2) (string< (downcase s1) (downcase s2)))))) mu4e-maildir-list) @@ -389,7 +388,7 @@ maildirs under `mu4e-maildir'." "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))) + (fullpath (concat (mu4e-root-maildir) mdir))) (unless (file-directory-p fullpath) (and (yes-or-no-p (mu4e-format "%s does not exist. Create now?" fullpath)) @@ -724,37 +723,27 @@ completion; for testing/debugging." (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")) + (unless (>= emacs-major-version 25) + (mu4e-error "Emacs >= 25.x is required for mu4e")) (when mu4e~server-props - (let ((version (plist-get mu4e~server-props :version)) - (mux (plist-get mu4e~server-props :mux))) - (unless (or (string= version mu4e-mu-version) mux) - (mu4e-error "mu server has version %s, but we need %s" - version mu4e-mu-version)))) + (unless (string= (mu4e-server-version) mu4e-mu-version) + (mu4e-error "mu server has version %s, but we need %s" + (mu4e-server-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))) + (path (concat (mu4e-root-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." @@ -787,14 +776,15 @@ nothing." mu4e-compose-complete-only-after mu4e~contacts-tstamp))) -(defun mu4e~pong-handler (props func) +(defun mu4e~pong-handler (data func) "Handle 'pong' responses from the mu server." - (setq mu4e~server-props props) ;; save props from the server - (let ((doccount (plist-get props :doccount))) + (setq mu4e~server-props (plist-get data :props)) ;; save info from the server + (let ((doccount (plist-get mu4e~server-props :doccount))) (mu4e~check-requirements) + (mu4e~context-autoswitch nil mu4e-context-policy) (when func (funcall func)) (when (zerop doccount) - (mu4e-message "Store is empty; (re)indexing. This can take a while.") ; + (mu4e-message "Store is empty; (re)indexing. This may take a while.") ; (mu4e-update-index)) (when (and mu4e-update-interval (null mu4e~update-timer)) (setq mu4e~update-timer @@ -811,11 +801,6 @@ non-nil). Otherwise, check various requireme`'nts, then start mu4e. When successful, call FUNC (if non-nil) afterwards." ;; if we're already running, simply go to the main view (unless (mu4e-running-p) ;; already running? - ;; 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) - (mu4e~check-requirements) - ;; when it's visible, re-draw the main view when there are changes. (add-hook 'mu4e-index-updated-hook (lambda() @@ -823,7 +808,7 @@ When successful, call FUNC (if non-nil) afterwards." (when (and (buffer-live-p mainbuf) (get-buffer-window mainbuf)) (mu4e~start 'mu4e~main-view)))))) - (setq mu4e-pong-func (lambda (props) (mu4e~pong-handler props func))) + (setq mu4e-pong-func (lambda (info) (mu4e~pong-handler info func))) (mu4e~proc-ping (mapcar ;; send it a list of queries we'd like to see read/unread info ;; for. @@ -906,12 +891,7 @@ Also scrolls to the final line, and update the progress throbber." (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)) + (mu4e~proc-index mu4e-index-cleanup mu4e-index-lazy-check)) (defvar mu4e~update-buffer nil "Internal, store the buffer of the update process when diff --git a/mu4e/mu4e-vars.el b/mu4e/mu4e-vars.el index acca69ef..87ecffe1 100644 --- a/mu4e/mu4e-vars.el +++ b/mu4e/mu4e-vars.el @@ -1,6 +1,6 @@ ;;; mu4e-vars.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;; -;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2020 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -29,6 +29,7 @@ (require 'mu4e-meta) (require 'message) +(declare-function mu4e-error "mu4e-utils") (defgroup mu4e nil "mu4e - mu for emacs" @@ -50,13 +51,8 @@ path." :group 'mu4e :safe 'stringp) -(defcustom mu4e-maildir (expand-file-name "~/Maildir") - "The absolute file system path to your Maildir. -Must not be a symbolic link, and after starting mu4e, cannot -change until after quitting." - :type 'directory - :safe 'stringp - :group 'mu4e) +(make-obsolete-variable 'mu4e-maildir + "determined by server; see `mu4e-root-maildir'." "1.3.8") (defcustom mu4e-org-support t "Support org-mode links." @@ -172,9 +168,10 @@ matched case-insensitively." ;; 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") +(make-obsolete-variable 'mu4e-user-mail-address-list + "determined by server; see `mu4e-personal-addresses'." "1.3.8") (defcustom mu4e-use-fancy-chars nil "When set, allow fancy (Unicode) characters for marks/threads. @@ -365,10 +362,8 @@ The setting is a symbol: (defcustom mu4e-compose-complete-only-personal nil "Whether to consider only 'personal' e-mail addresses for completion. That is, 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)." +of the address fields (this excludes mailing list messages). +These addresses are the ones specified with `mu init'." :type 'boolean :group 'mu4e-compose) @@ -926,13 +921,42 @@ 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.") +(defvar mu4e~server-props nil + "Information we receive from the mu4e server process \(in the 'pong-handler').") + +(defun mu4e-root-maildir() + "Get the root maildir." + (let ((root-maildir (and mu4e~server-props + (plist-get mu4e~server-props :root-maildir)))) + (unless root-maildir + (mu4e-error "root maildir unknown; did you start mu4e?")) + root-maildir)) + +(defun mu4e-database-path() + "Get the mu4e database path" + (let ((path (and mu4e~server-props + (plist-get mu4e~server-props :database-path)))) + (unless path + (mu4e-error "database-path unknown; did you start mu4e?")) + path)) + +(defun mu4e-personal-addresses() + "Get the user's personal addresses, if any. If none are set on the server-side, +fall back to the obsolete `mu4e-user-mail-address-list'." + (let ((addrs (and mu4e~server-props + (plist-get mu4e~server-props :personal-addresses)))) + (if addrs addrs mu4e-user-mail-address-list))) + +(defun mu4e-server-version() + "Get the server version, which should match mu4e's." + (let ((version (and mu4e~server-props (plist-get mu4e~server-props :version)))) + (unless version + (mu4e-error "version unknown; did you start mu4e?")) + version)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; our handlers funcs these handler funcs define what happens when we receive a diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el index 570e3192..d27e1966 100644 --- a/mu4e/mu4e-view.el +++ b/mu4e/mu4e-view.el @@ -1,6 +1,6 @@ ;;; mu4e-view.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;; -;; Copyright (C) 2011-2018 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2020 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -392,7 +392,7 @@ article-mode." (run-hooks 'gnus-article-decode-hook) (let ((mu4e~view-rendering t) ; customize gnus in mu4e (max-specpdl-size mu4e-view-max-specpdl-size) - (gnus-icalendar-additional-identities mu4e-user-mail-address-list)) + (gnus-icalendar-additional-identities (mu4e-personal-addresses))) (gnus-article-prepare-display)) (mu4e-view-mode) (setq mu4e~view-message msg) diff --git a/mu4e/mu4e.texi b/mu4e/mu4e.texi index b2976389..d5f3a844 100644 --- a/mu4e/mu4e.texi +++ b/mu4e/mu4e.texi @@ -217,6 +217,7 @@ After these steps, @t{mu4e} should be ready to go! * Requirements:: What is needed * Installation:: How to install @t{mu} and @t{mu4e} * Getting mail:: Getting mail from a server +* Initializing the message store:: Settings things up * Indexing your messages:: Creating and maintaining the index * Basic configuration:: Settings for @t{mu4e} * Folders:: Setting up standard folders @@ -230,14 +231,14 @@ After these steps, @t{mu4e} should be ready to go! @section Requirements @t{mu}/@t{mu4e} are known to work on a wide variety of Unix- and -Unix-like systems, including many Linux distributions, OS X and FreeBSD, -and even on MS-Windows (with Cygwin). @command{emacs} 23 or 24 -(recommended) is required, as well as +Unix-like systems, including many Linux distributions, OS X and +FreeBSD, and even on MS-Windows (with Cygwin). @command{emacs} 24 or +higher is required, as well as Xapian@footnote{@url{https://xapian.org/}} and GMime@footnote{@url{http://spruce.sourceforge.net/gmime/}}. @t{mu} has optional support for the Guile 2.x (Scheme) programming -language. There are also some GUI-tools, which require GTK+ 3.x and +language. There are also some GUI-toys, which require GTK+ 3.x and Webkit. If you intend to compile @t{mu} yourself, you need to have the typical @@ -249,16 +250,17 @@ you also need the development packages for GTK+, Webkit and Guile. @node Installation @section Installation -@t{mu4e} is part of @t{mu} --- by installing the latter, the former is installed -as well. Some Linux distributions provide packaged versions of -@t{mu}/@t{mu4e}; if you can use those, there is no need to compile anything -yourself. However, if there are no packages for your distribution, if they are -outdated, or if you want to use the latest development versions, you can -follow the steps below. +@t{mu4e} is part of @t{mu} --- by installing the latter, the former is +installed as well. Some Linux distributions provide packaged versions +of @t{mu}/@t{mu4e}; if you can use those, there is no need to compile +anything yourself. However, if there are no packages for your +distribution, if they are outdated, or if you want to use the latest +development versions, you can follow the steps below. -First, you need make sure you have the necessary dependencies; the details -depend on your distribution. If you're using another distribution (or another -OS), the below can at least be helpful in identifying the packages to install. +First, you need make sure you have the necessary dependencies; the +details depend on your distribution. If you're using another +distribution (or another OS), the below can at least be helpful in +identifying the packages to install. We provide some instructions for Debian, Ubuntu and Fedora; if those do not apply to you, you can follow either @ref{Building from a release tarball} or @@ -356,46 +358,74 @@ through things step-by-step. @node Getting mail @section Getting mail -In order for @t{mu} (and, by extension, @t{mu4e}) to work, you need to have -your e-mail messages stored in a -@emph{maildir}@footnote{@url{https://en.wikipedia.org/wiki/Maildir}; in this -manual we use the term `maildir' for both the standard and the hierarchy of -maildirs that store your messages} --- a specific directory structure with -one-file-per-message. If you are already using a maildir, you are lucky. If -not, some setup is required: +In order for @t{mu} (and, by extension, @t{mu4e}) to work, you need to +have your e-mail messages stored in a +@emph{maildir}@footnote{@url{https://en.wikipedia.org/wiki/Maildir}; +in this manual we use the term `maildir' for both the standard and the +hierarchy of maildirs that store your messages} --- a specific +directory structure with one-file-per-message. If you are already +using a maildir, you are lucky. If not, some setup is required: @itemize @item @emph{Using an external IMAP or POP server} --- if you are using an @abbr{IMAP} or @abbr{POP} server, you can use tools like @t{getmail}, -@t{fetchmail}, @t{offlineimap} or @t{isync} to download your messages into a -maildir (@file{~/Maildir}, often). Because it is such a common case, there is -a full example of setting @t{mu4e} up with @t{offlineimap} and Gmail; -@pxref{Gmail configuration}. +@t{fetchmail}, @t{offlineimap} or @t{isync} to download your messages +into a maildir (@file{~/Maildir}, often). Because it is such a common +case, there is a full example of setting @t{mu4e} up with +@t{offlineimap} and Gmail; @pxref{Gmail configuration}. @item @emph{Using a local mail server} --- if you are using a local mail-server -(such as @t{postfix} or @t{qmail}), you can teach them to deliver into a -maildir as well, maybe in combination with @t{procmail}. A bit of googling -should be able to provide you with the details. +(such as @t{postfix} or @t{qmail}), you can teach them to deliver into +a maildir as well, maybe in combination with @t{procmail}. A bit of +googling should be able to provide you with the details. @end itemize +@node Initializing the message store +@section Initializing the message store + +The first time your run @t{mu}, you need to initialize its store +(database). The default location for that is @t{~/.cache/mu/xapian}, +but you can change this using the @t{--muhome} option, and remember to +pass that to the other commands as well. + +Assuming that your maildir is at @file{~/Maildir}, we issue the +following command: +@example + $ mu init --maildir=~/Maildir +@end example + +Optionally, you can add some e-mail addresses, so @t{mu} recognizes +them as yours: + +@example + $ mu init --maildir=~/Maildir --my-address=jim@@example.com --my-address=bob@@example.com +@end example + +@t{mu} remembers the maildir and your addresses and uses them when +indexing messages. If you want to change them, you need to @t{init} +once again. + +If you want to see the current values, you can use @t{mu info}. + @node Indexing your messages @section Indexing your messages -After you have succeeded in @ref{Getting mail}, we need to @emph{index} the -messages. That is --- we need to scan the messages in the maildir and store the -information about them in a special database. We can do that from @t{mu4e} --- -@ref{Main view}, but the first time, it is a good idea to run it from the -command line, which makes it easier to verify that everything works correctly. +After you have succeeded in @ref{Getting mail} and initialized the +message database, we need to @emph{index} the messages. That is --- we +need to scan the messages in the maildir and store the information +about them in a special database. + +We can do that from @t{mu4e} --- @ref{Main view}, but the first time, +it is a good idea to run it from the command line, which makes it +easier to verify that everything works correctly. Assuming that your maildir is at @file{~/Maildir}, we issue the following command: @example - $ mu index --maildir=~/Maildir + $ mu index @end example -This should scan your @file{~/Maildir}@footnote{In most cases, you do not even -need to provide the @t{--maildir=~/Maildir} since it is the default; see the -@t{mu-index} man-page for details} and fill the database, and give progress -information while doing so. +This should scan your messages and fill the database, and give +progress information while doing so. The indexing process may take a few minutes the first time you do it (for thousands of e-mails); afterwards it is much faster, since @t{mu} @@ -448,19 +478,15 @@ situation. See @ref{Dynamic folders} for details.}: @lisp ;; these are actually the defaults (setq - mu4e-maildir "~/Maildir" ;; top-level Maildir mu4e-sent-folder "/sent" ;; folder for sent messages mu4e-drafts-folder "/drafts" ;; unfinished messages mu4e-trash-folder "/trash" ;; trashed messages mu4e-refile-folder "/archive") ;; saved messages @end lisp -Note, @code{mu4e-maildir} takes an actual filesystem-path, the other -folder names are all relative to @code{mu4e-maildir}. Also note that -this must @emph{not} be a symbolic link. - -If you use @t{mu4e-context}, see @ref{Contexts and special folders} for -what that means for these special folders. +Note, the folder names are all relative to the root-maildir (see the +output of @t{mu info}). If you use @t{mu4e-context}, see @ref{Contexts +and special folders} for what that means for these special folders. @node Retrieval and indexing @section Retrieval and indexing with mu4e @@ -684,9 +710,9 @@ The main view looks something like the following: Bookmarks - * [bu] Unread messages (26217/26217) - * [bt] Today's messages (2/8) - * [bw] Last 7 days (7/34) + * [bu] Unread messages (26217/26217) + * [bt] Today's messages (2/8) + * [bw] Last 7 days (7/34) * [bp] Messages with images (276/2315) Misc @@ -710,11 +736,11 @@ Let's walk through the menu. First, the @emph{Basics}: @itemize @item @t{[j]ump to some maildir}: after pressing @key{j} (``jump''), -@t{mu4e} asks you for a maildir to visit. These are the maildirs you set in -@ref{Basic configuration} and any of your own. If you choose @key{o} -(``other'') or @key{/}, you can choose from all maildirs under -@code{mu4e-maildir}. After choosing a maildir, the messages in that maildir -are listed, in the @ref{Headers view}. +@t{mu4e} asks you for a maildir to visit. These are the maildirs you +set in @ref{Basic configuration} and any of your own. If you choose +@key{o} (``other'') or @key{/}, you can choose from all maildirs under +the root-maildir. After choosing a maildir, the messages in that +maildir are listed, in the @ref{Headers view}. @item @t{enter a [s]earch query}: after pressing @key{s}, @t{mu4e} asks you for a search query, and after entering one, shows the results in the @ref{Headers view}. @@ -2630,9 +2656,8 @@ invoke those function even in that case. the current context is also visible in the mode-line when in headers, view or main mode. @item You can set any kind of variable; including settings for mail servers etc. -However, settings such as @code{mu4e-maildir} and @code{mu4e-mu-home} -are not changeable after they have been set without quitting @t{mu4e} -first. +However, settings such as @code{mu4e-mu-home} are not changeable after +they have been set without quitting @t{mu4e} first. @item @code{leave-func} (if defined) for the context we are leaving, is invoked before the @code{enter-func} (if defined) of the context we are entering. @@ -3512,10 +3537,6 @@ see, most of it is commented-out. ;; use mu4e for e-mail in emacs (setq mail-user-agent 'mu4e-user-agent) -;; Only needed if your maildir is _not_ ~/Maildir -;; Must be a real dir, not a symlink -;;(setq mu4e-maildir "/home/user/Maildir") - ;; these must start with a "/", and must exist ;; (i.e.. /home/user/Maildir/sent must exist) ;; you use e.g. 'mu mkdir' to make the Maildirs if they don't @@ -3549,10 +3570,8 @@ customize. ;; use mu4e for e-mail in emacs (setq mail-user-agent 'mu4e-user-agent) -;; path to our Maildir directory -(setq mu4e-maildir "/home/user/Maildir") - -;; the next are relative to `mu4e-maildir' +;; the next are relative to the root maildir +;; (see `mu info`). ;; instead of strings, they can be functions too, see ;; their docstring or the chapter 'Dynamic folders' (setq mu4e-sent-folder "/sent" @@ -3718,9 +3737,6 @@ Next step: let's make a @t{mu4e} configuration for this: ;; use mu4e for e-mail in emacs (setq mail-user-agent 'mu4e-user-agent) -;; default -;; (setq mu4e-maildir "~/Maildir") - (setq mu4e-drafts-folder "/[Gmail].Drafts") (setq mu4e-sent-folder "/[Gmail].Sent Mail") (setq mu4e-trash-folder "/[Gmail].Trash") @@ -4207,7 +4223,6 @@ github-repository. @menu * Fancy characters:: Non-ascii characters in the UI -* Multiple accounts:: (Obsolete) the old way to deal with multiple accounts * Refiling messages:: Moving message to some archive folder * Saving outgoing messages:: Automatically save sent messages * Confirmation before sending:: Check messages before sending @@ -4242,121 +4257,6 @@ try the @emph{unicode-fonts} package: (unicode-fonts-setup) @end lisp - -@node Multiple accounts -@section Multiple accounts - -@b{Note}: for @t{mu4e} version 0.9.16 and higher, the recommended way -to deal with multiple accounts is through @t{mu4e}'s built-in -@ref{Contexts} system. For older versions, the below still works. - -Using @t{mu4e} with multiple email accounts is fairly easy. Although -variables such as @code{user-mail-address}, @code{mu4e-sent-folder}, -@code{message-*}, @code{smtpmail-*}, etc. typically only take one value, -it is easy to change their values using @code{mu4e-compose-pre-hook}. -The setup described here is one way of doing this (though certainly not -the only way). - -This setup assumes that you have multiple mail accounts under -@code{mu4e-maildir}. As an example, we'll use @t{~/Maildir/Account1} -and @t{~/Maildir/Account2}, but the setup works just as well if -@code{mu4e-maildir} points to something else. - -First, you need to make sure that all variables that you wish to change -based on user account are set to some initial value. So set up your -environment with e.g., your main account: - -@lisp -(setq mu4e-sent-folder "/Account1/Saved Items" - mu4e-drafts-folder "/Account1/Drafts" - user-mail-address "my.address@@account1.example.com" - smtpmail-default-smtp-server "smtp.account1.example.com" - smtpmail-local-domain "account1.example.com" - smtpmail-smtp-server "smtp.account1.example.com" - smtpmail-stream-type 'starttls - smtpmail-smtp-service 25) -@end lisp - -Then create a variable @code{my-mu4e-account-alist}, which should -contain a list for each of your accounts. Each list should start with -the account name, (which @emph{must} be identical to the account's -directory name under @t{~/Maildir}), followed by @code{(variable -value)} pairs: - -@lisp -(defvar my-mu4e-account-alist - '(("Account1" - (mu4e-sent-folder "/Account1/Saved Items") - (mu4e-drafts-folder "/Account1/Drafts") - (user-mail-address "my.address@@account1.example.com") - (smtpmail-default-smtp-server "smtp.account1.example.com") - (smtpmail-local-domain "account1.example.com") - (smtpmail-smtp-user "username1") - (smtpmail-smtp-server "smtp.account1.example.com") - (smtpmail-stream-type starttls) - (smtpmail-smtp-service 25)) - ("Account2" - (mu4e-sent-folder "/Account2/Saved Items") - (mu4e-drafts-folder "/Account2/Drafts") - (user-mail-address "my.address@@account2.example.com") - (smtpmail-default-smtp-server "smtp.account2.example.com") - (smtpmail-local-domain "account2.example.com") - (smtpmail-smtp-user "username2") - (smtpmail-smtp-server "smtp.account2.example.com") - (smtpmail-stream-type starttls) - (smtpmail-smtp-service 587)))) -@end lisp - -You can put any variable you want in the account lists, just make sure -that you put in @emph{all} the variables that differ for each account. -Variables that do not differ need not be included. For example, if you -use the same SMTP server for both accounts, you don't need to include -the SMTP-related variables in @code{my-mu4e-account-alist}. - -Note that some SMTP servers (such as Gmail) require the SMTP username to -match the user mail address. In this case, your mail appears to -originate from whichever SMTP account you use. Thus unless you are -certain your SMTP server does not have this requirement, you should -generally use different SMTP account credentials for each mail account. - -Now, the following function can be used to select an account and set the -variables in @code{my-mu4e-account-alist} to the correct values: - -@lisp -(defun my-mu4e-set-account () - "Set the account for composing a message." - (let* ((account - (if mu4e-compose-parent-message - (let ((maildir (mu4e-message-field mu4e-compose-parent-message :maildir))) - (string-match "/\\(.*?\\)/" maildir) - (match-string 1 maildir)) - (completing-read (format "Compose with account: (%s) " - (mapconcat #'(lambda (var) (car var)) - my-mu4e-account-alist "/")) - (mapcar #'(lambda (var) (car var)) my-mu4e-account-alist) - nil t nil nil (caar my-mu4e-account-alist)))) - (account-vars (cdr (assoc account my-mu4e-account-alist)))) - (if account-vars - (mapc #'(lambda (var) - (set (car var) (cadr var))) - account-vars) - (error "No email account found")))) -@end lisp - -This function then needs to be added to @code{mu4e-compose-pre-hook}: - -@lisp -(add-hook 'mu4e-compose-pre-hook 'my-mu4e-set-account) -@end lisp - -This way, @code{my-mu4e-set-account} is called every time you edit a -message. If you compose a new message, it simply asks you for the -account you wish to send the message from (TAB completion works). If -you're replying or forwarding a message, or editing an existing draft, -the account is chosen automatically, based on the first component of the -maildir of the message being replied to, forwarded or edited (i.e., the -directory under @t{~/Maildir}). - @node Refiling messages @section Refiling messages