Merge branch 'wip/djcb/toggle-property'

This commit is contained in:
Dirk-Jan C. Binnema
2022-12-11 15:36:57 +02:00
7 changed files with 603 additions and 565 deletions

19
.dir-locals.el Normal file
View File

@ -0,0 +1,19 @@
;;; Directory Local Variables -*- no-byte-compile: t; -*-
;;; For more information see (info "(emacs) Directory Variables")
((nil . ((tab-width . 8)
(fill-column . 80)
;; (commment-fill-column . 80)
(emacs-lisp-docstring-fill-column . 65)
(bug-reference-url-format . "https://github.com/djcb/mu/issues/%s")))
(c-mode . ((c-file-style . "linux")
(indent-tabs-mode . t)
(mode . bug-reference-prog)))
(c++-mode . ((c-file-style . "linux")
(fill-column . 100)
;; (comment-fill-column . 80)
(mode . bug-reference-prog)))
(emacs-lisp-mode . ((indent-tabs-mode . nil)
(mode . bug-reference-prog)))
(lisp-data-mode . ((indent-tabs-mode . nil)))
(texinfo-mode . ((mode . bug-reference-prog)))
(org-mode . ((mode . bug-reference))))

View File

@ -15,13 +15,31 @@
there's now 'z' binding (~mu4e-view-detach~), to 'detach' view and alllow
for keeping multiple views around; see the Mu4e manual for details.
- One very visible (experimental) change is that the main-window is no
longer full-screen
- when moving messages (which includes changing flags), file-flags changes
are propagated to duplicates of the messages; that is, e.g. the /Seen/ or
/Replied/ status is propagated to all duplicates (earlier, this was only
done when marking a message as read). Note, /Draft/, /Flagged/ and /Trashed/
flags are deliberately *not* propagated.
- teach ~mu4e-copy-thing-at-point~ about shr links
- teach ~mu4e-copy-thing-at-point~ about ~shr~ links
- The ~mu4e-headers-toggle-setting~ has been renamed
~mu4e-headers-toggle-property~ and has the new default binding ~P~, which
works in both the headers-view and message-view. The older functions
~mu4e-headers-toggle-threading~, ~mu4e-headers-toggle-threading~,
~mu4e-headers-toggle-full-search~ ~mu4e-headers-toggle-include-related~,
~full-search~skip-duplicates~ have been removed (with their keybindings) in
favor of ~mu4e-headers-toggle-property~.
- There's also a new property ~mu4e-headers-hide-enabled~, which controls
wheter ~mu4e-headers-hide-predicate~ is applied (when non-~nil~). This can be
used to temporarily turn the predicate off/on.
- When searching, the number of hidden messages is now shown in the
message footer along with the number of Found messages
- all the obsolete function and variable aliases have been moved to
~mu4e-obsolete.el~ so we can unclutter the non-obsolete code a bit.

View File

@ -32,7 +32,8 @@
(require 'fringe)
(require 'hl-line)
(require 'mailcap)
(require 'mule-util) ;; seems _some_ people need this for truncate-string-ellipsis
(require 'mule-util) ;; seems _some_ people need this for
;; truncate-string-ellipsis
(require 'mu4e-update)
@ -129,11 +130,10 @@ next mail after marking a message in header view."
(defvar mu4e-headers-hide-predicate nil
"Predicate function to hide matching heasders.
If the function evaluates to non-nil when applied a a message
plist, do not show the corresponding header. The function takes
one parameter MSG, which is the message plist for the message to
be hidden or not.
"Predicate function to hide matching headers.
Either nil or a function taking one message plist parameter and
which which return non-nil for messages that should be hidden from
the search results. Also see `mu4e-headers-hide-enabled'.
Example that hides all trashed messages:
@ -141,8 +141,14 @@ Example that hides all trashed messages:
(lambda (msg)
(member \='trashed (mu4e-message-field msg :flags)))).")
(defvar mu4e-headers-hide-enabled t
"Whether `mu4e-headers-hide-predicate' should be active.
This can be used to toggle use of the predicate through
`mu4e-headers-toggle-property'.")
(defcustom mu4e-headers-visible-flags
'(draft flagged new passed replied trashed attach encrypted signed list personal)
'(draft flagged new passed replied trashed attach encrypted signed
list personal)
"An ordered list of flags to show in the headers buffer.
Each element is a symbol in the list.
@ -159,9 +165,9 @@ mostly covered by `new', and the display gets cluttered otherwise."
(const :tag "Attach" attach)
(const :tag "Encrypted" encrypted)
(const :tag "Signed" signed)
(const :tag "List" list)
(const :tag "Personal" personal)
(const :tag "Calendar" calendar))
(const :tag "List" list)
(const :tag "Personal" personal)
(const :tag "Calendar" calendar))
:group 'mu4e-headers)
(defcustom mu4e-headers-found-hook nil
@ -269,6 +275,9 @@ Must have the same length as `mu4e-headers-thread-connection-prefix'.")
"Non-fancy and fancy labels to indicate related search in the mode-line.")
(defvar mu4e-headers-skip-duplicates-label '("U" . "") ;; 'U' for 'unique'
"Non-fancy and fancy labels for include-related search in the mode-line.")
(defvar mu4e-headers-hide-label '("H" . "")
"Non-fancy and fancy labels to indicate header-hiding is active in
the mode-line.")
;;;; Various
@ -333,6 +342,9 @@ In the format needed for `mu4e-read-option'.")
"If non-nil, report on the time it took to render the messages.
This is mostly useful for profiling.")
(defvar mu4e~headers-hidden 0
"Number of headers hidden due to `mu4e-headers-hide-predicate'.")
;;; Clear
@ -341,12 +353,13 @@ This is mostly useful for profiling.")
"Clear the headers buffer and related data structures.
Optionally, show TEXT."
(when (buffer-live-p (mu4e-get-headers-buffer))
(setq mu4e~headers-render-start (float-time))
(setq mu4e~headers-render-start (float-time)
mu4e~headers-hidden 0)
(let ((inhibit-read-only t))
(with-current-buffer (mu4e-get-headers-buffer)
(mu4e--mark-clear)
(erase-buffer)
(when text
(when text
(goto-char (point-min))
(insert (propertize text 'face 'mu4e-system-face 'intangible t)))))))
@ -359,7 +372,7 @@ into a string."
(mapconcat
(lambda (contact)
(let ((name (mu4e-contact-name contact))
(email (mu4e-contact-email contact)))
(email (mu4e-contact-email contact)))
(or name email "?"))) contacts ", "))
(defun mu4e~headers-thread-prefix-map (type)
@ -374,7 +387,8 @@ into a string."
(connection (funcall get-prefix mu4e-headers-thread-connection-prefix))
(blank (funcall get-prefix mu4e-headers-thread-blank-prefix))
(orphan (funcall get-prefix mu4e-headers-thread-orphan-prefix))
(single-orphan (funcall get-prefix mu4e-headers-thread-single-orphan-prefix))
(single-orphan (funcall get-prefix
mu4e-headers-thread-single-orphan-prefix))
(duplicate (funcall get-prefix mu4e-headers-thread-duplicate-prefix))
(t "?"))))
@ -481,7 +495,8 @@ with DOCID which must be present in the headers buffer."
(if single-orphan 'single-orphan
(if (and orphan
(or first-child
(not (eq mu4e-headers-thread-mark-as-orphan 'first))))
(not (eq mu4e-headers-thread-mark-as-orphan
'first))))
'orphan
(if last-child 'last-child
(if first-child 'first-child
@ -507,14 +522,14 @@ 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)."
(or (mapconcat
(lambda (flag)
(when (member flag mu4e-headers-visible-flags)
(if-let* ((mark (intern-soft
(format "mu4e-headers-%s-mark" (symbol-name flag))))
(cell (symbol-value mark)))
(if mu4e-use-fancy-chars (cdr cell) (car cell))
"")))
flags "")
(lambda (flag)
(when (member flag mu4e-headers-visible-flags)
(if-let* ((mark (intern-soft
(format "mu4e-headers-%s-mark" (symbol-name flag))))
(cell (symbol-value mark)))
(if mu4e-use-fancy-chars (cdr cell) (car cell))
"")))
flags "")
""))
;;; Special headers
@ -526,8 +541,8 @@ addresses, (as per `mu4e-personal-address-p'), show the To
address. Otherwise, show the From address, prefixed with the
appropriate `mu4e-headers-from-or-to-prefix'."
(let* ((from1 (car-safe (mu4e-message-field msg :from)))
(from1-addr (and from1 (mu4e-contact-email from1)))
(is-user (and from1-addr (mu4e-personal-address-p from1-addr))))
(from1-addr (and from1 (mu4e-contact-email from1)))
(is-user (and from1-addr (mu4e-personal-address-p from1-addr))))
(if is-user
(concat (cdr mu4e-headers-from-or-to-prefix)
(mu4e~headers-contact-str (mu4e-message-field msg :to)))
@ -591,8 +606,8 @@ found."
(truncate-string-to-width val 600)))
(:thread-subject ;; if not searching threads, fall back to :subject
(if mu4e-search-threads
(mu4e~headers-thread-subject msg)
(mu4e~headers-field-value msg :subject)))
(mu4e~headers-thread-subject msg)
(mu4e~headers-field-value msg :subject)))
((:maildir :path :message-id) val)
((:to :from :cc :bcc) (mu4e~headers-contact-str val))
;; if we (ie. `user-mail-address' is the 'From', show
@ -650,41 +665,47 @@ space propertized with a `display' text property which expands to
(let* ((field (car f-w))
(width (cdr f-w))
(val (mu4e~headers-field-value msg field))
(val (and val (if width (mu4e~headers-truncate-field field val width) val))))
(val (and val
(if width
(mu4e~headers-truncate-field field val width)
val))))
val))
(defsubst mu4e~headers-apply-flags (msg fieldval)
"Adjust FIELDVAL's face property based on flags in MSG."
(let* ((flags (plist-get msg :flags))
(meta (plist-get msg :meta))
(face (cond
((memq 'trashed flags) 'mu4e-trashed-face)
(meta (plist-get msg :meta))
(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)
((plist-get meta :related) 'mu4e-related-face)
((plist-get meta :related) 'mu4e-related-face)
((memq 'replied flags) 'mu4e-replied-face)
((memq 'passed flags) 'mu4e-forwarded-face)
(t 'mu4e-header-face))))
(t 'mu4e-header-face))))
(add-face-text-property 0 (length fieldval) face t fieldval)
fieldval))
(defsubst mu4e~message-header-line (msg)
"Return a propertized description of MSG suitable for
"Return a propertized description of message MSG suitable for
displaying in the header view."
(unless (and mu4e-headers-hide-predicate
(funcall mu4e-headers-hide-predicate msg))
(mu4e~headers-apply-flags
msg
(mapconcat (lambda (f-w) (mu4e~headers-field-handler f-w msg))
mu4e-headers-fields " "))))
(if (and mu4e-headers-hide-enabled mu4e-headers-hide-predicate
(funcall mu4e-headers-hide-predicate msg))
(progn
(cl-incf mu4e~headers-hidden)
nil)
(progn
(mu4e~headers-apply-flags
msg
(mapconcat (lambda (f-w) (mu4e~headers-field-handler f-w msg))
mu4e-headers-fields " ")))))
(defsubst mu4e~headers-insert-header (msg pos)
"Insert a header for MSG at point POS."
(when-let ((line (mu4e~message-header-line msg))
(docid (plist-get msg :docid)))
(docid (plist-get msg :docid)))
(goto-char pos)
(insert
(propertize
@ -716,7 +737,8 @@ docid is not found."
(defun mu4e~headers-view-this-message-p (docid)
"Is DOCID currently being viewed?"
(mu4e-get-view-buffers (lambda (_) (eq docid (plist-get mu4e~view-message :docid)))))
(mu4e-get-view-buffers
(lambda (_) (eq docid (plist-get mu4e~view-message :docid)))))
;; note: this function is very performance-sensitive
(defun mu4e~headers-append-handler (msglst)
@ -725,11 +747,11 @@ Do this at the end of the headers-buffer."
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(save-excursion
(let ((inhibit-read-only t))
(seq-do
(lambda (msg)
(mu4e~headers-insert-header msg (point-max)))
msglst))))))
(let ((inhibit-read-only t))
(seq-do
(lambda (msg)
(mu4e~headers-insert-header msg (point-max)))
msglst))))))
(defun mu4e~headers-update-handler (msg is-move maybe-view)
@ -741,7 +763,7 @@ headers."
(let* ((docid (mu4e-message-field msg :docid))
(initial-message-at-point (mu4e~headers-docid-at-point))
(initial-column (current-column))
(inhibit-read-only t)
(inhibit-read-only t)
(point (mu4e~headers-docid-pos docid))
(markinfo (gethash docid mu4e--mark-map)))
(when point ;; is the message present in this list?
@ -772,8 +794,8 @@ headers."
;; 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
(save-excursion
(mu4e~headers-insert-header msg point)))
(save-excursion
(mu4e~headers-insert-header msg point)))
;; restore the mark, if any. See #2076.
(when (and markinfo (mu4e~headers-goto-docid docid))
@ -848,16 +870,16 @@ true, do *not* update the query history stack."
(defun mu4e~headers-benchmark-message (count)
"Get some report message for messaging search and rendering speed."
(if (and mu4e-headers-report-render-time
mu4e~headers-search-start
mu4e~headers-render-start
(> count 0))
mu4e~headers-search-start
mu4e~headers-render-start
(> count 0))
(let ((render-time-ms (* 1000(- (float-time) mu4e~headers-render-start)))
(search-time-ms (* 1000(- (float-time) mu4e~headers-search-start))))
(format (concat
"; search: %0.1f ms (%0.2f ms/msg)"
"; render: %0.1f ms (%0.2f ms/msg)")
search-time-ms (/ search-time-ms count)
render-time-ms (/ render-time-ms count)))
(search-time-ms (* 1000(- (float-time) mu4e~headers-search-start))))
(format (concat
"; search: %0.1f ms (%0.2f ms/msg)"
"; render: %0.1f ms (%0.2f ms/msg)")
search-time-ms (/ search-time-ms count)
render-time-ms (/ render-time-ms count)))
""))
(defun mu4e~headers-found-handler (count)
@ -869,9 +891,10 @@ after the end of the search results."
(goto-char (point-max))
(let ((inhibit-read-only t)
(str (if (zerop count) mu4e~no-matches mu4e~end-of-results))
(msg (format "Found %d matching message%s%s"
(msg (format "Found %d matching message%s; %d hidden%s"
count (if (= 1 count) "" "s")
(mu4e~headers-benchmark-message count))))
mu4e~headers-hidden
(mu4e~headers-benchmark-message count))))
(insert (propertize str 'face 'mu4e-system-face 'intangible t))
(unless (zerop count)
@ -882,7 +905,8 @@ after the end of the search results."
(when mu4e--search-msgid-target
(if (eq (current-buffer) (window-buffer))
(mu4e-headers-goto-message-id mu4e--search-msgid-target)
(let* ((pos (mu4e-headers-goto-message-id mu4e--search-msgid-target)))
(let* ((pos (mu4e-headers-goto-message-id
mu4e--search-msgid-target)))
(when pos
(set-window-point (get-buffer-window nil t) pos)))))
(when (and mu4e--search-view-target (mu4e-message-at-point 'noerror))
@ -931,14 +955,7 @@ after the end of the search results."
(define-key map "j" 'mu4e~headers-jump-to-maildir)
(define-key map "O" 'mu4e-headers-change-sorting)
(define-key map "M" 'mu4e-headers-toggle-setting)
;; these are impossible to remember; use mu4e-headers-toggle-setting
;; instead :)
(define-key map "P" 'mu4e-headers-toggle-threading)
(define-key map "Q" 'mu4e-headers-toggle-full-search)
(define-key map "W" 'mu4e-headers-toggle-include-related)
(define-key map "V" 'mu4e-headers-toggle-skip-duplicates)
(define-key map "P" 'mu4e-headers-toggle-property)
(define-key map "q" 'mu4e~headers-quit-buffer)
(define-key map "g" 'mu4e-search-rerun) ;; for compatibility
@ -947,7 +964,7 @@ after the end of the search results."
(define-key map "t" 'mu4e-headers-mark-subthread)
(define-key map "T" 'mu4e-headers-mark-thread)
(define-key map "," #'mu4e-sexp-at-point)
(define-key map "," #'mu4e-sexp-at-point)
;; navigation between messages
(define-key map "p" 'mu4e-headers-prev)
@ -962,7 +979,8 @@ after the end of the search results."
(define-key map (kbd "C-+") 'mu4e-headers-split-view-grow)
(define-key map (kbd "C--") 'mu4e-headers-split-view-shrink)
(define-key map (kbd "<C-kp-add>") 'mu4e-headers-split-view-grow)
(define-key map (kbd "<C-kp-subtract>") 'mu4e-headers-split-view-shrink)
(define-key map (kbd "<C-kp-subtract>")
'mu4e-headers-split-view-shrink)
;; switching to view mode (if it's visible)
(define-key map "y" 'mu4e-select-other-view)
@ -986,10 +1004,14 @@ after the end of the search results."
(define-key map (kbd "=") 'mu4e-headers-mark-for-untrash)
(define-key map (kbd "&") 'mu4e-headers-mark-custom)
(define-key map (kbd "*") 'mu4e-headers-mark-for-something)
(define-key map (kbd "<kp-multiply>") 'mu4e-headers-mark-for-something)
(define-key map (kbd "<insertchar>") 'mu4e-headers-mark-for-something)
(define-key map (kbd "<insert>") 'mu4e-headers-mark-for-something)
(define-key map (kbd "*")
'mu4e-headers-mark-for-something)
(define-key map (kbd "<kp-multiply>")
'mu4e-headers-mark-for-something)
(define-key map (kbd "<insertchar>")
'mu4e-headers-mark-for-something)
(define-key map (kbd "<insert>")
'mu4e-headers-mark-for-something)
(define-key map (kbd "#") 'mu4e-mark-resolve-deferred-marks)
@ -1023,18 +1045,6 @@ after the end of the search results."
(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-search-threads)
mu4e-search-threads))))
(define-key menumap "|" '("Pipe through shell" . mu4e-view-pipe))
(define-key menumap [sepa1] '("--"))
@ -1104,7 +1114,7 @@ after the end of the search results."
(field (car item)) (width (cdr item))
(info (cdr (assoc field
(append mu4e-header-info mu4e-header-info-custom))))
(sortable (plist-get info :sortable))
(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)
(this-field (when sortable (if (booleanp sortable) field sortable)))
@ -1143,8 +1153,8 @@ after the end of the search results."
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
mu4e-index-update-status
(not (zerop (plist-get mu4e-index-update-status :updated)))
mu4e-index-update-status
(not (zerop (plist-get mu4e-index-update-status :updated)))
;; NOTE: `mu4e-mark-marks-num' can return nil. Is that intended?
(zerop (or (mu4e-mark-marks-num) 0)) ;; non active marks
(not (active-minibuffer-window))) ;; no user input only
@ -1287,21 +1297,24 @@ message plist, or nil if not found."
(defvar mu4e~headers-mode-line-label "")
(defun mu4e~headers-update-mode-line ()
"Update mode-line settings."
(let* ((flagstr
(mapconcat
(lambda (flag-cell)
(if (car flag-cell)
(if mu4e-use-fancy-chars
(cddr flag-cell) (cadr flag-cell) ) ""))
`((,mu4e-search-full . ,mu4e-headers-full-label)
(,mu4e-headers-include-related . ,mu4e-headers-related-label)
(,mu4e-search-threads . ,mu4e-headers-threaded-label)
(,mu4e-headers-skip-duplicates . ,mu4e-headers-skip-duplicates-label))
""))
(name "mu4e-headers"))
(let* ((flagstr
(mapconcat
(lambda (flag-cell)
(if (car flag-cell)
(if mu4e-use-fancy-chars
(cddr flag-cell) (cadr flag-cell) ) ""))
`((,mu4e-search-full . ,mu4e-headers-full-label)
(,mu4e-headers-include-related . ,mu4e-headers-related-label)
(,mu4e-search-threads . ,mu4e-headers-threaded-label)
(,mu4e-headers-skip-duplicates
. ,mu4e-headers-skip-duplicates-label)
(,mu4e-headers-hide-enabled . ,mu4e-headers-hide-label))
""))
(name "mu4e-headers"))
(setq mode-name name)
(setq mu4e~headers-mode-line-label (concat flagstr " " mu4e--search-last-query))
(setq mu4e~headers-mode-line-label
(concat flagstr " " mu4e--search-last-query))
(make-local-variable 'global-mode-string)
@ -1405,9 +1418,10 @@ matching messages with that mark."
(if (member field '(:to :from :cc :bcc :reply-to))
(cl-find-if (lambda (contact)
(let ((name (mu4e-contact-name contact))
(email (mu4e-contact-email contact)))
(email (mu4e-contact-email contact)))
(or (and name (string-match pattern name))
(and email (string-match pattern email))))) value)
(and email (string-match pattern email)))))
value)
(string-match pattern (or value ""))))))))
(defun mu4e-headers-mark-custom ()
@ -1501,7 +1515,8 @@ user)."
(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 field), _or_ another field (meaning: sort by this other
;; field).
(sortable (plist-get (cdr (assoc field mu4e-header-info)) :sortable))
;; error check
(sortable
@ -1526,68 +1541,30 @@ user)."
(symbol-name mu4e-headers-sort-direction))
(mu4e-search-rerun)))
(defun mu4e-headers-toggle-setting (&optional dont-refresh)
(defun mu4e-headers-toggle-property (&optional dont-refresh key)
"Toggle some aspect of headers display.
When prefix-argument DONT-REFRESH is non-nill, do not refresh the
last search with the new setting."
When prefix-argument DONT-REFRESH is non-nil, do not refresh the
last search with the new setting.
If KEY is provided, use it instead of asking user."
(interactive "P")
(let* ((toggles '(("fFull-search" . mu4e-search-full)
("rInclude-related" . mu4e-headers-include-related)
("tShow threads" . mu4e-search-threads)
("uSkip duplicates" . mu4e-headers-skip-duplicates)))
(toggles (seq-map
(lambda (cell)
(cons
(concat (car cell) (format" (%s)"
(if (symbol-value (cdr cell)) "on" "off")))
(cdr cell))) toggles))
(choice (mu4e-read-option "Toggle setting " toggles)))
("rInclude-related" . mu4e-headers-include-related)
("tShow threads" . mu4e-search-threads)
("uSkip duplicates" . mu4e-headers-skip-duplicates)
("pHide-predicate" . mu4e-headers-hide-enabled)))
(toggles (seq-map
(lambda (cell)
(cons
(concat (car cell)
(format" (%s)"
(if (symbol-value (cdr cell)) "on" "off")))
(cdr cell))) toggles))
(choice (mu4e-read-option "Toggle property " toggles key)))
(when choice
(set choice (not (symbol-value choice)))
(mu4e-message "Set `%s' to %s" (symbol-name choice) (symbol-value choice))
(unless dont-refresh
(mu4e-search-rerun)))))
(defun mu4e~headers-toggle (name togglevar dont-refresh)
"Toggle variable TOGGLEVAR for feature NAME. Unless DONT-REFRESH is non-nil,
re-run the last search."
(set togglevar (not (symbol-value togglevar)))
(mu4e-message "%s turned %s%s"
name
(if (symbol-value togglevar) "on" "off")
(if dont-refresh
" (press 'g' to refresh)" ""))
(unless dont-refresh
(mu4e-search-rerun)))
(defun mu4e-headers-toggle-threading (&optional dont-refresh)
"Toggle `mu4e-search-threads'. With prefix-argument, do
_not_ refresh the last search with the new setting for threading."
(interactive "P")
(mu4e~headers-toggle "Threading" 'mu4e-search-threads dont-refresh))
(defun mu4e-headers-toggle-full-search (&optional dont-refresh)
"Toggle `mu4e-search-full'. With prefix-argument, do
_not_ refresh the last search with the new setting for threading."
(interactive "P")
(mu4e~headers-toggle "Full-search"
'mu4e-search-full dont-refresh))
(defun mu4e-headers-toggle-include-related (&optional dont-refresh)
"Toggle `mu4e-headers-include-related'. With prefix-argument, do
_not_ refresh the last search with the new setting for threading."
(interactive "P")
(mu4e~headers-toggle "Include-related"
'mu4e-headers-include-related dont-refresh))
(defun mu4e-headers-toggle-skip-duplicates (&optional dont-refresh)
"Toggle `mu4e-headers-skip-duplicates'. With prefix-argument, do
_not_ refresh the last search with the new setting for threading."
(interactive "P")
(mu4e~headers-toggle "Skip-duplicates"
'mu4e-headers-skip-duplicates dont-refresh))
(mu4e-search-rerun)))))
(defun mu4e-headers-view-message ()
"View message at point."
@ -1595,9 +1572,9 @@ _not_ refresh the last search with the new setting for threading."
(unless (eq major-mode 'mu4e-headers-mode)
(mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode))
(let* ((msg (mu4e-message-at-point))
(path (mu4e-message-field msg :path))
(_exists (or (file-readable-p path)
(mu4e-warn "No message at %s" path)))
(path (mu4e-message-field msg :path))
(_exists (or (file-readable-p path)
(mu4e-warn "No message at %s" path)))
(docid (or (mu4e-message-field msg :docid)
(mu4e-warn "No message at point")))
(mark-as-read
@ -1622,12 +1599,12 @@ return nil."
(condition-case _err
(prog1
(let (line-move-visual)
(and (line-move arg) 0))
(and (line-move arg) 0))
;; Skip invisible text at BOL possibly hidden by
;; the end of another invisible overlay covering
;; previous EOL.
(move-to-column 2))
((beginning-of-buffer end-of-buffer)
((beginning-of-buffer end-of-buffer)
1))))
(let* ((succeeded (zerop (goto-next-line lines)))
(docid (mu4e~headers-docid-at-point)))
@ -1637,7 +1614,8 @@ return nil."
;; update all windows showing the headers buffer
(walk-windows
(lambda (win)
(when (eq (window-buffer win) (mu4e-get-headers-buffer (buffer-name)))
(when (eq (window-buffer win)
(mu4e-get-headers-buffer (buffer-name)))
(set-window-point win (point))))
nil t)
;; If the assigned (and buffer-local) `mu4e~headers-view-win'
@ -1649,8 +1627,8 @@ return nil."
;; attempt to highlight the new line, display the message
(mu4e~headers-highlight docid)
(if succeeded
docid
nil)))))
docid
nil)))))
(defun mu4e-headers-next (&optional n)
"Move point to the next message header.
@ -1701,7 +1679,8 @@ given, offer to edit the search query before executing it."
(list maildir current-prefix-arg)))
(when maildir
(let* ((query (format "maildir:\"%s\"" maildir))
(query (if edit (mu4e-search-read-query "Refine query: " query) query)))
(query (if edit
(mu4e-search-read-query "Refine query: " query) query)))
(mu4e-mark-handle-when-leaving)
(mu4e-search query))))
@ -1741,7 +1720,8 @@ 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)
@ -1779,14 +1759,18 @@ other windows."
(when mu4e-dim-when-loading
(setq mu4e--loading-overlay-bg
(let ((overlay (make-overlay (point-min) (point-max))))
(overlay-put overlay 'face `(:foreground "gray22" :background
,(face-attribute 'default :background)))
(overlay-put overlay 'face
`(:foreground "gray22" :background
,(face-attribute 'default
:background)))
(overlay-put overlay 'priority 9998)
overlay)))
(setq mu4e--loading-overlay-text
(let ((overlay (make-overlay (point-min) (point-min))))
(overlay-put overlay 'priority 9999)
(overlay-put overlay 'before-string (propertize "Loading…\n" 'face 'mu4e-header-title-face))
(overlay-put overlay 'before-string
(propertize "Loading…\n"
'face 'mu4e-header-title-face))
overlay)))
(when mu4e--loading-overlay-bg
(delete-overlay mu4e--loading-overlay-bg))

View File

@ -161,25 +161,27 @@ Does a local-exit and does not return."
"Get PROP from plist LST and raise an error if not present."
(or (plist-get lst prop)
(if (plist-member lst prop)
nil
(mu4e-error "Missing property %s in %s" prop lst))))
nil
(mu4e-error "Missing property %s in %s" prop lst))))
(defun mu4e--read-char-choice (prompt choices)
(defun mu4e--read-char-choice (prompt choices &optional key)
"Read and return one of CHOICES, prompting for PROMPT.
Any input that is not one of CHOICES is ignored. This is mu4e's
version of `read-char-choice' which becomes case-insentive after
trying an exact match."
trying an exact match.
If optional KEY is provided, use that instead of asking user."
(let ((choice) (chosen) (inhibit-quit nil))
(while (not chosen)
(message nil);; this seems needed...
(setq choice (read-char-exclusive prompt))
(setq choice (or key (read-char-exclusive prompt)))
(if (eq choice 27) (keyboard-quit)) ;; quit if ESC is pressed
(setq chosen (or (member choice choices)
(member (downcase choice) choices)
(member (upcase choice) choices))))
(car chosen)))
(defun mu4e-read-option (prompt options)
(defun mu4e-read-option (prompt options &optional key)
"Ask user for an option from a list on the input area.
PROMPT describes a multiple-choice question to the user. OPTIONS
describe the options, and is a list of cells describing
@ -200,6 +202,9 @@ user can then choose by typing CHAR. Example:
User now will be presented with a list: \"Choose an animal:
[M]onkey, [G]nu, [x]Moose\".
If optional character KEY is provied, use that instead of asking
the user.
Function returns the cdr of the list element."
(let* ((prompt (mu4e-format "%s" prompt))
(optionsstr
@ -221,7 +226,8 @@ Function returns the cdr of the list element."
" [" (propertize "C-g" 'face 'mu4e-highlight-face)
" to cancel]")
;; the allowable chars
(seq-map (lambda(elm) (string-to-char (car elm))) options)))
(seq-map (lambda(elm) (string-to-char (car elm))) options)
key))
(chosen
(seq-find
(lambda (option) (eq response (string-to-char (car option))))
@ -369,18 +375,18 @@ http://cr.yp.to/proto/maildir.html."
(seq-mapcat
(lambda (flag)
(pcase flag
(`draft "D")
(`flagged "F")
(`new "N")
(`passed "P")
(`replied "R")
(`seen "S")
(`trashed "T")
(`attach "a")
(`encrypted "x")
(`signed "s")
(`unread "u")
(_ "")))
(`draft "D")
(`flagged "F")
(`new "N")
(`passed "P")
(`replied "R")
(`seen "S")
(`trashed "T")
(`attach "a")
(`encrypted "x")
(`signed "s")
(`unread "u")
(_ "")))
(seq-uniq flags) 'string)))
(defun mu4e-string-to-flags (str)
@ -394,14 +400,14 @@ http://cr.yp.to/proto/maildir.html."
(seq-mapcat
(lambda (kar)
(list
(pcase kar
('?D 'draft)
('?F 'flagged)
('?P 'passed)
('?R 'replied)
('?S 'seen)
('?T 'trashed)
(_ nil))))
(pcase kar
('?D 'draft)
('?F 'flagged)
('?P 'passed)
('?R 'replied)
('?S 'seen)
('?T 'trashed)
(_ nil))))
str))))
@ -470,7 +476,7 @@ The file will self-destruct in a short while, enough to open it
in an external program."
(let ((tmpfile (make-temp-file "mu4e-" nil (concat "." ext))))
(run-at-time "30 sec" nil
(lambda () (ignore-errors (delete-file tmpfile))))
(lambda () (ignore-errors (delete-file tmpfile))))
tmpfile))
(defsubst mu4e-is-mode-or-derived-p (mode)
@ -492,12 +498,12 @@ Or go to the top level if there is none."
(defun mu4e--make-bookmark-record ()
"Create a bookmark for the message at point."
(let* ((msg (mu4e-message-at-point))
(subject (or (plist-get msg :subject) "No subject"))
(date (plist-get msg :date))
(date (if date (format-time-string "%F: " date) ""))
(title (format "%s%s" date subject))
(msgid (or (plist-get msg :message-id)
(mu4e-error "Cannot bookmark message without message-id"))))
(subject (or (plist-get msg :subject) "No subject"))
(date (plist-get msg :date))
(date (if date (format-time-string "%F: " date) ""))
(title (format "%s%s" date subject))
(msgid (or (plist-get msg :message-id)
(mu4e-error "Cannot bookmark message without message-id"))))
`(,title
,@(bookmark-make-record-default 'no-file 'no-context)
(message-id . ,msgid)

View File

@ -60,9 +60,9 @@
(make-obsolete-variable 'mu4e-html2text-command "No longer in use" "1.7.0")
(make-obsolete-variable 'mu4e-view-prefer-html "No longer in use" "1.7.0")
(make-obsolete-variable 'mu4e-view-html-plaintext-ratio-heuristic
"No longer in use" "1.7.0")
"No longer in use" "1.7.0")
(make-obsolete-variable 'mu4e-message-body-rewrite-functions
"No longer in use" "1.7.0")
"No longer in use" "1.7.0")
;;; Html2Text
(make-obsolete 'mu4e-shr2text "No longer in use" "1.7.0")
@ -70,32 +70,32 @@
;; old message view
(make-obsolete-variable 'mu4e-view-show-addresses
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-view-wrap-lines nil "0.9.9-dev7")
(make-obsolete-variable 'mu4e-view-hide-cited nil "0.9.9-dev7")
(make-obsolete-variable 'mu4e-view-date-format
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-view-image-max-width
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-view-image-max-height
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-save-multiple-attachments-without-asking
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-view-attachment-assoc
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-view-attachment-actions
"See mu4e-view-mime-part-actions" "1.7.0")
"See mu4e-view-mime-part-actions" "1.7.0")
(make-obsolete-variable 'mu4e-view-header-field-keymap
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-view-header-field-keymap
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-view-contacts-header-keymap
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-view-attachments-header-keymap
"Unused with the new message view" "1.7.0")
"Unused with the new message view" "1.7.0")
(make-obsolete-variable 'mu4e-imagemagick-identify nil "1.7.0")
(make-obsolete-variable 'mu4e-view-show-images
"No longer used" "1.7.0")
"No longer used" "1.7.0")
(make-obsolete-variable 'mu4e-view-gnus "Old view is gone" "1.7.0")
(make-obsolete-variable 'mu4e-view-use-gnus "Gnus view is the default" "1.5.10")
@ -150,6 +150,16 @@
(make-obsolete-variable 'mu4e-headers-field-properties-function
"not used" "1.6.1")
(define-obsolete-function-alias 'mu4e-headers-toggle-setting
'mu4e-headers-toggle-property "1.9.5")
(define-obsolete-function-alias 'mu4e-headers-toggle-threading
'mu4e-headers-toggle-property "1.9.5")
(define-obsolete-function-alias 'mu4e-headers-toggle-full-search
'mu4e-headers-toggle-settin "1.9.5")
(define-obsolete-function-alias 'mu4e-headers-toggle-include-related
'mu4e-headers-toggle-property "1.9.5")
(define-obsolete-function-alias 'mu4e-headers-toggle-skip-duplicates
'mu4e-headers-toggle-property "1.9.5")
;; mu4e-main

View File

@ -60,7 +60,7 @@ Otherwise, don't move to the next message."
(defcustom mu4e-view-fields
'(:from :to :cc :subject :flags :date :maildir :mailing-list :tags
:attachments :signature :decryption)
:attachments :signature :decryption)
"Header fields to display in the message view buffer.
For the complete list of available headers, see
`mu4e-header-info'.
@ -75,11 +75,11 @@ details."
(defcustom mu4e-view-actions
(seq-filter 'identity
`( ("capture message" . mu4e-action-capture-message)
("view in browser" . mu4e-action-view-in-browser)
,(when (fboundp 'xwidget-webkit-browse-url)
'("xview in xwidget" . mu4e-action-view-in-xwidget))
("show this thread" . mu4e-action-show-thread)))
`( ("capture message" . mu4e-action-capture-message)
("view in browser" . mu4e-action-view-in-browser)
,(when (fboundp 'xwidget-webkit-browse-url)
'("xview in xwidget" . mu4e-action-view-in-xwidget))
("show this thread" . mu4e-action-show-thread)))
"List of actions to perform on messages in view mode.
The actions are cons-cells of the form:
(NAME . FUNC)
@ -136,13 +136,13 @@ other windows."
"Display the raw contents of message at point in a new buffer."
(interactive)
(let ((path (mu4e-message-readable-path))
(buf (get-buffer-create mu4e~view-raw-buffer-name)))
(buf (get-buffer-create mu4e~view-raw-buffer-name)))
(with-current-buffer buf
(let ((inhibit-read-only t))
(erase-buffer)
(erase-buffer)
(mu4e-raw-view-mode)
(insert-file-contents path)
(goto-char (point-min))))
(goto-char (point-min))))
(mu4e-display-buffer buf t)))
(defun mu4e-view-pipe (cmd)
@ -163,16 +163,17 @@ Then, display the results."
;; it have linked headers buffer?
((mu4e-current-buffer-type-p 'view)
(when (mu4e--view-detached-p (current-buffer))
(mu4e-error "You cannot navigate in a detached view buffer."))
(mu4e-error
"Cannot navigate in a detached view buffer."))
(mu4e-get-headers-buffer))
;; fallback; but what would trigger this?
(t (mu4e-get-headers-buffer))))
(docid (mu4e-message-field msg :docid)))
(docid (mu4e-message-field msg :docid)))
(unless docid
(mu4e-error "Message without docid: action is not possible"))
(mu4e-error "Message without docid: action is not possible"))
(with-current-buffer buffer
(mu4e-display-buffer buffer)
(if (or (mu4e~headers-goto-docid docid)
(mu4e-display-buffer buffer)
(if (or (mu4e~headers-goto-docid docid)
;; TODO: Is this the best way to find another
;; relevant docid for a view buffer?
;;
@ -185,8 +186,8 @@ Then, display the results."
(mu4e~headers-goto-docid
(with-current-buffer buffer
(mu4e-message-field (mu4e-message-at-point) :docid))))
,@body
(mu4e-error "Cannot find message in headers buffer"))))))
,@body
(mu4e-error "Cannot find message in headers buffer"))))))
(defun mu4e-view-headers-next (&optional n)
"Move point to the next message header.
@ -238,7 +239,7 @@ bymessage-at-point. The actions are specified in
`mu4e-view-actions'."
(interactive)
(let* ((msg (or msg (mu4e-message-at-point)))
(actionfunc (mu4e-read-option "Action: " mu4e-view-actions)))
(actionfunc (mu4e-read-option "Action: " mu4e-view-actions)))
(funcall actionfunc msg)))
(defun mu4e-view-mark-pattern ()
@ -289,25 +290,25 @@ Add this function to `mu4e-view-mode-hook' to enable this feature."
(save-excursion
(goto-char (point-min))
(while (re-search-forward
(concat "^" message-mark-insert-begin) nil t)
(setq ov-beg (match-beginning 0)
ov-end (match-end 0)
ov-inv (make-overlay ov-beg ov-end)
beg ov-end)
(overlay-put ov-inv 'invisible t)
(overlay-put ov-inv 'mu4e-overlay t)
(when (re-search-forward
(concat "^" message-mark-insert-end) nil t)
(setq ov-beg (match-beginning 0)
ov-end (match-end 0)
ov-inv (make-overlay ov-beg ov-end)
end ov-beg)
(overlay-put ov-inv 'invisible t))
(when (and beg end)
(let ((ov (make-overlay beg end)))
(overlay-put ov 'mu4e-overlay t)
(overlay-put ov 'face 'mu4e-region-code))
(setq beg nil end nil))))))
(concat "^" message-mark-insert-begin) nil t)
(setq ov-beg (match-beginning 0)
ov-end (match-end 0)
ov-inv (make-overlay ov-beg ov-end)
beg ov-end)
(overlay-put ov-inv 'invisible t)
(overlay-put ov-inv 'mu4e-overlay t)
(when (re-search-forward
(concat "^" message-mark-insert-end) nil t)
(setq ov-beg (match-beginning 0)
ov-end (match-end 0)
ov-inv (make-overlay ov-beg ov-end)
end ov-beg)
(overlay-put ov-inv 'invisible t))
(when (and beg end)
(let ((ov (make-overlay beg end)))
(overlay-put ov 'mu4e-overlay t)
(overlay-put ov 'face 'mu4e-region-code))
(setq beg nil end nil))))))
;;; View Utilities
@ -391,12 +392,12 @@ list."
(defmacro mu4e~view-defun-mark-for (mark)
"Define a function mu4e-view-mark-for- MARK."
(let ((funcname (intern (format "mu4e-view-mark-for-%s" mark)))
(docstring (format "Mark the current message for %s." mark)))
(docstring (format "Mark the current message for %s." mark)))
`(progn
(defun ,funcname () ,docstring
(interactive)
(mu4e~view-in-headers-context
(mu4e-headers-mark-and-next ',mark)))
(interactive)
(mu4e~view-in-headers-context
(mu4e-headers-mark-and-next ',mark)))
(put ',funcname 'definition-name ',mark))))
(mu4e~view-defun-mark-for move)
@ -446,8 +447,8 @@ If the url is mailto link, start writing an email to that address."
(let* (( url (or url (mu4e~view-get-property-from-event 'mu4e-url))))
(when url
(if (string-match-p "^mailto:" url)
(browse-url-mail url)
(browse-url url)))))
(browse-url-mail url)
(browse-url url)))))
(defun mu4e~view-get-property-from-event (prop)
@ -455,13 +456,13 @@ If the url is mailto link, start writing an email to that address."
The action is chosen based on the `last-command-event'.
Meant to be evoked from interactive commands."
(if (and (eventp last-command-event)
(mouse-event-p last-command-event))
(mouse-event-p last-command-event))
(let ((posn (event-end last-command-event)))
(when (numberp (posn-point posn))
(get-text-property
(posn-point posn)
prop
(window-buffer (posn-window posn)))))
(when (numberp (posn-point posn))
(get-text-property
(posn-point posn)
prop
(window-buffer (posn-window posn)))))
(get-text-property (point) prop)))
;; this is fairly simplistic...
@ -471,27 +472,27 @@ Also number them so they can be opened using `mu4e-view-go-to-url'."
(let ((num 0))
(save-excursion
(setq mu4e~view-link-map ;; buffer local
(make-hash-table :size 32 :weakness nil))
(make-hash-table :size 32 :weakness nil))
(goto-char (point-min))
(while (re-search-forward mu4e~view-beginning-of-url-regexp nil t)
(let ((bounds (thing-at-point-bounds-of-url-at-point)))
(when bounds
(let* ((url (thing-at-point-url-at-point))
(ov (make-overlay (car bounds) (cdr bounds))))
(puthash (cl-incf num) url mu4e~view-link-map)
(add-text-properties
(car bounds)
(cdr bounds)
`(face mu4e-link-face
mouse-face highlight
mu4e-url ,url
keymap ,mu4e-view-active-urls-keymap
help-echo
"[mouse-1] or [M-RET] to open the link"))
(overlay-put ov 'mu4e-overlay t)
(overlay-put ov 'after-string
(propertize (format "\u200B[%d]" num)
'face 'mu4e-url-number-face)))))))))
(let ((bounds (thing-at-point-bounds-of-url-at-point)))
(when bounds
(let* ((url (thing-at-point-url-at-point))
(ov (make-overlay (car bounds) (cdr bounds))))
(puthash (cl-incf num) url mu4e~view-link-map)
(add-text-properties
(car bounds)
(cdr bounds)
`(face mu4e-link-face
mouse-face highlight
mu4e-url ,url
keymap ,mu4e-view-active-urls-keymap
help-echo
"[mouse-1] or [M-RET] to open the link"))
(overlay-put ov 'mu4e-overlay t)
(overlay-put ov 'after-string
(propertize (format "\u200B[%d]" num)
'face 'mu4e-url-number-face)))))))))
(defun mu4e~view-get-urls-num (prompt &optional multi)
@ -504,21 +505,21 @@ string."
(let* ((count (hash-table-count mu4e~view-link-map)) (def))
(when (zerop count) (mu4e-error "No links for this message"))
(if (not multi)
(if (= count 1)
(read-number (mu4e-format "%s: " prompt) 1)
(read-number (mu4e-format "%s (1-%d): " prompt count)))
(if (= count 1)
(read-number (mu4e-format "%s: " prompt) 1)
(read-number (mu4e-format "%s (1-%d): " prompt count)))
(progn
(setq def (if (= count 1) "1" (format "1-%d" count)))
(read-string (mu4e-format "%s (default %s): " prompt def)
nil nil def)))))
(setq def (if (= count 1) "1" (format "1-%d" count)))
(read-string (mu4e-format "%s (default %s): " prompt def)
nil nil def)))))
(defun mu4e-view-go-to-url (&optional multi)
"Offer to go visit one or more URLs.
If MULTI (prefix-argument) is non-nil, offer to go to a range of URLs."
(interactive "P")
(mu4e~view-handle-urls "URL to visit"
multi
(lambda (url) (mu4e~view-browse-url-from-binding url))))
multi
(lambda (url) (mu4e~view-browse-url-from-binding url))))
(defun mu4e-view-save-url (&optional multi)
"Offer to save URLs to the kill ring.
@ -526,9 +527,9 @@ If MULTI (prefix-argument) is nil, save a single one, otherwise, offer
to save a range of URLs."
(interactive "P")
(mu4e~view-handle-urls "URL to save" multi
(lambda (url)
(kill-new url)
(mu4e-message "Saved %s to the kill-ring" url))))
(lambda (url)
(kill-new url)
(mu4e-message "Saved %s to the kill-ring" url))))
(defun mu4e-view-fetch-url (&optional multi)
"Offer to fetch (download) URLs.
@ -540,7 +541,7 @@ URLs. The urls are fetched to `mu4e-attachment-dir'."
"URL to fetch" multi
(lambda (url)
(let ((target (concat (mu4e~get-attachment-dir url) "/"
(file-name-nondirectory url))))
(file-name-nondirectory url))))
(url-copy-file url target)
(mu4e-message "Fetched %s -> %s" url target)))))
@ -556,7 +557,7 @@ it to a range of uris. PROMPT is the query to present to the user."
"Apply URLFUNC to some URL with NUM in the current message.
Prompting the user with PROMPT for the number."
(let* ((num (or num (mu4e~view-get-urls-num prompt)))
(url (gethash num mu4e~view-link-map)))
(url (gethash num mu4e~view-link-map)))
(unless url (mu4e-warn "Invalid number for URL"))
(funcall urlfunc url)))
@ -572,9 +573,9 @@ of urls. You can type multiple values separated by space, e.g. 1
Furthermore, there is a shortcut \"a\" which means all urls, but as
this is the default, you may not need it."
(let* ((linkstr (mu4e~view-get-urls-num
"URL number range (or 'a' for 'all')" t))
(count (hash-table-count mu4e~view-link-map))
(linknums (mu4e-split-ranges-to-numbers linkstr count)))
"URL number range (or 'a' for 'all')" t))
(count (hash-table-count mu4e~view-link-map))
(linknums (mu4e-split-ranges-to-numbers linkstr count)))
(dolist (num linknums)
(mu4e~view-handle-single-url prompt urlfunc num))))
@ -645,7 +646,8 @@ As a side-effect, a message that is being viewed loses its
;;
;; Otherwise, `mu4e-display-buffer' may adjust the view buffer's
;; window height based on a buffer that has no text in it yet!
(setq-local mu4e~headers-view-win (mu4e-display-buffer gnus-article-buffer nil))
(setq-local mu4e~headers-view-win
(mu4e-display-buffer gnus-article-buffer nil))
(unless (window-live-p mu4e~headers-view-win)
(mu4e-error "Cannot get a message view"))
(select-window mu4e~headers-view-win)))
@ -673,17 +675,17 @@ determine which browser function to use."
(mu4e-message-readable-path msg) nil nil nil t)
(run-hooks 'gnus-article-decode-hook)
(let ((header (unless skip-headers
(cl-loop for field in '("from" "to" "cc" "date" "subject")
when (message-fetch-field field)
concat (format "%s: %s\n" (capitalize field) it))))
(parts (mm-dissect-buffer t t)))
(cl-loop for field in '("from" "to" "cc" "date" "subject")
when (message-fetch-field field)
concat (format "%s: %s\n" (capitalize field) it))))
(parts (mm-dissect-buffer t t)))
;; If singlepart, enforce a list.
(when (and (bufferp (car parts))
(stringp (car (mm-handle-type parts))))
(setq parts (list parts)))
(stringp (car (mm-handle-type parts))))
(setq parts (list parts)))
;; Process the list
(unless (gnus-article-browse-html-parts parts header)
(mu4e-warn "Message does not contain a \"text/html\" part"))
(mu4e-warn "Message does not contain a \"text/html\" part"))
(mm-destroy-parts parts))))
(defun mu4e-action-view-in-xwidget (msg)
@ -700,38 +702,38 @@ determine which browser function to use."
"Render current buffer with MSG using Gnus' article mode."
(setq gnus-summary-buffer (get-buffer-create " *appease-gnus*"))
(let* ((inhibit-read-only t)
(max-specpdl-size mu4e-view-max-specpdl-size)
(mm-decrypt-option 'known)
(ct (mail-fetch-field "Content-Type"))
(ct (and ct (mail-header-parse-content-type ct)))
(charset (mail-content-type-get ct 'charset))
(charset (and charset (intern charset)))
(mu4e~view-rendering t); Needed if e.g. an ics file is buttonized
(gnus-article-emulate-mime t)
(gnus-unbuttonized-mime-types '(".*/.*"))
(gnus-buttonized-mime-types
(append (list "multipart/signed" "multipart/encrypted")
gnus-buttonized-mime-types))
(gnus-newsgroup-charset
(if (and charset (coding-system-p charset)) charset
(detect-coding-region (point-min) (point-max) t)))
;; Possibly add headers (before "Attachments")
(gnus-display-mime-function (mu4e~view-gnus-display-mime msg))
(gnus-icalendar-additional-identities
(mu4e-personal-addresses 'no-regexp)))
(max-specpdl-size mu4e-view-max-specpdl-size)
(mm-decrypt-option 'known)
(ct (mail-fetch-field "Content-Type"))
(ct (and ct (mail-header-parse-content-type ct)))
(charset (mail-content-type-get ct 'charset))
(charset (and charset (intern charset)))
(mu4e~view-rendering t); Needed if e.g. an ics file is buttonized
(gnus-article-emulate-mime t)
(gnus-unbuttonized-mime-types '(".*/.*"))
(gnus-buttonized-mime-types
(append (list "multipart/signed" "multipart/encrypted")
gnus-buttonized-mime-types))
(gnus-newsgroup-charset
(if (and charset (coding-system-p charset)) charset
(detect-coding-region (point-min) (point-max) t)))
;; Possibly add headers (before "Attachments")
(gnus-display-mime-function (mu4e~view-gnus-display-mime msg))
(gnus-icalendar-additional-identities
(mu4e-personal-addresses 'no-regexp)))
(condition-case err
(progn
(mm-enable-multibyte)
(run-hooks 'gnus-article-decode-hook)
(gnus-article-prepare-display)
(mu4e~view-activate-urls)
(setq mu4e~gnus-article-mime-handles gnus-article-mime-handles
gnus-article-decoded-p gnus-article-decode-hook)
(set-buffer-modified-p nil)
(add-hook 'kill-buffer-hook #'mu4e~view-kill-mime-handles))
(progn
(mm-enable-multibyte)
(run-hooks 'gnus-article-decode-hook)
(gnus-article-prepare-display)
(mu4e~view-activate-urls)
(setq mu4e~gnus-article-mime-handles gnus-article-mime-handles
gnus-article-decoded-p gnus-article-decode-hook)
(set-buffer-modified-p nil)
(add-hook 'kill-buffer-hook #'mu4e~view-kill-mime-handles))
(epg-error
(mu4e-warn "EPG error: %s; fall back to raw view"
(error-message-string err))))))
(error-message-string err))))))
(defun mu4e~view-kill-mime-handles ()
"Kill cached MIME-handles, if any."
@ -750,7 +752,7 @@ determine which browser function to use."
"Toggle whether to show all MIME-parts."
(interactive)
(setq gnus-inhibit-mime-unbuttonizing
(not gnus-inhibit-mime-unbuttonizing))
(not gnus-inhibit-mime-unbuttonizing))
(mu4e-view-refresh))
(defun mu4e-view-toggle-fill-flowed()
@ -765,54 +767,54 @@ determine which browser function to use."
(gnus-display-mime ihandles)
(unless ihandles
(save-restriction
(article-goto-body)
(forward-line -1)
(narrow-to-region (point) (point))
(dolist (field mu4e-view-fields)
(let ((fieldval (mu4e-message-field msg field)))
(pcase field
((or ':path ':maildir :list ':user-agent ':message-id)
(mu4e~view-gnus-insert-header field fieldval))
(':mailing-list
(let ((list (plist-get msg :list)))
(if list (mu4e-get-mailing-list-shortname list) "")))
((or ':flags ':tags)
(let ((flags (mapconcat (lambda (flag)
(if (symbolp flag)
(symbol-name flag)
flag)) fieldval ", ")))
(mu4e~view-gnus-insert-header field flags)))
(':size (mu4e~view-gnus-insert-header
field (mu4e-display-size fieldval)))
((or ':subject ':to ':from ':cc ':bcc ':from-or-to
':date :attachments ':signature
':decryption)) ; handled by Gnus
(_
(mu4e~view-gnus-insert-header-custom msg field)))))
(let ((gnus-treatment-function-alist
'((gnus-treat-highlight-headers
gnus-article-highlight-headers))))
(gnus-treat-article 'head))))))
(article-goto-body)
(forward-line -1)
(narrow-to-region (point) (point))
(dolist (field mu4e-view-fields)
(let ((fieldval (mu4e-message-field msg field)))
(pcase field
((or ':path ':maildir :list ':user-agent ':message-id)
(mu4e~view-gnus-insert-header field fieldval))
(':mailing-list
(let ((list (plist-get msg :list)))
(if list (mu4e-get-mailing-list-shortname list) "")))
((or ':flags ':tags)
(let ((flags (mapconcat (lambda (flag)
(if (symbolp flag)
(symbol-name flag)
flag)) fieldval ", ")))
(mu4e~view-gnus-insert-header field flags)))
(':size (mu4e~view-gnus-insert-header
field (mu4e-display-size fieldval)))
((or ':subject ':to ':from ':cc ':bcc ':from-or-to
':date :attachments ':signature
':decryption)) ; handled by Gnus
(_
(mu4e~view-gnus-insert-header-custom msg field)))))
(let ((gnus-treatment-function-alist
'((gnus-treat-highlight-headers
gnus-article-highlight-headers))))
(gnus-treat-article 'head))))))
(defun mu4e~view-gnus-insert-header (field val)
"Insert a header FIELD with value VAL."
(let* ((info (cdr (assoc field mu4e-header-info)))
(key (plist-get info :name))
(help (plist-get info :help)))
(key (plist-get info :name))
(help (plist-get info :help)))
(if (and val (> (length val) 0))
(insert (propertize (concat key ":") 'help-echo help)
" " val "\n"))))
(insert (propertize (concat key ":") 'help-echo help)
" " val "\n"))))
(defun mu4e~view-gnus-insert-header-custom (msg field)
"Insert MSG's custom FIELD."
(let* ((info (cdr-safe (or (assoc field mu4e-header-info-custom)
(mu4e-error "Custom field %S not found" field))))
(key (plist-get info :name))
(func (or (plist-get info :function)
(mu4e-error "No :function defined for custom field %S %S"
field info)))
(val (funcall func msg))
(help (plist-get info :help)))
(mu4e-error "Custom field %S not found" field))))
(key (plist-get info :name))
(func (or (plist-get info :function)
(mu4e-error "No :function defined for custom field %S %S"
field info)))
(val (funcall func msg))
(help (plist-get info :help)))
(when (and val (> (length val) 0))
(insert (propertize (concat key ":") 'help-echo help) " " val "\n"))))
@ -821,14 +823,14 @@ determine which browser function to use."
"Avoid error when displaying an ical attachment without a charset."
(if (and (boundp 'mu4e~view-rendering) mu4e~view-rendering)
(let* ((handle (car handle-attendee))
(attendee (cadr handle-attendee))
(buf (mm-handle-buffer handle))
(ty (mm-handle-type handle))
(rest (cddr handle)))
;; Put the fallback at the end:
(setq ty (append ty '((charset . "utf-8"))))
(setq handle (cons buf (cons ty rest)))
(list handle attendee))
(attendee (cadr handle-attendee))
(buf (mm-handle-buffer handle))
(ty (mm-handle-type handle))
(rest (cddr handle)))
;; Put the fallback at the end:
(setq ty (append ty '((charset . "utf-8"))))
(setq handle (cons buf (cons ty rest)))
(list handle attendee))
handle-attendee))
(defun mu4e~view-mode-p ()
@ -887,9 +889,7 @@ This is useful for advising some Gnus-functionality that does not work in mu4e."
;; toggle header settings
(define-key map "O" #'mu4e-headers-change-sorting)
(define-key map "P" #'mu4e-headers-toggle-threading)
(define-key map "Q" #'mu4e-headers-toggle-full-search)
(define-key map "W" #'mu4e-headers-toggle-include-related)
(define-key map "P" #'mu4e-headers-toggle-property)
;; change the number of headers
(define-key map (kbd "C-+") #'mu4e-headers-split-view-grow)
@ -961,26 +961,26 @@ This is useful for advising some Gnus-functionality that does not work in mu4e."
(define-key map [menu-bar headers] (cons "Mu4e" menumap))
(define-key menumap [quit-buffer]
'("Quit view" . mu4e~view-quit-buffer))
'("Quit view" . mu4e~view-quit-buffer))
(define-key menumap [display-help] '("Help" . mu4e-display-manual))
(define-key menumap [sepa0] '("--"))
(define-key menumap [wrap-lines]
'("Toggle wrap lines" . visual-line-mode))
'("Toggle wrap lines" . visual-line-mode))
(define-key menumap [raw-view]
'("View raw message" . mu4e-view-raw-message))
'("View raw message" . mu4e-view-raw-message))
(define-key menumap [pipe]
'("Pipe through shell" . mu4e-view-pipe))
'("Pipe through shell" . mu4e-view-pipe))
(define-key menumap [sepa1] '("--"))
(define-key menumap [mark-delete]
'("Mark for deletion" . mu4e-view-mark-for-delete))
'("Mark for deletion" . mu4e-view-mark-for-delete))
(define-key menumap [mark-untrash]
'("Mark for untrash" . mu4e-view-mark-for-untrash))
'("Mark for untrash" . mu4e-view-mark-for-untrash))
(define-key menumap [mark-trash]
'("Mark for trash" . mu4e-view-mark-for-trash))
'("Mark for trash" . mu4e-view-mark-for-trash))
(define-key menumap [mark-move]
'("Mark for move" . mu4e-view-mark-for-move))
'("Mark for move" . mu4e-view-mark-for-move))
(define-key menumap [sepa2] '("--"))
(define-key menumap [resend] '("Resend" . mu4e-compose-resend))
@ -990,17 +990,17 @@ This is useful for advising some Gnus-functionality that does not work in mu4e."
(define-key menumap [sepa3] '("--"))
(define-key menumap [query-next]
'("Next query" . mu4e-headers-query-next))
'("Next query" . mu4e-headers-query-next))
(define-key menumap [query-prev]
'("Previous query" . mu4e-headers-query-prev))
'("Previous query" . mu4e-headers-query-prev))
(define-key menumap [narrow-search]
'("Narrow search" . mu4e-headers-search-narrow))
'("Narrow search" . mu4e-headers-search-narrow))
(define-key menumap [bookmark]
'("Search bookmark" . mu4e-headers-search-bookmark))
'("Search bookmark" . mu4e-headers-search-bookmark))
(define-key menumap [jump]
'("Jump to maildir" . mu4e~headers-jump-to-maildir))
'("Jump to maildir" . mu4e~headers-jump-to-maildir))
(define-key menumap [search]
'("Search" . mu4e-headers-search))
'("Search" . mu4e-headers-search))
(define-key menumap [sepa4] '("--"))
(define-key menumap [next] '("Next" . mu4e-view-headers-next))
@ -1048,9 +1048,9 @@ Based on Gnus' article-mode."
;; advice gnus-block-private-groups to always return "."
;; so that by default we block images.
(advice-add 'gnus-block-private-groups :around
(lambda(func &rest args)
(if (mu4e~view-mode-p)
"." (apply func args))))
(lambda(func &rest args)
(if (mu4e~view-mode-p)
"." (apply func args))))
(use-local-map mu4e-view-mode-map)
(mu4e-context-minor-mode)
(mu4e-search-minor-mode)
@ -1094,12 +1094,12 @@ The alist uniquely maps the number to the gnus-part."
(save-excursion
(goto-char (point-min))
(while (not (eobp))
(let ((part (get-text-property (point) 'gnus-data))
(index (get-text-property (point) 'gnus-part)))
(when (and part (numberp index) (not (assoc index parts))
(push `(,index . ,part) parts)))
(goto-char (or (next-single-property-change (point) 'gnus-part)
(point-max))))))
(let ((part (get-text-property (point) 'gnus-data))
(index (get-text-property (point) 'gnus-part)))
(when (and part (numberp index) (not (assoc index parts))
(push `(,index . ,part) parts)))
(goto-char (or (next-single-property-change (point) 'gnus-part)
(point-max))))))
parts))
@ -1118,47 +1118,47 @@ Note, currently this does not work well with file names
containing commas."
(interactive "P")
(cl-assert (and (eq major-mode 'mu4e-view-mode)
(derived-mode-p 'gnus-article-mode)))
(derived-mode-p 'gnus-article-mode)))
(let* ((parts (mu4e~view-gather-mime-parts))
(handles '())
(files '())
(compfn (if (and (boundp 'helm-mode) helm-mode)
#'completing-read
;; Fallback to `completing-read-multiple' with poor
;; completion
#'completing-read-multiple))
dir)
(handles '())
(files '())
(compfn (if (and (boundp 'helm-mode) helm-mode)
#'completing-read
;; Fallback to `completing-read-multiple' with poor
;; completion
#'completing-read-multiple))
dir)
(dolist (part parts)
(let ((fname (or (cdr (assoc 'filename (assoc "attachment" (cdr part))))
(cl-loop for item in part
for name = (and (listp item)
(assoc-default 'name item))
(assoc-default 'name item))
thereis (and (stringp name) name)))))
(when fname
(push `(,fname . ,(cdr part)) handles)
(push fname files))))
(when fname
(push `(,fname . ,(cdr part)) handles)
(push fname files))))
(if files
(progn
(setq files (let ((helm-comp-read-use-marked t))
(funcall compfn "Save part(s): " files))
dir (if arg (read-directory-name "Save to directory: ")
mu4e-attachment-dir))
(cl-loop for (f . h) in handles
when (member f files)
do (mm-save-part-to-file
h (let ((file (expand-file-name f dir)))
(if (file-exists-p file)
(let (newname (count 1))
(while (and
(setq newname
(concat
(file-name-sans-extension file)
(format "(%s)" count)
(file-name-extension file t)))
(file-exists-p newname))
(cl-incf count))
newname)
file)))))
(progn
(setq files (let ((helm-comp-read-use-marked t))
(funcall compfn "Save part(s): " files))
dir (if arg (read-directory-name "Save to directory: ")
mu4e-attachment-dir))
(cl-loop for (f . h) in handles
when (member f files)
do (mm-save-part-to-file
h (let ((file (expand-file-name f dir)))
(if (file-exists-p file)
(let (newname (count 1))
(while (and
(setq newname
(concat
(file-name-sans-extension file)
(format "(%s)" count)
(file-name-extension file t)))
(file-exists-p newname))
(cl-incf count))
newname)
file)))))
(mu4e-message "No attached files found"))))
@ -1176,7 +1176,7 @@ containing commas."
(:name "open" :handler mu4e~view-open-file :receives temp)
;; open with some custom file.
(:name "wopen-with" :handler (lambda (file)(mu4e~view-open-file file t))
:receives temp)
:receives temp)
;;
;; some more examples
@ -1191,13 +1191,13 @@ containing commas."
(:name "emacs" :handler find-file-read-only :receives temp)
;; open in this emacs instance, "raw"
(:name "raw" :handler (lambda (str)
(let ((tmpbuf
(get-buffer-create " *mu4e-raw-mime*")))
(with-current-buffer tmpbuf
(insert str)
(view-mode)
(goto-char (point-min)))
(display-buffer tmpbuf))) :receives pipe))
(let ((tmpbuf
(get-buffer-create " *mu4e-raw-mime*")))
(with-current-buffer tmpbuf
(insert str)
(view-mode)
(goto-char (point-min)))
(display-buffer tmpbuf))) :receives pipe))
"Specifies actions for MIME-parts.
@ -1205,16 +1205,16 @@ Each of the actions is a plist with keys
`(:name <name> ;; name of the action; shortcut is first letter of name
:handler ;; one of:
;; - a function receiving the index/temp/pipe
;; - a string, which is taken as a shell command
;; - a function receiving the index/temp/pipe
;; - a string, which is taken as a shell command
:receives ;; a symbol specifying what the handler receives
;; - index: the index number of the mime part (default)
;; - temp: the full path to the mime part in a
;; temporary file, which is deleted immediately
;; after invoking handler
;; - pipe: the attachment is piped to some shell command
;; or as a string parameter to a function
;; - index: the index number of the mime part (default)
;; - temp: the full path to the mime part in a
;; temporary file, which is deleted immediately
;; after invoking handler
;; - pipe: the attachment is piped to some shell command
;; or as a string parameter to a function
).")
@ -1225,17 +1225,17 @@ otherwise random; the result is placed in a temporary directory
with a unique name. Returns the full path for the file created.
The directory and file are self-destructed."
(let* ((tmpdir (make-temp-file "mu4e-temp-" t))
(fname (mm-handle-filename handle))
(fname (and fname
(gnus-map-function mm-file-name-rewrite-functions
(file-name-nondirectory fname))))
(fname (if fname
(concat tmpdir "/" (replace-regexp-in-string "/" "-" fname))
(let ((temporary-file-directory tmpdir))
(make-temp-file "mimepart")))))
(fname (mm-handle-filename handle))
(fname (and fname
(gnus-map-function mm-file-name-rewrite-functions
(file-name-nondirectory fname))))
(fname (if fname
(concat tmpdir "/" (replace-regexp-in-string "/" "-" fname))
(let ((temporary-file-directory tmpdir))
(make-temp-file "mimepart")))))
(mm-save-part-to-file handle fname)
(run-at-time "30 sec" nil
(lambda () (ignore-errors (delete-directory tmpdir t))))
(lambda () (ignore-errors (delete-directory tmpdir t))))
fname))
@ -1247,9 +1247,9 @@ open with."
(functionp mu4e-view-open-program))
(funcall mu4e-view-open-program file)
(let ((opener
(or (and (not force-ask) mu4e-view-open-program
(executable-find mu4e-view-open-program))
(read-shell-command "Open MIME-part with: "))))
(or (and (not force-ask) mu4e-view-open-program
(executable-find mu4e-view-open-program))
(read-shell-command "Open MIME-part with: "))))
(call-process opener nil 0 nil file))))
(defun mu4e-view-mime-part-action (&optional n)
@ -1258,43 +1258,43 @@ If N is not specified, ask for it. For instance, '3 A o' opens
the third MIME-part."
(interactive "NNumber of MIME-part: ")
(let* ((parts (mu4e~view-gather-mime-parts))
(options
(mapcar (lambda (action) `(,(plist-get action :name) . ,action))
mu4e-view-mime-part-actions))
(handle
(or (cdr-safe (seq-find (lambda (part) (eq (car part) n)) parts))
(mu4e-error "MIME-part %s not found" n)))
(action
(or (and options (mu4e-read-option "Action on MIME-part: " options))
(mu4e-error "No such action")))
(handler
(or (plist-get action :handler)
(mu4e-error "No :handler item found for action %S" action)))
(receives
(or (plist-get action :receives)
(mu4e-error "No :receives item found for action %S" action))))
(options
(mapcar (lambda (action) `(,(plist-get action :name) . ,action))
mu4e-view-mime-part-actions))
(handle
(or (cdr-safe (seq-find (lambda (part) (eq (car part) n)) parts))
(mu4e-error "MIME-part %s not found" n)))
(action
(or (and options (mu4e-read-option "Action on MIME-part: " options))
(mu4e-error "No such action")))
(handler
(or (plist-get action :handler)
(mu4e-error "No :handler item found for action %S" action)))
(receives
(or (plist-get action :receives)
(mu4e-error "No :receives item found for action %S" action))))
(save-excursion
(cond
((functionp handler)
(cond
((eq receives 'index) (funcall handler n))
((eq receives 'pipe) (funcall handler (mm-with-unibyte-buffer
(mm-insert-part handle)
(buffer-string))))
((eq receives 'temp)
(funcall handler (mu4e~view-mime-part-to-temp-file handle)))
(t (mu4e-error "Invalid :receive for %S" action))))
(cond
((eq receives 'index) (funcall handler n))
((eq receives 'pipe) (funcall handler (mm-with-unibyte-buffer
(mm-insert-part handle)
(buffer-string))))
((eq receives 'temp)
(funcall handler (mu4e~view-mime-part-to-temp-file handle)))
(t (mu4e-error "Invalid :receive for %S" action))))
((stringp handler)
(cond
((eq receives 'index)
(shell-command (concat handler " " (shell-quote-argument n))))
((eq receives 'pipe) (mm-pipe-part handle handler))
((eq receives 'temp)
(shell-command
(shell-command (concat handler " "
(shell-quote-argument
(mu4e~view-mime-part-to-temp-file handle))))))
(t (mu4e-error "Invalid action %S" action))))))))
(cond
((eq receives 'index)
(shell-command (concat handler " " (shell-quote-argument n))))
((eq receives 'pipe) (mm-pipe-part handle handler))
((eq receives 'temp)
(shell-command
(shell-command (concat handler " "
(shell-quote-argument
(mu4e~view-mime-part-to-temp-file handle))))))
(t (mu4e-error "Invalid action %S" action))))))))
(defun mu4e-view-toggle-html ()
"Toggle html-display of the first html-part found."
@ -1303,9 +1303,9 @@ the third MIME-part."
;; pertinence, i.e. the first HTML part found in it is the most important one.
(save-excursion
(if-let ((html-part
(seq-find (lambda (handle)
(equal (mm-handle-media-type (cdr handle)) "text/html"))
gnus-article-mime-handle-alist)))
(seq-find (lambda (handle)
(equal (mm-handle-media-type (cdr handle)) "text/html"))
gnus-article-mime-handle-alist)))
(gnus-article-inline-part (car html-part))
(mu4e-warn "No html part in this message"))))
@ -1347,7 +1347,7 @@ value against HEADER-REGEXP in
header-values))))
(add-hook 'bug-reference-auto-setup-functions
#'mu4e--view-try-setup-bug-reference-mode)
#'mu4e--view-try-setup-bug-reference-mode)
(provide 'mu4e-view)

View File

@ -706,46 +706,46 @@ The main view looks something like the following:
Basics
* [j]ump to some maildir
* enter a [s]earch query
* [C]ompose a new message
* [j]ump to some maildir
* enter a [s]earch query
* [C]ompose a new message
Bookmarks
* [bu] Unread messages (13085/13085)
* [bt] Today's messages
* [bw] Last 7 days (53/128)
* [bp] Messages with images (75/2441)
* [bu] Unread messages (13085/13085)
* [bt] Today's messages
* [bw] Last 7 days (53/128)
* [bp] Messages with images (75/2441)
Maildirs
* [ja] /archive (2101/18837)
* [ji] /inbox (1/2)
* [jb] /bulk (33/35)
* [jB] /bulkarchive (179/2090)
* [jm] /mu (694/17687)
* [jn] /sauron
* [js] /sent
* [ja] /archive (2101/18837)
* [ji] /inbox (1/2)
* [jb] /bulk (33/35)
* [jB] /bulkarchive (179/2090)
* [jm] /mu (694/17687)
* [jn] /sauron
* [js] /sent
Misc
* [;]Switch context
* [U]pdate email & database
* toggle [m]ail sending mode (currently direct)
* [f]lush 1 queued mail
* [;]Switch context
* [U]pdate email & database
* toggle [m]ail sending mode (currently direct)
* [f]lush 1 queued mail
* [N]ews
* [A]bout mu4e
* [H]elp
* [q]uit
* [N]ews
* [A]bout mu4e
* [H]elp
* [q]uit
Info
* last updated : Sat May 7 20:37:37 2022
* database-path : /home/pam/.cache/mu/xapian
* maildir : /home/pam/Maildir
* in store : 86179 messages
* personal addresses : /.*example.com/, pam@fo
* last updated : Sat May 7 20:37:37 2022
* database-path : /home/pam/.cache/mu/xapian
* maildir : /home/pam/Maildir
* in store : 86179 messages
* personal addresses : /.*example.com/, pam@fo
@end verbatim
@end cartouche
@ -950,10 +950,7 @@ M-left,\ previous query
M-right next query
O change sort order
P toggle threading
Q toggle full-search
V toggle skip-duplicates
W toggle include-related
P toggle search property
marking
-------
@ -1037,14 +1034,14 @@ The header field used for sorting is indicated by ``@t{V}'' or
variable @code{mu4e-use-fancy-chars}}, indicating the sort order
(descending or ascending, respectively).
You can change the sort order by clicking the corresponding column with
the mouse, or with @kbd{M-x mu4e-headers-change-sorting} (@key{O}); note
that not all fields can be used for sorting. You can toggle threading
on/off using @kbd{M-x mu4e-headers-toggle-threading} or @key{P}. For
both of these functions, unless you provide a prefix argument
(@key{C-u}), the current search is updated immediately using the new
parameters. You can toggle full-search (@ref{Searching}) using @kbd{M-x
mu4e-headers-toggle-full-search} or @key{Q}.
You can change the sort order by clicking the corresponding column with the
mouse, or with @kbd{M-x mu4e-headers-change-sorting} (@key{O}); note that not
all fields can be used for sorting. You can toggle threading on/off through
@kbd{M-x mu4e-headers-toggle-property} or @key{Pt}. For both of these functions,
unless you provide a prefix argument (@key{C-u}), the current search is updated
immediately using the new parameters. You can toggle full-search
(@ref{Searching}) through @kbd{M-x mu4e-headers-toggle-property} as well; or
@key{Pf}.
Note that with threading enabled, the sorting is exclusively by date,
regardless of the column clicked.
@ -1274,6 +1271,9 @@ b search bookmark
B edit bookmark before search
j jump to maildir
O change sort order
P toggle search property
M-left previous query
M-right next query
@ -1728,12 +1728,11 @@ order for this to work properly you need to pass your address to
executing a query for messages that happen to have the property of being in a
certain folder (maildir).
Normally, queries return up to @code{mu4e-headers-results-limit} (default:
500) results. That is usually more than enough, and makes things significantly
Normally, queries return up to @code{mu4e-headers-results-limit} (default: 500)
results. That is usually more than enough, and makes things significantly
faster. Sometimes, however, you may want to show @emph{all} results; you can
enable this with @kbd{M-x mu4e-headers-toggle-full-search}, or by customizing
the variable @code{mu4e-headers-full-search}. This applies to all search
commands.
enable this with @kbd{M-x mu4e-headers-toggle-property}, or by customizing the
variable @code{mu4e-headers-full-search}. This applies to all search commands.
You can also influence the sort order and whether threads are shown or not;
see @ref{Sorting and threading}.
@ -3798,10 +3797,12 @@ completion.
Set @code{mu4e-cache-maildir-list} to @code{t} (make sure to read
its docstring).
@subsection How can I hide messages from the search results?
See the variable @code{mu4e-headers-hide-predicate}.
@subsection How can I hide certain messages from the search results?
See the variables @code{mu4e-headers-hide-predicate} and
@code{mu4e-headers-hide-enabled}. The latter can be toggled through
@code{mu4e-headers-toggle-property}.
For example, to filter out GMail's spam, set it to:
For example, to filter out GMail's spam folder, set it to:
@lisp
(setq mu4e-headers-hide-predicate
(lambda (msg)