]> git.notmuchmail.org Git - notmuch/blobdiff - emacs/notmuch-show.el
emacs: make header line in notmuch-show buffers optional
[notmuch] / emacs / notmuch-show.el
index 72e21d94387c698739e7cc435d34d13ce40598d1..7c1f02c92f1499be58df08929863d4e1aa013118 100644 (file)
 
 ;;; Code:
 
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl-lib)
-  (require 'pcase))
-
 (require 'mm-view)
 (require 'message)
 (require 'mm-decode)
 (require 'mm-view)
 (require 'message)
 (require 'mm-decode)
 (require 'notmuch-print)
 (require 'notmuch-draft)
 
 (require 'notmuch-print)
 (require 'notmuch-draft)
 
-(declare-function notmuch-call-notmuch-process "notmuch" (&rest args))
+(declare-function notmuch-call-notmuch-process "notmuch-lib" (&rest args))
 (declare-function notmuch-search-next-thread "notmuch" nil)
 (declare-function notmuch-search-previous-thread "notmuch" nil)
 (declare-function notmuch-search-next-thread "notmuch" nil)
 (declare-function notmuch-search-previous-thread "notmuch" nil)
-(declare-function notmuch-search-show-thread "notmuch" nil)
+(declare-function notmuch-search-show-thread "notmuch")
 (declare-function notmuch-foreach-mime-part "notmuch" (function mm-handle))
 (declare-function notmuch-count-attachments "notmuch" (mm-handle))
 (declare-function notmuch-save-attachments "notmuch" (mm-handle &optional queryp))
 (declare-function notmuch-tree "notmuch-tree"
                  (&optional query query-context target buffer-name
 (declare-function notmuch-foreach-mime-part "notmuch" (function mm-handle))
 (declare-function notmuch-count-attachments "notmuch" (mm-handle))
 (declare-function notmuch-save-attachments "notmuch" (mm-handle &optional queryp))
 (declare-function notmuch-tree "notmuch-tree"
                  (&optional query query-context target buffer-name
-                            open-target unthreaded))
+                            open-target unthreaded parent-buffer))
 (declare-function notmuch-tree-get-message-properties "notmuch-tree" nil)
 (declare-function notmuch-tree-get-message-properties "notmuch-tree" nil)
-(declare-function notmuch-unthreaded
-                 (&optional query query-context target buffer-name open-target))
+(declare-function notmuch-unthreaded "notmuch-tree"
+                 (&optional query query-context target buffer-name
+                            open-target))
 (declare-function notmuch-read-query "notmuch" (prompt))
 (declare-function notmuch-draft-resume "notmuch-draft" (id))
 
 (defvar shr-blocked-images)
 (defvar gnus-blocked-images)
 (defvar shr-content-function)
 (declare-function notmuch-read-query "notmuch" (prompt))
 (declare-function notmuch-draft-resume "notmuch-draft" (id))
 
 (defvar shr-blocked-images)
 (defvar gnus-blocked-images)
 (defvar shr-content-function)
+(defvar w3m-ignored-image-url-regexp)
 
 ;;; Options
 
 
 ;;; Options
 
@@ -86,6 +84,11 @@ visible for any given message."
   :type 'boolean
   :group 'notmuch-show)
 
   :type 'boolean
   :group 'notmuch-show)
 
+(defcustom notmuch-show-header-line t
+  "Show a header line with the current message's subject."
+  :type 'boolean
+  :group 'notmuch-show)
+
 (defcustom notmuch-show-relative-dates t
   "Display relative dates in the message summary line."
   :type 'boolean
 (defcustom notmuch-show-relative-dates t
   "Display relative dates in the message summary line."
   :type 'boolean
@@ -182,6 +185,8 @@ indentation."
 
 (defvar-local notmuch-show-indent-content t)
 
 
 (defvar-local notmuch-show-indent-content t)
 
+(defvar-local notmuch-show-single-message nil)
+
 (defvar notmuch-show-attachment-debug nil
   "If t log stdout and stderr from attachment handlers.
 
 (defvar notmuch-show-attachment-debug nil
   "If t log stdout and stderr from attachment handlers.
 
@@ -193,10 +198,10 @@ each attachment handler is logged in buffers with names beginning
 ;;; Options
 
 (defcustom notmuch-show-stash-mlarchive-link-alist
 ;;; Options
 
 (defcustom notmuch-show-stash-mlarchive-link-alist
-  '(("Gmane" . "https://mid.gmane.org/")
-    ("MARC" . "https://marc.info/?i=")
+  '(("MARC" . "https://marc.info/?i=")
     ("Mail Archive, The" . "https://mid.mail-archive.com/")
     ("Mail Archive, The" . "https://mid.mail-archive.com/")
-    ("LKML" . "https://lkml.kernel.org/r/")
+    ("Lore" . "https://lore.kernel.org/r/")
+    ("Notmuch" . "https://nmbug.notmuchmail.org/nmweb/show/")
     ;; FIXME: can these services be searched by `Message-Id' ?
     ;; ("MarkMail" . "http://markmail.org/")
     ;; ("Nabble" . "http://nabble.com/")
     ;; FIXME: can these services be searched by `Message-Id' ?
     ;; ("MarkMail" . "http://markmail.org/")
     ;; ("Nabble" . "http://nabble.com/")
@@ -221,7 +226,7 @@ return the ML archive reference URI."
                             (function :tag "Function returning the URL")))
   :group 'notmuch-show)
 
                             (function :tag "Function returning the URL")))
   :group 'notmuch-show)
 
-(defcustom notmuch-show-stash-mlarchive-link-default "Gmane"
+(defcustom notmuch-show-stash-mlarchive-link-default "MARC"
   "Default Mailing List Archive to use when stashing links.
 
 This is used when `notmuch-show-stash-mlarchive-link' isn't
   "Default Mailing List Archive to use when stashing links.
 
 This is used when `notmuch-show-stash-mlarchive-link' isn't
@@ -279,7 +284,7 @@ position of the message in the thread."
        (let ((buf (generate-new-buffer (concat "*notmuch-msg-" id "*"))))
         (with-current-buffer buf
           (let ((coding-system-for-read 'no-conversion))
        (let ((buf (generate-new-buffer (concat "*notmuch-msg-" id "*"))))
         (with-current-buffer buf
           (let ((coding-system-for-read 'no-conversion))
-            (call-process notmuch-command nil t nil "show" "--format=raw" id))
+            (notmuch--call-process notmuch-command nil t nil "show" "--format=raw" id))
           ,@body)
         (kill-buffer buf)))))
 
           ,@body)
         (kill-buffer buf)))))
 
@@ -337,7 +342,7 @@ operation on the contents of the current buffer."
         (header (concat
                  "Subject: " subject "\n"
                  "To: " to "\n"
         (header (concat
                  "Subject: " subject "\n"
                  "To: " to "\n"
-                 (if (not (string= cc ""))
+                 (if (not (string-empty-p cc))
                      (concat "Cc: " cc "\n")
                    "")
                  "From: " from "\n"
                      (concat "Cc: " cc "\n")
                    "")
                  "From: " from "\n"
@@ -517,7 +522,7 @@ message at DEPTH in the current thread."
   'face 'message-mml
   :supertype 'notmuch-button-type)
 
   'face 'message-mml
   :supertype 'notmuch-button-type)
 
-(defun notmuch-show-insert-part-header (nth content-type declared-type
+(defun notmuch-show-insert-part-header (_nth content-type declared-type
                                            &optional name comment)
   (let ((base-label (concat (and name (concat name ": "))
                            declared-type
                                            &optional name comment)
   (let ((base-label (concat (and name (concat name ": "))
                            declared-type
@@ -585,16 +590,17 @@ message at DEPTH in the current thread."
       ;; alternative (even if we can't render it).
       (push (list content-id msg part) notmuch-show--cids)))
   ;; Recurse on sub-parts
       ;; alternative (even if we can't render it).
       (push (list content-id msg part) notmuch-show--cids)))
   ;; Recurse on sub-parts
-  (pcase-let ((`(,content ,type)
-              (split-string (downcase (plist-get part :content-type)) "/")))
-    (cond ((equal content "multipart")
-          (mapc (apply-partially #'notmuch-show--register-cids msg)
-                (plist-get part :content)))
-         ((and (equal content "message")
-               (equal type "rfc822"))
-          (notmuch-show--register-cids
-           msg
-           (car (plist-get (car (plist-get part :content)) :body)))))))
+  (when-let ((type (plist-get part :content-type)))
+    (pcase-let ((`(,type ,subtype)
+                (split-string (downcase type) "/")))
+      (cond ((equal type "multipart")
+            (mapc (apply-partially #'notmuch-show--register-cids msg)
+                  (plist-get part :content)))
+           ((and (equal type "message")
+                 (equal subtype "rfc822"))
+            (notmuch-show--register-cids
+             msg
+             (car (plist-get (car (plist-get part :content)) :body))))))))
 
 (defun notmuch-show--get-cid-content (cid)
   "Return a list (CID-content content-type) or nil.
 
 (defun notmuch-show--get-cid-content (cid)
   "Return a list (CID-content content-type) or nil.
@@ -603,16 +609,13 @@ This will only find parts from messages that have been inserted
 into the current buffer.  CID must be a raw content ID, without
 enclosing angle brackets, a cid: prefix, or URL encoding.  This
 will return nil if the CID is unknown or cannot be retrieved."
 into the current buffer.  CID must be a raw content ID, without
 enclosing angle brackets, a cid: prefix, or URL encoding.  This
 will return nil if the CID is unknown or cannot be retrieved."
-  (let ((descriptor (cdr (assoc cid notmuch-show--cids))))
-    (when descriptor
-      (let* ((msg (car descriptor))
-            (part (cadr descriptor))
-            ;; Request caching for this content, as some messages
-            ;; reference the same cid: part many times (hundreds!).
-            (content (notmuch-get-bodypart-binary
-                      msg part notmuch-show-process-crypto 'cache))
-            (content-type (plist-get part :content-type)))
-       (list content content-type)))))
+  (when-let ((descriptor (cdr (assoc cid notmuch-show--cids))))
+    (pcase-let ((`(,msg ,part) descriptor))
+      ;; Request caching for this content, as some messages
+      ;; reference the same cid: part many times (hundreds!).
+      (list (notmuch-get-bodypart-binary
+            msg part notmuch-show-process-crypto 'cache)
+           (plist-get part :content-type)))))
 
 (defun notmuch-show-setup-w3m ()
   "Instruct w3m how to retrieve content from a \"related\" part of a message."
 
 (defun notmuch-show-setup-w3m ()
   "Instruct w3m how to retrieve content from a \"related\" part of a message."
@@ -624,7 +627,7 @@ will return nil if the CID is unknown or cannot be retrieved."
   (setq mm-html-inhibit-images nil))
 
 (defvar w3m-current-buffer) ;; From `w3m.el'.
   (setq mm-html-inhibit-images nil))
 
 (defvar w3m-current-buffer) ;; From `w3m.el'.
-(defun notmuch-show--cid-w3m-retrieve (url &rest args)
+(defun notmuch-show--cid-w3m-retrieve (url &rest _args)
   ;; url includes the cid: prefix and is URL encoded (see RFC 2392).
   (let* ((cid (url-unhex-string (substring url 4)))
         (content-and-type
   ;; url includes the cid: prefix and is URL encoded (see RFC 2392).
   (let* ((cid (url-unhex-string (substring url 4)))
         (content-and-type
@@ -640,7 +643,7 @@ will return nil if the CID is unknown or cannot be retrieved."
   (mapcar (lambda (inner-part) (plist-get inner-part :content-type))
          (plist-get part :content)))
 
   (mapcar (lambda (inner-part) (plist-get inner-part :content-type))
          (plist-get part :content)))
 
-(defun notmuch-show-insert-part-multipart/alternative (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-multipart/alternative (msg part _content-type _nth depth _button)
   (let ((chosen-type (car (notmuch-multipart/alternative-choose
                           msg (notmuch-show-multipart/*-to-list part))))
        (inner-parts (plist-get part :content))
   (let ((chosen-type (car (notmuch-multipart/alternative-choose
                           msg (notmuch-show-multipart/*-to-list part))))
        (inner-parts (plist-get part :content))
@@ -659,7 +662,7 @@ will return nil if the CID is unknown or cannot be retrieved."
       (indent-rigidly start (point) 1)))
   t)
 
       (indent-rigidly start (point) 1)))
   t)
 
-(defun notmuch-show-insert-part-multipart/related (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-multipart/related (msg part _content-type _nth depth _button)
   (let ((inner-parts (plist-get part :content))
        (start (point)))
     ;; Render the primary part.  FIXME: Support RFC 2387 Start header.
   (let ((inner-parts (plist-get part :content))
        (start (point)))
     ;; Render the primary part.  FIXME: Support RFC 2387 Start header.
@@ -672,7 +675,7 @@ will return nil if the CID is unknown or cannot be retrieved."
       (indent-rigidly start (point) 1)))
   t)
 
       (indent-rigidly start (point) 1)))
   t)
 
-(defun notmuch-show-insert-part-multipart/signed (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-multipart/signed (msg part _content-type _nth depth button)
   (when button
     (button-put button 'face 'notmuch-crypto-part-header))
   ;; Insert a button detailing the signature status.
   (when button
     (button-put button 'face 'notmuch-crypto-part-header))
   ;; Insert a button detailing the signature status.
@@ -688,7 +691,7 @@ will return nil if the CID is unknown or cannot be retrieved."
       (indent-rigidly start (point) 1)))
   t)
 
       (indent-rigidly start (point) 1)))
   t)
 
-(defun notmuch-show-insert-part-multipart/encrypted (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-multipart/encrypted (msg part _content-type _nth depth button)
   (when button
     (button-put button 'face 'notmuch-crypto-part-header))
   ;; Insert a button detailing the encryption status.
   (when button
     (button-put button 'face 'notmuch-crypto-part-header))
   ;; Insert a button detailing the encryption status.
@@ -706,10 +709,10 @@ will return nil if the CID is unknown or cannot be retrieved."
       (indent-rigidly start (point) 1)))
   t)
 
       (indent-rigidly start (point) 1)))
   t)
 
-(defun notmuch-show-insert-part-application/pgp-encrypted (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-application/pgp-encrypted (_msg _part _content-type _nth _depth _button)
   t)
 
   t)
 
-(defun notmuch-show-insert-part-multipart/* (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-multipart/* (msg part _content-type _nth depth _button)
   (let ((inner-parts (plist-get part :content))
        (start (point)))
     ;; Show all of the parts.
   (let ((inner-parts (plist-get part :content))
        (start (point)))
     ;; Show all of the parts.
@@ -720,24 +723,26 @@ will return nil if the CID is unknown or cannot be retrieved."
       (indent-rigidly start (point) 1)))
   t)
 
       (indent-rigidly start (point) 1)))
   t)
 
-(defun notmuch-show-insert-part-message/rfc822 (msg part content-type nth depth button)
-  (let* ((message (car (plist-get part :content)))
-        (body (car (plist-get message :body)))
-        (start (point)))
-    ;; Override `notmuch-message-headers' to force `From' to be
-    ;; displayed.
-    (let ((notmuch-message-headers '("From" "Subject" "To" "Cc" "Date")))
-      (notmuch-show-insert-headers (plist-get message :headers)))
-    ;; Blank line after headers to be compatible with the normal
-    ;; message display.
-    (insert "\n")
-    ;; Show the body
-    (notmuch-show-insert-bodypart msg body depth)
-    (when notmuch-show-indent-multipart
-      (indent-rigidly start (point) 1)))
-  t)
-
-(defun notmuch-show-insert-part-text/plain (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-message/rfc822 (msg part _content-type _nth depth _button)
+  (let ((message (car (plist-get part :content))))
+    (and
+     message
+     (let ((body (car (plist-get message :body)))
+          (start (point)))
+       ;; Override `notmuch-message-headers' to force `From' to be
+       ;; displayed.
+       (let ((notmuch-message-headers '("From" "Subject" "To" "Cc" "Date")))
+        (notmuch-show-insert-headers (plist-get message :headers)))
+       ;; Blank line after headers to be compatible with the normal
+       ;; message display.
+       (insert "\n")
+       ;; Show the body
+       (notmuch-show-insert-bodypart msg body depth)
+       (when notmuch-show-indent-multipart
+        (indent-rigidly start (point) 1))
+       t))))
+
+(defun notmuch-show-insert-part-text/plain (msg part _content-type _nth depth button)
   ;; For backward compatibility we want to apply the text/plain hook
   ;; to the whole of the part including the part button if there is
   ;; one.
   ;; For backward compatibility we want to apply the text/plain hook
   ;; to the whole of the part including the part button if there is
   ;; one.
@@ -751,7 +756,7 @@ will return nil if the CID is unknown or cannot be retrieved."
        (run-hook-with-args 'notmuch-show-insert-text/plain-hook msg depth))))
   t)
 
        (run-hook-with-args 'notmuch-show-insert-text/plain-hook msg depth))))
   t)
 
-(defun notmuch-show-insert-part-text/calendar (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-text/calendar (msg part _content-type _nth _depth _button)
   (insert (with-temp-buffer
            (insert (notmuch-get-bodypart-text msg part notmuch-show-process-crypto))
            ;; notmuch-get-bodypart-text does no newline conversion.
   (insert (with-temp-buffer
            (insert (notmuch-get-bodypart-text msg part notmuch-show-process-crypto))
            ;; notmuch-get-bodypart-text does no newline conversion.
@@ -775,8 +780,8 @@ will return nil if the CID is unknown or cannot be retrieved."
   t)
 
 ;; For backwards compatibility.
   t)
 
 ;; For backwards compatibility.
-(defun notmuch-show-insert-part-text/x-vcalendar (msg part content-type nth depth button)
-  (notmuch-show-insert-part-text/calendar msg part content-type nth depth button))
+(defun notmuch-show-insert-part-text/x-vcalendar (msg part _content-type _nth depth _button)
+  (notmuch-show-insert-part-text/calendar msg part nil nil depth nil))
 
 (when (version< emacs-version "25.3")
   ;; https://bugs.gnu.org/28350
 
 (when (version< emacs-version "25.3")
   ;; https://bugs.gnu.org/28350
@@ -792,7 +797,7 @@ will return nil if the CID is unknown or cannot be retrieved."
     ;; the first time).
     (require 'enriched)
     (cl-letf (((symbol-function 'enriched-decode-display-prop)
     ;; the first time).
     (require 'enriched)
     (cl-letf (((symbol-function 'enriched-decode-display-prop)
-              (lambda (start end &optional param) (list start end))))
+              (lambda (start end &optional _param) (list start end))))
       (notmuch-show-insert-part-*/* msg part content-type nth depth button))))
 
 (defun notmuch-show-get-mime-type-of-application/octet-stream (part)
       (notmuch-show-insert-part-*/* msg part content-type nth depth button))))
 
 (defun notmuch-show-get-mime-type-of-application/octet-stream (part)
@@ -826,7 +831,8 @@ will return nil if the CID is unknown or cannot be retrieved."
     (let ((mm-inline-text-html-with-w3m-keymap nil)
          ;; FIXME: If we block an image, offer a button to load external
          ;; images.
     (let ((mm-inline-text-html-with-w3m-keymap nil)
          ;; FIXME: If we block an image, offer a button to load external
          ;; images.
-         (gnus-blocked-images notmuch-show-text/html-blocked-images))
+         (gnus-blocked-images notmuch-show-text/html-blocked-images)
+         (w3m-ignored-image-url-regexp notmuch-show-text/html-blocked-images))
       (notmuch-show-insert-part-*/* msg part content-type nth depth button))))
 
 ;;; Functions used by notmuch-show--insert-part-text/html-shr
       (notmuch-show-insert-part-*/* msg part content-type nth depth button))))
 
 ;;; Functions used by notmuch-show--insert-part-text/html-shr
@@ -850,7 +856,7 @@ will return nil if the CID is unknown or cannot be retrieved."
     (shr-insert-document dom)
     t))
 
     (shr-insert-document dom)
     t))
 
-(defun notmuch-show-insert-part-*/* (msg part content-type nth depth button)
+(defun notmuch-show-insert-part-*/* (msg part content-type _nth _depth _button)
   ;; This handler _must_ succeed - it is the handler of last resort.
   (notmuch-mm-display-part-inline msg part content-type notmuch-show-process-crypto)
   t)
   ;; This handler _must_ succeed - it is the handler of last resort.
   (notmuch-mm-display-part-inline msg part content-type notmuch-show-process-crypto)
   t)
@@ -955,7 +961,8 @@ will return nil if the CID is unknown or cannot be retrieved."
 
 (defun notmuch-show-mime-type (part)
   "Return the correct mime-type to use for PART."
 
 (defun notmuch-show-mime-type (part)
   "Return the correct mime-type to use for PART."
-  (let ((content-type (downcase (plist-get part :content-type))))
+  (when-let ((content-type (plist-get part :content-type)))
+    (setq content-type (downcase content-type))
     (or (and (string= content-type "application/octet-stream")
             (notmuch-show-get-mime-type-of-application/octet-stream part))
        (and (string= content-type "inline patch")
     (or (and (string= content-type "application/octet-stream")
             (notmuch-show-get-mime-type-of-application/octet-stream part))
        (and (string= content-type "inline patch")
@@ -970,13 +977,13 @@ The function should take two parameters, PART and HIDE, and
 should return non-NIL if a header button should be inserted for
 this part.")
 
 should return non-NIL if a header button should be inserted for
 this part.")
 
-(defun notmuch-show-insert-header-p (part hide)
+(defun notmuch-show-insert-header-p (part _hide)
   ;; Show all part buttons except for the first part if it is text/plain.
   (let ((mime-type (notmuch-show-mime-type part)))
     (not (and (string= mime-type "text/plain")
              (<= (plist-get part :id) 1)))))
 
   ;; Show all part buttons except for the first part if it is text/plain.
   (let ((mime-type (notmuch-show-mime-type part)))
     (not (and (string= mime-type "text/plain")
              (<= (plist-get part :id) 1)))))
 
-(defun notmuch-show-reply-insert-header-p-never (part hide)
+(defun notmuch-show-reply-insert-header-p-never (_part _hide)
   nil)
 
 (defun notmuch-show-reply-insert-header-p-trimmed (part hide)
   nil)
 
 (defun notmuch-show-reply-insert-header-p-trimmed (part hide)
@@ -995,7 +1002,7 @@ this part.")
 HIDE determines whether to show or hide the part and the button
 as follows: If HIDE is nil, show the part and the button. If HIDE
 is t, hide the part initially and show the button."
 HIDE determines whether to show or hide the part and the button
 as follows: If HIDE is nil, show the part and the button. If HIDE
 is t, hide the part initially and show the button."
-  (let* ((content-type (downcase (plist-get part :content-type)))
+  (let* ((content-type (plist-get part :content-type))
         (mime-type (notmuch-show-mime-type part))
         (nth (plist-get part :id))
         (long (and (notmuch-match-content-type mime-type "text/*")
         (mime-type (notmuch-show-mime-type part))
         (nth (plist-get part :id))
         (long (and (notmuch-match-content-type mime-type "text/*")
@@ -1007,7 +1014,8 @@ is t, hide the part initially and show the button."
         ;; the first (or only) part if this is text/plain.
         (button (and (funcall notmuch-show-insert-header-p-function part hide)
                      (notmuch-show-insert-part-header
         ;; the first (or only) part if this is text/plain.
         (button (and (funcall notmuch-show-insert-header-p-function part hide)
                      (notmuch-show-insert-part-header
-                      nth mime-type content-type
+                      nth mime-type
+                      (and content-type (downcase content-type))
                       (plist-get part :filename))))
         ;; Hide the part initially if HIDE is t, or if it is too long
         ;; and we have a button to allow toggling.
                       (plist-get part :filename))))
         ;; Hide the part initially if HIDE is t, or if it is too long
         ;; and we have a button to allow toggling.
@@ -1318,9 +1326,10 @@ Apply the previously saved STATE if supplied, otherwise show the
 first relevant message.
 
 If no messages match the query return NIL."
 first relevant message.
 
 If no messages match the query return NIL."
-  (let* ((cli-args (cons "--exclude=false"
-                        (and notmuch-show-elide-non-matching-messages
-                             (list "--entire-thread=false"))))
+  (let* ((cli-args (list "--exclude=false"))
+        (cli-args (if notmuch-show-elide-non-matching-messages (cons "--entire-thread=false" cli-args) cli-args))
+        ;; "part 0 is the whole message (headers and body)" notmuch-show(1)
+        (cli-args (if notmuch-show-single-message (cons "--part=0" cli-args) cli-args))
         (queries (notmuch-show--build-queries
                   notmuch-show-thread-id notmuch-show-query-context))
         (forest nil)
         (queries (notmuch-show--build-queries
                   notmuch-show-thread-id notmuch-show-query-context))
         (forest nil)
@@ -1331,6 +1340,8 @@ If no messages match the query return NIL."
     (while (and (not forest) queries)
       (setq forest (notmuch-query-get-threads
                    (append cli-args (list "'") (car queries) (list "'"))))
     (while (and (not forest) queries)
       (setq forest (notmuch-query-get-threads
                    (append cli-args (list "'") (car queries) (list "'"))))
+      (when (and forest notmuch-show-single-message)
+       (setq forest (list (list (list forest)))))
       (setq queries (cdr queries)))
     (when forest
       (notmuch-show-insert-forest forest)
       (setq queries (cdr queries)))
     (when forest
       (notmuch-show-insert-forest forest)
@@ -1339,11 +1350,12 @@ If no messages match the query return NIL."
       (notmuch-show-mapc
        (lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags))))
       ;; Set the header line to the subject of the first message.
       (notmuch-show-mapc
        (lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags))))
       ;; Set the header line to the subject of the first message.
-      (setq header-line-format
-           (replace-regexp-in-string "%" "%%"
-                                     (notmuch-sanitize
-                                      (notmuch-show-strip-re
-                                       (notmuch-show-get-subject)))))
+      (when notmuch-show-header-line
+       (setq header-line-format
+             (replace-regexp-in-string "%" "%%"
+                                       (notmuch-sanitize
+                                        (notmuch-show-strip-re
+                                         (notmuch-show-get-subject))))))
       (run-hooks 'notmuch-show-hook)
       (if state
          (notmuch-show-apply-state state)
       (run-hooks 'notmuch-show-hook)
       (if state
          (notmuch-show-apply-state state)
@@ -1666,13 +1678,13 @@ It gets property PROP from PROPS or, if PROPS is nil, the current
 message in either tree or show. This means that several utility
 functions in notmuch-show can be used directly by notmuch-tree as
 they just need the correct message properties."
 message in either tree or show. This means that several utility
 functions in notmuch-show can be used directly by notmuch-tree as
 they just need the correct message properties."
-  (let ((props (or props
-                  (cond ((eq major-mode 'notmuch-show-mode)
-                         (notmuch-show-get-message-properties))
-                        ((eq major-mode 'notmuch-tree-mode)
-                         (notmuch-tree-get-message-properties))
-                        (t nil)))))
-    (plist-get props prop)))
+  (plist-get (or props
+                (cond ((eq major-mode 'notmuch-show-mode)
+                       (notmuch-show-get-message-properties))
+                      ((eq major-mode 'notmuch-tree-mode)
+                       (notmuch-tree-get-message-properties))
+                      (t nil)))
+            prop))
 
 (defun notmuch-show-get-message-id (&optional bare)
   "Return an id: query for the Message-Id of the current message.
 
 (defun notmuch-show-get-message-id (&optional bare)
   "Return an id: query for the Message-Id of the current message.
@@ -1759,7 +1771,7 @@ marked as unread, i.e. the tag changes in
     (apply 'notmuch-show-tag-message
           (notmuch-tag-change-list notmuch-show-mark-read-tags unread))))
 
     (apply 'notmuch-show-tag-message
           (notmuch-tag-change-list notmuch-show-mark-read-tags unread))))
 
-(defun notmuch-show-seen-current-message (start end)
+(defun notmuch-show-seen-current-message (_start _end)
   "Mark the current message read if it is open.
 
 We only mark it read once: if it is changed back then that is a
   "Mark the current message read if it is open.
 
 We only mark it read once: if it is changed back then that is a
@@ -1777,7 +1789,7 @@ user decision and we should not override it."
     ;; We need to redisplay to get window-start and window-end correct.
     (redisplay)
     (save-excursion
     ;; We need to redisplay to get window-start and window-end correct.
     (redisplay)
     (save-excursion
-      (condition-case err
+      (condition-case nil
          (funcall notmuch-show-mark-read-function (window-start) (window-end))
        ((debug error)
         (unless notmuch-show--seen-has-errored
          (funcall notmuch-show-mark-read-function (window-start) (window-end))
        ((debug error)
         (unless notmuch-show--seen-has-errored
@@ -1794,7 +1806,7 @@ user decision and we should not override it."
 Reshows the current thread with matches defined by the new query-string."
   (interactive (list (notmuch-read-query "Filter thread: ")))
   (let ((msg-id (notmuch-show-get-message-id)))
 Reshows the current thread with matches defined by the new query-string."
   (interactive (list (notmuch-read-query "Filter thread: ")))
   (let ((msg-id (notmuch-show-get-message-id)))
-    (setq notmuch-show-query-context (if (string= query "") nil query))
+    (setq notmuch-show-query-context (if (string-empty-p query) nil query))
     (notmuch-show-refresh-view t)
     (notmuch-show-goto-message msg-id)))
 
     (notmuch-show-refresh-view t)
     (notmuch-show-goto-message msg-id)))
 
@@ -2028,7 +2040,7 @@ to show, nil otherwise."
     (pop-to-buffer-same-window buf)
     (erase-buffer)
     (let ((coding-system-for-read 'no-conversion))
     (pop-to-buffer-same-window buf)
     (erase-buffer)
     (let ((coding-system-for-read 'no-conversion))
-      (call-process notmuch-command nil t nil "show" "--format=raw" id))
+      (notmuch--call-process notmuch-command nil t nil "show" "--format=raw" id))
     (goto-char (point-min))
     (set-buffer-modified-p nil)
     (setq buffer-read-only t)
     (goto-char (point-min))
     (set-buffer-modified-p nil)
     (setq buffer-read-only t)
@@ -2074,19 +2086,19 @@ message."
     (let ((cwd default-directory)
          (buf (get-buffer-create (concat "*notmuch-pipe*"))))
       (with-current-buffer buf
     (let ((cwd default-directory)
          (buf (get-buffer-create (concat "*notmuch-pipe*"))))
       (with-current-buffer buf
-       (setq buffer-read-only nil)
-       (erase-buffer)
-       ;; Use the originating buffer's working directory instead of
-       ;; that of the pipe buffer.
-       (cd cwd)
-       (let ((exit-code (call-process-shell-command shell-command nil buf)))
-         (goto-char (point-max))
-         (set-buffer-modified-p nil)
-         (setq buffer-read-only t)
-         (unless (zerop exit-code)
-           (pop-to-buffer buf)
-           (message (format "Command '%s' exited abnormally with code %d"
-                            shell-command exit-code))))))))
+       (setq buffer-read-only t)
+       (let ((inhibit-read-only t))
+         (erase-buffer)
+         ;; Use the originating buffer's working directory instead of
+         ;; that of the pipe buffer.
+         (cd cwd)
+         (let ((exit-code (call-process-shell-command shell-command nil buf)))
+           (goto-char (point-max))
+           (set-buffer-modified-p nil)
+           (unless (zerop exit-code)
+             (pop-to-buffer buf)
+             (message (format "Command '%s' exited abnormally with code %d"
+                              shell-command exit-code)))))))))
 
 (defun notmuch-show-tag-message (&rest tag-changes)
   "Change tags for the current message.
 
 (defun notmuch-show-tag-message (&rest tag-changes)
   "Change tags for the current message.