]> git.cworth.org Git - notmuch/blobdiff - emacs/notmuch-tree.el
emacs: new command notmuch-tree-filter
[notmuch] / emacs / notmuch-tree.el
index 713b00daef1003a629208320e61d8e75dac72077..f2938330b8877d35b9b61c1c708ca8077ab6986f 100644 (file)
@@ -1,4 +1,4 @@
-;;; notmuch-tree.el --- displaying notmuch forests
+;;; notmuch-tree.el --- displaying notmuch forests  -*- lexical-binding: t -*-
 ;;
 ;; Copyright © Carl Worth
 ;; Copyright © David Edmondson
@@ -24,8 +24,6 @@
 
 ;;; Code:
 
-(eval-when-compile (require 'cl-lib))
-
 (require 'mail-parse)
 
 (require 'notmuch-lib)
@@ -36,8 +34,9 @@
 (require 'notmuch-jump)
 
 (declare-function notmuch-search "notmuch"
-                 (&optional query oldest-first target-thread target-line))
-(declare-function notmuch-call-notmuch-process "notmuch" (&rest args))
+                 (&optional query oldest-first target-thread target-line
+                            no-display))
+(declare-function notmuch-call-notmuch-process "notmuch-lib" (&rest args))
 (declare-function notmuch-read-query "notmuch" (prompt))
 (declare-function notmuch-search-find-thread-id "notmuch" (&optional bare))
 (declare-function notmuch-search-find-subject "notmuch" ())
@@ -327,6 +326,7 @@ then NAME behaves like CMD."
     (define-key map [remap notmuch-mua-new-mail] 'notmuch-tree-new-mail)
     (define-key map [remap notmuch-jump-search]  'notmuch-tree-jump-search)
 
+    (define-key map "o" 'notmuch-tree-toggle-order)
     (define-key map "S" 'notmuch-search-from-tree-current-query)
     (define-key map "U" 'notmuch-unthreaded-from-tree-current-query)
     (define-key map "Z" 'notmuch-tree-from-unthreaded-current-query)
@@ -349,6 +349,7 @@ then NAME behaves like CMD."
     (define-key map "r" 'notmuch-tree-reply-sender)
     (define-key map "R" 'notmuch-tree-reply)
     (define-key map "V" 'notmuch-tree-view-raw-message)
+    (define-key map "l" 'notmuch-tree-filter)
 
     ;; The main tree view bindings
     (define-key map (kbd "RET") 'notmuch-tree-show-message)
@@ -401,9 +402,8 @@ Some useful entries are:
     (notmuch-tree-set-message-properties props)))
 
 (defun notmuch-tree-get-prop (prop &optional props)
-  (let ((props (or props
-                  (notmuch-tree-get-message-properties))))
-    (plist-get props prop)))
+  (plist-get (or props (notmuch-tree-get-message-properties))
+            prop))
 
 (defun notmuch-tree-set-tags (tags)
   "Set the tags of the current message."
@@ -424,7 +424,6 @@ Some useful entries are:
 
 (defun notmuch-tree-get-match ()
   "Return whether the current message is a match."
-  (interactive)
   (notmuch-tree-get-prop :match))
 
 ;;; Update display
@@ -578,7 +577,7 @@ NOT change the database."
       (with-selected-window notmuch-tree-message-window
        (let (;; Since we are only displaying one message do not indent.
              (notmuch-show-indent-messages-width 0)
-             (notmuch-show-only-matching-messages t)
+             (notmuch-show-single-message t)
              ;; Ensure that `pop-to-buffer-same-window' uses the
              ;; window we want it to use.
              (display-buffer-overriding-action
@@ -598,12 +597,13 @@ NOT change the database."
   "Show the current message (in whole window)."
   (interactive)
   (let ((id (notmuch-tree-get-message-id))
-       (inhibit-read-only t)
-       buffer)
+       (inhibit-read-only t))
     (when id
       ;; We close the window to kill off un-needed buffers.
       (notmuch-tree-close-message-window)
-      (notmuch-show id))))
+      ;; n-s-s-m is buffer local, so use inner let.
+      (let ((notmuch-show-single-message t))
+       (notmuch-show id)))))
 
 (defun notmuch-tree-show-message (arg)
   "Show the current message.
@@ -753,7 +753,8 @@ nil otherwise."
                         query-context
                         target
                         nil
-                        unthreaded)))
+                        unthreaded
+                        notmuch-search-oldest-first)))
 
 (defun notmuch-tree-thread-top ()
   (when (notmuch-tree-get-message-properties)
@@ -792,8 +793,7 @@ search results instead."
       (notmuch-tree-from-search-thread))))
 
 (defun notmuch-tree-next-thread (&optional previous)
-  "Move to the next thread in the current tree or parent search
-results
+  "Move to the next thread in the current tree or parent search results.
 
 If PREVIOUS is non-nil, move to the previous thread in the tree or
 search results instead."
@@ -803,14 +803,13 @@ search results instead."
     (notmuch-tree-next-thread-from-search previous)))
 
 (defun notmuch-tree-prev-thread ()
-  "Move to the previous thread in the current tree or parent search
-results"
+  "Move to the previous thread in the current tree or parent search results."
   (interactive)
   (notmuch-tree-next-thread t))
 
 (defun notmuch-tree-thread-mapcar (function)
-  "Iterate through all messages in the current thread
- and call FUNCTION for side effects."
+  "Call FUNCTION for each message in the current thread.
+FUNCTION is called for side effects only."
   (save-excursion
     (notmuch-tree-thread-top)
     (cl-loop collect (funcall function)
@@ -956,7 +955,8 @@ unchanged ADDRESS if parsing fails."
       (goto-char (point-max))
       (forward-line -1)
       (when notmuch-tree-open-target
-       (notmuch-tree-show-message-in)))))
+       (notmuch-tree-show-message-in)
+       (notmuch-tree-command-hook)))))
 
 (defun notmuch-tree-insert-tree (tree depth tree-status first last)
   "Insert the message tree TREE at depth DEPTH in the current thread.
@@ -1003,10 +1003,9 @@ message together with all its descendents."
 
 (defun notmuch-tree-insert-forest-thread (forest-thread)
   "Insert a single complete thread."
-  (let (tree-status)
-    ;; Reset at the start of each main thread.
-    (setq notmuch-tree-previous-subject nil)
-    (notmuch-tree-insert-thread forest-thread 0 tree-status)))
+  ;; Reset at the start of each main thread.
+  (setq notmuch-tree-previous-subject nil)
+  (notmuch-tree-insert-thread forest-thread 0 nil))
 
 (defun notmuch-tree-insert-forest (forest)
   "Insert a forest of threads.
@@ -1033,19 +1032,17 @@ Complete list of currently available key bindings:
   (setq buffer-read-only t)
   (setq truncate-lines t))
 
-(defun notmuch-tree-process-sentinel (proc msg)
+(defun notmuch-tree-process-sentinel (proc _msg)
   "Add a message to let user know when \"notmuch tree\" exits."
   (let ((buffer (process-buffer proc))
        (status (process-status proc))
-       (exit-status (process-exit-status proc))
-       (never-found-target-thread nil))
+       (exit-status (process-exit-status proc)))
     (when (memq status '(exit signal))
       (kill-buffer (process-get proc 'parse-buf))
       (when (buffer-live-p buffer)
        (with-current-buffer buffer
          (save-excursion
-           (let ((inhibit-read-only t)
-                 (atbob (bobp)))
+           (let ((inhibit-read-only t))
              (goto-char (point-max))
              (when (eq status 'signal)
                (insert "Incomplete search results (tree view process was killed).\n"))
@@ -1059,8 +1056,7 @@ Complete list of currently available key bindings:
   "Process and filter the output of \"notmuch show\" for tree view."
   (let ((results-buf (process-buffer proc))
        (parse-buf (process-get proc 'parse-buf))
-       (inhibit-read-only t)
-       done)
+       (inhibit-read-only t))
     (if (not (buffer-live-p results-buf))
        (delete-process proc)
       (with-current-buffer parse-buf
@@ -1071,7 +1067,8 @@ Complete list of currently available key bindings:
        (notmuch-sexp-parse-partial-list 'notmuch-tree-insert-forest-thread
                                         results-buf)))))
 
-(defun notmuch-tree-worker (basic-query &optional query-context target open-target unthreaded)
+(defun notmuch-tree-worker (basic-query &optional query-context target
+                                       open-target unthreaded oldest-first)
   "Insert the tree view of the search in the current buffer.
 
 This is is a helper function for notmuch-tree. The arguments are
@@ -1079,6 +1076,7 @@ the same as for the function notmuch-tree."
   (interactive)
   (notmuch-tree-mode)
   (add-hook 'post-command-hook #'notmuch-tree-command-hook t t)
+  (setq notmuch-search-oldest-first oldest-first)
   (setq notmuch-tree-unthreaded unthreaded)
   (setq notmuch-tree-basic-query basic-query)
   (setq notmuch-tree-query-context (if (or (string= query-context "")
@@ -1097,6 +1095,7 @@ the same as for the function notmuch-tree."
   (let* ((search-args (concat basic-query
                              (and query-context
                                   (concat " and (" query-context ")"))))
+        (sort-arg (if oldest-first "--sort=oldest-first" "--sort=newest-first"))
         (message-arg (if unthreaded "--unthreaded" "--entire-thread")))
     (when (equal (car (process-lines notmuch-command "count" search-args)) "0")
       (setq search-args basic-query))
@@ -1104,7 +1103,7 @@ the same as for the function notmuch-tree."
     (let ((proc (notmuch-start-notmuch
                 "notmuch-tree" (current-buffer) #'notmuch-tree-process-sentinel
                 "show" "--body=false" "--format=sexp" "--format-version=4"
-                message-arg search-args))
+                sort-arg message-arg search-args))
          ;; Use a scratch buffer to accumulate partial output.
          ;; This buffer will be killed by the sentinel, which
          ;; should be called no matter how the process dies.
@@ -1122,7 +1121,17 @@ the same as for the function notmuch-tree."
              ")")
     notmuch-tree-basic-query))
 
-(defun notmuch-tree (&optional query query-context target buffer-name open-target unthreaded parent-buffer)
+(defun notmuch-tree-toggle-order ()
+  "Toggle the current search order.
+
+This command toggles the sort order for the current search. The
+default sort order is defined by `notmuch-search-oldest-first'."
+  (interactive)
+  (setq notmuch-search-oldest-first (not notmuch-search-oldest-first))
+  (notmuch-tree-refresh-view))
+
+(defun notmuch-tree (&optional query query-context target buffer-name
+                              open-target unthreaded parent-buffer oldest-first)
   "Display threads matching QUERY in tree view.
 
 The arguments are:
@@ -1151,14 +1160,30 @@ The arguments are:
     (pop-to-buffer-same-window buffer))
   ;; Don't track undo information for this buffer
   (setq buffer-undo-list t)
-  (notmuch-tree-worker query query-context target open-target unthreaded)
+  (notmuch-tree-worker query query-context target open-target unthreaded oldest-first)
   (setq notmuch-tree-parent-buffer parent-buffer)
   (setq truncate-lines t))
 
-(defun notmuch-unthreaded (&optional query query-context target buffer-name open-target)
+(defun notmuch-unthreaded (&optional query query-context target buffer-name
+                                    open-target)
   (interactive)
   (notmuch-tree query query-context target buffer-name open-target t))
 
+(defun notmuch-tree-filter (query)
+  "Filter or LIMIT the current search results based on an additional query string.
+
+Runs a new tree search matching only messages that match both the
+current search results AND the additional query string provided."
+  (interactive (list (notmuch-read-query "Filter search: ")))
+  (let ((notmuch-show-process-crypto (notmuch-tree--message-process-crypto))
+       (grouped-query (notmuch-group-disjunctive-query-string query))
+       (grouped-original-query (notmuch-group-disjunctive-query-string
+                                (notmuch-tree-get-query))))
+    (notmuch-tree-close-message-window)
+    (notmuch-tree (if (string= grouped-original-query "*")
+                     grouped-query
+                   (concat grouped-original-query " and " grouped-query)))))
+
 ;;; _
 
 (provide 'notmuch-tree)