]> git.notmuchmail.org Git - notmuch/blobdiff - emacs/notmuch-show.el
emacs: A prefix argument to `notmuch-show' should invert the matching message behaviour.
[notmuch] / emacs / notmuch-show.el
index faa9f9b43b77ad34ec5f5b8beaa901e5fa34be5f..284e9cd1e8fad1471dc4860fb1131d2473851313 100644 (file)
@@ -126,6 +126,35 @@ indentation."
                 (const :tag "View interactively"
                        notmuch-show-interactively-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)
+
 (defmacro with-current-notmuch-show-message (&rest body)
   "Evaluate body with current buffer set to the text of current message"
   `(save-excursion
 (defmacro with-current-notmuch-show-message (&rest body)
   "Evaluate body with current buffer set to the text of current message"
   `(save-excursion
@@ -236,10 +265,12 @@ operation on the contents of the current buffer."
         (all (buffer-substring (notmuch-show-message-top)
                                (notmuch-show-message-bottom)))
 
         (all (buffer-substring (notmuch-show-message-top)
                                (notmuch-show-message-bottom)))
 
-        (props (notmuch-show-get-message-properties)))
+        (props (notmuch-show-get-message-properties))
+        (indenting notmuch-show-indent-content))
     (with-temp-buffer
       (insert all)
     (with-temp-buffer
       (insert all)
-      (indent-rigidly (point-min) (point-max) (- depth))
+      (if indenting
+         (indent-rigidly (point-min) (point-max) (- depth)))
       ;; Remove the original header.
       (goto-char (point-min))
       (re-search-forward "^$" (point-max) nil)
       ;; Remove the original header.
       (goto-char (point-min))
       (re-search-forward "^$" (point-max) nil)
@@ -611,7 +642,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
               (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)))
 
   (let ((inner-parts (plist-get part :content))
        (start (point)))
@@ -637,7 +668,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
                     (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)))
 
   (let ((inner-parts (plist-get part :content))
        (start (point)))
@@ -764,8 +795,6 @@ current buffer, if possible."
 
 ;; Helper for parts which are generally not included in the default
 ;; JSON output.
 
 ;; 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)))
 (defun notmuch-show-get-bodypart-internal (message-id part-number)
   (let ((args '("show" "--format=raw"))
        (part-arg (format "--part=%s" part-number)))
@@ -882,7 +911,8 @@ current buffer, if possible."
     (setq body-start (point-marker))
     ;; A blank line between the headers and the body.
     (insert "\n")
     (setq body-start (point-marker))
     ;; A blank line between the headers and the body.
     (insert "\n")
-    (notmuch-show-insert-body msg (plist-get msg :body) depth)
+    (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"))
     ;; Ensure that the body ends with a newline.
     (unless (bolp)
       (insert "\n"))
@@ -890,7 +920,8 @@ current buffer, if possible."
     (setq content-end (point-marker))
 
     ;; Indent according to the depth in the thread.
     (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))
 
 
     (setq message-end (point-max-marker))
 
@@ -919,11 +950,40 @@ current buffer, if possible."
     ;; criteria.
     (notmuch-show-message-visible msg (plist-get msg :match))))
 
     ;; criteria.
     (notmuch-show-message-visible msg (plist-get msg :match))))
 
+(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 t))
+
+(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 t))
+
+(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 t))
+
 (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)))
 (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)
+    (if (or (not notmuch-show-elide-non-matching-messages)
+           (plist-get msg :match))
+       (notmuch-show-insert-msg msg depth))
     (notmuch-show-insert-thread replies (1+ depth))))
 
 (defun notmuch-show-insert-thread (thread depth)
     (notmuch-show-insert-thread replies (1+ depth))))
 
 (defun notmuch-show-insert-thread (thread depth)
@@ -934,15 +994,6 @@ current buffer, if possible."
   "Insert the forest of threads FOREST."
   (mapc (lambda (thread) (notmuch-show-insert-thread thread 0)) forest))
 
   "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)
-
 (defun notmuch-show-buttonise-links (start end)
   "Buttonise URLs and mail addresses between START and END.
 
 (defun notmuch-show-buttonise-links (start end)
   "Buttonise URLs and mail addresses between START and END.
 
@@ -962,7 +1013,7 @@ a corresponding notmuch search."
                        'face goto-address-mail-face))))
 
 ;;;###autoload
                        'face goto-address-mail-face))))
 
 ;;;###autoload
-(defun notmuch-show (thread-id &optional parent-buffer query-context buffer-name crypto-switch)
+(defun notmuch-show (thread-id &optional parent-buffer query-context buffer-name)
   "Run \"notmuch show\" with the given thread ID and display results.
 
 The optional PARENT-BUFFER is the notmuch-search buffer from
   "Run \"notmuch show\" with the given thread ID and display results.
 
 The optional PARENT-BUFFER is the notmuch-search buffer from
@@ -977,46 +1028,47 @@ non-nil.
 The optional BUFFER-NAME provides the name of the buffer in
 which the message thread is shown. If it is nil (which occurs
 when the command is called interactively) the argument to the
 The optional BUFFER-NAME provides the name of the buffer in
 which the message thread is shown. If it is nil (which occurs
 when the command is called interactively) the argument to the
-function is used.
-
-The optional CRYPTO-SWITCH toggles the value of the
-notmuch-crypto-process-mime customization variable for this show
-buffer."
+function is used."
   (interactive "sNotmuch show: ")
   (interactive "sNotmuch show: ")
-  (let* ((process-crypto (if crypto-switch
-                            (not notmuch-crypto-process-mime)
-                          notmuch-crypto-process-mime)))
-    (notmuch-show-worker thread-id parent-buffer query-context buffer-name process-crypto)))
-
-(defun notmuch-show-worker (thread-id parent-buffer query-context buffer-name process-crypto)
-  (let* ((buffer-name (generate-new-buffer-name
-                      (or buffer-name
-                          (concat "*notmuch-" thread-id "*"))))
-        (buffer (get-buffer-create buffer-name))
-        (inhibit-read-only t))
-    (switch-to-buffer buffer)
+  (let ((buffer-name (generate-new-buffer-name
+                     (or buffer-name
+                         (concat "*notmuch-" thread-id "*")))))
+    (switch-to-buffer (get-buffer-create buffer-name))
+    ;; Set the default value for `notmuch-show-process-crypto' in this
+    ;; buffer.
+    (setq notmuch-show-process-crypto notmuch-crypto-process-mime)
+    ;; Set the default value for
+    ;; `notmuch-show-elide-non-matching-messages' in this buffer. If
+    ;; there is a prefix argument, invert the default.
+    (setq notmuch-show-elide-non-matching-messages notmuch-show-only-matching-messages)
+    (if current-prefix-arg
+       (setq notmuch-show-elide-non-matching-messages (not notmuch-show-elide-non-matching-messages)))
+
+    (setq notmuch-show-thread-id thread-id
+         notmuch-show-parent-buffer parent-buffer
+         notmuch-show-query-context query-context)
+    (notmuch-show-worker)))
+
+(defun notmuch-show-worker ()
+  (let ((inhibit-read-only t))
+
     (notmuch-show-mode)
     ;; Don't track undo information for this buffer
     (set 'buffer-undo-list t)
 
     (notmuch-show-mode)
     ;; Don't track undo information for this buffer
     (set 'buffer-undo-list t)
 
-    (setq notmuch-show-thread-id thread-id)
-    (setq notmuch-show-parent-buffer parent-buffer)
-    (setq notmuch-show-query-context query-context)
-    (setq notmuch-show-buffer-name buffer-name)
-    (setq notmuch-show-process-crypto process-crypto)
-
     (erase-buffer)
     (goto-char (point-min))
     (save-excursion
     (erase-buffer)
     (goto-char (point-min))
     (save-excursion
-      (let* ((basic-args (list thread-id))
-            (args (if query-context
-                      (append (list "\'") basic-args (list "and (" query-context ")\'"))
+      (let* ((basic-args (list notmuch-show-thread-id))
+            (args (if notmuch-show-query-context
+                      (append (list "\'") basic-args
+                              (list "and (" notmuch-show-query-context ")\'"))
                     (append (list "\'") basic-args (list "\'")))))
        (notmuch-show-insert-forest (notmuch-query-get-threads args))
        ;; If the query context reduced the results to nothing, run
        ;; the basic query.
        (when (and (eq (buffer-size) 0)
                     (append (list "\'") basic-args (list "\'")))))
        (notmuch-show-insert-forest (notmuch-query-get-threads args))
        ;; If the query context reduced the results to nothing, run
        ;; the basic query.
        (when (and (eq (buffer-size) 0)
-                  query-context)
+                  notmuch-show-query-context)
          (notmuch-show-insert-forest
           (notmuch-query-get-threads basic-args))))
 
          (notmuch-show-insert-forest
           (notmuch-query-get-threads basic-args))))
 
@@ -1033,21 +1085,54 @@ buffer."
 
     (notmuch-show-mark-read)))
 
 
     (notmuch-show-mark-read)))
 
-(defun notmuch-show-refresh-view (&optional crypto-switch)
-  "Refresh the current view (with crypto switch if prefix given).
+(defun notmuch-show-capture-state ()
+  "Capture the state of the current buffer.
 
 
-Kills the current buffer and reruns notmuch show with the same
-thread id.  If a prefix is given, crypto processing is toggled."
-  (interactive "P")
-  (let ((thread-id notmuch-show-thread-id)
-       (parent-buffer notmuch-show-parent-buffer)
-       (query-context notmuch-show-query-context)
-       (buffer-name notmuch-show-buffer-name)
-       (process-crypto (if crypto-switch
-                           (not notmuch-show-process-crypto)
-                         notmuch-show-process-crypto)))
-    (notmuch-kill-this-buffer)
-    (notmuch-show-worker thread-id parent-buffer query-context buffer-name process-crypto)))
+This includes:
+ - the list of open messages,
+ - the current message."
+  (list (notmuch-show-get-message-id) (notmuch-show-get-message-ids-for-open-messages)))
+
+(defun notmuch-show-apply-state (state)
+  "Apply STATE to the current buffer.
+
+This includes:
+ - opening the messages previously opened,
+ - closing all other messages,
+ - moving to the correct current message."
+  (let ((current (car state))
+       (open (cadr state)))
+
+    ;; Open those that were open.
+    (goto-char (point-min))
+    (loop do (notmuch-show-message-visible (notmuch-show-get-message-properties)
+                                          (member (notmuch-show-get-message-id) open))
+         until (not (notmuch-show-goto-message-next)))
+
+    ;; Go to the previously open message.
+    (goto-char (point-min))
+    (unless (loop if (string= current (notmuch-show-get-message-id))
+                 return t
+                 until (not (notmuch-show-goto-message-next)))
+      (goto-char (point-min))
+      (message "Previously current message not found."))
+    (notmuch-show-message-adjust)))
+
+(defun notmuch-show-refresh-view (&optional retain-state)
+  "Refresh the current view.
+
+Refreshes the current view, observing changes in display
+preferences. If RETAIN-STATE is non-nil then the state of the
+buffer is stored and re-applied after the refresh."
+  (interactive)
+  (let ((inhibit-read-only t)
+       state)
+    (if retain-state
+       (setq state (notmuch-show-capture-state)))
+    (erase-buffer)
+    (notmuch-show-worker)
+    (if state
+       (notmuch-show-apply-state state))))
 
 (defvar notmuch-show-stash-map
   (let ((map (make-sparse-keymap)))
 
 (defvar notmuch-show-stash-map
   (let ((map (make-sparse-keymap)))
@@ -1087,9 +1172,10 @@ thread id.  If a prefix is given, crypto processing is toggled."
        (define-key map "*" 'notmuch-show-tag-all)
        (define-key map "-" 'notmuch-show-remove-tag)
        (define-key map "+" 'notmuch-show-add-tag)
        (define-key map "*" 'notmuch-show-tag-all)
        (define-key map "-" 'notmuch-show-remove-tag)
        (define-key map "+" 'notmuch-show-add-tag)
-       (define-key map "x" 'notmuch-show-archive-thread-then-exit)
-       (define-key map "a" 'notmuch-show-archive-message-then-next)
+       (define-key map "X" 'notmuch-show-archive-thread-then-exit)
+       (define-key map "x" 'notmuch-show-archive-message-then-next-or-exit)
        (define-key map "A" 'notmuch-show-archive-thread-then-next)
        (define-key map "A" 'notmuch-show-archive-thread-then-next)
+       (define-key map "a" 'notmuch-show-archive-message-then-next-or-next-thread)
        (define-key map "N" 'notmuch-show-next-message)
        (define-key map "P" 'notmuch-show-previous-message)
        (define-key map "n" 'notmuch-show-next-open-message)
        (define-key map "N" 'notmuch-show-next-message)
        (define-key map "P" 'notmuch-show-previous-message)
        (define-key map "n" 'notmuch-show-next-open-message)
@@ -1099,6 +1185,10 @@ thread id.  If a prefix is given, crypto processing is toggled."
        (define-key map (kbd "M-RET") 'notmuch-show-open-or-close-all)
        (define-key map (kbd "RET") 'notmuch-show-toggle-message)
        (define-key map "#" 'notmuch-show-print-message)
        (define-key map (kbd "M-RET") 'notmuch-show-open-or-close-all)
        (define-key map (kbd "RET") 'notmuch-show-toggle-message)
        (define-key map "#" 'notmuch-show-print-message)
+       (define-key map "!" 'notmuch-show-toggle-elide-non-matching)
+       (define-key map "$" 'notmuch-show-toggle-process-crypto)
+       (define-key map "<" 'notmuch-show-toggle-thread-indentation)
+       (define-key map "t" 'toggle-truncate-lines)
        map)
       "Keymap for \"notmuch show\" buffers.")
 (fset 'notmuch-show-mode-map notmuch-show-mode-map)
        map)
       "Keymap for \"notmuch show\" buffers.")
 (fset 'notmuch-show-mode-map notmuch-show-mode-map)
@@ -1452,7 +1542,8 @@ thread, navigate to the next thread in the parent search buffer."
 
 If a prefix argument is given and this is the last open message
 in the thread, navigate to the next thread in the parent search
 
 If a prefix argument is given and this is the last open message
 in the thread, navigate to the next thread in the parent search
-buffer."
+buffer. Return t if there was a next open message in the thread
+to show, nil otherwise."
   (interactive "P")
   (let (r)
     (while (and (setq r (notmuch-show-goto-message-next))
   (interactive "P")
   (let (r)
     (while (and (setq r (notmuch-show-goto-message-next))
@@ -1463,7 +1554,8 @@ buffer."
          (notmuch-show-message-adjust))
       (if pop-at-end
          (notmuch-show-next-thread)
          (notmuch-show-message-adjust))
       (if pop-at-end
          (notmuch-show-next-thread)
-       (goto-char (point-max))))))
+       (goto-char (point-max))))
+    r))
 
 (defun notmuch-show-previous-open-message ()
   "Show the previous open message."
 
 (defun notmuch-show-previous-open-message ()
   "Show the previous open message."
@@ -1623,7 +1715,7 @@ added."
   (interactive "P")
   (let ((parent-buffer notmuch-show-parent-buffer))
     (notmuch-kill-this-buffer)
   (interactive "P")
   (let ((parent-buffer notmuch-show-parent-buffer))
     (notmuch-kill-this-buffer)
-    (when parent-buffer
+    (when (buffer-live-p parent-buffer)
       (switch-to-buffer parent-buffer)
       (notmuch-search-next-thread)
       (if show-next
       (switch-to-buffer parent-buffer)
       (notmuch-search-next-thread)
       (if show-next
@@ -1671,12 +1763,25 @@ removed)."
   (let ((op (if unarchive "+" "-")))
     (notmuch-show-tag-message (concat op "inbox"))))
 
   (let ((op (if unarchive "+" "-")))
     (notmuch-show-tag-message (concat op "inbox"))))
 
-(defun notmuch-show-archive-message-then-next ()
-  "Archive the current message, then show the next open message in the current thread."
+(defun notmuch-show-archive-message-then-next-or-exit ()
+  "Archive the current message, then show the next open message in the current thread.
+
+If at the last open message in the current thread, then exit back
+to search results."
   (interactive)
   (notmuch-show-archive-message)
   (notmuch-show-next-open-message t))
 
   (interactive)
   (notmuch-show-archive-message)
   (notmuch-show-next-open-message t))
 
+(defun notmuch-show-archive-message-then-next-or-next-thread ()
+  "Archive the current message, then show the next open message in the current thread.
+
+If at the last open message in the current thread, then show next
+thread from search."
+  (interactive)
+  (notmuch-show-archive-message)
+  (unless (notmuch-show-next-open-message)
+    (notmuch-show-next-thread t)))
+
 (defun notmuch-show-stash-cc ()
   "Copy CC field of current message to kill-ring."
   (interactive)
 (defun notmuch-show-stash-cc ()
   "Copy CC field of current message to kill-ring."
   (interactive)