X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=emacs%2Fnotmuch-show.el;h=49961fbb454700dc068b4910909e47d6e7d94534;hp=fbf3f9bba1c6940e6dab8081356a051e70490791;hb=580997252f80b765b208fcd9f4c696c061e2b9ad;hpb=d2a1140c44f0cdaf16c06730b7311cbe222bc2da diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index fbf3f9bb..49961fbb 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -30,14 +30,16 @@ (require 'goto-addr) (require 'notmuch-lib) +(require 'notmuch-tag) (require 'notmuch-query) (require 'notmuch-wash) (require 'notmuch-mua) (require 'notmuch-crypto) +(require 'notmuch-print) (declare-function notmuch-call-notmuch-process "notmuch" (&rest args)) (declare-function notmuch-fontify-headers "notmuch" nil) -(declare-function notmuch-select-tag-with-completion "notmuch" (prompt &rest search-terms)) +(declare-function notmuch-search-next-thread "notmuch" nil) (declare-function notmuch-search-show-thread "notmuch" nil) (defcustom notmuch-message-headers '("Subject" "To" "Cc" "Date") @@ -45,8 +47,8 @@ For an open message, all of these headers will be made visible according to `notmuch-message-headers-visible' or can be toggled -with `notmuch-show-toggle-headers'. For a closed message, only -the first header in the list will be visible." +with `notmuch-show-toggle-visibility-headers'. For a closed message, +only the first header in the list will be visible." :type '(repeat string) :group 'notmuch-show) @@ -56,8 +58,8 @@ the first header in the list will be visible." If this value is non-nil, then all of the headers defined in `notmuch-message-headers' will be visible by default in the display of each message. Otherwise, these headers will be hidden and -`notmuch-show-toggle-headers' can be used to make the visible for -any given message." +`notmuch-show-toggle-visibility-headers' can be used to make them +visible for any given message." :type 'boolean :group 'notmuch-show) @@ -70,9 +72,10 @@ any given message." "A list of functions called to decorate the headers listed in `notmuch-message-headers'.") -(defcustom notmuch-show-hook nil +(defcustom notmuch-show-hook '(notmuch-show-turn-on-visual-line-mode) "Functions called after populating a `notmuch-show' buffer." :type 'hook + :options '(notmuch-show-turn-on-visual-line-mode) :group 'notmuch-show :group 'notmuch-hooks) @@ -112,6 +115,88 @@ indentation." :type 'boolean :group 'notmuch-show) +(defcustom notmuch-show-part-button-default-action 'notmuch-show-save-part + "Default part header button action (on ENTER or mouse click)." + :group 'notmuch-show + :type '(choice (const :tag "Save part" + notmuch-show-save-part) + (const :tag "View part" + notmuch-show-view-part) + (const :tag "View interactively" + notmuch-show-interactively-view-part))) + +(defcustom notmuch-show-only-matching-messages nil + "Only matching messages are shown by default." + :type 'boolean + :group 'notmuch-show) + +(defvar notmuch-show-thread-id nil) +(make-variable-buffer-local 'notmuch-show-thread-id) +(put 'notmuch-show-thread-id 'permanent-local t) + +(defvar notmuch-show-parent-buffer nil) +(make-variable-buffer-local 'notmuch-show-parent-buffer) +(put 'notmuch-show-parent-buffer 'permanent-local t) + +(defvar notmuch-show-query-context nil) +(make-variable-buffer-local 'notmuch-show-query-context) +(put 'notmuch-show-query-context 'permanent-local t) + +(defvar notmuch-show-process-crypto nil) +(make-variable-buffer-local 'notmuch-show-process-crypto) +(put 'notmuch-show-process-crypto 'permanent-local t) + +(defvar notmuch-show-elide-non-matching-messages nil) +(make-variable-buffer-local 'notmuch-show-elide-non-matching-messages) +(put 'notmuch-show-elide-non-matching-messages 'permanent-local t) + +(defvar notmuch-show-indent-content t) +(make-variable-buffer-local 'notmuch-show-indent-content) +(put 'notmuch-show-indent-content 'permanent-local t) + +(defcustom notmuch-show-stash-mlarchive-link-alist + '(("Gmane" . "http://mid.gmane.org/") + ("MARC" . "http://marc.info/?i=") + ("Mail Archive, The" . "http://mail-archive.com/search?l=mid&q=") + ;; FIXME: can these services be searched by `Message-Id' ? + ;; ("MarkMail" . "http://markmail.org/") + ;; ("Nabble" . "http://nabble.com/") + ;; ("opensubscriber" . "http://opensubscriber.com/") + ) + "List of Mailing List Archives to use when stashing links. + +These URIs are concatenated with the current message's +Message-Id in `notmuch-show-stash-mlarchive-link'." + :type '(alist :key-type (string :tag "Name") + :value-type (string :tag "URL")) + :group 'notmuch-show) + +(defcustom notmuch-show-stash-mlarchive-link-default "Gmane" + "Default Mailing List Archive to use when stashing links. + +This is used when `notmuch-show-stash-mlarchive-link' isn't +provided with an MLA argument nor `completing-read' input." + :type `(choice + ,@(mapcar + (lambda (mla) + (list 'const :tag (car mla) :value (car mla))) + notmuch-show-stash-mlarchive-link-alist)) + :group 'notmuch-show) + +(defcustom notmuch-show-mark-read-tags '("-unread") + "List of tag changes to apply to a message when it is marked as read. + +Tags starting with \"+\" (or not starting with either \"+\" or +\"-\") in the list will be added, and tags starting with \"-\" +will be removed from the message being marked as read. + +For example, if you wanted to remove an \"unread\" tag and add a +\"read\" tag (which would make little sense), you would set: + (\"-unread\" \"+read\")" + :type '(repeat string) + :group 'notmuch-show) + + (defmacro with-current-notmuch-show-message (&rest body) "Evaluate body with current buffer set to the text of current message" `(save-excursion @@ -122,18 +207,22 @@ indentation." ,@body) (kill-buffer buf))))) +(defun notmuch-show-turn-on-visual-line-mode () + "Enable Visual Line mode." + (visual-line-mode t)) + (defun notmuch-show-view-all-mime-parts () "Use external viewers to view all attachments from the current message." (interactive) (with-current-notmuch-show-message - ; We override the mm-inline-media-tests to indicate which message - ; parts are already sufficiently handled by the original - ; presentation of the message in notmuch-show mode. These parts - ; will be inserted directly into the temporary buffer of - ; with-current-notmuch-show-message and silently discarded. - ; - ; Any MIME part not explicitly mentioned here will be handled by an - ; external viewer as configured in the various mailcap files. + ;; We override the mm-inline-media-tests to indicate which message + ;; parts are already sufficiently handled by the original + ;; presentation of the message in notmuch-show mode. These parts + ;; will be inserted directly into the temporary buffer of + ;; with-current-notmuch-show-message and silently discarded. + ;; + ;; Any MIME part not explicitly mentioned here will be handled by an + ;; external viewer as configured in the various mailcap files. (let ((mm-inline-media-tests '( ("text/.*" ignore identity) ("application/pgp-signature" ignore identity) @@ -188,6 +277,54 @@ indentation." mm-handle (> (notmuch-count-attachments mm-handle) 1)))) (message "Done")) +(defun notmuch-show-with-message-as-text (fn) + "Apply FN to a text representation of the current message. + +FN is called with one argument, the message properties. It should +operation on the contents of the current buffer." + + ;; Remake the header to ensure that all information is available. + (let* ((to (notmuch-show-get-to)) + (cc (notmuch-show-get-cc)) + (from (notmuch-show-get-from)) + (subject (notmuch-show-get-subject)) + (date (notmuch-show-get-date)) + (tags (notmuch-show-get-tags)) + (depth (notmuch-show-get-depth)) + + (header (concat + "Subject: " subject "\n" + "To: " to "\n" + (if (not (string= cc "")) + (concat "Cc: " cc "\n") + "") + "From: " from "\n" + "Date: " date "\n" + (if tags + (concat "Tags: " + (mapconcat #'identity tags ", ") "\n") + ""))) + (all (buffer-substring (notmuch-show-message-top) + (notmuch-show-message-bottom))) + + (props (notmuch-show-get-message-properties)) + (indenting notmuch-show-indent-content)) + (with-temp-buffer + (insert all) + (if indenting + (indent-rigidly (point-min) (point-max) (- depth))) + ;; Remove the original header. + (goto-char (point-min)) + (re-search-forward "^$" (point-max) nil) + (delete-region (point-min) (point)) + (insert header) + (funcall fn props)))) + +(defun notmuch-show-print-message () + "Print the current message." + (interactive) + (notmuch-show-with-message-as-text 'notmuch-print-message)) + (defun notmuch-show-fontify-header () (let ((face (cond ((looking-at "[Tt]o:") @@ -232,21 +369,57 @@ indentation." "Try to clean a single email ADDRESS for display. Return unchanged ADDRESS if parsing fails." (condition-case nil - (let* ((parsed (mail-header-parse-address address)) - (address (car parsed)) - (name (cdr parsed))) - ;; Remove double quotes. They might be required during transport, - ;; but we don't need to see them. - (when name - (setq name (replace-regexp-in-string "\"" "" name))) + (let (p-name p-address) + ;; It would be convenient to use `mail-header-parse-address', + ;; but that expects un-decoded mailbox parts, whereas our + ;; mailbox parts are already decoded (and hence may contain + ;; UTF-8). Given that notmuch should handle most of the awkward + ;; cases, some simple string deconstruction should be sufficient + ;; here. + (cond + ;; "User " style. + ((string-match "\\(.*\\) <\\(.*\\)>" address) + (setq p-name (match-string 1 address) + p-address (match-string 2 address))) + + ;; "" style. + ((string-match "<\\(.*\\)>" address) + (setq p-address (match-string 1 address))) + + ;; Everything else. + (t + (setq p-address address))) + + (when p-name + ;; Remove elements of the mailbox part that are not relevant for + ;; display, even if they are required during transport: + ;; + ;; Backslashes. + (setq p-name (replace-regexp-in-string "\\\\" "" p-name)) + + ;; Outer single and double quotes, which might be nested. + (loop + with start-of-loop + do (setq start-of-loop p-name) + + when (string-match "^\"\\(.*\\)\"$" p-name) + do (setq p-name (match-string 1 p-name)) + + when (string-match "^'\\(.*\\)'$" p-name) + do (setq p-name (match-string 1 p-name)) + + until (string= start-of-loop p-name))) + ;; If the address is 'foo@bar.com ' then show just ;; 'foo@bar.com'. - (when (string= name address) - (setq name nil)) - - (if (not name) - address - (concat name " <" address ">"))) + (when (string= p-name p-address) + (setq p-name nil)) + + ;; If no name results, return just the address. + (if (not p-name) + p-address + ;; Otherwise format the name and address together. + (concat p-name " <" p-address ">"))) (error address))) (defun notmuch-show-insert-headerline (headers date tags depth) @@ -283,10 +456,22 @@ message at DEPTH in the current thread." (run-hooks 'notmuch-show-markup-headers-hook))))) (define-button-type 'notmuch-show-part-button-type - 'action 'notmuch-show-part-button-action + 'action 'notmuch-show-part-button-default + 'keymap 'notmuch-show-part-button-map 'follow-link t 'face 'message-mml) +(defvar notmuch-show-part-button-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map button-map) + (define-key map "s" 'notmuch-show-part-button-save) + (define-key map "v" 'notmuch-show-part-button-view) + (define-key map "o" 'notmuch-show-part-button-interactively-view) + (define-key map "|" 'notmuch-show-part-button-pipe) + map) + "Submap for button commands") +(fset 'notmuch-show-part-button-map notmuch-show-part-button-map) + (defun notmuch-show-insert-part-header (nth content-type declared-type &optional name comment) (let ((button)) (setq button @@ -301,70 +486,71 @@ message at DEPTH in the current thread." " ]") :type 'notmuch-show-part-button-type :notmuch-part nth - :notmuch-filename name)) + :notmuch-filename name + :notmuch-content-type content-type)) (insert "\n") ;; return button button)) ;; Functions handling particular MIME parts. -(defun notmuch-show-save-part (message-id nth &optional filename) - (let ((process-crypto notmuch-show-process-crypto)) - (with-temp-buffer - (setq notmuch-show-process-crypto process-crypto) - ;; Always acquires the part via `notmuch part', even if it is - ;; available in the JSON output. - (insert (notmuch-show-get-bodypart-internal message-id nth)) - (let ((file (read-file-name - "Filename to save as: " - (or mailcap-download-directory "~/") - nil nil - filename))) - ;; Don't re-compress .gz & al. Arguably we should make - ;; `file-name-handler-alist' nil, but that would chop - ;; ange-ftp, which is reasonable to use here. - (mm-write-region (point-min) (point-max) file nil nil nil 'no-conversion t))))) - -(defun notmuch-show-mm-display-part-inline (msg part nth content-type) - "Use the mm-decode/mm-view functions to display a part in the -current buffer, if possible." - (let ((display-buffer (current-buffer))) - (with-temp-buffer - (let* ((charset (plist-get part :content-charset)) - (handle (mm-make-handle (current-buffer) `(,content-type (charset . ,charset))))) - (if (and (mm-inlinable-p handle) - (mm-inlined-p handle)) - (let ((content (notmuch-show-get-bodypart-content msg part nth))) - (insert content) - (set-buffer display-buffer) - (mm-display-part handle) - t) - nil))))) - -(defvar notmuch-show-multipart/alternative-discouraged - '( - ;; Avoid HTML parts. - "text/html" - ;; multipart/related usually contain a text/html part and some associated graphics. - "multipart/related" - )) +(defmacro notmuch-with-temp-part-buffer (message-id nth &rest body) + (declare (indent 2)) + (let ((process-crypto (make-symbol "process-crypto"))) + `(let ((,process-crypto notmuch-show-process-crypto)) + (with-temp-buffer + (setq notmuch-show-process-crypto ,process-crypto) + ;; Always acquires the part via `notmuch part', even if it is + ;; available in the JSON output. + (insert (notmuch-get-bodypart-internal ,message-id ,nth notmuch-show-process-crypto)) + ,@body)))) + +(defun notmuch-show-save-part (message-id nth &optional filename content-type) + (notmuch-with-temp-part-buffer message-id nth + (let ((file (read-file-name + "Filename to save as: " + (or mailcap-download-directory "~/") + nil nil + filename))) + ;; Don't re-compress .gz & al. Arguably we should make + ;; `file-name-handler-alist' nil, but that would chop + ;; ange-ftp, which is reasonable to use here. + (mm-write-region (point-min) (point-max) file nil nil nil 'no-conversion t)))) + +(defun notmuch-show-view-part (message-id nth &optional filename content-type ) + (notmuch-with-temp-part-buffer message-id nth + ;; set mm-inlined-types to nil to force an external viewer + (let ((handle (mm-make-handle (current-buffer) (list content-type))) + (mm-inlined-types nil)) + ;; We override mm-save-part as notmuch-show-save-part is better + ;; since it offers the filename. We need to lexically bind + ;; everything we need for notmuch-show-save-part to prevent + ;; potential dynamic shadowing. + (lexical-let ((message-id message-id) + (nth nth) + (filename filename) + (content-type content-type)) + (flet ((mm-save-part (&rest args) (notmuch-show-save-part + message-id nth filename content-type))) + (mm-display-part handle)))))) + +(defun notmuch-show-interactively-view-part (message-id nth &optional filename content-type) + (notmuch-with-temp-part-buffer message-id nth + (let ((handle (mm-make-handle (current-buffer) (list content-type)))) + (mm-interactively-view-part handle)))) + +(defun notmuch-show-pipe-part (message-id nth &optional filename content-type) + (notmuch-with-temp-part-buffer message-id nth + (let ((handle (mm-make-handle (current-buffer) (list content-type)))) + (mm-pipe-part handle)))) (defun notmuch-show-multipart/*-to-list (part) (mapcar (lambda (inner-part) (plist-get inner-part :content-type)) (plist-get part :content))) -(defun notmuch-show-multipart/alternative-choose (types) - ;; Based on `mm-preferred-alternative-precedence'. - (let ((seq types)) - (dolist (pref (reverse notmuch-show-multipart/alternative-discouraged)) - (dolist (elem (copy-sequence seq)) - (when (string-match pref elem) - (setq seq (nconc (delete elem seq) (list elem)))))) - seq)) - (defun notmuch-show-insert-part-multipart/alternative (msg part content-type nth depth declared-type) (notmuch-show-insert-part-header nth declared-type content-type nil) - (let ((chosen-type (car (notmuch-show-multipart/alternative-choose (notmuch-show-multipart/*-to-list part)))) + (let ((chosen-type (car (notmuch-multipart/alternative-choose (notmuch-show-multipart/*-to-list part)))) (inner-parts (plist-get part :content)) (start (point))) ;; This inserts all parts of the chosen type rather than just one, @@ -429,8 +615,8 @@ current buffer, if possible." ;; times (hundreds!), which results in many calls to ;; `notmuch part'. (unless content - (setq content (notmuch-show-get-bodypart-internal (concat "id:" message-id) - part-number)) + (setq content (notmuch-get-bodypart-internal (notmuch-id-to-query message-id) + part-number notmuch-show-process-crypto)) (with-current-buffer w3m-current-buffer (notmuch-show-w3m-cid-store-internal url message-id @@ -470,7 +656,7 @@ current buffer, if possible." (sigstatus (car (plist-get part :sigstatus)))) (notmuch-crypto-insert-sigstatus-button sigstatus from)) ;; if we're not adding sigstatus, tell the user how they can get it - (button-put button 'help-echo "Set notmuch-crypto-process-mime to process cryptographic mime parts."))) + (button-put button 'help-echo "Set notmuch-crypto-process-mime to process cryptographic MIME parts."))) (let ((inner-parts (plist-get part :content)) (start (point))) @@ -496,7 +682,7 @@ current buffer, if possible." (sigstatus (car (plist-get part :sigstatus)))) (notmuch-crypto-insert-sigstatus-button sigstatus from)))) ;; if we're not adding encstatus, tell the user how they can get it - (button-put button 'help-echo "Set notmuch-crypto-process-mime to process cryptographic mime parts."))) + (button-put button 'help-echo "Set notmuch-crypto-process-mime to process cryptographic MIME parts."))) (let ((inner-parts (plist-get part :content)) (start (point))) @@ -550,17 +736,17 @@ current buffer, if possible." ;; insert a header to make this clear. (if (> nth 1) (notmuch-show-insert-part-header nth declared-type content-type (plist-get part :filename))) - (insert (notmuch-show-get-bodypart-content msg part nth)) + (insert (notmuch-get-bodypart-content msg part nth notmuch-show-process-crypto)) (save-excursion (save-restriction (narrow-to-region start (point-max)) (run-hook-with-args 'notmuch-show-insert-text/plain-hook msg depth)))) t) -(defun notmuch-show-insert-part-text/x-vcalendar (msg part content-type nth depth declared-type) +(defun notmuch-show-insert-part-text/calendar (msg part content-type nth depth declared-type) (notmuch-show-insert-part-header nth declared-type content-type (plist-get part :filename)) (insert (with-temp-buffer - (insert (notmuch-show-get-bodypart-content msg part nth)) + (insert (notmuch-get-bodypart-content msg part nth notmuch-show-process-crypto)) (goto-char (point-min)) (let ((file (make-temp-file "notmuch-ical")) result) @@ -575,6 +761,10 @@ current buffer, if possible." result))) t) +;; For backwards compatibility. +(defun notmuch-show-insert-part-text/x-vcalendar (msg part content-type nth depth declared-type) + (notmuch-show-insert-part-text/calendar msg part content-type nth depth declared-type)) + (defun notmuch-show-insert-part-application/octet-stream (msg part content-type nth depth declared-type) ;; If we can deduce a MIME type from the filename of the attachment, ;; do so and pass it on to the handler for that type. @@ -598,14 +788,11 @@ current buffer, if possible." (defun notmuch-show-insert-part-*/* (msg part content-type nth depth declared-type) ;; This handler _must_ succeed - it is the handler of last resort. (notmuch-show-insert-part-header nth content-type declared-type (plist-get part :filename)) - (notmuch-show-mm-display-part-inline msg part nth content-type) + (notmuch-mm-display-part-inline msg part nth content-type notmuch-show-process-crypto) t) ;; Functions for determining how to handle MIME parts. -(defun notmuch-show-split-content-type (content-type) - (split-string content-type "/")) - (defun notmuch-show-handlers-for (content-type) "Return a list of content handlers for a part of type CONTENT-TYPE." (let (result) @@ -616,32 +803,11 @@ current buffer, if possible." (list (intern (concat "notmuch-show-insert-part-*/*")) (intern (concat "notmuch-show-insert-part-" - (car (notmuch-show-split-content-type content-type)) + (car (notmuch-split-content-type content-type)) "/*")) (intern (concat "notmuch-show-insert-part-" content-type)))) result)) -;; Helper for parts which are generally not included in the default -;; JSON output. -;; Uses the buffer-local variable notmuch-show-process-crypto to -;; determine if parts should be decrypted first. -(defun notmuch-show-get-bodypart-internal (message-id part-number) - (let ((args '("show" "--format=raw")) - (part-arg (format "--part=%s" part-number))) - (setq args (append args (list part-arg))) - (if notmuch-show-process-crypto - (setq args (append args '("--decrypt")))) - (setq args (append args (list message-id))) - (with-temp-buffer - (let ((coding-system-for-read 'no-conversion)) - (progn - (apply 'call-process (append (list notmuch-command nil (list t nil) nil) args)) - (buffer-string)))))) - -(defun notmuch-show-get-bodypart-content (msg part nth) - (or (plist-get part :content) - (notmuch-show-get-bodypart-internal (concat "id:" (plist-get msg :id)) nth))) - ;; (defun notmuch-show-insert-bodypart-internal (msg part content-type nth depth declared-type) @@ -649,7 +815,12 @@ current buffer, if possible." ;; Run the content handlers until one of them returns a non-nil ;; value. (while (and handlers - (not (funcall (car handlers) msg part content-type nth depth declared-type))) + (not (condition-case err + (funcall (car handlers) msg part content-type nth depth declared-type) + (error (progn + (insert "!!! Bodypart insert error: ") + (insert (error-message-string err)) + (insert " !!!\n") nil))))) (setq handlers (cdr handlers)))) t) @@ -673,7 +844,7 @@ current buffer, if possible." (make-symbol (concat "notmuch-show-" type))) (defun notmuch-show-strip-re (string) - (replace-regexp-in-string "\\([Rr]e: *\\)+" "" string)) + (replace-regexp-in-string "^\\([Rr]e: *\\)+" "" string)) (defvar notmuch-show-previous-subject "") (make-variable-buffer-local 'notmuch-show-previous-subject) @@ -725,8 +896,6 @@ current buffer, if possible." ;; compatible with the existing implementation. This just sets it ;; to after the first header. (notmuch-show-insert-headers headers) - ;; Headers should include a blank line (backwards compatibility). - (insert "\n") (save-excursion (goto-char content-start) ;; If the subject of this message is the same as that of the @@ -741,7 +910,10 @@ current buffer, if possible." (setq notmuch-show-previous-subject bare-subject) (setq body-start (point-marker)) - (notmuch-show-insert-body msg (plist-get msg :body) depth) + ;; A blank line between the headers and the body. + (insert "\n") + (notmuch-show-insert-body msg (plist-get msg :body) + (if notmuch-show-indent-content depth 0)) ;; Ensure that the body ends with a newline. (unless (bolp) (insert "\n")) @@ -749,7 +921,8 @@ current buffer, if possible." (setq content-end (point-marker)) ;; Indent according to the depth in the thread. - (indent-rigidly content-start content-end (* notmuch-show-indent-messages-width depth)) + (if notmuch-show-indent-content + (indent-rigidly content-start content-end (* notmuch-show-indent-messages-width depth))) (setq message-end (point-max-marker)) @@ -763,6 +936,8 @@ current buffer, if possible." (overlay-put headers-overlay 'priority 10)) (overlay-put (make-overlay body-start body-end) 'invisible message-invis-spec) + (plist-put msg :depth depth) + ;; Save the properties for this message. Currently this saves the ;; entire message (augmented it with other stuff), which seems ;; like overkill. We might save a reduced subset (for example, not @@ -774,13 +949,43 @@ current buffer, if possible." ;; Message visibility depends on whether it matched the search ;; criteria. - (notmuch-show-message-visible msg (plist-get msg :match)))) + (notmuch-show-message-visible msg (and (plist-get msg :match) + (not (plist-get msg :excluded)))))) + +(defun notmuch-show-toggle-process-crypto () + "Toggle the processing of cryptographic MIME parts." + (interactive) + (setq notmuch-show-process-crypto (not notmuch-show-process-crypto)) + (message (if notmuch-show-process-crypto + "Processing cryptographic MIME parts." + "Not processing cryptographic MIME parts.")) + (notmuch-show-refresh-view)) + +(defun notmuch-show-toggle-elide-non-matching () + "Toggle the display of non-matching messages." + (interactive) + (setq notmuch-show-elide-non-matching-messages (not notmuch-show-elide-non-matching-messages)) + (message (if notmuch-show-elide-non-matching-messages + "Showing matching messages only." + "Showing all messages.")) + (notmuch-show-refresh-view)) + +(defun notmuch-show-toggle-thread-indentation () + "Toggle the indentation of threads." + (interactive) + (setq notmuch-show-indent-content (not notmuch-show-indent-content)) + (message (if notmuch-show-indent-content + "Content is indented." + "Content is not indented.")) + (notmuch-show-refresh-view)) (defun notmuch-show-insert-tree (tree depth) "Insert the message tree TREE at depth DEPTH in the current thread." (let ((msg (car tree)) (replies (cadr tree))) - (notmuch-show-insert-msg msg depth) + ;; We test whether there is a message or just some replies. + (when msg + (notmuch-show-insert-msg msg depth)) (notmuch-show-insert-thread replies (1+ depth)))) (defun notmuch-show-insert-thread (thread depth) @@ -791,14 +996,23 @@ current buffer, if possible." "Insert the forest of threads FOREST." (mapc (lambda (thread) (notmuch-show-insert-thread thread 0)) forest)) -(defvar notmuch-show-thread-id nil) -(make-variable-buffer-local 'notmuch-show-thread-id) -(defvar notmuch-show-parent-buffer nil) -(make-variable-buffer-local 'notmuch-show-parent-buffer) -(defvar notmuch-show-query-context nil) -(make-variable-buffer-local 'notmuch-show-query-context) -(defvar notmuch-show-buffer-name nil) -(make-variable-buffer-local 'notmuch-show-buffer-name) +(defvar notmuch-id-regexp + (concat + ;; Match the id: prefix only if it begins a word (to disallow, for + ;; example, matching cid:). + "\\