-(defun notmuch-show-save-attachments ()
- "Save all attachments from the current message."
- (interactive)
- (with-current-notmuch-show-message
- (let ((mm-handle (mm-dissect-buffer)))
- (notmuch-save-attachments
- mm-handle (> (notmuch-count-attachments mm-handle) 1))))
- (message "Done"))
-
-(defun notmuch-reply (query-string)
- (switch-to-buffer (generate-new-buffer "notmuch-draft"))
- (call-process notmuch-command nil t nil "reply" query-string)
- (message-insert-signature)
- (goto-char (point-min))
- (if (re-search-forward "^$" nil t)
- (progn
- (insert "--text follows this line--")
- (forward-line)))
- (message-mode))
-
-(defun notmuch-show-reply ()
- "Begin composing a reply to the current message in a new buffer."
- (interactive)
- (let ((message-id (notmuch-show-get-message-id)))
- (notmuch-reply message-id)))
-
-(defun notmuch-show-forward-current ()
- "Forward the current message."
- (interactive)
- (with-current-notmuch-show-message
- (message-forward)))
-
-(defun notmuch-show-pipe-message (command)
- "Pipe the contents of the current message to the given command.
-
-The given command will be executed with the raw contents of the
-current email message as stdin. Anything printed by the command
-to stdout or stderr will appear in the *Messages* buffer."
- (interactive "sPipe message to command: ")
- (apply 'start-process-shell-command "notmuch-pipe-command" "*notmuch-pipe*"
- (list command " < " (shell-quote-argument (notmuch-show-get-filename)))))
-
-(defun notmuch-show-move-to-current-message-summary-line ()
- "Move to the beginning of the one-line summary of the current message.
-
-This gives us a stable place to move to and work from since the
-summary line is always visible. This is important since moving to
-an invisible location is unreliable, (the main command loop moves
-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
-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))
- (if (re-search-backward notmuch-show-message-begin-regexp nil t)
- (forward-line 2)
- (error "Not within a valid message."))
- (forward-line 2)))
-
-(defun notmuch-show-last-message-p ()
- "Predicate testing whether point is within the last message."
- (save-window-excursion
- (save-excursion
- (notmuch-show-move-to-current-message-summary-line)
- (not (re-search-forward notmuch-show-message-begin-regexp nil t)))))
-
-(defun notmuch-show-message-unread-p ()
- "Predicate testing whether current message is unread."
- (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-without-marking-read ()
- "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.
-
-Returns nil if already on the last message in the buffer."
- (notmuch-show-move-to-current-message-summary-line)
- (if (re-search-forward notmuch-show-message-begin-regexp nil t)
- (progn
- (notmuch-show-move-to-current-message-summary-line)
- (recenter 0)
- t)
- (goto-char (- (point-max) 1))
- (while (point-invisible-p)
- (backward-char))
- (recenter 0)
- nil))
-
-(defun notmuch-show-next-message ()
- "Advance to the next message (whether open or closed)
-and remove the unread tag from that message.
-
-Moves to the last visible character of the current message if
-already on the last message in the buffer.
-
-Returns nil if already on the last message in the buffer."
- (interactive)
- (notmuch-show-next-message-without-marking-read)
- (notmuch-show-mark-read))
-
-(defun notmuch-show-find-next-message ()
- "Returns the position of the next message in the buffer.
-
-Or the position of the last visible character of the current
-message if already within the last message in the buffer."
- ; save-excursion doesn't save our window position
- ; save-window-excursion doesn't save point
- ; Looks like we have to use both.
- (save-excursion
- (save-window-excursion
- (notmuch-show-next-message-without-marking-read)
- (point))))
-
-(defun notmuch-show-next-unread-message ()
- "Advance to the next unread message.
-
-Moves to the last visible character of the current message if
-there are no more unread messages past the current point."
- (notmuch-show-next-message-without-marking-read)
- (while (and (not (notmuch-show-last-message-p))
- (not (notmuch-show-message-unread-p)))
- (notmuch-show-next-message-without-marking-read))
- (if (not (notmuch-show-message-unread-p))
- (notmuch-show-next-message-without-marking-read))
- (notmuch-show-mark-read))
-
-(defun notmuch-show-next-open-message ()
- "Advance to the next open message (that is, body is visible).
-
-Moves to the last visible character of the final message in the buffer
-if there are no more open messages."
- (interactive)
- (while (and (notmuch-show-next-message-without-marking-read)
- (not (notmuch-show-message-open-p))))
- (notmuch-show-mark-read))
-
-(defun notmuch-show-previous-message-without-marking-read ()
- "Backup to the beginning of the previous message in the buffer.
-
-If within a message rather than at the beginning of it, then
-simply move to the beginning of the current message.
-
-Returns nil if already on the first message in the buffer."
- (let ((start (point)))
- (notmuch-show-move-to-current-message-summary-line)
- (if (not (< (point) start))
- ; Go backward twice to skip the current message's marker
- (progn
- (re-search-backward notmuch-show-message-begin-regexp nil t)
- (re-search-backward notmuch-show-message-begin-regexp nil t)
- (notmuch-show-move-to-current-message-summary-line)
- (recenter 0)
- (if (= (point) start)
- nil
- t))
- (recenter 0)
- nil)))
-
-(defun notmuch-show-previous-message ()
- "Backup to the previous message (whether open or closed)
-and remove the unread tag from that message.
-
-If within a message rather than at the beginning of it, then
-simply move to the beginning of the current message."
- (interactive)
- (notmuch-show-previous-message-without-marking-read)
- (notmuch-show-mark-read))
-
-(defun notmuch-show-find-previous-message ()
- "Returns the position of the previous message in the buffer.
-
-Or the position of the beginning of the current message if point
-is originally within the message rather than at the beginning of
-it."
- ; save-excursion doesn't save our window position
- ; save-window-excursion doesn't save point
- ; Looks like we have to use both.
- (save-excursion
- (save-window-excursion
- (notmuch-show-previous-message-without-marking-read)
- (point))))
-
-(defun notmuch-show-previous-open-message ()
- "Backup to previous open message (that is, body is visible).
-
-Moves to the first message in the buffer if there are no previous
-open messages."
- (interactive)
- (while (and (notmuch-show-previous-message-without-marking-read)
- (not (notmuch-show-message-open-p))))
- (notmuch-show-mark-read))
-
-(defun notmuch-show-rewind ()
- "Backup through the thread, (reverse scrolling compared to \\[notmuch-show-advance-and-archive]).
-
-Specifically, if the beginning of the previous email is fewer
-than `window-height' lines from the current point, move to it
-just like `notmuch-show-previous-message'.
-
-Otherwise, just scroll down a screenful of the current message.
-
-This command does not modify any message tags, (it does not undo
-any effects from previous calls to
-`notmuch-show-advance-and-archive'."
- (interactive)
- (let ((previous (notmuch-show-find-previous-message)))
- (if (> (count-lines previous (point)) (- (window-height) next-screen-context-lines))
- (progn
- (condition-case nil
- (scroll-down nil)
- ((beginning-of-buffer) nil))
- (goto-char (window-start))
- ; Because count-lines counts invivisible lines, we may have
- ; scrolled to far. If so., notice this and fix it up.
- (if (< (point) previous)
- (progn
- (goto-char previous)
- (recenter 0))))
- (notmuch-show-previous-message))))
-
-(defun notmuch-show-advance-and-archive ()
- "Advance through thread and archive.
-
-This command is intended to be one of the simplest ways to
-process a thread of email. It does the following:
-
-If the current message in the thread is not yet fully visible,
-scroll by a near screenful to read more of the message.
-
-Otherwise, (the end of the current message is already within the
-current window), advance to the next open message.
-
-Finally, if there is no further message to advance to, and this
-last message is already read, then archive the entire current
-thread, (remove the \"inbox\" tag from each message). Also kill
-this buffer, and display the next thread from the search from
-which this thread was originally shown."
- (interactive)
- (let ((next (notmuch-show-find-next-message))
- (unread (notmuch-show-message-unread-p)))
- (if (> next (window-end))
- (scroll-up nil)
- (let ((last (notmuch-show-last-message-p)))
- (notmuch-show-next-open-message)
- (if last
- (notmuch-show-archive-thread))))))
-
-(defun notmuch-show-next-button ()
- "Advance point to the next button in the buffer."
- (interactive)
- (forward-button 1))
-
-(defun notmuch-show-previous-button ()
- "Move point back to the previous button in the buffer."
- (interactive)
- (backward-button 1))
-
-(defun notmuch-toggle-invisible-action (cite-button)
- (let ((invis-spec (button-get cite-button 'invisibility-spec)))
- (if (invisible-p invis-spec)
- (remove-from-invisibility-spec invis-spec)
- (add-to-invisibility-spec invis-spec)
- ))
- (force-window-update)
- (redisplay 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-body-toggle-type
- 'help-echo "mouse-1, RET: Show message"
- 'face 'notmuch-message-summary-face
- :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)
- "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
- (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)
- (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 hiding the body itself if the message 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-end-regexp)
- (let ((end (copy-marker (match-beginning 0))))
- (notmuch-show-markup-parts-region beg end depth)
- (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 match)
- (add-to-invisibility-spec invis-spec)))
- (set-marker beg nil)
- (set-marker end nil)
- )))
-
-(defun notmuch-fontify-headers ()
- (while (looking-at "[[:space:]]")
- (forward-char))
- (if (looking-at "[Tt]o:")
- (progn
- (overlay-put (make-overlay (point) (re-search-forward ":"))
- 'face 'message-header-name)
- (overlay-put (make-overlay (point) (re-search-forward ".*$"))
- 'face 'message-header-to))
- (if (looking-at "[B]?[Cc][Cc]:")
- (progn
- (overlay-put (make-overlay (point) (re-search-forward ":"))
- 'face 'message-header-name)
- (overlay-put (make-overlay (point) (re-search-forward ".*$"))
- 'face 'message-header-cc))
- (if (looking-at "[Ss]ubject:")
- (progn
- (overlay-put (make-overlay (point) (re-search-forward ":"))
- 'face 'message-header-name)
- (overlay-put (make-overlay (point) (re-search-forward ".*$"))
- 'face 'message-header-subject))
- (if (looking-at "[Ff]rom:")
- (progn
- (overlay-put (make-overlay (point) (re-search-forward ":"))
- 'face 'message-header-name)
- (overlay-put (make-overlay (point) (re-search-forward ".*$"))
- 'face 'message-header-other)))))))
-
-(defun notmuch-show-markup-header (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))
- (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))
- (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)
- (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 message-begin depth))
- (notmuch-show-markup-body depth match btn)))
- (goto-char (point-max))))
-
-(defun notmuch-show-hide-markers ()
- (save-excursion
- (goto-char (point-min))
- (while (not (eobp))
- (if (re-search-forward notmuch-show-marker-regexp nil t)
- (progn
- (overlay-put (make-overlay (match-beginning 0) (+ (match-end 0) 1))
- 'invisible 'notmuch-show-marker))
- (goto-char (point-max))))))
-
-(defun notmuch-show-markup-messages ()
- (save-excursion
- (goto-char (point-min))
- (while (not (eobp))
- (notmuch-show-markup-message)))
- (notmuch-show-hide-markers))
-