(declare-function notmuch-foreach-mime-part "notmuch" (function mm-handle))
(declare-function notmuch-count-attachments "notmuch" (mm-handle))
(declare-function notmuch-save-attachments "notmuch" (mm-handle &optional queryp))
+(declare-function notmuch-tree "notmuch-tree"
+ (&optional query query-context target buffer-name open-target))
+(declare-function notmuch-tree-get-message-properties "notmuch-tree" nil)
(defcustom notmuch-message-headers '("Subject" "To" "Cc" "Date")
"Headers that should be shown in a message, in this order.
(make-variable-buffer-local 'notmuch-show-indent-content)
(put 'notmuch-show-indent-content 'permanent-local t)
+(defvar notmuch-show-attachment-debug nil
+ "If t log stdout and stderr from attachment handlers
+
+When set to nil (the default) stdout and stderr from attachment
+handlers is discarded. When set to t the stdout and stderr from
+each attachment handler is logged in buffers with names beginning
+\" *notmuch-part*\". This option requires emacs version at least
+24.3 to work.")
+
(defcustom notmuch-show-stash-mlarchive-link-alist
'(("Gmane" . "http://mid.gmane.org/")
("MARC" . "http://marc.info/?i=")
- ("Mail Archive, The" . "http://mail-archive.com/search?l=mid&q=")
+ ("Mail Archive, The" . "http://mid.mail-archive.com/")
("LKML" . "http://lkml.kernel.org/r/")
;; FIXME: can these services be searched by `Message-Id' ?
;; ("MarkMail" . "http://markmail.org/")
)
"List of Mailing List Archives to use when stashing links.
-These URIs are concatenated with the current message's
-Message-Id in `notmuch-show-stash-mlarchive-link'."
+This list is used for generating a Mailing List Archive reference
+URI with the current message's Message-Id in
+`notmuch-show-stash-mlarchive-link'.
+
+If the cdr of the alist element is not a function, the cdr is
+expected to contain a URI that is concatenated with the current
+message's Message-Id to create a ML archive reference URI.
+
+If the cdr is a function, the function is called with the
+Message-Id as the argument, and the function is expected to
+return the ML archive reference URI."
:type '(alist :key-type (string :tag "Name")
- :value-type (string :tag "URL"))
+ :value-type (choice
+ (string :tag "URL")
+ (function :tag "Function returning the URL")))
:group 'notmuch-show)
(defcustom notmuch-show-stash-mlarchive-link-default "Gmane"
:type '(repeat string)
:group 'notmuch-show)
+(defcustom notmuch-show-mark-read-function #'notmuch-show-seen-current-message
+ "Function to control which messages are marked read."
+ :type 'function
+ :group 'notmuch-show)
(defmacro with-current-notmuch-show-message (&rest body)
"Evaluate body with current buffer set to the text of current message"
(if (re-search-forward "(\\([^()]*\\))$" (line-end-position) t)
(let ((inhibit-read-only t))
(replace-match (concat "("
- (notmuch-tag-format-tags tags)
+ (notmuch-tag-format-tags tags (notmuch-show-get-prop :orig-tags))
")"))))))
(defun notmuch-clean-address (address)
message at DEPTH in the current thread."
(let ((start (point)))
(insert (notmuch-show-spaces-n (* notmuch-show-indent-messages-width depth))
- (notmuch-show-clean-address (plist-get headers :From))
+ (notmuch-sanitize
+ (notmuch-show-clean-address (plist-get headers :From)))
" ("
date
") ("
- (notmuch-tag-format-tags tags)
+ (notmuch-tag-format-tags tags tags)
")\n")
(overlay-put (make-overlay start (point)) 'face 'notmuch-message-summary-face)))
(defun notmuch-show-insert-header (header header-value)
"Insert a single header."
- (insert header ": " header-value "\n"))
+ (insert header ": " (notmuch-sanitize header-value) "\n"))
(defun notmuch-show-insert-headers (headers)
"Insert the headers of the current message."
(while (and handlers
(not (condition-case err
(funcall (car handlers) msg part content-type nth depth button)
- (error (progn
+ ;; Specifying `debug' here lets the debugger
+ ;; run if `debug-on-error' is non-nil.
+ ((debug error)
+ (progn
(insert "!!! Bodypart insert error: ")
(insert (error-message-string err))
(insert " !!!\n") nil)))))
(let ((inhibit-read-only t))
(notmuch-show-mode)
+ (add-hook 'post-command-hook #'notmuch-show-command-hook nil t)
+
;; Don't track undo information for this buffer
(set 'buffer-undo-list t)
+ (notmuch-tag-clear-cache)
(erase-buffer)
(goto-char (point-min))
(save-excursion
(jit-lock-register #'notmuch-show-buttonise-links)
+ (notmuch-show-mapc (lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags))))
+
;; Set the header line to the subject of the first message.
- (setq header-line-format (notmuch-show-strip-re (notmuch-show-get-subject)))
+ (setq header-line-format (notmuch-sanitize (notmuch-show-strip-re (notmuch-show-get-subject))))
(run-hooks 'notmuch-show-hook))))
- the current message."
(list (notmuch-show-get-message-id) (notmuch-show-get-message-ids-for-open-messages)))
+(defun notmuch-show-get-query ()
+ "Return the current query in this show buffer"
+ (if notmuch-show-query-context
+ (concat notmuch-show-thread-id
+ " and ("
+ notmuch-show-query-context
+ ")")
+ notmuch-show-thread-id))
+
(defun notmuch-show-apply-state (state)
"Apply STATE to the current buffer.
(define-key map "t" 'notmuch-show-stash-to)
(define-key map "l" 'notmuch-show-stash-mlarchive-link)
(define-key map "L" 'notmuch-show-stash-mlarchive-link-and-go)
+ (define-key map "?" 'notmuch-subkeymap-help)
map)
"Submap for stash commands")
(fset 'notmuch-show-stash-map notmuch-show-stash-map)
(define-key map "v" 'notmuch-show-view-part)
(define-key map "o" 'notmuch-show-interactively-view-part)
(define-key map "|" 'notmuch-show-pipe-part)
+ (define-key map "?" 'notmuch-subkeymap-help)
map)
"Submap for part commands")
(fset 'notmuch-show-part-map notmuch-show-part-map)
(defvar notmuch-show-mode-map
- (let ((map (make-sparse-keymap)))
- (set-keymap-parent map notmuch-common-keymap)
- (define-key map (kbd "<C-tab>") 'widget-backward)
- (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
- (define-key map (kbd "<backtab>") 'notmuch-show-previous-button)
- (define-key map (kbd "TAB") 'notmuch-show-next-button)
- (define-key map "f" 'notmuch-show-forward-message)
- (define-key map "r" 'notmuch-show-reply-sender)
- (define-key map "R" 'notmuch-show-reply)
- (define-key map "|" 'notmuch-show-pipe-message)
- (define-key map "w" 'notmuch-show-save-attachments)
- (define-key map "V" 'notmuch-show-view-raw-message)
- (define-key map "c" 'notmuch-show-stash-map)
- (define-key map "h" 'notmuch-show-toggle-visibility-headers)
- (define-key map "*" 'notmuch-show-tag-all)
- (define-key map "-" 'notmuch-show-remove-tag)
- (define-key map "+" 'notmuch-show-add-tag)
- (define-key map "X" 'notmuch-show-archive-thread-then-exit)
- (define-key map "x" 'notmuch-show-archive-message-then-next-or-exit)
- (define-key map "A" 'notmuch-show-archive-thread-then-next)
- (define-key map "a" 'notmuch-show-archive-message-then-next-or-next-thread)
- (define-key map "N" 'notmuch-show-next-message)
- (define-key map "P" 'notmuch-show-previous-message)
- (define-key map "n" 'notmuch-show-next-open-message)
- (define-key map "p" 'notmuch-show-previous-open-message)
- (define-key map (kbd "M-n") 'notmuch-show-next-thread-show)
- (define-key map (kbd "M-p") 'notmuch-show-previous-thread-show)
- (define-key map (kbd "DEL") 'notmuch-show-rewind)
- (define-key map " " 'notmuch-show-advance-and-archive)
- (define-key map (kbd "M-RET") 'notmuch-show-open-or-close-all)
- (define-key map (kbd "RET") 'notmuch-show-toggle-message)
- (define-key map "#" 'notmuch-show-print-message)
- (define-key map "!" 'notmuch-show-toggle-elide-non-matching)
- (define-key map "$" 'notmuch-show-toggle-process-crypto)
- (define-key map "<" 'notmuch-show-toggle-thread-indentation)
- (define-key map "t" 'toggle-truncate-lines)
- (define-key map "." 'notmuch-show-part-map)
- map)
- "Keymap for \"notmuch show\" buffers.")
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map notmuch-common-keymap)
+ (define-key map "Z" 'notmuch-tree-from-show-current-query)
+ (define-key map (kbd "<C-tab>") 'widget-backward)
+ (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
+ (define-key map (kbd "<backtab>") 'notmuch-show-previous-button)
+ (define-key map (kbd "TAB") 'notmuch-show-next-button)
+ (define-key map "f" 'notmuch-show-forward-message)
+ (define-key map "r" 'notmuch-show-reply-sender)
+ (define-key map "R" 'notmuch-show-reply)
+ (define-key map "|" 'notmuch-show-pipe-message)
+ (define-key map "w" 'notmuch-show-save-attachments)
+ (define-key map "V" 'notmuch-show-view-raw-message)
+ (define-key map "c" 'notmuch-show-stash-map)
+ (define-key map "h" 'notmuch-show-toggle-visibility-headers)
+ (define-key map "*" 'notmuch-show-tag-all)
+ (define-key map "-" 'notmuch-show-remove-tag)
+ (define-key map "+" 'notmuch-show-add-tag)
+ (define-key map "X" 'notmuch-show-archive-thread-then-exit)
+ (define-key map "x" 'notmuch-show-archive-message-then-next-or-exit)
+ (define-key map "A" 'notmuch-show-archive-thread-then-next)
+ (define-key map "a" 'notmuch-show-archive-message-then-next-or-next-thread)
+ (define-key map "N" 'notmuch-show-next-message)
+ (define-key map "P" 'notmuch-show-previous-message)
+ (define-key map "n" 'notmuch-show-next-open-message)
+ (define-key map "p" 'notmuch-show-previous-open-message)
+ (define-key map (kbd "M-n") 'notmuch-show-next-thread-show)
+ (define-key map (kbd "M-p") 'notmuch-show-previous-thread-show)
+ (define-key map (kbd "DEL") 'notmuch-show-rewind)
+ (define-key map " " 'notmuch-show-advance-and-archive)
+ (define-key map (kbd "M-RET") 'notmuch-show-open-or-close-all)
+ (define-key map (kbd "RET") 'notmuch-show-toggle-message)
+ (define-key map "#" 'notmuch-show-print-message)
+ (define-key map "!" 'notmuch-show-toggle-elide-non-matching)
+ (define-key map "$" 'notmuch-show-toggle-process-crypto)
+ (define-key map "<" 'notmuch-show-toggle-thread-indentation)
+ (define-key map "t" 'toggle-truncate-lines)
+ (define-key map "." 'notmuch-show-part-map)
+ map)
+ "Keymap for \"notmuch show\" buffers.")
(fset 'notmuch-show-mode-map notmuch-show-mode-map)
(defun notmuch-show-mode ()
(setq buffer-read-only t
truncate-lines t))
+(defun notmuch-tree-from-show-current-query ()
+ "Call notmuch tree with the current query"
+ (interactive)
+ (notmuch-tree notmuch-show-thread-id
+ notmuch-show-query-context
+ (notmuch-show-get-message-id)))
+
(defun notmuch-show-move-to-message-top ()
(goto-char (notmuch-show-message-top)))
(notmuch-show-set-message-properties props)))
(defun notmuch-show-get-prop (prop &optional props)
+ "Get property PROP from current message in show or tree mode.
+
+It gets property PROP from PROPS or, if PROPS is nil, the current
+message in either tree or show. This means that several utility
+functions in notmuch-show can be used directly by notmuch-tree as
+they just need the correct message properties."
(let ((props (or props
- (notmuch-show-get-message-properties))))
+ (cond ((eq major-mode 'notmuch-show-mode)
+ (notmuch-show-get-message-properties))
+ ((eq major-mode 'notmuch-tree-mode)
+ (notmuch-tree-get-message-properties))
+ (t nil)))))
(plist-get props prop)))
(defun notmuch-show-get-message-id (&optional bare)
(apply 'notmuch-show-tag-message
(notmuch-tag-change-list notmuch-show-mark-read-tags unread))))
+(defun notmuch-show-seen-current-message (start end)
+ "Mark the current message read if it is open.
+
+We only mark it read once: if it is changed back then that is a
+user decision and we should not override it."
+ (when (and (notmuch-show-message-visible-p)
+ (not (notmuch-show-get-prop :seen)))
+ (notmuch-show-mark-read)
+ (notmuch-show-set-prop :seen t)))
+
+(defun notmuch-show-command-hook ()
+ (when (eq major-mode 'notmuch-show-mode)
+ ;; We need to redisplay to get window-start and window-end correct.
+ (redisplay)
+ (save-excursion
+ (funcall notmuch-show-mark-read-function (window-start) (window-end)))))
+
;; Functions for getting attributes of several messages in the current
;; thread.
thread, navigate to the next thread in the parent search buffer."
(interactive "P")
(if (notmuch-show-goto-message-next)
- (progn
- (notmuch-show-mark-read)
- (notmuch-show-message-adjust))
+ (notmuch-show-message-adjust)
(if pop-at-end
(notmuch-show-next-thread)
(goto-char (point-max)))))
(if (= (point) (notmuch-show-message-top))
(notmuch-show-goto-message-previous)
(notmuch-show-move-to-message-top))
- (notmuch-show-mark-read)
(notmuch-show-message-adjust))
(defun notmuch-show-next-open-message (&optional pop-at-end)
(while (and (setq r (notmuch-show-goto-message-next))
(not (notmuch-show-message-visible-p))))
(if r
- (progn
- (notmuch-show-mark-read)
- (notmuch-show-message-adjust))
+ (notmuch-show-message-adjust)
(if pop-at-end
(notmuch-show-next-thread)
(goto-char (point-max))))
(while (and (setq r (notmuch-show-goto-message-next))
(not (notmuch-show-get-prop :match))))
(if r
- (progn
- (notmuch-show-mark-read)
- (notmuch-show-message-adjust))
+ (notmuch-show-message-adjust)
(goto-char (point-max)))))
(defun notmuch-show-open-if-matched ()
(defun notmuch-show-goto-first-wanted-message ()
"Move to the first open message and mark it read"
(goto-char (point-min))
- (if (notmuch-show-message-visible-p)
- (notmuch-show-mark-read)
+ (unless (notmuch-show-message-visible-p)
(notmuch-show-next-open-message))
(when (eobp)
;; There are no matched non-excluded messages so open all matched
(notmuch-show-mapc 'notmuch-show-open-if-matched)
(force-window-update)
(goto-char (point-min))
- (if (notmuch-show-message-visible-p)
- (notmuch-show-mark-read)
+ (unless (notmuch-show-message-visible-p)
(notmuch-show-next-open-message))))
(defun notmuch-show-previous-open-message ()
(notmuch-show-goto-message-previous)
(notmuch-show-move-to-message-top))
(not (notmuch-show-message-visible-p))))
- (notmuch-show-mark-read)
(notmuch-show-message-adjust))
(defun notmuch-show-view-raw-message ()
(setq shell-command
(concat notmuch-command " show --format=raw "
(shell-quote-argument (notmuch-show-get-message-id)) " | " command)))
- (let ((buf (get-buffer-create (concat "*notmuch-pipe*"))))
+ (let ((cwd default-directory)
+ (buf (get-buffer-create (concat "*notmuch-pipe*"))))
(with-current-buffer buf
(setq buffer-read-only nil)
(erase-buffer)
+ ;; Use the originating buffer's working directory instead of
+ ;; that of the pipe buffer.
+ (cd cwd)
(let ((exit-code (call-process-shell-command shell-command nil buf)))
(goto-char (point-max))
(set-buffer-modified-p nil)
If optional argument MLA is non-nil, use the provided key instead of prompting
the user (see `notmuch-show-stash-mlarchive-link-alist')."
(interactive)
- (notmuch-common-do-stash
- (concat (cdr (assoc
- (or mla
- (let ((completion-ignore-case t))
- (completing-read
- "Mailing List Archive: "
- notmuch-show-stash-mlarchive-link-alist
- nil t nil nil notmuch-show-stash-mlarchive-link-default)))
- notmuch-show-stash-mlarchive-link-alist))
- (notmuch-show-get-message-id t))))
+ (let ((url (cdr (assoc
+ (or mla
+ (let ((completion-ignore-case t))
+ (completing-read
+ "Mailing List Archive: "
+ notmuch-show-stash-mlarchive-link-alist
+ nil t nil nil
+ notmuch-show-stash-mlarchive-link-default)))
+ notmuch-show-stash-mlarchive-link-alist))))
+ (notmuch-common-do-stash
+ (if (functionp url)
+ (funcall url (notmuch-show-get-message-id t))
+ (concat url (notmuch-show-get-message-id t))))))
(defun notmuch-show-stash-mlarchive-link-and-go (&optional mla)
"Copy an ML Archive URI for the current message to the kill-ring and visit it.
This ensures that the temporary buffer created for the mm-handle
is destroyed when FN returns."
(let ((handle (notmuch-show-current-part-handle)))
+ ;; emacs 24.3+ puts stdout/stderr into the calling buffer so we
+ ;; call it from a temp-buffer, unless
+ ;; notmuch-show-attachment-debug is non-nil in which case we put
+ ;; it in " *notmuch-part*".
(unwind-protect
- (funcall fn handle)
+ (if notmuch-show-attachment-debug
+ (with-current-buffer (generate-new-buffer " *notmuch-part*")
+ (funcall fn handle))
+ (with-temp-buffer
+ (funcall fn handle)))
(kill-buffer (mm-handle-buffer handle)))))
(defun notmuch-show-part-button-default (&optional button)