]> 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 48374b38451ed67887c27595cd90a3710a6b12d8..7c1f02c92f1499be58df08929863d4e1aa013118 100644 (file)
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl-lib)
-  (require 'pcase))
-
 (require 'mm-view)
 (require 'message)
 (require 'mm-decode)
 (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-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
-                            open-target unthreaded))
+                            open-target unthreaded parent-buffer))
 (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)
+(defvar w3m-ignored-image-url-regexp)
 
 ;;; Options
 
@@ -86,6 +84,11 @@ visible for any given message."
   :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
@@ -182,6 +185,8 @@ indentation."
 
 (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.
 
@@ -193,10 +198,10 @@ each attachment handler is logged in buffers with names beginning
 ;;; 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/")
-    ("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/")
@@ -221,7 +226,7 @@ return the ML archive reference URI."
                             (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
@@ -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))
-            (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)))))
 
@@ -337,7 +342,7 @@ operation on the contents of the current buffer."
         (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"
@@ -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
-  (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.
@@ -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."
-  (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."
@@ -721,21 +724,23 @@ will return nil if the CID is unknown or cannot be retrieved."
   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)
+  (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
@@ -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.
-         (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
@@ -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."
-  (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")
@@ -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."
-  (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/*")
@@ -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
-                      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.
@@ -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."
-  (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)
@@ -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 "'"))))
+      (when (and forest notmuch-show-single-message)
+       (setq forest (list (list (list 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.
-      (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)
@@ -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."
-  (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.
@@ -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)))
-    (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)))
 
@@ -2028,7 +2040,7 @@ to show, nil otherwise."
     (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)
@@ -2074,19 +2086,19 @@ message."
     (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.