(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 "(\\([^)]*\\))$")
(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))
(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)) "' ")))
(while (< (point) end)
(let ((beg-sub (point-marker))
(indent (make-string depth ? ))
- (citation "[[:space:]]*>"))
+ (citation ">"))
+ (move-to-column depth)
(if (looking-at citation)
(progn
(while (looking-at citation)
- (forward-line))
+ (forward-line)
+ (move-to-column depth))
(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))
+ (let ((p (point-marker))
(cite-button-text
(concat "[" (number-to-string (count-lines beg-sub (point)))
- "-line citation.]")))
+ "-line citation. Click/Enter to show.]")))
(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))
+ (forward-line)
))))
(move-to-column depth)
(if (looking-at notmuch-show-signature-regexp)
(goto-char (- beg-sub 1))
(insert (concat "\n" indent))
(let ((sig-button-text (concat "[" (number-to-string sig-lines)
- "-line signature.]")))
+ "-line signature. Click/Enter to show.]")))
(insert-button sig-button-text 'invisibility-spec invis-spec
:type 'notmuch-button-signature-toggle-type)
)
(goto-char end))))))
(forward-line))))
-(defun notmuch-show-markup-part (beg end depth mime-message)
+(defun notmuch-show-markup-part (beg end depth)
(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)))
+ (let ((beg (point-marker)))
(re-search-forward notmuch-show-part-end-regexp)
-
- (let ((part-end (copy-marker (match-beginning 0))))
- (goto-char part-end)
+ (let ((end (copy-marker (match-beginning 0))))
+ (goto-char 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)
+ (indent-rigidly beg end depth)
+ (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))
- mime-message)
+ (goto-char end)))
(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)))
(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)
(overlay-put (make-overlay (point) (re-search-forward ".*$"))
'face 'message-header-other)))))))
-(defun notmuch-show-markup-header (depth)
+(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))
(let ((end (point-marker)))
(indent-rigidly beg end depth)
(goto-char beg)
- (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)
(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 ()
(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
-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)
(erase-buffer)
(goto-char (point-min))
(save-excursion
- (call-process notmuch-command nil t nil "show" "--entire-thread" 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)
- ; 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-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)
"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)
- (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 ()
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)
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 ()
(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))))
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.