]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch.el
added syntax files for search and show screens
[notmuch] / notmuch.el
index 815368957d4d11607fc19e4856568e5faee96638..551048adf03e6910428696582585c1ce13111316 100644 (file)
@@ -61,9 +61,6 @@
     ; overlays-at to query and manipulate the current overlay.
     (define-key map "a" 'notmuch-show-archive-thread)
     (define-key map "A" 'notmuch-show-mark-read-then-archive-thread)
-    (define-key map "b" 'notmuch-show-toggle-body-read-visible)
-    (define-key map "c" 'notmuch-show-toggle-citations-visible)
-    (define-key map "h" 'notmuch-show-toggle-headers-visible)
     (define-key map "m" 'message-mail)
     (define-key map "n" 'notmuch-show-next-message)
     (define-key map "N" 'notmuch-show-mark-read-then-next-open-message)
@@ -72,7 +69,6 @@
     (define-key map (kbd "C-p") 'notmuch-show-previous-line)
     (define-key map "q" 'kill-this-buffer)
     (define-key map "r" 'notmuch-show-reply)
-    (define-key map "s" 'notmuch-show-toggle-signatures-visible)
     (define-key map "v" 'notmuch-show-view-all-mime-parts)
     (define-key map "w" 'notmuch-show-view-raw-message)
     (define-key map "x" 'kill-this-buffer)
@@ -82,6 +78,8 @@
     (define-key map " " 'notmuch-show-advance-marking-read-and-archiving)
     (define-key map "|" 'notmuch-show-pipe-message)
     (define-key map "?" 'describe-mode)
+    (define-key map (kbd "TAB") 'notmuch-show-next-button)
+    (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
     map)
   "Keymap for \"notmuch show\" buffers.")
 (fset 'notmuch-show-mode-map notmuch-show-mode-map)
@@ -479,6 +477,16 @@ which this thread was originally shown."
        (if last
            (notmuch-show-archive-thread))))))
 
+(defun notmuch-show-next-button ()
+  "Advance point to the next button in the buffer."
+  (interactive)
+  (goto-char (button-start (next-button (point)))))
+
+(defun notmuch-show-previous-button ()
+  "Move point back to the previous button in the buffer."
+  (interactive)
+  (goto-char (button-start (previous-button (point)))))
+
 (defun notmuch-toggle-invisible-action (cite-button)
   (let ((invis-spec (button-get button 'invisibility-spec)))
         (if (invisible-p invis-spec)
@@ -488,6 +496,16 @@ 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-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"
+  :supertype 'notmuch-button-invisibility-toggle-type)
+
 (defun notmuch-show-markup-citations-region (beg end depth)
   (goto-char beg)
   (beginning-of-line)
@@ -503,22 +521,15 @@ which this thread was originally shown."
                   (invis-spec (make-symbol "notmuch-citation-region")))
               (add-to-invisibility-spec invis-spec)
              (overlay-put overlay 'invisible invis-spec)
-              (let (
-                    (p (point))
+              (let ((p (point))
                     (cite-button-text
                      (concat "["  (number-to-string (count-lines beg-sub (point)))
-                             "-line citation.]"))
-                    )
+                             "-line citation.]")))
                 (goto-char (- beg-sub 1))
                 (insert (concat "\n" indent))
-                (let ((cite-button (insert-button cite-button-text)))
-                  (button-put cite-button 'invisibility-spec invis-spec)
-                  (button-put cite-button 'action 'notmuch-toggle-invisible-action)
-                  (button-put cite-button 'follow-link t)
-                  (button-put cite-button 'help-echo
-                              "mouse-1, RET: Show citation")
-
-                  )
+                (insert-button cite-button-text
+                               'invisibility-spec invis-spec
+                               :type 'notmuch-button-citation-toggle-type)
                 (insert "\n")
                 (goto-char (+ (length cite-button-text) p))
               ))))
@@ -534,16 +545,11 @@ which this thread was originally shown."
                   
                     (goto-char (- beg-sub 1))
                     (insert (concat "\n" indent))
-                    (let ((sig-button (insert-button 
-                                       (concat "[" (number-to-string sig-lines)
-                                         "-line signature.]"))))
-                      (button-put sig-button 'invisibility-spec invis-spec)
-                      (button-put sig-button 'action
-                                  'notmuch-toggle-invisible-action)
-                      (button-put sig-button 'follow-link t)
-                      (button-put sig-button 'help-echo
-                                  "mouse-1, RET: Show signature")
-                      )
+                    (let ((sig-button-text (concat "[" (number-to-string sig-lines)
+                                                   "-line signature.]")))
+                      (insert-button sig-button-text 'invisibility-spec invis-spec
+                                     :type 'notmuch-button-signature-toggle-type)
+                     )
                     (insert "\n")
                     (goto-char end))))))
       (forward-line))))
@@ -572,16 +578,19 @@ which this thread was originally shown."
     (while (< (point) end)
       (notmuch-show-markup-part beg end depth))))
 
-(defun notmuch-show-markup-body (depth)
+(defun notmuch-show-markup-body (depth btn)
   (re-search-forward notmuch-show-body-begin-regexp)
   (forward-line)
   (let ((beg (point-marker)))
     (re-search-forward notmuch-show-body-end-regexp)
     (let ((end (copy-marker (match-beginning 0))))
       (notmuch-show-markup-parts-region beg end depth)
-      (if (not (notmuch-show-message-unread-p))
-         (overlay-put (make-overlay beg end)
-                      'invisible 'notmuch-show-body-read))
+      (let ((invis-spec (make-symbol "notmuch-show-body-read")))
+        (overlay-put (make-overlay beg end)
+                     'invisible invis-spec)
+        (button-put btn 'invisibility-spec invis-spec)
+        (if (not (notmuch-show-message-unread-p))
+            (add-to-invisibility-spec invis-spec)))
       (set-marker beg nil)
       (set-marker end nil)
       )))
@@ -589,11 +598,14 @@ which this thread was originally shown."
 (defun notmuch-show-markup-header (depth)
   (re-search-forward notmuch-show-header-begin-regexp)
   (forward-line)
-  (let ((beg (point-marker)))
+  (let ((beg (point-marker))
+        (btn nil))
     (end-of-line)
     ; Inverse video for subject
     (overlay-put (make-overlay beg (point)) 'face '(:inverse-video t))
-    (forward-line 2)
+    (setq btn (make-button beg (point) :type 'notmuch-button-body-toggle-type))
+    (forward-line 1)
+    (end-of-line)
     (let ((beg-hidden (point-marker)))
       (re-search-forward notmuch-show-header-end-regexp)
       (beginning-of-line)
@@ -607,22 +619,31 @@ which this thread was originally shown."
           (forward-line)
           )
        (indent-rigidly beg end depth)
-       (overlay-put (make-overlay beg-hidden end)
-                    'invisible 'notmuch-show-header)
+        (let ((invis-spec (make-symbol "notmuch-show-header")))
+          (add-to-invisibility-spec (cons invis-spec t))
+          (overlay-put (make-overlay beg-hidden end)
+                       'invisible invis-spec)
+          (goto-char beg)
+          (forward-line)
+          (make-button (line-beginning-position) (line-end-position)
+                        'invisibility-spec (cons invis-spec t)
+                        :type 'notmuch-button-headers-toggle-type))
         (goto-char end)
         (insert "\n")
        (set-marker beg nil)
        (set-marker beg-hidden nil)
        (set-marker end nil)
-       ))))
+       ))
+    btn))
 
 (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 ((depth (string-to-number (buffer-substring (match-beginning 1) (match-end 1)))))
-         (notmuch-show-markup-header depth)
-         (notmuch-show-markup-body depth)))
+       (let ((depth (string-to-number (buffer-substring (match-beginning 1) (match-end 1))))
+              (btn nil))
+         (setq btn (notmuch-show-markup-header depth))
+         (notmuch-show-markup-body depth btn)))
     (goto-char (point-max))))
 
 (defun notmuch-show-hide-markers ()
@@ -642,50 +663,6 @@ which this thread was originally shown."
       (notmuch-show-markup-message)))
   (notmuch-show-hide-markers))
 
-(defun notmuch-show-toggle-citations-visible ()
-  "Toggle visibility of citations"
-  (interactive)
-  (if notmuch-show-citations-visible
-      (add-to-invisibility-spec 'notmuch-show-citation)
-    (remove-from-invisibility-spec 'notmuch-show-citation))
-  (set 'notmuch-show-citations-visible (not notmuch-show-citations-visible))
-  ; Need to force the redisplay for some reason
-  (force-window-update)
-  (redisplay t))
-
-(defun notmuch-show-toggle-signatures-visible ()
-  "Toggle visibility of signatures"
-  (interactive)
-  (if notmuch-show-signatures-visible
-      (add-to-invisibility-spec 'notmuch-show-signature)
-    (remove-from-invisibility-spec 'notmuch-show-signature))
-  (set 'notmuch-show-signatures-visible (not notmuch-show-signatures-visible))
-  ; Need to force the redisplay for some reason
-  (force-window-update)
-  (redisplay t))
-
-(defun notmuch-show-toggle-headers-visible ()
-  "Toggle visibility of header fields"
-  (interactive)
-  (if notmuch-show-headers-visible
-      (add-to-invisibility-spec 'notmuch-show-header)
-    (remove-from-invisibility-spec 'notmuch-show-header))
-  (set 'notmuch-show-headers-visible (not notmuch-show-headers-visible))
-  ; Need to force the redisplay for some reason
-  (force-window-update)
-  (redisplay t))
-
-(defun notmuch-show-toggle-body-read-visible ()
-  "Toggle visibility of message bodies of read messages"
-  (interactive)
-  (if notmuch-show-body-read-visible
-      (add-to-invisibility-spec 'notmuch-show-body-read)
-    (remove-from-invisibility-spec 'notmuch-show-body-read))
-  (set 'notmuch-show-body-read-visible (not notmuch-show-body-read-visible))
-  ; Need to force the redisplay for some reason
-  (force-window-update)
-  (redisplay t))
-
 ;;;###autoload
 (defun notmuch-show-mode ()
   "Major mode for viewing a thread with notmuch.
@@ -714,14 +691,6 @@ view, (remove the \"inbox\" tag from each), with
 \\{notmuch-show-mode-map}"
   (interactive)
   (kill-all-local-variables)
-  (set (make-local-variable 'notmuch-show-headers-visible) t)
-  (notmuch-show-toggle-headers-visible)
-  (set (make-local-variable 'notmuch-show-body-read-visible) t)
-  (notmuch-show-toggle-body-read-visible)
-  (set (make-local-variable 'notmuch-show-citations-visible) t)
-  (notmuch-show-toggle-citations-visible)
-  (set (make-local-variable 'notmuch-show-signatures-visible) t)
-  (notmuch-show-toggle-signatures-visible)
   (add-to-invisibility-spec 'notmuch-show-marker)
   (use-local-map notmuch-show-mode-map)
   (setq major-mode 'notmuch-show-mode
@@ -790,7 +759,16 @@ thread from that buffer can be show when done with this one)."
            (if (not (notmuch-show-message-unread-p))
                (progn
                  (goto-char (point-min))
-                 (notmuch-show-toggle-body-read-visible)))))
+                  (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)))
+                    ))
+                  (beginning-of-buffer)
+                  ))))
       )))
 
 (defvar notmuch-search-authors-width 40
@@ -828,6 +806,7 @@ thread from that buffer can be show when done with this one)."
 (defvar notmuch-search-query-string)
 (defvar notmuch-search-oldest-first)
 
+
 (defun notmuch-search-scroll-up ()
   "Scroll up, moving point to last message in thread if at end."
   (interactive)
@@ -889,11 +868,8 @@ global search.
   (setq buffer-read-only t))
 
 (defun notmuch-search-find-thread-id ()
-  (save-excursion
-    (beginning-of-line)
-    (let ((beg (point)))
-      (re-search-forward "thread:[a-fA-F0-9]*" nil t)
-      (filter-buffer-substring beg (point)))))
+  "Return the thread for the current thread"
+  (get-text-property (point) 'notmuch-search-thread-id))
 
 (defun notmuch-search-markup-this-thread-id ()
   (beginning-of-line)
@@ -990,6 +966,54 @@ This function advances point to the next line when finished."
   (notmuch-search-remove-tag "inbox")
   (forward-line))
 
+(defun notmuch-search-process-sentinel (proc msg)
+  "Add a message to let user know when \"notmuch search\" exits"
+  (let ((buffer (process-buffer proc))
+       (status (process-status proc))
+       (exit-status (process-exit-status proc)))
+    (if (memq status '(exit signal))
+       (if (buffer-live-p buffer)
+           (with-current-buffer buffer
+             (save-excursion
+               (let ((inhibit-read-only t))
+                 (goto-char (point-max))
+                 (if (eq status 'signal)
+                     (insert "Incomplete search results (search process was killed).\n"))
+                 (if (eq status 'exit)
+                     (progn
+                       (insert "End of search results.")
+                       (if (not (= exit-status 0))
+                           (insert (format " (process returned %d)" exit-status)))
+                       (insert "\n"))))))))))
+
+(defun notmuch-search-process-filter (proc string)
+  "Process and filter the output of \"notmuch search\""
+  (let ((buffer (process-buffer proc)))
+    (if (buffer-live-p buffer)
+       (with-current-buffer buffer
+         (save-excursion
+           (let ((line 0)
+                 (more t)
+                 (inhibit-read-only t))
+             (while more
+               (if (string-match "^\\(thread:[0-9A-Fa-f]*\\) \\(.*\\) \\(\\[[0-9/]*\\]\\) \\([^:]*\\); \\(.*\\) (\\([^()]*\\))$" string line)
+                   (let* ((thread-id (match-string 1 string))
+                          (date (match-string 2 string))
+                          (count (match-string 3 string))
+                          (authors (match-string 4 string))
+                          (authors-length (length authors))
+                          (subject (match-string 5 string))
+                          (tags (match-string 6 string)))
+                     (if (> authors-length 40)
+                         (set 'authors (concat (substring authors 0 (- 40 3)) "...")))
+                     (goto-char (point-max))
+                     (let ((beg (point-marker)))
+                       (insert (format "%s %-7s %-40s %s (%s)\n" date count authors subject tags))
+                       (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id))
+                     (set 'line (match-end 0)))
+                 (set 'more nil))))))
+      (delete-process proc))))
+
 (defun notmuch-search (query &optional oldest-first)
   "Run \"notmuch search\" with the given query string and display results."
   (interactive "sNotmuch search: ")
@@ -1006,11 +1030,12 @@ This function advances point to the next line when finished."
       (erase-buffer)
       (goto-char (point-min))
       (save-excursion
-       (if oldest-first
-           (call-process notmuch-command nil t nil "search" "--sort=oldest-first" query)
-         (call-process notmuch-command nil t nil "search" "--sort=newest-first" query))
-       (notmuch-search-markup-thread-ids)
-       ))
+       (let ((proc (start-process-shell-command
+                    "notmuch-search" buffer notmuch-command "search"
+                    (if oldest-first "--sort=oldest-first" "--sort=newest-first")
+                    (shell-quote-argument query))))
+         (set-process-sentinel proc 'notmuch-search-process-sentinel)
+         (set-process-filter proc 'notmuch-search-process-filter))))
     (run-hooks 'notmuch-search-hook)))
 
 (defun notmuch-search-refresh-view ()
@@ -1074,4 +1099,85 @@ current search results AND that are tagged with the given tag."
 
 (setq mail-user-agent 'message-user-agent)
 
+(defvar notmuch-folder-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "n" 'next-line)
+    (define-key map "p" 'previous-line)
+    (define-key map "x" 'kill-this-buffer)
+    (define-key map "q" 'kill-this-buffer)
+    (define-key map "s" 'notmuch-search)
+    (define-key map (kbd "RET") 'notmuch-folder-show-search)
+    (define-key map "<" 'beginning-of-buffer)
+    (define-key map "=" 'notmuch-folder)
+    (define-key map "?" 'describe-mode)
+    (define-key map [mouse-1] 'notmuch-folder-show-search)
+    map)
+  "Keymap for \"notmuch folder\" buffers.")
+
+(fset 'notmuch-folder-mode-map notmuch-folder-mode-map)
+
+(defcustom notmuch-folders (quote (("inbox" . "tag:inbox") ("unread" . "tag:unread")))
+  "List of searches for the notmuch folder view"
+  :type '(alist :key-type (string) :value-type (string))
+  :group 'notmuch)
+
+(defun notmuch-folder-mode ()
+  "Major mode for showing notmuch 'folders'.
+
+This buffer contains a list of messages counts returned by a
+customizable set of searches of your email archives. Each line
+in the buffer shows the search terms and the resulting message count.
+
+Pressing RET on any line opens a search window containing the
+results for the search terms in that line.
+
+\\{notmuch-folder-mode-map}"
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map 'notmuch-folder-mode-map)
+  (setq truncate-lines t)
+  (hl-line-mode 1)
+  (setq major-mode 'notmuch-folder-mode
+       mode-name "notmuch-folder")
+  (setq buffer-read-only t))
+
+(defun notmuch-folder-add (folders)
+  (if folders
+      (let ((name (car (car folders)))
+           (inhibit-read-only t)
+           (search (cdr (car folders))))
+       (insert name)
+       (indent-to 16 1)
+       (call-process notmuch-command nil t nil "count" search)
+       (notmuch-folder-add (cdr folders)))))
+
+(defun notmuch-folder-find-name ()
+  (save-excursion
+    (beginning-of-line)
+    (let ((beg (point)))
+      (forward-word)
+      (filter-buffer-substring beg (point)))))
+
+(defun notmuch-folder-show-search (&optional folder)
+  "Show a search window for the search related to the specified folder."
+  (interactive)
+  (if (null folder)
+      (setq folder (notmuch-folder-find-name)))
+  (let ((search (assoc folder notmuch-folders)))
+    (if search
+       (notmuch-search (cdr search) t))))
+
+(defun notmuch-folder ()
+  "Show the notmuch folder view and update the displayed counts."
+  (interactive)
+  (let ((buffer (get-buffer-create "*notmuch-folders*")))
+    (switch-to-buffer buffer)
+    (let ((inhibit-read-only t)
+         (n (line-number-at-pos)))
+      (erase-buffer)
+      (notmuch-folder-mode)
+      (notmuch-folder-add notmuch-folders)
+      (goto-char (point-min))
+      (goto-line n))))
+
 (provide 'notmuch)