2022-05-05 00:32:46 +02:00
|
|
|
|
;;; mu4e-headers.el -- part of mu4e -*- lexical-binding: t -*-
|
2020-02-11 12:23:40 +01:00
|
|
|
|
|
2022-01-15 09:44:03 +01:00
|
|
|
|
;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema
|
2011-09-12 19:52:32 +02:00
|
|
|
|
|
|
|
|
|
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
|
|
|
|
|
;; This file is not part of GNU Emacs.
|
2020-02-11 12:23:40 +01:00
|
|
|
|
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; mu4e is free software: you can redistribute it and/or modify
|
2011-09-12 19:52:32 +02:00
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; mu4e is distributed in the hope that it will be useful,
|
2011-09-12 19:52:32 +02:00
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; along with mu4e. If not, see <http://www.gnu.org/licenses/>.
|
2011-09-12 19:52:32 +02:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
2012-05-01 21:45:54 +02:00
|
|
|
|
;; In this file are function related mu4e-headers-mode, to creating the list of
|
2012-03-31 16:20:03 +02:00
|
|
|
|
;; one-line descriptions of emails, aka 'headers' (not to be confused with
|
|
|
|
|
;; headers like 'To:' or 'Subject:')
|
2011-09-12 19:52:32 +02:00
|
|
|
|
|
2020-02-11 12:32:47 +01:00
|
|
|
|
;;; Code:
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2018-09-18 02:53:27 +02:00
|
|
|
|
(require 'cl-lib)
|
2012-06-14 18:10:02 +02:00
|
|
|
|
(require 'fringe)
|
2012-04-24 20:12:15 +02:00
|
|
|
|
(require 'hl-line)
|
2021-01-18 16:39:11 +01:00
|
|
|
|
(require 'mailcap)
|
2020-06-07 17:36:34 +02:00
|
|
|
|
(require 'mule-util) ;; seems _some_ people need this for truncate-string-ellipsis
|
|
|
|
|
|
2021-08-29 16:30:10 +02:00
|
|
|
|
(require 'mu4e-update)
|
|
|
|
|
|
2021-08-30 07:36:41 +02:00
|
|
|
|
;; utility functions
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(require 'mu4e-server)
|
2012-04-09 15:34:52 +02:00
|
|
|
|
(require 'mu4e-vars)
|
2012-04-23 18:07:20 +02:00
|
|
|
|
(require 'mu4e-mark)
|
2021-08-28 13:56:31 +02:00
|
|
|
|
(require 'mu4e-context)
|
2022-05-05 00:32:46 +02:00
|
|
|
|
(require 'mu4e-contacts)
|
2021-08-28 21:21:00 +02:00
|
|
|
|
(require 'mu4e-search)
|
2012-05-01 21:45:54 +02:00
|
|
|
|
(require 'mu4e-compose)
|
2012-04-24 17:13:12 +02:00
|
|
|
|
(require 'mu4e-actions)
|
2012-09-26 11:25:38 +02:00
|
|
|
|
(require 'mu4e-message)
|
2021-08-30 07:39:25 +02:00
|
|
|
|
(require 'mu4e-lists)
|
2021-10-21 18:13:02 +02:00
|
|
|
|
(require 'mu4e-update)
|
2021-08-29 16:30:10 +02:00
|
|
|
|
(require 'mu4e-folders)
|
2012-04-24 17:13:12 +02:00
|
|
|
|
|
2020-02-09 17:06:04 +01:00
|
|
|
|
(declare-function mu4e-view "mu4e-view")
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(declare-function mu4e--main-view "mu4e-main")
|
2020-02-09 17:06:04 +01:00
|
|
|
|
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Configuration
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2012-04-24 17:13:12 +02:00
|
|
|
|
(defgroup mu4e-headers nil
|
|
|
|
|
"Settings for the headers view."
|
|
|
|
|
:group 'mu4e)
|
|
|
|
|
|
|
|
|
|
(defcustom mu4e-headers-fields
|
2012-12-18 22:29:17 +01:00
|
|
|
|
'( (:human-date . 12)
|
|
|
|
|
(:flags . 6)
|
|
|
|
|
(:mailing-list . 10)
|
|
|
|
|
(:from . 22)
|
|
|
|
|
(:subject . nil))
|
2012-11-09 16:42:25 +01:00
|
|
|
|
"A list of header fields to show in the headers buffer.
|
2014-10-26 20:21:51 +01:00
|
|
|
|
Each element has the form (HEADER . WIDTH), where HEADER is one of
|
|
|
|
|
the available headers (see `mu4e-header-info') and WIDTH is the
|
|
|
|
|
respective width in characters. A width of `nil' means
|
|
|
|
|
'unrestricted', and this is best reserved for the rightmost (last)
|
|
|
|
|
field. Note that emacs may become very slow with excessively long
|
|
|
|
|
lines (1000s of characters), so if you regularly get such messages,
|
|
|
|
|
you want to avoid fields with `nil' altogether."
|
2012-11-09 16:42:25 +01:00
|
|
|
|
:type `(repeat (cons (choice ,@(mapcar (lambda (h)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(list 'const :tag
|
|
|
|
|
(plist-get (cdr h) :help)
|
|
|
|
|
(car h)))
|
|
|
|
|
mu4e-header-info))
|
|
|
|
|
(choice (integer :tag "width")
|
|
|
|
|
(const :tag "unrestricted width" nil))))
|
2012-04-24 17:13:12 +02:00
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2012-10-19 11:02:13 +02:00
|
|
|
|
(defcustom mu4e-headers-date-format "%x"
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Date format to use in the headers view.
|
|
|
|
|
In the format of `format-time-string'."
|
2012-04-24 17:13:12 +02:00
|
|
|
|
:type 'string
|
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2012-10-19 11:02:13 +02:00
|
|
|
|
(defcustom mu4e-headers-time-format "%X"
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Time format to use in the headers view.
|
|
|
|
|
In the format of `format-time-string'."
|
2012-10-19 11:02:13 +02:00
|
|
|
|
:type 'string
|
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2013-09-18 08:06:35 +02:00
|
|
|
|
(defcustom mu4e-headers-long-date-format "%c"
|
2013-09-17 19:55:31 +02:00
|
|
|
|
"Date format to use in the headers view tooltip.
|
|
|
|
|
In the format of `format-time-string'."
|
|
|
|
|
:type 'string
|
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2012-04-24 17:13:12 +02:00
|
|
|
|
(defcustom mu4e-headers-visible-lines 10
|
|
|
|
|
"Number of lines to display in the header view when using the
|
|
|
|
|
horizontal split-view. This includes the header-line at the top,
|
|
|
|
|
and the mode-line."
|
|
|
|
|
:type 'integer
|
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
|
|
|
|
(defcustom mu4e-headers-visible-columns 30
|
|
|
|
|
"Number of columns to display for the header view when using the
|
|
|
|
|
vertical split-view."
|
|
|
|
|
:type 'integer
|
|
|
|
|
:group 'mu4e-headers)
|
2012-04-28 08:08:28 +02:00
|
|
|
|
|
2020-10-11 11:35:48 +02:00
|
|
|
|
(defcustom mu4e-headers-precise-alignment nil
|
|
|
|
|
"When set, use precise (but relatively slow) alignment for columns.
|
2020-10-26 10:23:09 +01:00
|
|
|
|
By default, do it in a slightly inaccurate but faster way. To get
|
|
|
|
|
an idea about the difference, In some tests, the rendering time
|
|
|
|
|
was around 5.8 ms per messages for precise alignment, versus 3.3
|
|
|
|
|
for non-precise aligment (for 445 messages)."
|
2020-10-11 11:35:48 +02:00
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2012-10-24 17:36:50 +02:00
|
|
|
|
(defcustom mu4e-headers-auto-update t
|
|
|
|
|
"Whether to automatically update the current headers buffer if an
|
|
|
|
|
indexing operation showed changes."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2017-04-25 16:40:41 +02:00
|
|
|
|
(defcustom mu4e-headers-advance-after-mark t
|
|
|
|
|
"With this option set to non-nil, automatically advance to the
|
|
|
|
|
next mail after marking a message in header view."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2012-12-25 10:58:32 +01:00
|
|
|
|
|
2016-07-31 12:07:27 +02:00
|
|
|
|
(defvar mu4e-headers-hide-predicate nil
|
|
|
|
|
"Predicate function applied to headers before they are shown;
|
|
|
|
|
if function is nil or evaluates to nil, show the header,
|
|
|
|
|
otherwise don't. function takes one parameter MSG, which is the
|
|
|
|
|
message plist for the message to be hidden or not.
|
|
|
|
|
|
|
|
|
|
Example that hides all 'trashed' messages:
|
|
|
|
|
(setq mu4e-headers-hide-predicate
|
|
|
|
|
(lambda (msg)
|
|
|
|
|
(member 'trashed (mu4e-message-field msg :flags))))
|
|
|
|
|
|
|
|
|
|
Note that this is merely a display filter.")
|
|
|
|
|
|
2013-03-07 00:42:08 +01:00
|
|
|
|
(defcustom mu4e-headers-visible-flags
|
|
|
|
|
'(draft flagged new passed replied seen trashed attach encrypted signed unread)
|
|
|
|
|
"An ordered list of flags to show in the headers buffer. Each
|
|
|
|
|
element is a symbol in the list (DRAFT FLAGGED NEW PASSED
|
|
|
|
|
REPLIED SEEN TRASHED ATTACH ENCRYPTED SIGNED UNREAD)."
|
|
|
|
|
:type '(set
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(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))
|
2013-03-07 00:42:08 +01:00
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2013-10-19 11:06:04 +02:00
|
|
|
|
(defcustom mu4e-headers-found-hook nil
|
|
|
|
|
"Hook run just *after* all of the headers for the last search
|
|
|
|
|
query have been received and are displayed."
|
|
|
|
|
:type 'hook
|
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2020-02-11 15:10:35 +01:00
|
|
|
|
;;; Public variables
|
|
|
|
|
|
2015-03-22 09:57:07 +01:00
|
|
|
|
(defvar mu4e-headers-sort-field :date
|
2022-05-05 00:32:46 +02:00
|
|
|
|
"Field to sort the headers by. A symbol:
|
2017-07-02 09:31:21 +02:00
|
|
|
|
one of: `:date', `:subject', `:size', `:prio', `:from', `:to.',
|
2021-02-12 17:49:55 +01:00
|
|
|
|
`:list'.
|
|
|
|
|
|
|
|
|
|
Note that when threading is enabled (through
|
2021-08-29 16:30:10 +02:00
|
|
|
|
`mu4e-search-threads'), the headers are exclusively sorted
|
2021-02-12 17:49:55 +01:00
|
|
|
|
chronologically (`:date') by the newest message in the thread.")
|
2015-03-22 09:57:07 +01:00
|
|
|
|
|
|
|
|
|
(defvar mu4e-headers-sort-direction 'descending
|
|
|
|
|
"Direction to sort by; a symbol either `descending' (sorting
|
|
|
|
|
Z->A) or `ascending' (sorting A->Z).")
|
|
|
|
|
|
2020-02-11 15:10:35 +01:00
|
|
|
|
;;;; Fancy marks
|
|
|
|
|
|
2012-08-27 17:04:58 +02:00
|
|
|
|
;; marks for headers of the form; each is a cons-cell (basic . fancy)
|
|
|
|
|
;; each of which is basic ascii char and something fancy, respectively
|
2015-12-15 06:02:24 +01:00
|
|
|
|
(defvar mu4e-headers-draft-mark '("D" . "⚒") "Draft.")
|
2015-12-15 08:07:17 +01:00
|
|
|
|
(defvar mu4e-headers-flagged-mark '("F" . "✚") "Flagged.")
|
|
|
|
|
(defvar mu4e-headers-new-mark '("N" . "✱") "New.")
|
2015-12-15 06:02:24 +01:00
|
|
|
|
(defvar mu4e-headers-passed-mark '("P" . "❯") "Passed (fwd).")
|
|
|
|
|
(defvar mu4e-headers-replied-mark '("R" . "❮") "Replied.")
|
|
|
|
|
(defvar mu4e-headers-seen-mark '("S" . "✔") "Seen.")
|
2015-12-16 20:31:17 +01:00
|
|
|
|
(defvar mu4e-headers-trashed-mark '("T" . "⏚") "Trashed.")
|
2015-12-15 06:02:24 +01:00
|
|
|
|
(defvar mu4e-headers-attach-mark '("a" . "⚓") "W/ attachments.")
|
|
|
|
|
(defvar mu4e-headers-encrypted-mark '("x" . "⚴") "Encrypted.")
|
|
|
|
|
(defvar mu4e-headers-signed-mark '("s" . "☡") "Signed.")
|
2015-12-16 20:31:17 +01:00
|
|
|
|
(defvar mu4e-headers-unread-mark '("u" . "⎕") "Unread.")
|
2012-08-27 17:04:58 +02:00
|
|
|
|
|
2020-02-11 15:10:35 +01:00
|
|
|
|
;;;; Graph drawing
|
|
|
|
|
|
2022-02-06 09:44:47 +01:00
|
|
|
|
(defvar mu4e-headers-thread-mark-as-orphan 'first
|
2022-01-19 20:55:28 +01:00
|
|
|
|
"Define which messages should be prefixed with the orphan mark.
|
2022-02-06 09:44:47 +01:00
|
|
|
|
`all' marks all the messages without a parent as orphan, `first'
|
|
|
|
|
only marks the first message in the thread.")
|
2022-01-19 20:55:28 +01:00
|
|
|
|
|
2021-01-20 10:21:10 +01:00
|
|
|
|
(defvar mu4e-headers-thread-root-prefix '("* " . "□ ")
|
|
|
|
|
"Prefix for root messages.")
|
|
|
|
|
(defvar mu4e-headers-thread-child-prefix '("|>" . "│ ")
|
2018-05-03 04:04:50 +02:00
|
|
|
|
"Prefix for messages in sub threads that do have a following sibling.")
|
2021-01-20 10:21:10 +01:00
|
|
|
|
(defvar mu4e-headers-thread-first-child-prefix '("o " . "⚬ ")
|
2018-05-03 04:04:50 +02:00
|
|
|
|
"Prefix for messages in sub threads that do not have a following sibling.")
|
2021-01-20 10:21:10 +01:00
|
|
|
|
(defvar mu4e-headers-thread-last-child-prefix '("L" . "└ ")
|
|
|
|
|
"Prefix for messages in sub threads that do not have a following sibling.")
|
|
|
|
|
(defvar mu4e-headers-thread-connection-prefix '("|" . "│ ")
|
2018-04-23 06:07:58 +02:00
|
|
|
|
"Prefix to connect sibling messages that do not follow each other.
|
2021-03-04 20:58:25 +01:00
|
|
|
|
Must have the same length as `mu4e-headers-thread-blank-prefix'.")
|
2018-04-23 06:07:58 +02:00
|
|
|
|
(defvar mu4e-headers-thread-blank-prefix '(" " . " ")
|
|
|
|
|
"Prefix to separate non connected messages.
|
2021-03-04 20:58:25 +01:00
|
|
|
|
Must have the same length as `mu4e-headers-thread-connection-prefix'.")
|
2021-01-20 10:21:10 +01:00
|
|
|
|
(defvar mu4e-headers-thread-orphan-prefix '("<>" . "♢ ")
|
2018-05-03 04:31:33 +02:00
|
|
|
|
"Prefix for orphan messages with siblings.")
|
2021-01-20 10:21:10 +01:00
|
|
|
|
(defvar mu4e-headers-thread-single-orphan-prefix '("<>" . "♢ ")
|
2018-05-03 04:31:33 +02:00
|
|
|
|
"Prefix for orphan messages with no siblings.")
|
2018-04-23 06:07:58 +02:00
|
|
|
|
(defvar mu4e-headers-thread-duplicate-prefix '("=" . "≡ ")
|
2018-05-03 04:04:50 +02:00
|
|
|
|
"Prefix for duplicate messages.")
|
2012-08-27 17:04:58 +02:00
|
|
|
|
|
2021-02-12 19:39:49 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defvar mu4e-headers-threaded-label '("T" . "🎄")
|
|
|
|
|
"Non-fancy and fancy labels for threaded search in the mode-line.")
|
|
|
|
|
(defvar mu4e-headers-full-label '("F" . "∀")
|
|
|
|
|
"Non-fancy and fancy labels for full search in the mode-line.")
|
|
|
|
|
(defvar mu4e-headers-related-label '("R" . "🤝")
|
2021-07-29 13:40:56 +02:00
|
|
|
|
"Non-fancy and fancy labels for include-related search in the mode-line.")
|
2021-02-12 19:39:49 +01:00
|
|
|
|
|
2020-02-11 15:10:35 +01:00
|
|
|
|
;;;; Various
|
|
|
|
|
|
2012-04-24 17:13:12 +02:00
|
|
|
|
(defvar mu4e-headers-actions
|
2016-06-08 18:25:52 +02:00
|
|
|
|
'( ("capture message" . mu4e-action-capture-message)
|
2015-11-07 06:36:31 +01:00
|
|
|
|
("show this thread" . mu4e-action-show-thread))
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"List of actions to perform on messages in the headers list.
|
2017-11-04 14:06:45 +01:00
|
|
|
|
The actions are cons-cells of the form (NAME . FUNC) where:
|
2012-04-24 17:13:12 +02:00
|
|
|
|
* NAME is the name of the action (e.g. \"Count lines\")
|
2016-12-18 11:38:50 +01:00
|
|
|
|
* FUNC is a function which receives a message plist as an argument.
|
2017-11-04 14:06:45 +01:00
|
|
|
|
|
2016-12-18 11:38:50 +01:00
|
|
|
|
The first character of NAME is used as the shortcut.")
|
2012-06-10 10:19:51 +02:00
|
|
|
|
|
2012-06-14 20:54:24 +02:00
|
|
|
|
(defvar mu4e-headers-custom-markers
|
|
|
|
|
'(("Older than"
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(lambda (msg date) (time-less-p (mu4e-msg-field msg :date) date))
|
|
|
|
|
(lambda () (mu4e-get-time-date "Match messages before: ")))
|
|
|
|
|
("Newer than"
|
|
|
|
|
(lambda (msg date) (time-less-p date (mu4e-msg-field msg :date)))
|
|
|
|
|
(lambda () (mu4e-get-time-date "Match messages after: ")))
|
|
|
|
|
("Bigger than"
|
|
|
|
|
(lambda (msg bytes) (> (mu4e-msg-field msg :size) (* 1024 bytes)))
|
|
|
|
|
(lambda () (read-number "Match messages bigger than (Kbytes): "))))
|
2012-06-14 20:54:24 +02:00
|
|
|
|
"List of custom markers -- functions to mark message that match
|
|
|
|
|
some custom function. Each of the list members has the following format:
|
|
|
|
|
(NAME PREDICATE-FUNC PARAM-FUNC)
|
|
|
|
|
* NAME is the name of the predicate function, and the first character
|
|
|
|
|
is the shortcut (so keep those unique).
|
2012-07-10 18:15:13 +02:00
|
|
|
|
* PREDICATE-FUNC is a function that takes two parameters, MSG
|
|
|
|
|
and (optionally) PARAM, and should return non-nil when there's a
|
|
|
|
|
match.
|
2012-06-14 20:54:24 +02:00
|
|
|
|
* PARAM-FUNC is function that is evaluated once, and its value is then passed to
|
|
|
|
|
PREDICATE-FUNC as PARAM. This is useful for getting user-input.")
|
2020-02-11 15:10:35 +01:00
|
|
|
|
;;; Internal variables/constants
|
2012-04-28 08:08:28 +02:00
|
|
|
|
|
|
|
|
|
;; docid cookies
|
2015-12-15 06:02:24 +01:00
|
|
|
|
(defconst mu4e~headers-docid-pre "\376"
|
2012-05-02 16:22:45 +02:00
|
|
|
|
"Each header starts (invisibly) with the `mu4e~headers-docid-pre',
|
2012-11-10 14:01:17 +01:00
|
|
|
|
followed by the docid, followed by `mu4e~headers-docid-post'.")
|
2015-12-15 06:02:24 +01:00
|
|
|
|
(defconst mu4e~headers-docid-post "\377"
|
2012-05-02 16:22:45 +02:00
|
|
|
|
"Each header starts (invisibly) with the `mu4e~headers-docid-pre',
|
2012-11-10 14:01:17 +01:00
|
|
|
|
followed by the docid, followed by `mu4e~headers-docid-post'.")
|
2012-04-24 20:12:15 +02:00
|
|
|
|
|
2012-11-11 18:27:55 +01:00
|
|
|
|
(defvar mu4e~headers-sort-field-choices
|
2020-02-11 12:00:46 +01:00
|
|
|
|
'( ("date" . :date)
|
|
|
|
|
("from" . :from)
|
2017-10-30 23:57:02 +01:00
|
|
|
|
("list" . :list)
|
2016-10-25 19:34:57 +02:00
|
|
|
|
("maildir" . :maildir)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
("prio" . :prio)
|
|
|
|
|
("zsize" . :size)
|
|
|
|
|
("subject" . :subject)
|
|
|
|
|
("to" . :to))
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"List of cells describing the various sort-options.
|
|
|
|
|
In the format needed for `mu4e-read-option'.")
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
2022-04-10 10:52:42 +02:00
|
|
|
|
(defvar mu4e~headers-search-start nil)
|
2020-10-24 13:51:04 +02:00
|
|
|
|
(defvar mu4e~headers-render-start nil)
|
|
|
|
|
(defvar mu4e~headers-render-time nil)
|
|
|
|
|
|
|
|
|
|
(defvar mu4e-headers-report-render-time nil
|
|
|
|
|
"If non-nil, report on the time it took to render the messages.
|
|
|
|
|
This is mostly useful for profiling.")
|
|
|
|
|
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
2022-04-10 10:52:42 +02:00
|
|
|
|
|
|
|
|
|
;;; Clear
|
|
|
|
|
|
2017-10-29 11:10:10 +01:00
|
|
|
|
(defun mu4e~headers-clear (&optional msg)
|
2012-03-25 12:28:06 +02:00
|
|
|
|
"Clear the header buffer and related data structures."
|
2017-05-18 03:42:52 +02:00
|
|
|
|
(when (buffer-live-p (mu4e-get-headers-buffer))
|
2020-10-24 13:51:04 +02:00
|
|
|
|
(setq mu4e~headers-render-start (float-time))
|
2012-03-25 12:28:06 +02:00
|
|
|
|
(let ((inhibit-read-only t))
|
2017-05-18 03:42:52 +02:00
|
|
|
|
(with-current-buffer (mu4e-get-headers-buffer)
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--mark-clear)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(erase-buffer)
|
|
|
|
|
(when msg
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(insert (propertize msg 'face 'mu4e-system-face 'intangible t)))))))
|
2012-03-25 12:28:06 +02:00
|
|
|
|
|
2011-09-20 22:59:20 +02:00
|
|
|
|
|
2020-02-18 22:39:30 +01:00
|
|
|
|
;;; Misc
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2020-02-09 01:26:17 +01:00
|
|
|
|
(defun mu4e~headers-contact-str (contacts)
|
2011-11-09 07:35:24 +01:00
|
|
|
|
"Turn the list of contacts CONTACTS (with elements (NAME . EMAIL)
|
|
|
|
|
into a string."
|
|
|
|
|
(mapconcat
|
2022-05-05 00:32:46 +02:00
|
|
|
|
(lambda (contact)
|
|
|
|
|
(let ((name (mu4e-contact-name contact))
|
|
|
|
|
(email (mu4e-contact-email contact)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(or name email "?"))) contacts ", "))
|
2011-11-09 07:35:24 +01:00
|
|
|
|
|
2020-02-09 01:26:17 +01:00
|
|
|
|
(defun mu4e~headers-thread-prefix-map (type)
|
2018-04-23 06:07:58 +02:00
|
|
|
|
"Return the thread prefix based on the symbol TYPE."
|
|
|
|
|
(let ((get-prefix
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(lambda (cell)
|
|
|
|
|
(if mu4e-use-fancy-chars (cdr cell) (car cell)))))
|
2018-09-18 02:53:27 +02:00
|
|
|
|
(cl-case type
|
2018-05-03 04:31:33 +02:00
|
|
|
|
('child (funcall get-prefix mu4e-headers-thread-child-prefix))
|
2021-05-01 07:32:39 +02:00
|
|
|
|
('first-child (funcall get-prefix mu4e-headers-thread-first-child-prefix))
|
2018-05-03 04:31:33 +02:00
|
|
|
|
('last-child (funcall get-prefix mu4e-headers-thread-last-child-prefix))
|
|
|
|
|
('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))
|
|
|
|
|
('duplicate (funcall get-prefix mu4e-headers-thread-duplicate-prefix))
|
|
|
|
|
(t "?"))))
|
2018-04-23 06:07:58 +02:00
|
|
|
|
|
2021-05-01 07:32:39 +02:00
|
|
|
|
|
2020-02-11 15:10:35 +01:00
|
|
|
|
;; headers in the buffer are prefixed by an invisible string with the docid
|
|
|
|
|
;; followed by an EOT ('end-of-transmission', \004, ^D) non-printable ascii
|
|
|
|
|
;; character. this string also has a text-property with the docid. the former
|
|
|
|
|
;; is used for quickly finding a certain header, the latter for retrieving the
|
|
|
|
|
;; docid at point without string matching etc.
|
2020-02-09 01:26:17 +01:00
|
|
|
|
|
|
|
|
|
(defun mu4e~headers-docid-pos (docid)
|
|
|
|
|
"Return the pos of the beginning of the line with the header with
|
|
|
|
|
docid DOCID, or nil if it cannot be found."
|
|
|
|
|
(let ((pos))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(setq pos (mu4e~headers-goto-docid docid)))
|
|
|
|
|
pos))
|
|
|
|
|
|
|
|
|
|
(defun mu4e~headers-docid-cookie (docid)
|
|
|
|
|
"Create an invisible string containing DOCID; this is to be used
|
|
|
|
|
at the beginning of lines to identify headers."
|
|
|
|
|
(propertize (format "%s%d%s"
|
2020-02-11 12:00:46 +01:00
|
|
|
|
mu4e~headers-docid-pre docid mu4e~headers-docid-post)
|
|
|
|
|
'docid docid 'invisible t));;
|
2020-02-09 01:26:17 +01:00
|
|
|
|
|
|
|
|
|
(defun 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
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(goto-char point))
|
2020-02-09 01:26:17 +01:00
|
|
|
|
(get-text-property (line-beginning-position) 'docid)))
|
|
|
|
|
|
|
|
|
|
|
2021-10-21 18:21:09 +02:00
|
|
|
|
|
2020-02-09 01:26:17 +01:00
|
|
|
|
(defun mu4e~headers-goto-docid (docid &optional to-mark)
|
|
|
|
|
"Go to the beginning of the line with the header with docid
|
|
|
|
|
DOCID, or nil if it cannot be found. If the optional TO-MARK is
|
|
|
|
|
non-nil, go to the point directly *after* the docid-cookie instead
|
|
|
|
|
of the beginning of the line."
|
|
|
|
|
(let ((oldpoint (point)) (newpoint))
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(setq newpoint
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(search-forward (mu4e~headers-docid-cookie docid) nil t))
|
2020-02-09 01:26:17 +01:00
|
|
|
|
(unless to-mark
|
|
|
|
|
(if (null newpoint)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(goto-char oldpoint) ;; not found; restore old pos
|
|
|
|
|
(progn
|
|
|
|
|
(beginning-of-line) ;; found, move to beginning of line
|
|
|
|
|
(setq newpoint (point)))))
|
2020-02-09 01:26:17 +01:00
|
|
|
|
newpoint)) ;; return the point, or nil if not found
|
|
|
|
|
|
|
|
|
|
(defun mu4e~headers-field-for-docid (docid field)
|
|
|
|
|
"Get FIELD (a symbol, see `mu4e-headers-names') for the message
|
|
|
|
|
with DOCID which must be present in the headers buffer."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(when (mu4e~headers-goto-docid docid)
|
|
|
|
|
(mu4e-message-field (mu4e-message-at-point) field))))
|
|
|
|
|
|
|
|
|
|
|
2018-04-23 06:07:58 +02:00
|
|
|
|
;; In order to print a thread tree with all the message connections,
|
|
|
|
|
;; it's necessary to keep track of all sub levels that still have
|
|
|
|
|
;; following messages. For each level, mu4e~headers-thread-state keeps
|
|
|
|
|
;; the value t for a connection or nil otherwise.
|
|
|
|
|
(defvar-local mu4e~headers-thread-state '())
|
|
|
|
|
|
2020-02-09 01:26:17 +01:00
|
|
|
|
(defun mu4e~headers-thread-prefix (thread)
|
2011-11-20 00:19:07 +01:00
|
|
|
|
"Calculate the thread prefix based on thread info THREAD."
|
2012-08-27 17:04:58 +02:00
|
|
|
|
(when thread
|
2021-05-01 07:32:39 +02:00
|
|
|
|
(let* ((prefix "")
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(level (plist-get thread :level))
|
|
|
|
|
(has-child (plist-get thread :has-child))
|
|
|
|
|
(first-child (plist-get thread :first-child))
|
|
|
|
|
(last-child (plist-get thread :last-child))
|
2021-05-01 07:32:39 +02:00
|
|
|
|
(orphan (plist-get thread :orphan))
|
|
|
|
|
(single-orphan(and orphan first-child last-child))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(duplicate (plist-get thread :duplicate)))
|
2018-04-23 06:07:58 +02:00
|
|
|
|
;; Do not prefix root messages.
|
2018-05-03 13:27:20 +02:00
|
|
|
|
(if (= level 0)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(setq mu4e~headers-thread-state '()))
|
2018-04-23 06:07:58 +02:00
|
|
|
|
(if (> level 0)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(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
|
2021-05-01 07:32:39 +02:00
|
|
|
|
(if single-orphan 'single-orphan
|
2022-01-19 20:55:28 +01:00
|
|
|
|
(if (and orphan
|
2022-02-06 09:44:47 +01:00
|
|
|
|
(or first-child
|
|
|
|
|
(not (eq mu4e-headers-thread-mark-as-orphan 'first))))
|
|
|
|
|
'orphan
|
2021-05-01 07:32:39 +02:00
|
|
|
|
(if last-child 'last-child
|
|
|
|
|
(if first-child 'first-child
|
|
|
|
|
'child)))))))))
|
2018-04-23 06:07:58 +02:00
|
|
|
|
;; 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))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(setq mu4e~headers-thread-state
|
|
|
|
|
(append mu4e~headers-thread-state '(t))))
|
2018-04-23 06:07:58 +02:00
|
|
|
|
;; Return the thread prefix.
|
2018-05-03 04:31:33 +02:00
|
|
|
|
(format "%s%s"
|
2020-02-11 12:00:46 +01:00
|
|
|
|
prefix
|
|
|
|
|
(if duplicate
|
|
|
|
|
(mu4e~headers-thread-prefix-map 'duplicate) "")))))
|
2012-08-27 17:04:58 +02:00
|
|
|
|
|
2020-02-09 01:26:17 +01:00
|
|
|
|
(defun mu4e~headers-flags-str (flags)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Get a display string for the flags.
|
|
|
|
|
Note that `mu4e-flags-to-string' is for internal use only; this
|
|
|
|
|
function is for display. (This difference is significant, since
|
|
|
|
|
internally, the Maildir spec determines what the flags look like,
|
|
|
|
|
while our display may be different)."
|
2013-03-07 00:42:08 +01:00
|
|
|
|
(let ((str "")
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(get-prefix
|
|
|
|
|
(lambda (cell) (if mu4e-use-fancy-chars (cdr cell) (car cell)))))
|
2013-03-07 00:42:08 +01:00
|
|
|
|
(dolist (flag mu4e-headers-visible-flags)
|
|
|
|
|
(when (member flag flags)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(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)))))))
|
2013-03-07 00:42:08 +01:00
|
|
|
|
str))
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2020-02-18 22:39:30 +01:00
|
|
|
|
;;; Special headers
|
2012-08-27 17:04:58 +02:00
|
|
|
|
|
2020-02-09 01:26:17 +01:00
|
|
|
|
(defun mu4e~headers-from-or-to (msg)
|
2022-05-05 00:32:46 +02:00
|
|
|
|
"Get the From: address from MSG if not one of user's; otherwise get To:.
|
|
|
|
|
When the from address for message MSG is one of the the user's addresses,
|
2020-10-17 09:14:04 +02:00
|
|
|
|
\(as per `mu4e-personal-address-p'), show the To address;
|
2012-10-19 15:01:55 +02:00
|
|
|
|
otherwise ; show the from address; prefixed with the appropriate
|
2012-09-15 20:10:46 +02:00
|
|
|
|
`mu4e-headers-from-or-to-prefix'."
|
2022-05-05 00:32:46 +02:00
|
|
|
|
(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))))
|
|
|
|
|
(if is-user
|
|
|
|
|
(concat "To " (mu4e~headers-contact-str (mu4e-message-field msg :to)))
|
|
|
|
|
(mu4e~headers-contact-str (mu4e-message-field msg :from)))))
|
|
|
|
|
|
|
|
|
|
|