mu4e: updates for core changes

Deal with (name . email) --> (:name "boo" :email "boo@example.com")
Add support for the new "changed" field.
This commit is contained in:
Dirk-Jan C. Binnema 2022-05-05 01:32:46 +03:00
parent 4b56b8779d
commit 45aec819f6
9 changed files with 187 additions and 153 deletions

View File

@ -859,8 +859,8 @@ buffer buried."
(when other-headers (when other-headers
(dolist (h other-headers other-headers) (dolist (h other-headers other-headers)
(if (symbolp (car h)) (setcar h (symbol-name (car h)))) (if (symbolp (car h)) (setcar h (symbol-name (car h))))
(message-add-header (concat (capitalize (car h)) ": " (cdr h) "\n" )) (message-add-header
)) (concat (capitalize (car h)) ": " (cdr h) "\n" ))))
;; yank message ;; yank message
(if (bufferp yank-action) (if (bufferp yank-action)

View File

@ -1,6 +1,6 @@
;;; mu4e-contacts.el -- part of mu4e -*- lexical-binding: t -*- ;;; mu4e-contacts.el -- part of mu4e -*- lexical-binding: t -*-
;; Copyright (C) 2021 Dirk-Jan C. Binnema ;; Copyright (C) 2022 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -44,14 +44,14 @@ These addresses are the ones specified with `mu init'."
:type 'boolean :type 'boolean
:group 'mu4e-compose) :group 'mu4e-compose)
(defcustom mu4e-compose-complete-only-after "2014-01-01" (defcustom mu4e-compose-complete-only-after "2018-01-01"
"Consider only contacts last seen after this date. "Consider only contacts last seen after this date.
Date must be a string of the form YYY-MM-DD. Date must be a string of the form YYYY-MM-DD.
This is useful for limiting a potentially enormous set of This is useful for limiting a potentially enormous set of
contacts for auto-completion to just those that are present in contacts for auto-completion to just those that are present in
the e-mail corpus in recent timses. Set to nil to not have any the e-mail corpus in recent times. Set to nil to not have any
time-based restriction." time-based restriction."
:type 'string :type 'string
:group 'mu4e-compose) :group 'mu4e-compose)
@ -155,6 +155,73 @@ addresses and /regular expressions/."
"determined by server; see `mu4e-personal-addresses'." "determined by server; see `mu4e-personal-addresses'."
"1.3.8") "1.3.8")
;; Helpers
;;; RFC2822 handling of phrases in mail-addresses
;;
;; The optional display-name contains a phrase, it sits before the
;; angle-addr as specified in RFC2822 for email-addresses in header
;; fields. Contributed by jhelberg.
(defun mu4e--rfc822-phrase-type (ph)
"Return an atom or quoted-string for the phrase PH.
This checks for empty string first. Then quotes around the phrase
\(returning 'rfc822-quoted-string). Then whether there is a quote
inside the phrase (returning 'rfc822-containing-quote). The
reverse of the RFC atext definition is then tested. If it
matches, nil is returned, if not, it is an 'rfc822-atom, which is
returned."
(cond
((= (length ph) 0) 'rfc822-empty)
((= (aref ph 0) ?\")
(if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph)
'rfc822-quoted-string
'rfc822-containing-quote)) ; starts with quote, but doesn't end with one
((string-match-p "[\"]" ph) 'rfc822-containing-quote)
((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil)
(t 'rfc822-atom)))
(defun mu4e--rfc822-quote-phrase (ph)
"Quote an RFC822 phrase PH only if necessary.
Atoms and quoted strings don't need quotes. The rest do. In
case a phrase contains a quote, it will be escaped."
(let ((type (mu4e--rfc822-phrase-type ph)))
(cond
((eq type 'rfc822-atom) ph)
((eq type 'rfc822-quoted-string) ph)
((eq type 'rfc822-containing-quote)
(format "\"%s\""
(replace-regexp-in-string "\"" "\\\\\"" ph)))
(t (format "\"%s\"" ph)))))
(defsubst mu4e-contact-name (contact)
"Get the name of this CONTACT, or nil."
(plist-get contact :name))
(defsubst mu4e-contact-email (contact)
"Get the name of this CONTACT, or nil."
(plist-get contact :email))
(defsubst mu4e-contact-cons (contact)
"Convert a CONTACT plist into a old-style (name . email)."
(cons
(mu4e-contact-name contact)
(mu4e-contact-email contact)))
(defsubst mu4e-contact-make (name email)
"Creata contact plist from NAME and EMAIL."
`(:name ,name :email ,email))
(defun mu4e-contact-full (contact)
"Get the full combination of name and email address from CONTACT."
(let* ((email (mu4e-contact-email contact))
(name (mu4e-contact-name contact)))
(if (and name (> (length name) 0))
(format "%s <%s>" (mu4e--rfc822-quote-phrase name) email)
email)))
(defun mu4e--update-contacts (contacts &optional tstamp) (defun mu4e--update-contacts (contacts &optional tstamp)
"Receive a sorted list of CONTACTS newer than TSTAMP. "Receive a sorted list of CONTACTS newer than TSTAMP.
@ -212,7 +279,7 @@ For testing/debugging."
(setq contacts (sort contacts (setq contacts (sort contacts
(lambda(cell1 cell2) (< (car cell1) (car cell2))))) (lambda(cell1 cell2) (< (car cell1) (car cell2)))))
(dolist (contact contacts) (dolist (contact contacts)
(insert (format "%s\n" (cdr contact)))))) (insert (format "%s\n" (mu4e-contact-email contact))))))
(pop-to-buffer "*mu4e-contacts-info*"))) (pop-to-buffer "*mu4e-contacts-info*")))

View File

@ -1,6 +1,6 @@
;;; mu4e-draft.el -- part of mu4e, the mu mail user agent for emacs -*- lexical-binding: t -*- ;;; mu4e-draft.el -- part of mu4e, the mu mail user agent for emacs -*- lexical-binding: t -*-
;; ;;
;; Copyright (C) 2011-2020 Dirk-Jan C. Binnema ;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -312,7 +312,6 @@ one. Code borrowed from `message-shorten-1'."
(nthcdr (+ (- cut 2) surplus 1) list))) (nthcdr (+ (- cut 2) surplus 1) list)))
(defun mu4e~fontify-signature () (defun mu4e~fontify-signature ()
"Give the message signatures a distinctive color. This is used "Give the message signatures a distinctive color. This is used
in the view and compose modes and will color each signature in in the view and compose modes and will color each signature in
@ -360,13 +359,7 @@ This is specified as a comma-separated list of e-mail addresses.
If LST is nil, returns nil." If LST is nil, returns nil."
(when lst (when lst
(mapconcat (mapconcat
(lambda (addrcell) (lambda (contact) (mu4e-contact-full contact)) lst ", ")))
(let ((name (car addrcell))
(email (cdr addrcell)))
(if name
(format "%s <%s>" (mu4e~rfc822-quoteit name) email)
(format "%s" email))))
lst ", ")))
(defun mu4e~draft-address-cell-equal (cell1 cell2) (defun mu4e~draft-address-cell-equal (cell1 cell2)
"Return t if CELL1 and CELL2 have the same e-mail address. "Return t if CELL1 and CELL2 have the same e-mail address.
@ -374,8 +367,8 @@ The comparison is done case-insensitively. If the cells done
match return nil. CELL1 and CELL2 are cons cells of the match return nil. CELL1 and CELL2 are cons cells of the
form (NAME . EMAIL)." form (NAME . EMAIL)."
(string= (string=
(downcase (or (cdr cell1) "")) (downcase (or (mu4e-contact-email cell1) ""))
(downcase (or (cdr cell2) "")))) (downcase (or (mu4e-contact-email cell2) ""))))
(defun mu4e~draft-create-to-lst (origmsg) (defun mu4e~draft-create-to-lst (origmsg)
@ -391,7 +384,7 @@ of the original, we simple copy the list form the original."
(if mu4e-compose-dont-reply-to-self (if mu4e-compose-dont-reply-to-self
(cl-delete-if (cl-delete-if
(lambda (to-cell) (lambda (to-cell)
(mu4e-personal-address-p (cdr to-cell))) (mu4e-personal-address-p (mu4e-contact-email to-cell)))
reply-to) reply-to)
reply-to))) reply-to)))
@ -406,7 +399,7 @@ I.e. return all the addresses in ADDRS not matching
((functionp mu4e-compose-reply-ignore-address) ((functionp mu4e-compose-reply-ignore-address)
(cl-remove-if (cl-remove-if
(lambda (elt) (lambda (elt)
(funcall mu4e-compose-reply-ignore-address (cdr elt))) (funcall mu4e-compose-reply-ignore-address (mu4e-contact-email elt)))
addrs)) addrs))
(t (t
;; regexp or list of regexps ;; regexp or list of regexps
@ -417,7 +410,7 @@ I.e. return all the addresses in ADDRS not matching
regexp))) regexp)))
(cl-remove-if (cl-remove-if
(lambda (elt) (lambda (elt)
(string-match regexp (cdr elt))) (string-match regexp (mu4e-contact-email elt)))
addrs))))) addrs)))))
(defun mu4e~draft-create-cc-lst (origmsg &optional reply-all include-from) (defun mu4e~draft-create-cc-lst (origmsg &optional reply-all include-from)
@ -452,7 +445,7 @@ REPLY-ALL."
cc-lst cc-lst
(cl-delete-if (cl-delete-if
(lambda (cc-cell) (lambda (cc-cell)
(mu4e-personal-address-p (cdr cc-cell))) (mu4e-personal-address-p (mu4e-contact-email cc-cell)))
cc-lst)))) cc-lst))))
cc-lst))) cc-lst)))
@ -470,53 +463,14 @@ message. Return nil if there are no recipients for the particular field."
(otherwise (otherwise
(mu4e-error "Unsupported field"))))) (mu4e-error "Unsupported field")))))
;;; RFC2822 handling of phrases in mail-addresses
;;
;; The optional display-name contains a phrase, it sits before the
;; angle-addr as specified in RFC2822 for email-addresses in header
;; fields. Contributed by jhelberg.
(defun mu4e~rfc822-phrase-type (ph)
"Return an atom or quoted-string for the phrase PH.
This checks for empty string first. Then quotes around the phrase
\(returning 'rfc822-quoted-string). Then whether there is a quote
inside the phrase (returning 'rfc822-containing-quote).
The reverse of the RFC atext definition is then tested.
If it matches, nil is returned, if not, it is an 'rfc822-atom, which
is returned."
(cond
((= (length ph) 0) 'rfc822-empty)
((= (aref ph 0) ?\")
(if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph)
'rfc822-quoted-string
'rfc822-containing-quote)) ; starts with quote, but doesn't end with one
((string-match-p "[\"]" ph) 'rfc822-containing-quote)
((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil)
(t 'rfc822-atom)))
(defun mu4e~rfc822-quoteit (ph)
"Quote an RFC822 phrase PH only if necessary.
Atoms and quoted strings don't need quotes. The rest do. In
case a phrase contains a quote, it will be escaped."
(let ((type (mu4e~rfc822-phrase-type ph)))
(cond
((eq type 'rfc822-atom) ph)
((eq type 'rfc822-quoted-string) ph)
((eq type 'rfc822-containing-quote)
(format "\"%s\""
(replace-regexp-in-string "\"" "\\\\\"" ph)))
(t (format "\"%s\"" ph)))))
(defun mu4e~draft-from-construct () (defun mu4e~draft-from-construct ()
"Construct a value for the From:-field of the reply. "Construct a value for the From:-field of the reply.
This is based on the variable `user-full-name' and This is based on the variable `user-full-name' and
`user-mail-address'; if the latter is nil, function returns nil." `user-mail-address'; if the latter is nil, function returns nil."
(when user-mail-address (when user-mail-address
(if user-full-name (mu4e-contact-full (mu4e-contact-make
(format "%s <%s>" (mu4e~rfc822-quoteit user-full-name) user-mail-address) user-full-name
(format "%s" user-mail-address)))) user-mail-address))))
;;; Header separators ;;; Header separators
@ -527,34 +481,34 @@ the body starts. Note, in `mu4e-compose-mode', we use
`before-save-hook' and `after-save-hook' to ensure that this `before-save-hook' and `after-save-hook' to ensure that this
separator is never written to the message file. Also see separator is never written to the message file. Also see
`mu4e-remove-mail-header-separator'." `mu4e-remove-mail-header-separator'."
;; we set this here explicitly, since (as it has happened) a wrong ;; we set this here explicitly, since (as it has happened) a wrong
;; value for this (such as "") breaks address completion and other things ;; value for this (such as "") breaks address completion and other things
(set (make-local-variable 'mail-header-separator) "--text follows this line--") (set (make-local-variable 'mail-header-separator) "--text follows this line--")
(put 'mail-header-separator 'permanent-local t) (put 'mail-header-separator 'permanent-local t)
(save-excursion (save-excursion
;; make sure there's not one already ;; make sure there's not one already
(mu4e~draft-remove-mail-header-separator) (mu4e~draft-remove-mail-header-separator)
(let ((sepa (propertize mail-header-separator (let ((sepa (propertize mail-header-separator
'intangible t 'intangible t
;; don't make this read-only, message-mode ;; don't make this read-only, message-mode
;; seems to require it being writable in some cases ;; seems to require it being writable in some cases
;;'read-only "Can't touch this" ;;'read-only "Can't touch this"
'rear-nonsticky t 'rear-nonsticky t
'font-lock-face 'mu4e-compose-separator-face))) 'font-lock-face 'mu4e-compose-separator-face)))
(widen) (widen)
;; search for the first empty line ;; search for the first empty line
(goto-char (point-min)) (goto-char (point-min))
(if (search-forward-regexp "^$" nil t) (if (search-forward-regexp "^$" nil t)
(progn (progn
(replace-match sepa) (replace-match sepa)
;; `message-narrow-to-headers` searches for a ;; `message-narrow-to-headers` searches for a
;; `mail-header-separator` followed by a new line. Therefore, we ;; `mail-header-separator` followed by a new line. Therefore, we
;; must insert a newline if on the last line of the buffer. ;; must insert a newline if on the last line of the buffer.
(when (= (point) (point-max)) (when (= (point) (point-max))
(insert "\n"))) (insert "\n")))
(progn ;; no empty line? then prepend one (progn ;; no empty line? then prepend one
(goto-char (point-max)) (goto-char (point-max))
(insert "\n" sepa)))))) (insert "\n" sepa))))))
(defun mu4e~draft-remove-mail-header-separator () (defun mu4e~draft-remove-mail-header-separator ()
"Remove `mail-header-separator'. "Remove `mail-header-separator'.

View File

@ -1,4 +1,4 @@
;;; mu4e-headers.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;;; mu4e-headers.el -- part of mu4e -*- lexical-binding: t -*-
;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema ;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema
@ -41,6 +41,7 @@
(require 'mu4e-vars) (require 'mu4e-vars)
(require 'mu4e-mark) (require 'mu4e-mark)
(require 'mu4e-context) (require 'mu4e-context)
(require 'mu4e-contacts)
(require 'mu4e-search) (require 'mu4e-search)
(require 'mu4e-compose) (require 'mu4e-compose)
(require 'mu4e-actions) (require 'mu4e-actions)
@ -177,7 +178,7 @@ query have been received and are displayed."
;;; Public variables ;;; Public variables
(defvar mu4e-headers-sort-field :date (defvar mu4e-headers-sort-field :date
"Field to sort the headers by. Must be a symbol, "Field to sort the headers by. A symbol:
one of: `:date', `:subject', `:size', `:prio', `:from', `:to.', one of: `:date', `:subject', `:size', `:prio', `:from', `:to.',
`:list'. `:list'.
@ -305,10 +306,6 @@ In the format needed for `mu4e-read-option'.")
"If non-nil, report on the time it took to render the messages. "If non-nil, report on the time it took to render the messages.
This is mostly useful for profiling.") This is mostly useful for profiling.")
;;; Clear ;;; Clear
@ -332,8 +329,9 @@ This is mostly useful for profiling.")
"Turn the list of contacts CONTACTS (with elements (NAME . EMAIL) "Turn the list of contacts CONTACTS (with elements (NAME . EMAIL)
into a string." into a string."
(mapconcat (mapconcat
(lambda (ct) (lambda (contact)
(let ((name (car ct)) (email (cdr ct))) (let ((name (mu4e-contact-name contact))
(email (mu4e-contact-email contact)))
(or name email "?"))) contacts ", ")) (or name email "?"))) contacts ", "))
(defun mu4e~headers-thread-prefix-map (type) (defun mu4e~headers-thread-prefix-map (type)
@ -503,23 +501,20 @@ while our display may be different)."
;;; Special headers ;;; Special headers
(defconst mu4e-headers-from-or-to-prefix '("" . "To ")
"Prefix for the :from-or-to field.
It's a cons cell with the car element being the From: prefix, the
cdr element the To: prefix.")
(defun mu4e~headers-from-or-to (msg) (defun mu4e~headers-from-or-to (msg)
"When the from address for message MSG is one of the the user's addresses, "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,
\(as per `mu4e-personal-address-p'), show the To address; \(as per `mu4e-personal-address-p'), show the To address;
otherwise ; show the from address; prefixed with the appropriate otherwise ; show the from address; prefixed with the appropriate
`mu4e-headers-from-or-to-prefix'." `mu4e-headers-from-or-to-prefix'."
(let ((addr (cdr-safe (car-safe (mu4e-message-field msg :from))))) (let* ((from1 (car-safe (mu4e-message-field msg :from)))
(if (and addr (mu4e-personal-address-p addr)) (from1-addr (and from1 (mu4e-contact-email from1)))
(concat (cdr mu4e-headers-from-or-to-prefix) (is-user (and from1-addr (mu4e-personal-address-p from1-addr))))
(mu4e~headers-contact-str (mu4e-message-field msg :to))) (if is-user
(concat (car mu4e-headers-from-or-to-prefix) (concat "To " (mu4e~headers-contact-str (mu4e-message-field msg :to)))
(mu4e~headers-contact-str (mu4e-message-field msg :from)))))) (mu4e~headers-contact-str (mu4e-message-field msg :from)))))
(defun mu4e~headers-human-date (msg) (defun mu4e~headers-human-date (msg)
"Show a 'human' date. "Show a 'human' date.
If the date is today, show the time, otherwise, show the If the date is today, show the time, otherwise, show the
@ -1406,7 +1401,8 @@ matching messages with that mark."
(let* ((value (mu4e-msg-field msg field))) (let* ((value (mu4e-msg-field msg field)))
(if (member field '(:to :from :cc :bcc :reply-to)) (if (member field '(:to :from :cc :bcc :reply-to))
(cl-find-if (lambda (contact) (cl-find-if (lambda (contact)
(let ((name (car contact)) (email (cdr contact))) (let ((name (mu4e-contact-name contact))
(email (mu4e-contact-email contact)))
(or (and name (string-match pattern name)) (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 "")))))))) (string-match pattern (or value ""))))))))

View File

@ -504,6 +504,10 @@ in an external program."
(lambda () (ignore-errors (delete-file tmpfile)))) (lambda () (ignore-errors (delete-file tmpfile))))
tmpfile)) tmpfile))
(defsubst mu4e-is-mode-or-derived-p (mode)
"Is the current mode equal to MODE or derived from it?"
(or (eq major-mode 'mode) (derived-mode-p mode)))
(defun mu4e-display-manual () (defun mu4e-display-manual ()
"Display the mu4e manual page for the current mode. "Display the mu4e manual page for the current mode.
Or go to the top level if there is none." Or go to the top level if there is none."

View File

@ -96,7 +96,7 @@
(defun mu4e~icalendar-has-email (email list) (defun mu4e~icalendar-has-email (email list)
"Check that EMAIL is in LIST." "Check that EMAIL is in LIST."
(let ((email (downcase email))) (let ((email (downcase email)))
(cl-find-if (lambda (c) (let ((e (cdr c))) (cl-find-if (lambda (c) (let ((e (mu4e-contact-email c)))
(and (stringp e) (string= email (downcase e))))) (and (stringp e) (string= email (downcase e)))))
list))) list)))

View File

@ -27,6 +27,7 @@
;;; Code: ;;; Code:
(require 'mu4e-vars) (require 'mu4e-vars)
(require 'mu4e-contacts)
(require 'flow-fill) (require 'flow-fill)
(require 'shr) (require 'shr)
@ -54,9 +55,9 @@ Returns nil if the field does not exist.
A message plist looks something like: A message plist looks something like:
\(:docid 32461 \(:docid 32461
:from ((\"Nikola Tesla\" . \"niko@example.com\")) :from ((:name \"Nikola Tesla\" :email \"niko@example.com\"))
:to ((\"Thomas Edison\" . \"tom@example.com\")) :to ((:name \"Thomas Edison\" :email \"tom@example.com\"))
:cc ((\"Rupert The Monkey\" . \"rupert@example.com\")) :cc ((:name \"Rupert The Monkey\" :email \"rupert@example.com\"))
:subject \"RE: what about the 50K?\" :subject \"RE: what about the 50K?\"
:date (20369 17624 0) :date (20369 17624 0)
:size 4337 :size 4337
@ -74,7 +75,9 @@ A message plist looks something like:
:body-txt \"Hi Tom, ...\" :body-txt \"Hi Tom, ...\"
\)). \)).
Some notes on the format: Some notes on the format:
- The address fields are lists of pairs (NAME . EMAIL), where NAME can be nil. - The address fields are lists of plist (:name NAME :email EMAIL),
where the :name part can be absent. The `mu4e-contact-name' and
`mu4e-contact-email' accessors can be useful for this.
- The date is in format emacs uses in `current-time' - The date is in format emacs uses in `current-time'
- Attachments are a list of elements with fields :index (the number of - Attachments are a list of elements with fields :index (the number of
the MIME-part), :name (the file name, if any), :mime-type (the the MIME-part), :name (the file name, if any), :mime-type (the
@ -134,7 +137,7 @@ This is equivalent to:
) )
(defun mu4e-message-contact-field-matches (msg cfield rx) (defun mu4e-message-contact-field-matches (msg cfield rx)
"Does MSG's contact-field CFIELD match rx? "Does MSG's contact-field CFIELD match regexp RX?
Check if any of the of the CFIELD in MSG matches RX. I.e. Check if any of the of the CFIELD in MSG matches RX. I.e.
anything in field CFIELD (either :to, :from, :cc or :bcc, or a anything in field CFIELD (either :to, :from, :cc or :bcc, or a
list of those) of msg MSG matches (with their name or e-mail list of those) of msg MSG matches (with their name or e-mail
@ -142,8 +145,10 @@ address) regular expressions RX. If there is a match, return
non-nil; otherwise return nil. RX can also be a list of regular non-nil; otherwise return nil. RX can also be a list of regular
expressions, in which case any of those are tried for a match." expressions, in which case any of those are tried for a match."
(if (and cfield (listp cfield)) (if (and cfield (listp cfield))
(or (mu4e-message-contact-field-matches msg (car cfield) rx) (or (mu4e-message-contact-field-matches
(mu4e-message-contact-field-matches msg (cdr cfield) rx)) msg (mu4e-contact-name cfield) rx)
(mu4e-message-contact-field-matches
msg (mu4e-contact-email cfield) rx))
(when cfield (when cfield
(if (listp rx) (if (listp rx)
;; if rx is a list, try each one of them for a match ;; if rx is a list, try each one of them for a match
@ -153,7 +158,8 @@ expressions, in which case any of those are tried for a match."
;; not a list, check the rx ;; not a list, check the rx
(seq-find (seq-find
(lambda (ct) (lambda (ct)
(let ((name (car ct)) (email (cdr ct)) (let ((name (mu4e-contact-name ct))
(email (mu4e-contact-email ct))
;; the 'rx' may be some `/rx/` from mu4e-personal-addresses; ;; the 'rx' may be some `/rx/` from mu4e-personal-addresses;
;; so let's detect and extract in that case. ;; so let's detect and extract in that case.
(rx (if (string-match-p "^\\(.*\\)/$" rx) (rx (if (string-match-p "^\\(.*\\)/$" rx)
@ -170,7 +176,8 @@ of the of the contacts in field CFIELD (either :to, :from, :cc or
:bcc) of msg MSG matches *me*, that is, any of the addresses for :bcc) of msg MSG matches *me*, that is, any of the addresses for
which `mu4e-personal-address-p' return t. Returns the contact which `mu4e-personal-address-p' return t. Returns the contact
cell that matched, or nil." cell that matched, or nil."
(seq-find (lambda (cell) (mu4e-personal-address-p (cdr cell))) (seq-find (lambda (cell)
(mu4e-personal-address-p (mu4e-contact-email cell)))
(mu4e-message-field msg cfield))) (mu4e-message-field msg cfield)))
(defun mu4e-message-sent-by-me (msg) (defun mu4e-message-sent-by-me (msg)

View File

@ -1,6 +1,6 @@
;;; mu4e-org -- Org-links to mu4e messages/queries -*- lexical-binding: t -*- ;;; mu4e-org -- Org-links to mu4e messages/queries -*- lexical-binding: t -*-
;; Copyright (C) 2012-2021 Dirk-Jan C. Binnema ;; Copyright (C) 2012-2022 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -29,6 +29,7 @@
(require 'org) (require 'org)
(require 'mu4e-view) (require 'mu4e-view)
(require 'mu4e-contacts)
(defgroup mu4e-org nil (defgroup mu4e-org nil
@ -79,25 +80,23 @@ the current query; otherwise, it links to the message at point.")
(date (format-time-string "%FT%T" (plist-get msg :date))) (date (format-time-string "%FT%T" (plist-get msg :date)))
(msgid (or (plist-get msg :message-id) (msgid (or (plist-get msg :message-id)
(mu4e-error "Cannot link message without message-id"))) (mu4e-error "Cannot link message without message-id")))
(name-or-addr (lambda (addr) (or (car-safe addr) (cdr-safe addr)))) (props `(:type "mu4e"
(full-address (lambda(addr) :date ,date
(when addr :from ,(mu4e-contact-full from)
(if-let ((name (car addr))) :fromname ,(mu4e-contact-name from)
(format "%s <%s>" name (cdr addr)) :fromnameoraddress ,(or (mu4e-contact-name from)
(format "%s" (cdr addr))))))) (mu4e-contact-email from)) ;; mu4e-specific
(org-store-link-props :maildir ,(plist-get msg :maildir)
:type "mu4e" :message-id ,msgid
:date date :path ,(plist-get msg :path)
:from (funcall full-address from) :subject ,(plist-get msg :subject)
:fromnameoraddress (funcall name-or-addr from) ;; mu4e-specific :to ,(mu4e-contact-full to)
:maildir (plist-get msg :maildir) :tonameoraddress ,(or (mu4e-contact-name to)
:message-id msgid (mu4e-contact-email to)) ;; mu4e-specific
:path (plist-get msg :path) :link ,(concat "mu4e:msgid:" msgid)
:subject (plist-get msg :subject) :description ,(funcall mu4e-org-link-desc-func msg))))
:to (funcall full-address to) (message "PROPS %S" props)
:tonameoraddress (funcall name-or-addr to) ;; mu4e-specific (apply #'org-store-link-props props)))
:link (concat "mu4e:msgid:" msgid)
:description (funcall mu4e-org-link-desc-func msg))))
(defun mu4e-org-store-link () (defun mu4e-org-store-link ()
"Store a link to a mu4e message or query. "Store a link to a mu4e message or query.
@ -105,13 +104,15 @@ It links to the last known query when in `mu4e-headers-mode' with
`mu4e-org-link-query-in-headers-mode' set; otherwise it links to `mu4e-org-link-query-in-headers-mode' set; otherwise it links to
a specific message, based on its message-id, so that links stay a specific message, based on its message-id, so that links stay
valid even after moving the message around." valid even after moving the message around."
(when (derived-mode-p 'mu4e-view-mode 'mu4e-headers-mode) (let ((view-mode-p (mu4e-is-mode-or-derived-p 'mu4e-view-mode))
(if (and (derived-mode-p 'mu4e-headers-mode) (headers-mode-p (mu4e-is-mode-or-derived-p 'mu4e-headers-mode))
mu4e-org-link-query-in-headers-mode) (message-p (mu4e-message-at-point)))
(mu4e--org-store-link-query) (if view-mode-p
(when (mu4e-message-at-point) (mu4e--org-store-link-message)
(message "%S" (mu4e--org-store-link-message)) (if headers-mode-p
(mu4e--org-store-link-message))))) (if (or (not message-p) mu4e-org-link-query-in-headers-mode)
(mu4e--org-store-link-query)
(mu4e--org-store-link-message))))))
(defun mu4e-org-open (link) (defun mu4e-org-open (link)
"Open the org LINK. "Open the org LINK.

View File

@ -211,15 +211,20 @@ I.e. a message with the draft flag set."
:shortname "Cc" :shortname "Cc"
:help "Carbon-Copy recipients for the message" :help "Carbon-Copy recipients for the message"
:sortable t)) :sortable t))
(:changed
. (:name "Changed"
:shortname "Chg"
:help "Date/time when the message was changed most recently"
:sortable t))
(:date (:date
. (:name "Date" . (:name "Date"
:shortname "Date" :shortname "Date"
:help "Date/time when the message was written" :help "Date/time when the message was sent"
:sortable t)) :sortable t))
(:human-date (:human-date
. (:name "Date" . (:name "Date"
:shortname "Date" :shortname "Date"
:help "Date/time when the message was written." :help "Date/time when the message was sent"
:sortable :date)) :sortable :date))
(:flags (:flags
. (:name "Flags" . (:name "Flags"
@ -314,7 +319,7 @@ one of the addresses in `(mu4e-personal-addresses)', in which
case it will be equal to `:to'. case it will be equal to `:to'.
Furthermore, the property `:sortable' determines whether we can Furthermore, the property `:sortable' determines whether we can
sort by this field. This can be either a boolean (nil or t), or a sort by this field. This can be either a boolean (nil or t), or a
symbol for /another/ field. For example, the `:human-date' field symbol for /another/ field. For example, the `:human-date' field
uses `:date' for that. uses `:date' for that.