From 7563b89c9cda397d43dd3d8d985a677bb3c8495f Mon Sep 17 00:00:00 2001 From: djcb Date: Sat, 11 May 2019 13:35:22 +0300 Subject: [PATCH] mu4e: support incremental contacts --- mu4e/mu4e-draft.el | 39 +++++++ mu4e/mu4e-proc.el | 19 ++-- mu4e/mu4e-utils.el | 161 +++++++------------------- mu4e/mu4e-vars.el | 273 ++++++++++++++++++++++----------------------- 4 files changed, 225 insertions(+), 267 deletions(-) diff --git a/mu4e/mu4e-draft.el b/mu4e/mu4e-draft.el index f514b011..998a3cae 100644 --- a/mu4e/mu4e-draft.el +++ b/mu4e/mu4e-draft.el @@ -262,6 +262,45 @@ message. Return nil if there are no recipients for the particular field." (otherwise (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 either atom, quoted-string, a corner-case or nil. This + checks for empty string first. Then quotes around the phrase + (returning 'rfc822-quoted-string). Then whether there is a quote + inside the phrase (returning 'rfc822-containing-quote). + The reverse of the RFC atext definition is then tested. + If it matches, nil is returned, if not, it is an 'rfc822-atom, which + is returned." + (cond + ((= (length ph) 0) 'rfc822-empty) + ((= (aref ph 0) ?\") + (if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph) + 'rfc822-quoted-string + 'rfc822-containing-quote)) ; starts with quote, but doesn't end with one + ((string-match-p "[\"]" ph) 'rfc822-containing-quote) + ((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil) + (t 'rfc822-atom))) + +(defun mu4e~rfc822-quoteit (ph) + "Quote RFC822 phrase only if necessary. + Atoms and quoted strings don't need quotes. The rest do. In + case a phrase contains a quote, it will be escaped." + (let ((type (mu4e~rfc822-phrase-type ph))) + (cond + ((eq type 'rfc822-atom) ph) + ((eq type 'rfc822-quoted-string) ph) + ((eq type 'rfc822-containing-quote) + (format "\"%s\"" + (replace-regexp-in-string "\"" "\\\\\"" ph))) + (t (format "\"%s\"" ph))))) + + + + (defun mu4e~draft-from-construct () "Construct a value for the From:-field of the reply to MSG, diff --git a/mu4e/mu4e-proc.el b/mu4e/mu4e-proc.el index 0bfc902b..7ddbe809 100644 --- a/mu4e/mu4e-proc.el +++ b/mu4e/mu4e-proc.el @@ -187,7 +187,8 @@ The server output is as follows: ;; note: we use 'member', to match (:contacts nil) ((plist-member sexp :contacts) (funcall mu4e-contacts-func - (plist-get sexp :contacts))) + (plist-get sexp :contacts) + (plist-get sexp :tstamp))) ;; something got moved/flags changed ((plist-get sexp :update) @@ -495,15 +496,17 @@ to a temporary file, then respond with "Sends a ping to the mu server, expecting a (:pong ...) in response." (mu4e~proc-send-command "cmd:ping")) -(defun mu4e~proc-contacts (personal after) - "Sends the contacts command to the mu server. -A (:contacts ()) is expected in response. If PERSONAL is -non-nil, only get personal contacts, if AFTER is non-nil, get -only contacts seen AFTER (the time_t value)." +(defun mu4e~proc-contacts (personal after tstamp) + "Ask for contacts with PERSONAL AFTER TSTAMP. +S-expression (:contacts () :tstamp \"\") is expected in +response. If PERSONAL is non-nil, only get personal contacts, if +AFTER is non-nil, get only contacts seen AFTER (the time_t +value)." (mu4e~proc-send-command - "cmd:contacts personal:%s after:%d" + "cmd:contacts personal:%s after:%d tstamp:%s" (if personal "true" "false") - (or after 0))) + (or after 0) + (or tstamp "0"))) (defun mu4e~proc-view (docid-or-msgid &optional images decrypt) "Get a message DOCID-OR-MSGID. diff --git a/mu4e/mu4e-utils.el b/mu4e/mu4e-utils.el index 72fe2c7d..ae43a33b 100644 --- a/mu4e/mu4e-utils.el +++ b/mu4e/mu4e-utils.el @@ -1,7 +1,6 @@ ;;; mu4e-utils.el -- part of mu4e, the mu mail user agent ;; -;; Copyright (C) 2011-2017 Dirk-Jan C. Binnema -;; Copyright (C) 2013 Tibor Simko +;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -666,7 +665,8 @@ process." "Indexing completed; processed %d, updated %d, cleaned-up %d" (plist-get info :processed) (plist-get info :updated) (plist-get info :cleaned-up)) - (unless (zerop (plist-get info :updated)) + (unless (and (not (string= mu4e~contacts-tstamp "0")) + (zerop (plist-get info :updated))) (mu4e~request-contacts-maybe) (run-hooks 'mu4e-index-updated-hook))))) ((plist-get info :message) @@ -680,121 +680,39 @@ process." (t (error "Error %d: %s" errcode errmsg)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; RFC2822 handling of phrases in mail-addresses -;;; The optional display-name contains a phrase, it sits before the angle-addr -;;; as specified in RFC2822 for email-addresses in header fields. -;;; contributed by jhelberg +(defvar mu4e~contacts-tstamp "0" + "Timestamp for the most recent contacts update." ) -(defun mu4e~rfc822-phrase-type (ph) - "Return either atom, quoted-string, a corner-case or nil. This - checks for empty string first. Then quotes around the phrase - (returning 'rfc822-quoted-string). Then whether there is a quote - inside the phrase (returning 'rfc822-containing-quote). - The reverse of the RFC atext definition is then tested. - If it matches, nil is returned, if not, it is an 'rfc822-atom, which - is returned." - (cond - ((= (length ph) 0) 'rfc822-empty) - ((= (aref ph 0) ?\") - (if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph) - 'rfc822-quoted-string - 'rfc822-containing-quote)) ; starts with quote, but doesn't end with one - ((string-match-p "[\"]" ph) 'rfc822-containing-quote) - ((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil) - (t 'rfc822-atom))) - -(defun mu4e~rfc822-quoteit (ph) - "Quote RFC822 phrase only if necessary. - Atoms and quoted strings don't need quotes. The rest do. In - case a phrase contains a quote, it will be escaped." - (let ((type (mu4e~rfc822-phrase-type ph))) - (cond - ((eq type 'rfc822-atom) ph) - ((eq type 'rfc822-quoted-string) ph) - ((eq type 'rfc822-containing-quote) - (format "\"%s\"" - (replace-regexp-in-string "\"" "\\\\\"" ph))) - (t (format "\"%s\"" ph))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defsubst mu4e~process-contact (contact) - "Process CONTACT, and either return nil when it should not be included, -or (rfc822-string . CONTACT) otherwise." - (when mu4e-contact-rewrite-function - (setq contact (funcall mu4e-contact-rewrite-function contact))) - (when contact - (let ((name (plist-get contact :name)) - (mail (plist-get contact :mail)) - (ignore-rx (or mu4e-compose-complete-ignore-address-regexp "$^"))) - (when (and mail (not (string-match ignore-rx mail))) - (cons - (if name (format "%s <%s>" (mu4e~rfc822-quoteit name) mail) mail) - contact))))) - - -(defun mu4e~sort-contacts (contacts) - "Destructively sort contacts (only for cycling) in order of - 'mostly likely contact'.t See the code for the detail" - (let* ((now (+ (float-time) 3600)) ;; allow for clock diffs - (recent (- (float-time) (* 15 24 3600)))) - (cl-sort contacts - (lambda (c1 c2) - (let* ( (c1 (cdr c1)) (c2 (cdr c2)) - (personal1 (plist-get c1 :personal)) - (personal2 (plist-get c2 :personal)) - ;; note: freq, tstamp can only be missing if the rewrite - ;; function removed them. If the rewrite function changed the - ;; contact somehow, we guess it's important. - (freq1 (or (plist-get c1 :freq) 500)) - (freq2 (or (plist-get c2 :freq) 500)) - (tstamp1 (or (plist-get c1 :tstamp) now)) - (tstamp2 (or (plist-get c2 :tstamp) now))) - ;; only one is personal? if so, that one comes first - (if (not (equal personal1 personal2)) - (if personal1 t nil) - ;; only one is recent? that one comes first - (if (not (equal (> tstamp1 recent) (> tstamp2 recent))) - (> tstamp1 tstamp2) - ;; otherwise, use the frequency - (> freq1 freq2)))))))) - -(defun mu4e~sort-contacts-for-completion (contacts) - "Takes CONTACTS, which is a list of RFC-822 addresses, and sort them based -on the ranking in `mu4e~contacts.'" - (cl-sort contacts - (lambda (c1 c2) - (let ((rank1 (gethash c1 mu4e~contacts)) - (rank2 (gethash c2 mu4e~contacts))) - (< rank1 rank2))))) - -;; start and stopping -(defun mu4e~fill-contacts (contact-data) - "We receive a list of contacts, which each contact of the form - (:me NAME :mail EMAIL :tstamp TIMESTAMP :freq FREQUENCY) and -fill the hash `mu4e~contacts' with it, with each contact mapped -to an integer for their ranking. +(defun mu4e~update-contacts (contacts &optional tstamp) + "Rceive a sorted list of CONTACTS. +Each of the contacts has the form + (FULL_EMAIL_ADDRESS . RANK) and fill the hash +`mu4e~contacts' with it, with each contact mapped to an integer +for their ranking. This is used by the completion function in mu4e-compose." - (let ((contacts) (rank 0)) - (dolist (contact contact-data) - (let ((contact-maybe (mu4e~process-contact contact))) - ;; note, this gives cells (rfc822-address . contact) - (when contact-maybe (push contact-maybe contacts)))) - (setq contacts (mu4e~sort-contacts contacts)) - ;; now, we have our nicely sorted list, map them to a list - ;; of increasing integers. We use that map in the composer - ;; to sort them there. It would have been so much easier if emacs - ;; allowed us to use the sorted-list as-is, but no such luck. - (setq mu4e~contacts (make-hash-table :test 'equal :weakness nil - :size (length contacts))) + ;; We have our nicely sorted list, map them to a list + ;; of increasing integers. We use that map in the composer + ;; to sort them there. It would have been so much easier if emacs + ;; allowed us to use the sorted-list as-is, but no such luck. + (let ((n 0)) + (unless mu4e~contacts + (setq mu4e~contacts (make-hash-table :test 'equal :weakness nil + :size (length contacts)))) (dolist (contact contacts) - (puthash (car contact) rank mu4e~contacts) - (incf rank)) - (mu4e-index-message "Contacts received: %d" - (hash-table-count mu4e~contacts)))) + (incf n) + (let ((address + (if (functionp mu4e-contact-process-function) + (funcall mu4e-contact-process-function (car contact)) + (car contact)))) + (when address + (puthash address (cdr contact) mu4e~contacts)))) + + (setq mu4e~contacts-tstamp (or tstamp "0")) + (mu4e-index-message "Contacts updated: %d; total %d" + n (hash-table-count mu4e~contacts)))) (defun mu4e~check-requirements () "Check for the settings required for running mu4e." @@ -855,21 +773,21 @@ Checks whether the server process is live." the list of contacts we use for autocompletion; otherwise, do nothing." (when mu4e-compose-complete-addresses - (setq mu4e-contacts-func 'mu4e~fill-contacts) + (setq mu4e-contacts-func 'mu4e~update-contacts) (mu4e~proc-contacts mu4e-compose-complete-only-personal (when mu4e-compose-complete-only-after - (float-time - (apply 'encode-time - (mu4e-parse-time-string mu4e-compose-complete-only-after))))))) + (float-time + (apply 'encode-time + (mu4e-parse-time-string mu4e-compose-complete-only-after)))) + mu4e~contacts-tstamp))) (defun mu4e~start (&optional func) "If `mu4e-contexts' have been defined, but we don't have a context yet, switch to the matching one, or none matches, the -first. -If mu4e is already running, execute function FUNC (if non-nil). -Otherwise, check various requirements, then start mu4e. When -successful, call FUNC (if non-nil) afterwards." +first. If mu4e is already running, execute function FUNC (if +non-nil). Otherwise, check various requireme`'nts, then start mu4e. +When successful, call FUNC (if non-nil) afterwards." ;; if we're already running, simply go to the main view (if (mu4e-running-p) ;; already running? (when func (funcall func)) ;; yes! run func if defined @@ -905,7 +823,8 @@ successful, call FUNC (if non-nil) afterwards." "Clear any cached resources." (setq mu4e-maildir-list nil - mu4e~contacts nil)) + mu4e~contacts nil + mu4e~contacts-tstamp "0")) (defun mu4e~stop () "Stop the mu4e session." diff --git a/mu4e/mu4e-vars.el b/mu4e/mu4e-vars.el index 5b4dee11..8704ae86 100644 --- a/mu4e/mu4e-vars.el +++ b/mu4e/mu4e-vars.el @@ -1,6 +1,6 @@ ;;; mu4e-vars.el -- part of mu4e, the mu mail user agent ;; -;; Copyright (C) 2011-2018 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -37,7 +37,7 @@ "Location of the mu homedir, or nil for the default." :group 'mu4e :type '(choice (const :tag "Default location" nil) - (directory :tag "Specify location")) + (directory :tag "Specify location")) :safe 'stringp) (defcustom mu4e-mu-binary (executable-find "mu") @@ -111,7 +111,7 @@ faster." :type 'boolean :group 'mu4e :safe 'booleanp) If nil, don't update automatically. Note, changes in `mu4e-update-interval' only take effect after restarting mu4e." :type '(choice (const :tag "No automatic update" nil) - (integer :tag "Seconds")) + (integer :tag "Seconds")) :group 'mu4e :safe 'integerp) @@ -199,27 +199,27 @@ where QUERY is a string with a mu query, DESCRIPTION is a short description of the query (this will show up in the UI), and KEY is a shortcut key for the query." :type '(repeat (list (string :tag "Query") - (string :tag "Description") - character)) + (string :tag "Description") + character)) :group 'mu4e) (defvar mu4e-bookmarks `( ,(make-mu4e-bookmark - :name "Unread messages" - :query "flag:unread AND NOT flag:trashed" - :key ?u) + :name "Unread messages" + :query "flag:unread AND NOT flag:trashed" + :key ?u) ,(make-mu4e-bookmark - :name "Today's messages" - :query "date:today..now" - :key ?t) + :name "Today's messages" + :query "date:today..now" + :key ?t) ,(make-mu4e-bookmark - :name "Last 7 days" - :query "date:7d..now" - :key ?w) + :name "Last 7 days" + :query "date:7d..now" + :key ?w) ,(make-mu4e-bookmark - :name "Messages with images" - :query "mime:image/*" - :key ?p)) + :name "Messages with images" + :query "mime:image/*" + :key ?p)) "A list of pre-defined queries. Each query is represented by a mu4e-bookmark structure with parameters @t{:name} with the name of the bookmark, @t{:query} @@ -236,15 +236,15 @@ A symbol which is either: * `horizontal': split horizontally (headers on top) * `vertical': split vertically (headers on the left). * `single-window': view and headers in one window (mu4e will try not to - touch your window layout), main view in minibuffer + touch your window layout), main view in minibuffer * anything else: don't split (show either headers or messages, - not both) + not both) Also see `mu4e-headers-visible-lines' and `mu4e-headers-visible-columns'." :type '(choice (const :tag "Split horizontally" horizontal) - (const :tag "Split vertically" vertical) - (const :tag "Single window" single-window) - (const :tag "Don't split" nil)) + (const :tag "Split vertically" vertical) + (const :tag "Single window" single-window) + (const :tag "Don't split" nil)) :group 'mu4e-headers) (defcustom mu4e-view-max-specpdl-size 4096 @@ -301,12 +301,12 @@ contexts match, we have the following choices: Also see `mu4e-compose-context-policy'." :type '(choice - (const :tag "Always ask what context to use, even if one matches" - always-ask) - (const :tag "Ask if none of the contexts match" ask) - (const :tag "Ask when there's no context yet" ask-if-none) - (const :tag "Pick the first context if none match" pick-first) - (const :tag "Don't change the context when none match" nil)) + (const :tag "Always ask what context to use, even if one matches" + always-ask) + (const :tag "Ask if none of the contexts match" ask) + (const :tag "Ask when there's no context yet" ask-if-none) + (const :tag "Pick the first context if none match" pick-first) + (const :tag "Don't change the context when none match" nil)) :group 'mu4e) ;; crypto @@ -326,8 +326,8 @@ The setting is a symbol: * `ask': ask before decrypting anything * nil: don't try to decrypt anything." :type '(choice (const :tag "Try to decrypt automatically" t) - (const :tag "Ask before decrypting anything" ask) - (const :tag "Don't try to decrypt anything" nil)) + (const :tag "Ask before decrypting anything" ask) + (const :tag "Don't try to decrypt anything" nil)) :group 'mu4e-crypto) ;; completion; we put them here rather than in mu4e-compose, as mu4e-utils needs @@ -353,7 +353,7 @@ addresses)." :type 'boolean :group 'mu4e-compose) -(defcustom mu4e-compose-complete-only-after "2010-01-01" +(defcustom mu4e-compose-complete-only-after "2014-01-01" "Consider only contacts last seen after this date. Date must be a string, in a format parseable by `org-parse-time-string'. This excludes really old contacts. @@ -370,42 +370,39 @@ Set to nil to not have any time-based restriction." It is used as the identity function for converting contacts to their canonical counterpart; useful as an example." (let ((name (plist-get contact :name)) - (mail (plist-get contact :mail))) + (mail (plist-get contact :mail))) (list :name name :mail mail))) -(defcustom mu4e-contact-rewrite-function nil - "Function for rewriting or removing contacts. +(make-obsolete-variable 'mu4e-contacts-rewrite-function + "mu4e-contact-process-function (see docstring)" "mu4e 1.3.2") +(make-obsolete-variable 'mu4e-compose-complete-ignore-address-regexp + "mu4e-contact-process-function (see docstring)" "mu4e 1.3.2") -If the function receives the contact as a list of the form - (:name NAME :mail EMAIL ... other properties ... ) - (other properties may be there as well) +(defcustom mu4e-contact-process-function nil + "Function for processing contact information for use in auto-completion. + +The function receives the contact as a string, e.g + \"Foo Bar \" + \"cuux@example.com\" The function should return either: - - nil: remove this contact, or -- the rewritten cell, or -- the existing cell as-is - -For rewriting, it is recommended to use `plist-put' to set the -changed parameters, so the other properties stay in place. Those -are needed for sorting the contacts." +- nil: do not use this contact for completion +- the (possibly rewritten) address, which must be +an RFC-2822-compatible e-mail address." :type 'function :group 'mu4e-compose) -(defcustom mu4e-compose-complete-ignore-address-regexp "no-?reply" - "Ignore any e-mail addresses for completion if they match this regexp." - :type 'string - :group 'mu4e-compose) - -(defcustom mu4e-compose-reply-ignore-address message-dont-reply-to-names +(defcustom mu4e-compose-reply-ignore-address +message-dont-reply-to-names "Addresses to prune when doing wide replies. -This can be a regexp matching the address, a list of regexps -or a predicate function. A value of nil keeps all the addresses." +This can be a regexp matching the address, a list of regexps or a +predicate function. A value of nil keeps all the addresses." :type '(choice - (const nil) - function - string - (repeat string)) + (const nil) + function + string + (repeat string)) :group 'mu4e-compose) (defcustom mu4e-compose-reply-to-address nil @@ -448,8 +445,8 @@ parameter refers to the original message being replied to / being forwarded / re-edited and is nil otherwise. `mu4e-drafts-folder' is only evaluated once." :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) + (string :tag "Folder name") + (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-refile-folder "/archive" @@ -459,8 +456,8 @@ function that takes a message (a msg plist, see `mu4e-message-field'), and returns a folder. Note that the message parameter refers to the message-at-point." :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) + (string :tag "Folder name") + (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-sent-folder "/sent" @@ -471,8 +468,8 @@ function that takes a message (a msg plist, see message parameter refers to the original message being replied to / being forwarded / re-edited, and is nil otherwise." :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) + (string :tag "Folder name") + (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-trash-folder "/trash" @@ -487,8 +484,8 @@ message-at-point. When using it when composing a message (see message being replied to / being forwarded / re-edited, and is nil otherwise." :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) + (string :tag "Folder name") + (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-maildir-shortcuts nil @@ -714,113 +711,113 @@ mu4e-compose-mode." (defconst mu4e-header-info '( (:attachments . ( :name "Attachments" - :shortname "Atts" - :help "Message attachments" - :require-full t - :sortable nil)) + :shortname "Atts" + :help "Message attachments" + :require-full t + :sortable nil)) (:bcc . ( :name "Bcc" - :shortname "Bcc" - :help "Blind Carbon-Copy recipients for the message" - :sortable t)) + :shortname "Bcc" + :help "Blind Carbon-Copy recipients for the message" + :sortable t)) (:cc . ( :name "Cc" - :shortname "Cc" - :help "Carbon-Copy recipients for the message" - :sortable t)) + :shortname "Cc" + :help "Carbon-Copy recipients for the message" + :sortable t)) (:date . ( :name "Date" - :shortname "Date" - :help "Date/time when the message was written" - :sortable t)) + :shortname "Date" + :help "Date/time when the message was written" + :sortable t)) (:human-date . ( :name "Date" - :shortname "Date" - :help "Date/time when the message was written." - :sortable :date)) + :shortname "Date" + :help "Date/time when the message was written." + :sortable :date)) (:flags . ( :name "Flags" - :shortname "Flgs" - :help "Flags for the message" - :sortable nil)) + :shortname "Flgs" + :help "Flags for the message" + :sortable nil)) (:from . ( :name "From" - :shortname "From" - :help "The sender of the message" - :sortable t)) + :shortname "From" + :help "The sender of the message" + :sortable t)) (:from-or-to . ( :name "From/To" - :shortname "From/To" - :help "Sender of the message if it's not me; otherwise the recipient" - :sortable nil)) + :shortname "From/To" + :help "Sender of the message if it's not me; otherwise the recipient" + :sortable nil)) (:maildir . ( :name "Maildir" - :shortname "Maildir" - :help "Maildir for this message" - :sortable t)) + :shortname "Maildir" + :help "Maildir for this message" + :sortable t)) (:list . ( :name "List-Id" - :shortname "List" - :help "Mailing list id for this message" - :sortable t)) + :shortname "List" + :help "Mailing list id for this message" + :sortable t)) (:mailing-list . ( :name "List" - :shortname "List" - :help "Mailing list friendly name for this message" - :sortable :list)) + :shortname "List" + :help "Mailing list friendly name for this message" + :sortable :list)) (:message-id . ( :name "Message-Id" - :shortname "MsgID" - :help "Message-Id for this message" - :sortable nil)) + :shortname "MsgID" + :help "Message-Id for this message" + :sortable nil)) (:path . ( :name "Path" - :shortname "Path" - :help "Full filesystem path to the message" - :sortable t)) + :shortname "Path" + :help "Full filesystem path to the message" + :sortable t)) (:signature . ( :name "Signature" - :shortname "Sgn" - :help "Check for the cryptographic signature" - :require-full t - :sortable nil)) + :shortname "Sgn" + :help "Check for the cryptographic signature" + :require-full t + :sortable nil)) (:decryption . ( :name "Decryption" - :shortname "Dec" - :help "Check the cryptographic decryption status" - :require-full t - :sortable nil)) + :shortname "Dec" + :help "Check the cryptographic decryption status" + :require-full t + :sortable nil)) (:size . ( :name "Size" - :shortname "Size" - :help "Size of the message" - :sortable t)) + :shortname "Size" + :help "Size of the message" + :sortable t)) (:subject . ( :name "Subject" - :shortname "Subject" - :help "Subject of the message" - :sortable t)) + :shortname "Subject" + :help "Subject of the message" + :sortable t)) (:tags . ( :name "Tags" - :shortname "Tags" - :help "Tags for the message" - :sortable nil)) + :shortname "Tags" + :help "Tags for the message" + :sortable nil)) (:thread-subject . ( :name "Subject" - :shortname "Subject" - :help "Subject of the thread" - :sortable :subject)) + :shortname "Subject" + :help "Subject of the thread" + :sortable :subject)) (:to . ( :name "To" - :shortname "To" - :help "Recipient of the message" - :sortable t)) + :shortname "To" + :help "Recipient of the message" + :sortable t)) (:user-agent . ( :name "User-Agent" - :shortname "UA" - :help "Program used for writing this message" - :require-full t - :sortable t))) + :shortname "UA" + :help "Program used for writing this message" + :require-full t + :sortable t))) "An alist of all possible header fields and information about them. This is used in the user-interface (the column headers in the header list, and the fields the message view). @@ -845,13 +842,13 @@ Note, `:sortable' is not supported for custom header fields.") (defvar mu4e-header-info-custom '( (:recipnum . ( :name "Number of recipients" - :shortname "Recip#" - :help "Number of recipients for this message" - :function - (lambda (msg) - (format "%d" - (+ (length (mu4e-message-field msg :to)) - (length (mu4e-message-field msg :cc)))))))) + :shortname "Recip#" + :help "Number of recipients for this message" + :function + (lambda (msg) + (format "%d" + (+ (length (mu4e-message-field msg :to)) + (length (mu4e-message-field msg :cc)))))))) "A list of custom (user-defined) headers. The format is similar to `mu4e-header-info', but adds a :function property, which