]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch.el
Make search filters handle disjunctive queries.
[notmuch] / notmuch.el
index 6d7228fbbf026be083c67640cbda3dbe7b7c3a1e..c504f46de22aa89a3a3c68faf75db530f7b7205d 100644 (file)
@@ -111,7 +111,7 @@ pattern can still test against the entire line).")
 (defvar notmuch-show-marker-regexp "\f\\(message\\|header\\|body\\|attachment\\|part\\)[{}].*$")
 
 (defvar notmuch-show-id-regexp "\\(id:[^ ]*\\)")
-(defvar notmuch-show-depth-regexp " depth:\\([0-9]*\\) ")
+(defvar notmuch-show-depth-match-regexp " depth:\\([0-9]*\\).*match:\\([01]\\) ")
 (defvar notmuch-show-filename-regexp "filename:\\(.*\\)$")
 (defvar notmuch-show-tags-regexp "(\\([^)]*\\))$")
 
@@ -574,14 +574,19 @@ which this thread was originally shown."
   (force-window-update)
   (redisplay t))
 
-(define-button-type 'notmuch-button-invisibility-toggle-type 'action 'notmuch-toggle-invisible-action 'follow-link t)
+(define-button-type 'notmuch-button-invisibility-toggle-type
+  'action 'notmuch-toggle-invisible-action
+  'follow-link t
+  'face "default")
 (define-button-type 'notmuch-button-citation-toggle-type 'help-echo "mouse-1, RET: Show citation"
   :supertype 'notmuch-button-invisibility-toggle-type)
 (define-button-type 'notmuch-button-signature-toggle-type 'help-echo "mouse-1, RET: Show signature"
   :supertype 'notmuch-button-invisibility-toggle-type)
 (define-button-type 'notmuch-button-headers-toggle-type 'help-echo "mouse-1, RET: Show headers"
   :supertype 'notmuch-button-invisibility-toggle-type)
-(define-button-type 'notmuch-button-body-toggle-type 'help-echo "mouse-1, RET: Show message"
+(define-button-type 'notmuch-button-body-toggle-type
+  'help-echo "mouse-1, RET: Show message"
+  'face 'notmuch-message-summary-face
   :supertype 'notmuch-button-invisibility-toggle-type)
 
 (defun notmuch-show-markup-citations-region (beg end depth)
@@ -679,7 +684,20 @@ which this thread was originally shown."
               (notmuch-show-markup-part
                beg end depth mime-message))))))
 
-(defun notmuch-show-markup-body (depth btn)
+(defun notmuch-show-markup-body (depth match btn)
+  "Markup a message body, (indenting, buttonizing citations,
+etc.), and conditionally hiding the body itself if the message
+has been read and does not match the current search.
+
+DEPTH specifies the depth at which this message appears in the
+tree of the current thread, (the top-level messages have depth 0
+and each reply increases depth by 1). MATCH indicates whether
+this message is regarded as matching the current search. BTN is
+the button which is used to toggle the visibility of this
+message.
+
+When this function is called, point must be within the message, but
+before the delimiter marking the beginning of the body."
   (re-search-forward notmuch-show-body-begin-regexp)
   (forward-line)
   (let ((beg (point-marker)))
@@ -690,42 +708,52 @@ which this thread was originally shown."
         (overlay-put (make-overlay beg end)
                      'invisible invis-spec)
         (button-put btn 'invisibility-spec invis-spec)
-        (if (not (notmuch-show-message-unread-p))
+        (if (not (or (notmuch-show-message-unread-p) match))
             (add-to-invisibility-spec invis-spec)))
       (set-marker beg nil)
       (set-marker end nil)
       )))
 
 (defun notmuch-fontify-headers ()
-  (progn
-    (while (looking-at "[[:space:]]")
-      (forward-char))
-    (if (looking-at "[Tt]o:")
-       (progn
-         (overlay-put (make-overlay (point) (re-search-forward ":"))
-                       'face 'message-header-name)
-          (overlay-put (make-overlay (point) (re-search-forward ".*$"))
-                       'face 'message-header-to))
+  (while (looking-at "[[:space:]]")
+    (forward-char))
+  (if (looking-at "[Tt]o:")
+      (progn
+       (overlay-put (make-overlay (point) (re-search-forward ":"))
+                    'face 'message-header-name)
+       (overlay-put (make-overlay (point) (re-search-forward ".*$"))
+                    'face 'message-header-to))
     (if (looking-at "[B]?[Cc][Cc]:")
        (progn
          (overlay-put (make-overlay (point) (re-search-forward ":"))
-                       'face 'message-header-name)
-          (overlay-put (make-overlay (point) (re-search-forward ".*$"))
-                       'face 'message-header-cc))
-    (if (looking-at "[Ss]ubject:")
-       (progn
-         (overlay-put (make-overlay (point) (re-search-forward ":"))
-                       'face 'message-header-name)
-          (overlay-put (make-overlay (point) (re-search-forward ".*$"))
-                       'face 'message-header-subject))
-    (if (looking-at "[Ff]rom:")
-       (progn
-         (overlay-put (make-overlay (point) (re-search-forward ":"))
-                       'face 'message-header-name)
-          (overlay-put (make-overlay (point) (re-search-forward ".*$"))
-                       'face 'message-header-other))))))))
-
-(defun notmuch-show-markup-header (depth)
+                      'face 'message-header-name)
+         (overlay-put (make-overlay (point) (re-search-forward ".*$"))
+                      'face 'message-header-cc))
+      (if (looking-at "[Ss]ubject:")
+         (progn
+           (overlay-put (make-overlay (point) (re-search-forward ":"))
+                        'face 'message-header-name)
+           (overlay-put (make-overlay (point) (re-search-forward ".*$"))
+                        'face 'message-header-subject))
+       (if (looking-at "[Ff]rom:")
+           (progn
+             (overlay-put (make-overlay (point) (re-search-forward ":"))
+                          'face 'message-header-name)
+             (overlay-put (make-overlay (point) (re-search-forward ".*$"))
+                          'face 'message-header-other)))))))
+
+(defun notmuch-show-markup-header (message-begin depth)
+  "Buttonize and decorate faces in a message header.
+
+MESSAGE-BEGIN is the position of the absolute first character in
+the message (including all delimiters that will end up being
+invisible etc.). This is to allow a button to reliably extend to
+the beginning of the message even if point is positioned at an
+invisible character (such as the beginning of the buffer).
+
+DEPTH specifies the depth at which this message appears in the
+tree of the current thread, (the top-level messages have depth 0
+and each reply increases depth by 1)."
   (re-search-forward notmuch-show-header-begin-regexp)
   (forward-line)
   (let ((beg (point-marker))
@@ -738,8 +766,7 @@ which this thread was originally shown."
     (let ((end (point-marker)))
       (indent-rigidly beg end depth)
       (goto-char beg)
-      (overlay-put (make-overlay (line-beginning-position) summary-end) 'face 'notmuch-message-summary-face)
-      (setq btn (make-button (line-beginning-position) summary-end :type 'notmuch-button-body-toggle-type))
+      (setq btn (make-button message-begin summary-end :type 'notmuch-button-body-toggle-type))
       (forward-line)
       (add-to-invisibility-spec invis-spec)
       (overlay-put (make-overlay subject-end end)
@@ -763,12 +790,13 @@ which this thread was originally shown."
 
 (defun notmuch-show-markup-message ()
   (if (re-search-forward notmuch-show-message-begin-regexp nil t)
-      (progn
-       (re-search-forward notmuch-show-depth-regexp)
+      (let ((message-begin (match-beginning 0)))
+       (re-search-forward notmuch-show-depth-match-regexp)
        (let ((depth (string-to-number (buffer-substring (match-beginning 1) (match-end 1))))
+             (match (string= "1" (buffer-substring (match-beginning 2) (match-end 2))))
               (btn nil))
-         (setq btn (notmuch-show-markup-header depth))
-         (notmuch-show-markup-body depth btn)))
+         (setq btn (notmuch-show-markup-header message-begin depth))
+         (notmuch-show-markup-body depth match btn)))
     (goto-char (point-max))))
 
 (defun notmuch-show-hide-markers ()
@@ -928,7 +956,8 @@ The optional PARENT-BUFFER is the notmuch-search buffer from
 which this notmuch-show command was executed, (so that the next
 thread from that buffer can be show when done with this one)."
   (interactive "sNotmuch show: ")
-  (let ((buffer (get-buffer-create (concat "*notmuch-show-" thread-id "*"))))
+  (let ((query notmuch-search-query-string)
+       (buffer (get-buffer-create (concat "*notmuch-show-" thread-id "*"))))
     (switch-to-buffer buffer)
     (notmuch-show-mode)
     (set (make-local-variable 'notmuch-show-parent-buffer) parent-buffer)
@@ -940,30 +969,13 @@ thread from that buffer can be show when done with this one)."
       (erase-buffer)
       (goto-char (point-min))
       (save-excursion
-       (call-process notmuch-command nil t nil "show" "--entire-thread" thread-id)
+       (call-process notmuch-command nil t nil "show" "--entire-thread" thread-id "and (" query ")")
        (notmuch-show-markup-messages)
        )
       (run-hooks 'notmuch-show-hook)
-      ; Move straight to the first unread message
-      (if (not (notmuch-show-message-unread-p))
-         (progn
-           (notmuch-show-next-unread-message)
-           ; But if there are no unread messages, go back to the
-           ; beginning of the buffer, and open up the bodies of all
-           ; read message.
-           (if (not (notmuch-show-message-unread-p))
-               (progn
-                 (goto-char (point-min))
-                  (let ((btn (forward-button 1)))
-                    (while btn
-                      (if (button-has-type-p btn 'notmuch-button-body-toggle-type)
-                          (push-button))
-                      (condition-case err
-                          (setq btn (forward-button 1))
-                        (error (setq btn nil)))
-                    ))
-                  (goto-char (point-min))
-                  ))))
+      ; Move straight to the first open message
+      (if (not (notmuch-show-message-open-p))
+         (notmuch-show-next-open-message))
       )))
 
 (defvar notmuch-search-authors-width 40
@@ -1002,6 +1014,8 @@ thread from that buffer can be show when done with this one)."
 (defvar notmuch-search-oldest-first t
   "Show the oldest mail first in the search-mode")
 
+(defvar notmuch-search-disjunctive-regexp      "\\<[oO][rR]\\>")
+
 (defun notmuch-search-scroll-up ()
   "Move forward through search results by one window's worth."
   (interactive)
@@ -1340,7 +1354,8 @@ search."
 Runs a new search matching only messages that match both the
 current search results AND the additional query string provided."
   (interactive "sFilter search: ")
-  (notmuch-search (concat notmuch-search-query-string " and " query) notmuch-search-oldest-first))
+  (let ((grouped-query (if (string-match-p notmuch-search-disjunctive-regexp query) (concat "( " query " )") query)))
+    (notmuch-search (concat notmuch-search-query-string " and " grouped-query) notmuch-search-oldest-first)))
 
 (defun notmuch-search-filter-by-tag (tag)
   "Filter the current search results based on a single tag.