]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch.el
notmuch.el: convert sparse keymap to a list in notmuch-substitute-one-command-key...
[notmuch] / notmuch.el
index 94a6384419ad1a46cb744e6820f603c56c1b2ef1..82471b57dc9b4cabb7271e73b61e4a80a4ada3a9 100644 (file)
@@ -55,7 +55,6 @@
   (let ((map (make-sparse-keymap)))
     (define-key map "?" 'notmuch-help)
     (define-key map "q" 'kill-this-buffer)
   (let ((map (make-sparse-keymap)))
     (define-key map "?" 'notmuch-help)
     (define-key map "q" 'kill-this-buffer)
-    (define-key map "x" 'kill-this-buffer)
     (define-key map (kbd "C-p") 'notmuch-show-previous-line)
     (define-key map (kbd "C-n") 'notmuch-show-next-line)
     (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
     (define-key map (kbd "C-p") 'notmuch-show-previous-line)
     (define-key map (kbd "C-n") 'notmuch-show-next-line)
     (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
     (define-key map "w" 'notmuch-show-save-attachments)
     (define-key map "V" 'notmuch-show-view-raw-message)
     (define-key map "v" 'notmuch-show-view-all-mime-parts)
     (define-key map "w" 'notmuch-show-save-attachments)
     (define-key map "V" 'notmuch-show-view-raw-message)
     (define-key map "v" 'notmuch-show-view-all-mime-parts)
+    (define-key map "b" 'notmuch-show-toggle-current-body)
+    (define-key map "h" 'notmuch-show-toggle-current-header)
     (define-key map "-" 'notmuch-show-remove-tag)
     (define-key map "+" 'notmuch-show-add-tag)
     (define-key map "-" 'notmuch-show-remove-tag)
     (define-key map "+" 'notmuch-show-add-tag)
+    (define-key map "X" 'notmuch-show-mark-read-then-archive-then-exit)
+    (define-key map "x" 'notmuch-show-archive-thread-then-exit)
     (define-key map "A" 'notmuch-show-mark-read-then-archive-thread)
     (define-key map "a" 'notmuch-show-archive-thread)
     (define-key map "p" 'notmuch-show-previous-message)
     (define-key map "A" 'notmuch-show-mark-read-then-archive-thread)
     (define-key map "a" 'notmuch-show-archive-thread)
     (define-key map "p" 'notmuch-show-previous-message)
@@ -91,9 +94,27 @@ for indentation at the beginning of the line. But notmuch will
 move past the indentation when testing this pattern, (so that the
 pattern can still test against the entire line).")
 
 move past the indentation when testing this pattern, (so that the
 pattern can still test against the entire line).")
 
+(defvar notmuch-show-signature-button-format
+  "[ %d-line signature. Click/Enter to toggle visibility. ]"
+  "String used to construct button text for hidden signatures
+
+Can use up to one integer format parameter, i.e. %d")
+
+(defvar notmuch-show-citation-button-format
+  "[ %d more citation lines. Click/Enter to toggle visibility. ]"
+  "String used to construct button text for hidden citations.
+
+Can use up to one integer format parameter, i.e. %d")
+
 (defvar notmuch-show-signature-lines-max 12
   "Maximum length of signature that will be hidden by default.")
 
 (defvar notmuch-show-signature-lines-max 12
   "Maximum length of signature that will be hidden by default.")
 
+(defvar notmuch-show-citation-lines-prefix 4
+  "Always show at least this many lines of a citation.
+
+If there is one more line, show that, otherwise collapse
+remaining lines into a button.")
+
 (defvar notmuch-command "notmuch"
   "Command to run the notmuch binary.")
 
 (defvar notmuch-command "notmuch"
   "Command to run the notmuch binary.")
 
@@ -110,8 +131,10 @@ 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-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-filename-regexp "filename:\\(.*\\)$")
+(defvar notmuch-show-contentype-regexp "Content-type: \\(.*\\)")
+
 (defvar notmuch-show-tags-regexp "(\\([^)]*\\))$")
 
 (defvar notmuch-show-parent-buffer nil)
 (defvar notmuch-show-tags-regexp "(\\([^)]*\\))$")
 
 (defvar notmuch-show-parent-buffer nil)
@@ -202,6 +225,62 @@ Unlike builtin `previous-line' this version accepts no arguments."
     (re-search-forward notmuch-show-tags-regexp)
     (split-string (buffer-substring (match-beginning 1) (match-end 1)))))
 
     (re-search-forward notmuch-show-tags-regexp)
     (split-string (buffer-substring (match-beginning 1) (match-end 1)))))
 
+(defun notmuch-show-get-bcc ()
+  "Return BCC address(es) of current message"
+  (notmuch-show-get-header-field 'bcc))
+
+(defun notmuch-show-get-cc ()
+  "Return CC address(es) of current message"
+  (notmuch-show-get-header-field 'cc))
+
+(defun notmuch-show-get-date ()
+  "Return Date of current message"
+  (notmuch-show-get-header-field 'date))
+
+(defun notmuch-show-get-from ()
+  "Return From address of current message"
+  (notmuch-show-get-header-field 'from))
+
+(defun notmuch-show-get-subject ()
+  "Return Subject of current message"
+  (notmuch-show-get-header-field 'subject))
+
+(defun notmuch-show-get-to ()
+  "Return To address(es) of current message"
+  (notmuch-show-get-header-field 'to))
+
+(defun notmuch-show-get-header-field (name)
+  "Retrieve the header field NAME from the current message.
+NAME should be a symbol, in lower case, as returned by
+mail-header-extract-no-properties"
+  (let* ((result (assoc name (notmuch-show-get-header)))
+        (val (and result (cdr result))))
+    val))
+
+(defun notmuch-show-get-header ()
+  "Retrieve and parse the header from the current message. Returns an alist with of (header . value)
+where header is a symbol and value is a string.  The summary from notmuch-show is returned as the
+pseudoheader summary"
+  (require 'mailheader)
+  (save-excursion
+    (beginning-of-line)
+    (if (not (looking-at notmuch-show-message-begin-regexp))
+       (re-search-backward notmuch-show-message-begin-regexp))
+    (re-search-forward (concat notmuch-show-header-begin-regexp "\n[[:space:]]*\\(.*\\)\n"))
+    (let* ((summary (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
+         (beg (point)))
+      (re-search-forward notmuch-show-header-end-regexp)
+      (let ((text (buffer-substring beg (match-beginning 0))))
+       (with-temp-buffer
+         (insert text)
+         (goto-char (point-min))
+         (while (looking-at "\\([[:space:]]*\\)[A-Za-z][-A-Za-z0-9]*:")
+           (delete-region (match-beginning 1) (match-end 1))
+           (forward-line)
+           )
+         (goto-char (point-min))
+         (cons (cons 'summary summary) (mail-header-extract-no-properties)))))))
+
 (defun notmuch-show-add-tag (&rest toadd)
   "Add a tag to the current message."
   (interactive
 (defun notmuch-show-add-tag (&rest toadd)
   "Add a tag to the current message."
   (interactive
@@ -273,6 +352,18 @@ buffer."
   (interactive)
   (notmuch-show-archive-thread-maybe-mark-read nil))
 
   (interactive)
   (notmuch-show-archive-thread-maybe-mark-read nil))
 
+(defun notmuch-show-archive-thread-then-exit ()
+  "Archive each message in thread, then exit back to search results."
+  (interactive)
+  (notmuch-show-archive-thread)
+  (kill-this-buffer))
+
+(defun notmuch-show-mark-read-then-archive-then-exit ()
+  "Remove unread tags from thread, then archive and exit to search results."
+  (interactive)
+  (notmuch-show-mark-read-then-archive-thread)
+  (kill-this-buffer))
+
 (defun notmuch-show-view-raw-message ()
   "View the raw email of the current message."
   (interactive)
 (defun notmuch-show-view-raw-message ()
   "View the raw email of the current message."
   (interactive)
@@ -286,13 +377,28 @@ buffer."
          (with-current-buffer buf
            (insert-file-contents filename nil nil nil t)
            ,@body)
          (with-current-buffer buf
            (insert-file-contents filename nil nil nil t)
            ,@body)
-        (kill-buffer buf)))))
+        (kill-buffer buf)))))
 
 (defun notmuch-show-view-all-mime-parts ()
   "Use external viewers to view all attachments from the current message."
   (interactive)
   (with-current-notmuch-show-message
 
 (defun notmuch-show-view-all-mime-parts ()
   "Use external viewers to view all attachments from the current message."
   (interactive)
   (with-current-notmuch-show-message
-   (mm-display-parts (mm-dissect-buffer))))
+   ; We ovverride the mm-inline-media-tests to indicate which message
+   ; parts are already sufficiently handled by the original
+   ; presentation of the message in notmuch-show mode. These parts
+   ; will be inserted directly into the temporary buffer of
+   ; with-current-notmuch-show-message and silently discarded.
+   ;
+   ; Any MIME part not explicitly mentioned here will be handled by an
+   ; external viewer as configured in the various mailcap files.
+   (let ((mm-inline-media-tests '(
+                                 ("text/.*" ignore identity)
+                                 ("application/pgp-signature" ignore identity)
+                                 ("multipart/alternative" ignore identity)
+                                 ("multipart/mixed" ignore identity)
+                                 ("multipart/related" ignore identity)
+                                )))
+     (mm-display-parts (mm-dissect-buffer)))))
 
 (defun notmuch-foreach-mime-part (function mm-handle)
   (cond ((stringp (car mm-handle))
 
 (defun notmuch-foreach-mime-part (function mm-handle)
   (cond ((stringp (car mm-handle))
@@ -309,7 +415,9 @@ buffer."
      (lambda (p)
        (let ((disposition (mm-handle-disposition p)))
          (and (listp disposition)
      (lambda (p)
        (let ((disposition (mm-handle-disposition p)))
          (and (listp disposition)
-              (equal (car disposition) "attachment")
+              (or (equal (car disposition) "attachment")
+                  (and (equal (car disposition) "inline")
+                       (assq 'filename disposition)))
               (incf count))))
      mm-handle)
     count))
               (incf count))))
      mm-handle)
     count))
@@ -319,7 +427,9 @@ buffer."
    (lambda (p)
      (let ((disposition (mm-handle-disposition p)))
        (and (listp disposition)
    (lambda (p)
      (let ((disposition (mm-handle-disposition p)))
        (and (listp disposition)
-            (equal (car disposition) "attachment")
+            (or (equal (car disposition) "attachment")
+                (and (equal (car disposition) "inline")
+                     (assq 'filename disposition)))
             (or (not queryp)
                 (y-or-n-p
                  (concat "Save '" (cdr (assq 'filename disposition)) "' ")))
             (or (not queryp)
                 (y-or-n-p
                  (concat "Save '" (cdr (assq 'filename disposition)) "' ")))
@@ -378,7 +488,7 @@ point either forward or backward to the next visible character
 when a command ends with point on an invisible character).
 
 Emits an error if point is not within a valid message, (that is
 when a command ends with point on an invisible character).
 
 Emits an error if point is not within a valid message, (that is
-not pattern of `notmuch-show-message-begin-regexp' could be found
+no pattern of `notmuch-show-message-begin-regexp' could be found
 by searching backward)."
   (beginning-of-line)
   (if (not (looking-at notmuch-show-message-begin-regexp))
 by searching backward)."
   (beginning-of-line)
   (if (not (looking-at notmuch-show-message-begin-regexp))
@@ -395,22 +505,35 @@ by searching backward)."
       (not (re-search-forward notmuch-show-message-begin-regexp nil t)))))
 
 (defun notmuch-show-message-unread-p ()
       (not (re-search-forward notmuch-show-message-begin-regexp nil t)))))
 
 (defun notmuch-show-message-unread-p ()
-  "Preficate testing whether current message is unread."
+  "Predicate testing whether current message is unread."
   (member "unread" (notmuch-show-get-tags)))
 
   (member "unread" (notmuch-show-get-tags)))
 
+(defun notmuch-show-message-open-p ()
+  "Predicate testing whether current message is open (body is visible)."
+  (let ((btn (previous-button (point) t)))
+    (while (not (button-has-type-p btn 'notmuch-button-body-toggle-type))
+      (setq btn (previous-button (button-start btn))))
+    (not (invisible-p (button-get btn 'invisibility-spec)))))
+
 (defun notmuch-show-next-message ()
   "Advance to the beginning of the next message in the buffer.
 
 Moves to the last visible character of the current message if
 (defun notmuch-show-next-message ()
   "Advance to the beginning of the next message in the buffer.
 
 Moves to the last visible character of the current message if
-already on the last message in the buffer."
+already on the last message in the buffer.
+
+Returns nil if already on the last message in the buffer."
   (interactive)
   (notmuch-show-move-to-current-message-summary-line)
   (if (re-search-forward notmuch-show-message-begin-regexp nil t)
   (interactive)
   (notmuch-show-move-to-current-message-summary-line)
   (if (re-search-forward notmuch-show-message-begin-regexp nil t)
-      (notmuch-show-move-to-current-message-summary-line)
+      (progn
+       (notmuch-show-move-to-current-message-summary-line)
+       (recenter 0)
+       t)
     (goto-char (- (point-max) 1))
     (while (point-invisible-p)
     (goto-char (- (point-max) 1))
     (while (point-invisible-p)
-      (backward-char)))
-  (recenter 0))
+      (backward-char))
+    (recenter 0)
+    nil))
 
 (defun notmuch-show-find-next-message ()
   "Returns the position of the next message in the buffer.
 
 (defun notmuch-show-find-next-message ()
   "Returns the position of the next message in the buffer.
@@ -438,14 +561,9 @@ there are no more unread messages past the current point."
       (notmuch-show-next-message)))
 
 (defun notmuch-show-next-open-message ()
       (notmuch-show-next-message)))
 
 (defun notmuch-show-next-open-message ()
-  "Advance to the next message which is not hidden.
-
-If read messages are currently hidden, advance to the next unread
-message. Otherwise, advance to the next message."
-  (if (or (memq 'notmuch-show-body-read buffer-invisibility-spec)
-         (assq 'notmuch-show-body-read buffer-invisibility-spec))
-      (notmuch-show-next-unread-message)
-    (notmuch-show-next-message)))
+  "Advance to the next open message (that is, body is not invisible)."
+  (while (and (notmuch-show-next-message)
+             (not (notmuch-show-message-open-p)))))
 
 (defun notmuch-show-previous-message ()
   "Backup to the beginning of the previous message in the buffer.
 
 (defun notmuch-show-previous-message ()
   "Backup to the beginning of the previous message in the buffer.
@@ -537,12 +655,12 @@ which this thread was originally shown."
 (defun notmuch-show-next-button ()
   "Advance point to the next button in the buffer."
   (interactive)
 (defun notmuch-show-next-button ()
   "Advance point to the next button in the buffer."
   (interactive)
-  (goto-char (button-start (next-button (point)))))
+  (forward-button 1))
 
 (defun notmuch-show-previous-button ()
   "Move point back to the previous button in the buffer."
   (interactive)
 
 (defun notmuch-show-previous-button ()
   "Move point back to the previous button in the buffer."
   (interactive)
-  (goto-char (button-start (previous-button (point)))))
+  (backward-button 1))
 
 (defun notmuch-toggle-invisible-action (cite-button)
   (let ((invis-spec (button-get cite-button 'invisibility-spec)))
 
 (defun notmuch-toggle-invisible-action (cite-button)
   (let ((invis-spec (button-get cite-button 'invisibility-spec)))
@@ -553,112 +671,181 @@ which this thread was originally shown."
   (force-window-update)
   (redisplay t))
 
   (force-window-update)
   (redisplay t))
 
-(define-button-type 'notmuch-button-invisibility-toggle-type 'action 'notmuch-toggle-invisible-action 'follow-link t)
+(defun notmuch-show-toggle-current-body ()
+  "Toggle the display of the current message body."
+  (interactive)
+  (save-excursion
+    (notmuch-show-move-to-current-message-summary-line)
+    (unless (button-at (point))
+      (notmuch-show-next-button))
+    (push-button))
+  )
+
+(defun notmuch-show-toggle-current-header ()
+  "Toggle the display of the current message header."
+  (interactive)
+  (save-excursion
+    (notmuch-show-move-to-current-message-summary-line)
+    (forward-line)
+    (unless (button-at (point))
+      (notmuch-show-next-button))
+    (push-button))
+  )
+
+(define-button-type 'notmuch-button-invisibility-toggle-type
+  'action 'notmuch-toggle-invisible-action
+  'follow-link t
+  'face 'font-lock-comment-face)
 (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-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)
 
   :supertype 'notmuch-button-invisibility-toggle-type)
 
+(defun notmuch-show-citation-regexp (depth)
+  "Build a regexp for matching citations at a given DEPTH (indent)"
+  (let ((line-regexp (format "[[:space:]]\\{%d\\}>.*\n" depth)))
+    (concat "\\(?:^" line-regexp
+           "\\(?:[[:space:]]*\n" line-regexp
+           "\\)?\\)+")))
+
+(defun notmuch-show-region-to-button (beg end type prefix button-text)
+  "Auxilary function to do the actual making of overlays and buttons
+
+BEG and END are buffer locations. TYPE should a string, either
+\"citation\" or \"signature\". PREFIX is some arbitrary text to
+insert before the button, probably for indentation.  BUTTON-TEXT
+is what to put on the button."
+
+;; This uses some slightly tricky conversions between strings and
+;; symbols because of the way the button code works. Note that
+;; replacing intern-soft with make-symbol will cause this to fail,
+;; since the newly created symbol has no plist.
+
+  (let ((overlay (make-overlay beg end))
+       (invis-spec (make-symbol (concat "notmuch-" type "-region")))
+       (button-type (intern-soft (concat "notmuch-button-"
+                                         type "-toggle-type"))))
+    (add-to-invisibility-spec invis-spec)
+    (overlay-put overlay 'invisible invis-spec)
+    (goto-char (1+ end))
+    (save-excursion
+      (goto-char (1- beg))
+      (insert prefix)
+      (insert-button button-text
+                    'invisibility-spec invis-spec
+                    :type button-type)
+      )))
+
+
 (defun notmuch-show-markup-citations-region (beg end depth)
 (defun notmuch-show-markup-citations-region (beg end depth)
-  (goto-char beg)
-  (beginning-of-line)
-  (while (< (point) end)
-    (let ((beg-sub (point-marker))
-         (indent (make-string depth ? ))
-         (citation "[[:space:]]*>"))
-      (if (looking-at citation)
-         (progn
-           (while (looking-at citation)
-             (forward-line))
-           (let ((overlay (make-overlay beg-sub (point)))
-                  (invis-spec (make-symbol "notmuch-citation-region")))
-              (add-to-invisibility-spec invis-spec)
-             (overlay-put overlay 'invisible invis-spec)
-              (let ((p (point))
-                    (cite-button-text
-                     (concat "["  (number-to-string (count-lines beg-sub (point)))
-                             "-line citation.]")))
-                (goto-char (- beg-sub 1))
-                (insert (concat "\n" indent))
-                (insert-button cite-button-text
-                               'invisibility-spec invis-spec
-                               :type 'notmuch-button-citation-toggle-type)
-                (insert "\n")
-                (goto-char (+ (length cite-button-text) p))
-              ))))
-      (move-to-column depth)
-      (if (looking-at notmuch-show-signature-regexp)
-         (let ((sig-lines (- (count-lines beg-sub end) 1)))
-           (if (<= sig-lines notmuch-show-signature-lines-max)
-               (progn
-                  (let ((invis-spec (make-symbol "notmuch-signature-region")))
-                    (add-to-invisibility-spec invis-spec)
-                    (overlay-put (make-overlay beg-sub end)
-                                 'invisible invis-spec)
-                  
-                    (goto-char (- beg-sub 1))
-                    (insert (concat "\n" indent))
-                    (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))))
-
-(defun notmuch-show-markup-part (beg end depth mime-message)
+  "Markup citations, and up to one signature in the given region"
+  ;; it would be nice if the untabify was not required, but
+  ;; that would require notmuch to indent with spaces.
+  (untabify beg end)
+  (let ((citation-regexp (notmuch-show-citation-regexp depth))
+       (signature-regexp (concat (format "^[[:space:]]\\{%d\\}" depth)
+                                 notmuch-show-signature-regexp))
+       (indent (concat "\n" (make-string depth ? ))))
+    (goto-char beg)
+    (beginning-of-line)
+    (while (and (< (point) end)
+               (re-search-forward citation-regexp end t))
+      (let* ((cite-start (match-beginning 0))
+            (cite-end  (match-end 0))
+            (cite-lines (count-lines cite-start cite-end)))
+       (when (> cite-lines (1+ notmuch-show-citation-lines-prefix))
+         (goto-char cite-start)
+         (forward-line notmuch-show-citation-lines-prefix)
+         (notmuch-show-region-to-button
+          (point) cite-end
+          "citation"
+          indent
+          (format notmuch-show-citation-button-format
+                  (- cite-lines notmuch-show-citation-lines-prefix))
+          ))))
+    (if (and (< (point) end)
+            (re-search-forward signature-regexp end t))
+       (let* ((sig-start (match-beginning 0))
+              (sig-end (match-end 0))
+              (sig-lines (1- (count-lines sig-start end))))
+         (if (<= sig-lines notmuch-show-signature-lines-max)
+             (notmuch-show-region-to-button
+              sig-start
+              end
+              "signature"
+              indent
+              (format notmuch-show-signature-button-format sig-lines)
+              ))))))
+
+(defun notmuch-show-markup-part (beg end depth)
   (if (re-search-forward notmuch-show-part-begin-regexp nil t)
       (progn
   (if (re-search-forward notmuch-show-part-begin-regexp nil t)
       (progn
-        (if (eq mime-message nil)
-            (let ((filename (notmuch-show-get-filename)))
-              (with-temp-buffer
-                (insert-file-contents filename nil nil nil t)
-                (setq mime-message (mm-dissect-buffer)))))
-       (forward-line)
-       (let ((part-beg (point-marker)))
-         (re-search-forward notmuch-show-part-end-regexp)
-
-         (let ((part-end (copy-marker (match-beginning 0))))
-           (goto-char part-end)
-           (if (not (bolp))
-               (insert "\n"))
-           (indent-rigidly part-beg part-end depth)
-            (save-excursion
-              (goto-char part-beg)
-              (forward-line -1)
-              (beginning-of-line)
-              (let ((handle-type (mm-handle-type mime-message))
-                    mime-type)
-                (if (sequencep (car handle-type))
-                    (setq mime-type (car handle-type))
-                  (setq mime-type (car (car (cdr handle-type))))
-                  )
-                (if (equal mime-type "text/html")
-                    (mm-display-part mime-message))))
-
-           (notmuch-show-markup-citations-region part-beg part-end depth)
-           ; Advance to the next part (if any) (so the outer loop can
-           ; determine whether we've left the current message.
-           (if (re-search-forward notmuch-show-part-begin-regexp nil t)
-               (beginning-of-line)))))
-    (goto-char end))
-  mime-message)
+        (let (mime-message mime-type)
+          (save-excursion
+            (re-search-forward notmuch-show-contentype-regexp end t)
+            (setq mime-type (car (split-string (buffer-substring
+                                                (match-beginning 1) (match-end 1))))))
+
+          (if (equal mime-type "text/html")
+              (let ((filename (notmuch-show-get-filename)))
+                (with-temp-buffer
+                  (insert-file-contents filename nil nil nil t)
+                  (setq mime-message (mm-dissect-buffer)))))
+          (forward-line)
+          (let ((beg (point-marker)))
+            (re-search-forward notmuch-show-part-end-regexp)
+            (let ((end (copy-marker (match-beginning 0))))
+              (goto-char end)
+              (if (not (bolp))
+                  (insert "\n"))
+              (indent-rigidly beg end depth)
+              (if (not (eq mime-message nil))
+                  (save-excursion
+                    (goto-char beg)
+                    (forward-line -1)
+                    (let ((handle-type (mm-handle-type mime-message))
+                          mime-type)
+                      (if (sequencep (car handle-type))
+                          (setq mime-type (car handle-type))
+                        (setq mime-type (car (car (cdr handle-type))))
+                        )
+                      (if (equal mime-type "text/html")
+                          (mm-display-part mime-message))))
+                )
+              (notmuch-show-markup-citations-region beg end depth)
+              ; Advance to the next part (if any) (so the outer loop can
+              ; determine whether we've left the current message.
+              (if (re-search-forward notmuch-show-part-begin-regexp nil t)
+                  (beginning-of-line)))))
+        (goto-char end))
+    (goto-char end)))
 
 (defun notmuch-show-markup-parts-region (beg end depth)
   (save-excursion
     (goto-char beg)
 
 (defun notmuch-show-markup-parts-region (beg end depth)
   (save-excursion
     (goto-char beg)
-    (let (mime-message)
-      (while (< (point) end)
-        (setq mime-message
-              (notmuch-show-markup-part
-               beg end depth mime-message))))))
-
-(defun notmuch-show-markup-body (depth btn)
+    (while (< (point) end)
+      (notmuch-show-markup-part beg end depth))))
+
+(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)))
   (re-search-forward notmuch-show-body-begin-regexp)
   (forward-line)
   (let ((beg (point-marker)))
@@ -669,86 +856,95 @@ which this thread was originally shown."
         (overlay-put (make-overlay beg end)
                      'invisible invis-spec)
         (button-put btn 'invisibility-spec invis-spec)
         (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)
       )))
             (add-to-invisibility-spec invis-spec)))
       (set-marker beg nil)
       (set-marker end nil)
       )))
+
 (defun notmuch-fontify-headers ()
 (defun notmuch-fontify-headers ()
-  (progn
-    (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 ":"))
     (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))
   (re-search-forward notmuch-show-header-begin-regexp)
   (forward-line)
   (let ((beg (point-marker))
+       (summary-end (copy-marker (line-beginning-position 2)))
+       (subject-end (copy-marker (line-end-position 2)))
+       (invis-spec (make-symbol "notmuch-show-header"))
         (btn nil))
         (btn nil))
-    (end-of-line)
-    ; Inverse video for subject
-    (overlay-put (make-overlay beg (point)) 'face '(:inverse-video t))
-    (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)
-      (let ((end (point-marker)))
-        (goto-char beg)
-        (forward-line)
-        (while (looking-at "[A-Za-z][-A-Za-z0-9]*:")
-          (beginning-of-line)
-         (notmuch-fontify-headers)
-          (forward-line)
-          )
-       (indent-rigidly beg end depth)
-        (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))
+    (re-search-forward notmuch-show-header-end-regexp)
+    (beginning-of-line)
+    (let ((end (point-marker)))
+      (indent-rigidly beg end depth)
+      (goto-char beg)
+      (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)
+                  'invisible invis-spec)
+      (make-button (line-beginning-position) subject-end
+                  'invisibility-spec invis-spec
+                  :type 'notmuch-button-headers-toggle-type)
+      (while (looking-at "[[:space:]]*[A-Za-z][-A-Za-z0-9]*:")
+       (beginning-of-line)
+       (notmuch-fontify-headers)
+       (forward-line)
+       )
+      (goto-char end)
+      (insert "\n")
+      (set-marker beg nil)
+      (set-marker summary-end nil)
+      (set-marker subject-end nil)
+      (set-marker end nil)
+      )
+  btn))
 
 (defun notmuch-show-markup-message ()
   (if (re-search-forward notmuch-show-message-begin-regexp nil t)
 
 (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))))
        (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))
               (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 ()
     (goto-char (point-max))))
 
 (defun notmuch-show-hide-markers ()
@@ -804,8 +1000,12 @@ For a mouse binding, return nil."
     (if (mouse-event-p key)
        nil
       (if (keymapp action)
     (if (mouse-event-p key)
        nil
       (if (keymapp action)
-         (let ((substitute (apply-partially 'notmuch-substitute-one-command-key-with-prefix (notmuch-prefix-key-description key))))
-           (mapconcat substitute (cdr action) "\n"))
+         (let ((substitute (apply-partially 'notmuch-substitute-one-command-key-with-prefix (notmuch-prefix-key-description key)))
+               (as-list))
+           (map-keymap (lambda (a b)
+                         (push (cons a b) as-list))
+                       action)
+           (mapconcat substitute as-list "\n"))
        (concat prefix (format-kbd-macro (vector key))
                "\t"
                (notmuch-documentation-first-line action))))))
        (concat prefix (format-kbd-macro (vector key))
                "\t"
                (notmuch-documentation-first-line action))))))
@@ -901,12 +1101,15 @@ All currently available key bindings:
          (lambda()
            (hl-line-mode 1) ))
 
          (lambda()
            (hl-line-mode 1) ))
 
-(defun notmuch-show (thread-id &optional parent-buffer)
+(defun notmuch-show (thread-id &optional parent-buffer query-context)
   "Run \"notmuch show\" with the given thread ID and display results.
 
 The optional PARENT-BUFFER is the notmuch-search buffer from
 which this notmuch-show command was executed, (so that the next
   "Run \"notmuch show\" with the given thread ID and display results.
 
 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)."
+thread from that buffer can be show when done with this one).
+
+The optional QUERY-CONTEXT is a notmuch search term. Only messages from the thread
+matching this search term are shown if non-nil. "
   (interactive "sNotmuch show: ")
   (let ((buffer (get-buffer-create (concat "*notmuch-show-" thread-id "*"))))
     (switch-to-buffer buffer)
   (interactive "sNotmuch show: ")
   (let ((buffer (get-buffer-create (concat "*notmuch-show-" thread-id "*"))))
     (switch-to-buffer buffer)
@@ -920,30 +1123,17 @@ thread from that buffer can be show when done with this one)."
       (erase-buffer)
       (goto-char (point-min))
       (save-excursion
       (erase-buffer)
       (goto-char (point-min))
       (save-excursion
-       (call-process notmuch-command nil t nil "show" thread-id)
+       (let* ((basic-args (list notmuch-command nil t nil "show" "--entire-thread" thread-id))
+               (args (if query-context (append basic-args (list "and (" query-context ")")) basic-args)))
+         (apply 'call-process args)
+         (when (and (eq (buffer-size) 0) query-context)
+           (apply 'call-process basic-args)))
        (notmuch-show-markup-messages)
        )
       (run-hooks 'notmuch-show-hook)
        (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
       )))
 
 (defvar notmuch-search-authors-width 40
@@ -982,6 +1172,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-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)
 (defun notmuch-search-scroll-up ()
   "Move forward through search results by one window's worth."
   (interactive)
@@ -1025,6 +1217,12 @@ thread from that buffer can be show when done with this one)."
   (interactive)
   (goto-char (point-min)))
 
   (interactive)
   (goto-char (point-min)))
 
+(defface notmuch-message-summary-face
+ '((((class color) (background light)) (:background "#f0f0f0"))
+   (((class color) (background dark)) (:background "#303030")))
+ "Face for the single-line message summary in notmuch-show-mode."
+ :group 'notmuch)
+
 (defface notmuch-tag-face
   '((((class color)
       (background dark))
 (defface notmuch-tag-face
   '((((class color)
       (background dark))
@@ -1081,12 +1279,11 @@ Complete list of currently available key bindings:
   (if (not notmuch-tag-face-alist)
       (add-to-list 'notmuch-search-font-lock-keywords (list
                "(\\([^)]*\\))$" '(1  'notmuch-tag-face)))
   (if (not notmuch-tag-face-alist)
       (add-to-list 'notmuch-search-font-lock-keywords (list
                "(\\([^)]*\\))$" '(1  'notmuch-tag-face)))
-    (progn
-  (setq notmuch-search-tags (mapcar 'car notmuch-tag-face-alist))
-  (loop for notmuch-search-tag  in notmuch-search-tags
-    do (add-to-list 'notmuch-search-font-lock-keywords (list
-                               (concat "([^)]*\\(" notmuch-search-tag "\\)[^)]*)$")
-                   `(1  ,(cdr (assoc notmuch-search-tag notmuch-tag-face-alist))))))))
+    (let ((notmuch-search-tags (mapcar 'car notmuch-tag-face-alist)))
+      (loop for notmuch-search-tag  in notmuch-search-tags
+           do (add-to-list 'notmuch-search-font-lock-keywords (list
+                       (concat "([^)]*\\(" notmuch-search-tag "\\)[^)]*)$")
+                       `(1  ,(cdr (assoc notmuch-search-tag notmuch-tag-face-alist))))))))
   (set (make-local-variable 'font-lock-defaults)
          '(notmuch-search-font-lock-keywords t)))
 
   (set (make-local-variable 'font-lock-defaults)
          '(notmuch-search-font-lock-keywords t)))
 
@@ -1094,12 +1291,20 @@ Complete list of currently available key bindings:
   "Return the thread for the current thread"
   (get-text-property (point) 'notmuch-search-thread-id))
 
   "Return the thread for the current thread"
   (get-text-property (point) 'notmuch-search-thread-id))
 
+(defun notmuch-search-find-authors ()
+  "Return the authors for the current thread"
+  (get-text-property (point) 'notmuch-search-authors))
+
+(defun notmuch-search-find-subject ()
+  "Return the subject for the current thread"
+  (get-text-property (point) 'notmuch-search-subject))
+
 (defun notmuch-search-show-thread ()
   "Display the currently selected thread."
   (interactive)
   (let ((thread-id (notmuch-search-find-thread-id)))
     (if (> (length thread-id) 0)
 (defun notmuch-search-show-thread ()
   "Display the currently selected thread."
   (interactive)
   (let ((thread-id (notmuch-search-find-thread-id)))
     (if (> (length thread-id) 0)
-       (notmuch-show thread-id (current-buffer))
+       (notmuch-show thread-id (current-buffer) notmuch-search-query-string)
       (error "End of search results"))))
 
 (defun notmuch-search-reply-to-thread ()
       (error "End of search results"))))
 
 (defun notmuch-search-reply-to-thread ()
@@ -1154,7 +1359,7 @@ The tag is added to messages in the currently selected thread
 which match the current search terms."
   (interactive
    (list (notmuch-select-tag-with-completion "Tag to add: ")))
 which match the current search terms."
   (interactive
    (list (notmuch-select-tag-with-completion "Tag to add: ")))
-  (notmuch-call-notmuch-process "tag" (concat "+" tag) (notmuch-search-find-thread-id) " and " notmuch-search-query-string)
+  (notmuch-call-notmuch-process "tag" (concat "+" tag) (notmuch-search-find-thread-id))
   (notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<))))
 
 (defun notmuch-search-remove-tag (tag)
   (notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<))))
 
 (defun notmuch-search-remove-tag (tag)
@@ -1164,7 +1369,7 @@ The tag is removed from messages in the currently selected thread
 which match the current search terms."
   (interactive
    (list (notmuch-select-tag-with-completion "Tag to remove: " (notmuch-search-find-thread-id))))
 which match the current search terms."
   (interactive
    (list (notmuch-select-tag-with-completion "Tag to remove: " (notmuch-search-find-thread-id))))
-  (notmuch-call-notmuch-process "tag" (concat "-" tag) (notmuch-search-find-thread-id) " and " notmuch-search-query-string)
+  (notmuch-call-notmuch-process "tag" (concat "-" tag) (notmuch-search-find-thread-id))
   (notmuch-search-set-tags (delete tag (notmuch-search-get-tags))))
 
 (defun notmuch-search-archive-thread ()
   (notmuch-search-set-tags (delete tag (notmuch-search-get-tags))))
 
 (defun notmuch-search-archive-thread ()
@@ -1205,7 +1410,7 @@ This function advances the next thread when finished."
                  (more t)
                  (inhibit-read-only t))
              (while more
                  (more t)
                  (inhibit-read-only t))
              (while more
-               (if (string-match "^\\(thread:[0-9A-Fa-f]*\\) \\(.*\\) \\(\\[[0-9/]*\\]\\) \\([^:]*\\); \\(.*\\) (\\([^()]*\\))$" string line)
+               (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))
                    (let* ((thread-id (match-string 1 string))
                           (date (match-string 2 string))
                           (count (match-string 3 string))
@@ -1218,7 +1423,9 @@ This function advances the next thread when finished."
                      (goto-char (point-max))
                      (let ((beg (point-marker)))
                        (insert (format "%s %-7s %-40s %s (%s)\n" date count authors subject tags))
                      (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))
+                       (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id)
+                       (put-text-property beg (point-marker) 'notmuch-search-authors authors)
+                       (put-text-property beg (point-marker) 'notmuch-search-subject subject))
                      (set 'line (match-end 0)))
                  (set 'more nil))))))
       (delete-process proc))))
                      (set 'line (match-end 0)))
                  (set 'more nil))))))
       (delete-process proc))))
@@ -1315,7 +1522,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: ")
 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.
 
 (defun notmuch-search-filter-by-tag (tag)
   "Filter the current search results based on a single tag.
@@ -1340,12 +1548,15 @@ current search results AND that are tagged with the given tag."
     (define-key map "?" 'notmuch-help)
     (define-key map "x" 'kill-this-buffer)
     (define-key map "q" 'kill-this-buffer)
     (define-key map "?" 'notmuch-help)
     (define-key map "x" 'kill-this-buffer)
     (define-key map "q" 'kill-this-buffer)
+    (define-key map "m" 'message-mail)
+    (define-key map "e" 'notmuch-folder-show-empty-toggle)
     (define-key map ">" 'notmuch-folder-last)
     (define-key map "<" 'notmuch-folder-first)
     (define-key map "=" 'notmuch-folder)
     (define-key map "s" 'notmuch-search)
     (define-key map [mouse-1] 'notmuch-folder-show-search)
     (define-key map (kbd "RET") 'notmuch-folder-show-search)
     (define-key map ">" 'notmuch-folder-last)
     (define-key map "<" 'notmuch-folder-first)
     (define-key map "=" 'notmuch-folder)
     (define-key map "s" 'notmuch-search)
     (define-key map [mouse-1] 'notmuch-folder-show-search)
     (define-key map (kbd "RET") 'notmuch-folder-show-search)
+    (define-key map " " 'notmuch-folder-show-search)
     (define-key map "p" 'notmuch-folder-previous)
     (define-key map "n" 'notmuch-folder-next)
     map)
     (define-key map "p" 'notmuch-folder-previous)
     (define-key map "n" 'notmuch-folder-next)
     map)
@@ -1415,22 +1626,40 @@ Currently available key bindings:
   (goto-char (point-max))
   (forward-line -1))
 
   (goto-char (point-max))
   (forward-line -1))
 
+(defun notmuch-folder-count (search)
+  (car (process-lines notmuch-command "count" search)))
+
+(setq notmuch-folder-show-empty t)
+
+(defun notmuch-folder-show-empty-toggle ()
+  "Toggle the listing of empty folders"
+  (interactive)
+  (setq notmuch-folder-show-empty (not notmuch-folder-show-empty))
+  (notmuch-folder))
+
 (defun notmuch-folder-add (folders)
   (if folders
 (defun notmuch-folder-add (folders)
   (if folders
-      (let ((name (car (car folders)))
+      (let* ((name (car (car folders)))
            (inhibit-read-only t)
            (inhibit-read-only t)
-           (search (cdr (car folders))))
-       (insert name)
-       (indent-to 16 1)
-       (call-process notmuch-command nil t nil "count" search)
+           (search (cdr (car folders)))
+           (count (notmuch-folder-count search)))
+       (if (or notmuch-folder-show-empty
+               (not (equal count "0")))
+           (progn
+             (insert name)
+             (indent-to 16 1)
+             (insert count)
+             (insert "\n")
+             )
+         )
        (notmuch-folder-add (cdr folders)))))
 
 (defun notmuch-folder-find-name ()
   (save-excursion
     (beginning-of-line)
     (let ((beg (point)))
        (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)))))
+      (re-search-forward "\\([ \t]*[^ \t]+\\)")
+      (filter-buffer-substring (match-beginning 1) (match-end 1)))))
 
 (defun notmuch-folder-show-search (&optional folder)
   "Show a search window for the search related to the specified folder."
 
 (defun notmuch-folder-show-search (&optional folder)
   "Show a search window for the search related to the specified folder."