1 Return-Path: <dme@dme.org>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id AF0C74048D0
\r
6 for <notmuch@notmuchmail.org>; Fri, 12 Mar 2010 05:28:52 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=-2.317 tagged_above=-999 required=5 tests=[AWL=0.282,
\r
12 BAYES_00=-2.599] autolearn=ham
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id QS5W53hY8-4t for <notmuch@notmuchmail.org>;
\r
16 Fri, 12 Mar 2010 05:28:48 -0800 (PST)
\r
17 Received: from mail-bw0-f210.google.com (mail-bw0-f210.google.com
\r
19 by olra.theworths.org (Postfix) with ESMTP id 47ABE4048CE
\r
20 for <notmuch@notmuchmail.org>; Fri, 12 Mar 2010 05:28:47 -0800 (PST)
\r
21 Received: by bwz2 with SMTP id 2so1026802bwz.30
\r
22 for <notmuch@notmuchmail.org>; Fri, 12 Mar 2010 05:28:46 -0800 (PST)
\r
23 Received: by 10.204.10.3 with SMTP id n3mr897572bkn.81.1268400526097;
\r
24 Fri, 12 Mar 2010 05:28:46 -0800 (PST)
\r
25 Received: from aw.hh.sledj.net (gmp-ea-fw-1b.sun.com [192.18.8.1])
\r
26 by mx.google.com with ESMTPS id x16sm5208850bku.17.2010.03.12.05.28.42
\r
27 (version=TLSv1/SSLv3 cipher=RC4-MD5);
\r
28 Fri, 12 Mar 2010 05:28:45 -0800 (PST)
\r
29 Received: by aw.hh.sledj.net (Postfix, from userid 1000)
\r
30 id A9E5D3A0A1; Fri, 12 Mar 2010 13:28:33 +0000 (GMT)
\r
31 To: notmuch@notmuchmail.org
\r
32 From: David Edmondson <dme@dme.org>
\r
33 X-Now-Playing: Attribute (conntype) does not pass the type constraint because:
\r
34 Validation failed for 'CONNTYPE' failed with value undef at
\r
35 /home/dme/u/bin/now-playing line 33
\r
36 main::now_playing() called at /home/dme/u/bin/now-playing line 49
\r
37 Date: Fri, 12 Mar 2010 13:28:33 +0000
\r
38 Message-ID: <876351vfcu.fsf@aw.hh.sledj.net>
\r
40 Content-Type: text/plain; charset=utf-8
\r
41 Content-Transfer-Encoding: quoted-printable
\r
42 Subject: [notmuch] [PATCH] Move notmuch-show functionality to notmuch-show.el
\r
43 X-BeenThere: notmuch@notmuchmail.org
\r
44 X-Mailman-Version: 2.1.13
\r
46 List-Id: "Use and development of the notmuch mail system."
\r
47 <notmuch.notmuchmail.org>
\r
48 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
49 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
50 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
51 List-Post: <mailto:notmuch@notmuchmail.org>
\r
52 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
53 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
54 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
55 X-List-Received-Date: Fri, 12 Mar 2010 13:28:52 -0000
\r
57 To ease the transition to a JSON based implementation of
\r
58 `notmuch-show', move the current implementation into a separate file.
\r
60 Signed-off-by: David Edmondson <dme@dme.org>
\r
62 emacs/Makefile.local | 2 +-
\r
63 emacs/notmuch-show.el | 982 +++++++++++++++++++++++++++++++++++++++++++++=
\r
65 emacs/notmuch.el | 960 +--------------------------------------------=
\r
67 3 files changed, 984 insertions(+), 960 deletions(-)
\r
68 create mode 100644 emacs/notmuch-show.el
\r
70 diff --git a/emacs/Makefile.local b/emacs/Makefile.local
\r
71 index 17ede86..6bd8617 100644
\r
72 --- a/emacs/Makefile.local
\r
73 +++ b/emacs/Makefile.local
\r
76 -emacs_sources :=3D $(dir)/notmuch.el
\r
77 +emacs_sources :=3D $(dir)/notmuch.el $(dir)/notmuch-show.el
\r
79 emacs_bytecode :=3D $(subst .el,.elc,$(emacs_sources))
\r
81 diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
\r
82 new file mode 100644
\r
83 index 0000000..dd6343d
\r
85 +++ b/emacs/notmuch-show.el
\r
87 +;; notmuch-show.el --- display notmuch messages within emacs
\r
89 +;; Copyright =C2=A9 Carl Worth
\r
91 +;; This file is part of Notmuch.
\r
93 +;; Notmuch is free software: you can redistribute it and/or modify it
\r
94 +;; under the terms of the GNU General Public License as published by
\r
95 +;; the Free Software Foundation, either version 3 of the License, or
\r
96 +;; (at your option) any later version.
\r
98 +;; Notmuch is distributed in the hope that it will be useful, but
\r
99 +;; WITHOUT ANY WARRANTY; without even the implied warranty of
\r
100 +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
101 +;; General Public License for more details.
\r
103 +;; You should have received a copy of the GNU General Public License
\r
104 +;; along with Notmuch. If not, see <http://www.gnu.org/licenses/>.
\r
106 +;; Authors: Carl Worth <cworth@cworth.org>
\r
108 +;; This is an part of an emacs-based interface to the notmuch mail system.
\r
110 +(defvar notmuch-show-stash-map
\r
111 + (let ((map (make-sparse-keymap)))
\r
112 + (define-key map "c" 'notmuch-show-stash-cc)
\r
113 + (define-key map "d" 'notmuch-show-stash-date)
\r
114 + (define-key map "F" 'notmuch-show-stash-filename)
\r
115 + (define-key map "f" 'notmuch-show-stash-from)
\r
116 + (define-key map "i" 'notmuch-show-stash-message-id)
\r
117 + (define-key map "s" 'notmuch-show-stash-subject)
\r
118 + (define-key map "T" 'notmuch-show-stash-tags)
\r
119 + (define-key map "t" 'notmuch-show-stash-to)
\r
121 + "Submap for stash commands"
\r
124 +(fset 'notmuch-show-stash-map notmuch-show-stash-map)
\r
126 +(defvar notmuch-show-mode-map
\r
127 + (let ((map (make-sparse-keymap)))
\r
128 + (define-key map "?" 'notmuch-help)
\r
129 + (define-key map "q" 'kill-this-buffer)
\r
130 + (define-key map (kbd "C-p") 'notmuch-show-previous-line)
\r
131 + (define-key map (kbd "C-n") 'notmuch-show-next-line)
\r
132 + (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
\r
133 + (define-key map (kbd "TAB") 'notmuch-show-next-button)
\r
134 + (define-key map "s" 'notmuch-search)
\r
135 + (define-key map "m" 'message-mail)
\r
136 + (define-key map "f" 'notmuch-show-forward-current)
\r
137 + (define-key map "r" 'notmuch-show-reply)
\r
138 + (define-key map "|" 'notmuch-show-pipe-message)
\r
139 + (define-key map "w" 'notmuch-show-save-attachments)
\r
140 + (define-key map "V" 'notmuch-show-view-raw-message)
\r
141 + (define-key map "v" 'notmuch-show-view-all-mime-parts)
\r
142 + (define-key map "c" 'notmuch-show-stash-map)
\r
143 + (define-key map "b" 'notmuch-show-toggle-current-body)
\r
144 + (define-key map "h" 'notmuch-show-toggle-current-header)
\r
145 + (define-key map "-" 'notmuch-show-remove-tag)
\r
146 + (define-key map "+" 'notmuch-show-add-tag)
\r
147 + (define-key map "x" 'notmuch-show-archive-thread-then-exit)
\r
148 + (define-key map "a" 'notmuch-show-archive-thread)
\r
149 + (define-key map "P" 'notmuch-show-previous-message)
\r
150 + (define-key map "N" 'notmuch-show-next-message)
\r
151 + (define-key map "p" 'notmuch-show-previous-open-message)
\r
152 + (define-key map "n" 'notmuch-show-next-open-message)
\r
153 + (define-key map (kbd "DEL") 'notmuch-show-rewind)
\r
154 + (define-key map " " 'notmuch-show-advance-and-archive)
\r
156 + "Keymap for \"notmuch show\" buffers.")
\r
157 +(fset 'notmuch-show-mode-map notmuch-show-mode-map)
\r
159 +(defvar notmuch-show-signature-regexp "\\(-- ?\\|_+\\)$"
\r
160 + "Pattern to match a line that separates content from signature.
\r
162 +The regexp can (and should) include $ to match the end of the
\r
163 +line, but should not include ^ to match the beginning of the
\r
164 +line. This is because notmuch may have inserted additional space
\r
165 +for indentation at the beginning of the line. But notmuch will
\r
166 +move past the indentation when testing this pattern, (so that the
\r
167 +pattern can still test against the entire line).")
\r
169 +(defvar notmuch-show-signature-button-format
\r
170 + "[ %d-line signature. Click/Enter to toggle visibility. ]"
\r
171 + "String used to construct button text for hidden signatures
\r
173 +Can use up to one integer format parameter, i.e. %d")
\r
175 +(defvar notmuch-show-citation-button-format
\r
176 + "[ %d more citation lines. Click/Enter to toggle visibility. ]"
\r
177 + "String used to construct button text for hidden citations.
\r
179 +Can use up to one integer format parameter, i.e. %d")
\r
181 +(defvar notmuch-show-signature-lines-max 12
\r
182 + "Maximum length of signature that will be hidden by default.")
\r
184 +(defvar notmuch-show-citation-lines-prefix 4
\r
185 + "Always show at least this many lines of a citation.
\r
187 +If there is one more line, show that, otherwise collapse
\r
188 +remaining lines into a button.")
\r
190 +(defvar notmuch-show-message-begin-regexp "\fmessage{")
\r
191 +(defvar notmuch-show-message-end-regexp "\fmessage}")
\r
192 +(defvar notmuch-show-header-begin-regexp "\fheader{")
\r
193 +(defvar notmuch-show-header-end-regexp "\fheader}")
\r
194 +(defvar notmuch-show-body-begin-regexp "\fbody{")
\r
195 +(defvar notmuch-show-body-end-regexp "\fbody}")
\r
196 +(defvar notmuch-show-attachment-begin-regexp "\fattachment{")
\r
197 +(defvar notmuch-show-attachment-end-regexp "\fattachment}")
\r
198 +(defvar notmuch-show-part-begin-regexp "\fpart{")
\r
199 +(defvar notmuch-show-part-end-regexp "\fpart}")
\r
200 +(defvar notmuch-show-marker-regexp "\f\\(message\\|header\\|body\\|attachm=
\r
201 ent\\|part\\)[{}].*$")
\r
203 +(defvar notmuch-show-id-regexp "\\(id:[^ ]*\\)")
\r
204 +(defvar notmuch-show-depth-match-regexp " depth:\\([0-9]*\\).*match:\\([01=
\r
206 +(defvar notmuch-show-filename-regexp "filename:\\(.*\\)$")
\r
207 +(defvar notmuch-show-contentype-regexp "Content-type: \\(.*\\)")
\r
209 +(defvar notmuch-show-tags-regexp "(\\([^)]*\\))$")
\r
211 +(defvar notmuch-show-parent-buffer nil)
\r
212 +(defvar notmuch-show-body-read-visible nil)
\r
213 +(defvar notmuch-show-citations-visible nil)
\r
214 +(defvar notmuch-show-signatures-visible nil)
\r
215 +(defvar notmuch-show-headers-visible nil)
\r
217 +(defun notmuch-show-next-line ()
\r
218 + "Like builtin `next-line' but ensuring we end on a visible character.
\r
220 +By advancing forward until reaching a visible character.
\r
222 +Unlike builtin `next-line' this version accepts no arguments."
\r
224 + (set 'this-command 'next-line)
\r
225 + (call-interactively 'next-line)
\r
226 + (while (point-invisible-p)
\r
229 +(defun notmuch-show-previous-line ()
\r
230 + "Like builtin `previous-line' but ensuring we end on a visible character.
\r
232 +By advancing forward until reaching a visible character.
\r
234 +Unlike builtin `previous-line' this version accepts no arguments."
\r
236 + (set 'this-command 'previous-line)
\r
237 + (call-interactively 'previous-line)
\r
238 + (while (point-invisible-p)
\r
241 +(defun notmuch-show-get-message-id ()
\r
243 + (beginning-of-line)
\r
244 + (if (not (looking-at notmuch-show-message-begin-regexp))
\r
245 + (re-search-backward notmuch-show-message-begin-regexp))
\r
246 + (re-search-forward notmuch-show-id-regexp)
\r
247 + (buffer-substring-no-properties (match-beginning 1) (match-end 1))))
\r
249 +(defun notmuch-show-get-filename ()
\r
251 + (beginning-of-line)
\r
252 + (if (not (looking-at notmuch-show-message-begin-regexp))
\r
253 + (re-search-backward notmuch-show-message-begin-regexp))
\r
254 + (re-search-forward notmuch-show-filename-regexp)
\r
255 + (buffer-substring-no-properties (match-beginning 1) (match-end 1))))
\r
257 +(defun notmuch-show-set-tags (tags)
\r
259 + (beginning-of-line)
\r
260 + (if (not (looking-at notmuch-show-message-begin-regexp))
\r
261 + (re-search-backward notmuch-show-message-begin-regexp))
\r
262 + (re-search-forward notmuch-show-tags-regexp)
\r
263 + (let ((inhibit-read-only t)
\r
264 + (beg (match-beginning 1))
\r
265 + (end (match-end 1)))
\r
266 + (delete-region beg end)
\r
268 + (insert (mapconcat 'identity tags " ")))))
\r
270 +(defun notmuch-show-get-tags ()
\r
272 + (beginning-of-line)
\r
273 + (if (not (looking-at notmuch-show-message-begin-regexp))
\r
274 + (re-search-backward notmuch-show-message-begin-regexp))
\r
275 + (re-search-forward notmuch-show-tags-regexp)
\r
276 + (split-string (buffer-substring (match-beginning 1) (match-end 1)))))
\r
278 +(defun notmuch-show-get-bcc ()
\r
279 + "Return BCC address(es) of current message"
\r
280 + (notmuch-show-get-header-field 'bcc))
\r
282 +(defun notmuch-show-get-cc ()
\r
283 + "Return CC address(es) of current message"
\r
284 + (notmuch-show-get-header-field 'cc))
\r
286 +(defun notmuch-show-get-date ()
\r
287 + "Return Date of current message"
\r
288 + (notmuch-show-get-header-field 'date))
\r
290 +(defun notmuch-show-get-from ()
\r
291 + "Return From address of current message"
\r
292 + (notmuch-show-get-header-field 'from))
\r
294 +(defun notmuch-show-get-subject ()
\r
295 + "Return Subject of current message"
\r
296 + (notmuch-show-get-header-field 'subject))
\r
298 +(defun notmuch-show-get-to ()
\r
299 + "Return To address(es) of current message"
\r
300 + (notmuch-show-get-header-field 'to))
\r
302 +(defun notmuch-show-get-header-field (name)
\r
303 + "Retrieve the header field NAME from the current message.
\r
304 +NAME should be a symbol, in lower case, as returned by
\r
305 +mail-header-extract-no-properties"
\r
306 + (let* ((result (assoc name (notmuch-show-get-header)))
\r
307 + (val (and result (cdr result))))
\r
310 +(defun notmuch-show-get-header ()
\r
311 + "Retrieve and parse the header from the current message. Returns an alis=
\r
312 t with of (header . value)
\r
313 +where header is a symbol and value is a string. The summary from notmuch-=
\r
314 show is returned as the
\r
315 +pseudoheader summary"
\r
316 + (require 'mailheader)
\r
318 + (beginning-of-line)
\r
319 + (if (not (looking-at notmuch-show-message-begin-regexp))
\r
320 + (re-search-backward notmuch-show-message-begin-regexp))
\r
321 + (re-search-forward (concat notmuch-show-header-begin-regexp "\n[[:spac=
\r
323 + (let* ((summary (buffer-substring-no-properties (match-beginning 1) (m=
\r
326 + (re-search-forward notmuch-show-header-end-regexp)
\r
327 + (let ((text (buffer-substring beg (match-beginning 0))))
\r
328 + (with-temp-buffer
\r
330 + (goto-char (point-min))
\r
331 + (while (looking-at "\\([[:space:]]*\\)[A-Za-z][-A-Za-z0-9]*:")
\r
332 + (delete-region (match-beginning 1) (match-end 1))
\r
335 + (goto-char (point-min))
\r
336 + (cons (cons 'summary summary) (mail-header-extract-no-properties)))))))
\r
338 +(defun notmuch-show-add-tag (&rest toadd)
\r
339 + "Add a tag to the current message."
\r
341 + (list (notmuch-select-tag-with-completion "Tag to add: ")))
\r
342 + (apply 'notmuch-call-notmuch-process
\r
343 + (append (cons "tag"
\r
344 + (mapcar (lambda (s) (concat "+" s)) toadd))
\r
345 + (cons (notmuch-show-get-message-id) nil)))
\r
346 + (notmuch-show-set-tags (sort (union toadd (notmuch-show-get-tags) :test =
\r
347 'string=3D) 'string<)))
\r
349 +(defun notmuch-show-remove-tag (&rest toremove)
\r
350 + "Remove a tag from the current message."
\r
352 + (list (notmuch-select-tag-with-completion "Tag to remove: " (notmuch-sh=
\r
353 ow-get-message-id))))
\r
354 + (let ((tags (notmuch-show-get-tags)))
\r
355 + (if (intersection tags toremove :test 'string=3D)
\r
357 + (apply 'notmuch-call-notmuch-process
\r
358 + (append (cons "tag"
\r
359 + (mapcar (lambda (s) (concat "-" s)) toremove))
\r
360 + (cons (notmuch-show-get-message-id) nil)))
\r
361 + (notmuch-show-set-tags (sort (set-difference tags toremove :test 'strin=
\r
362 g=3D) 'string<))))))
\r
364 +(defun notmuch-show-archive-thread ()
\r
365 + "Archive each message in thread, then show next thread from search.
\r
367 +Archive each message currently shown by removing the \"inbox\"
\r
368 +tag from each. Then kill this buffer and show the next thread
\r
369 +from the search from which this thread was originally shown.
\r
371 +Note: This command is safe from any race condition of new messages
\r
372 +being delivered to the same thread. It does not archive the
\r
373 +entire thread, but only the messages shown in the current
\r
377 + (goto-char (point-min))
\r
378 + (while (not (eobp))
\r
379 + (notmuch-show-remove-tag "inbox")
\r
382 + (if (not (re-search-forward notmuch-show-message-begin-regexp nil t))
\r
383 + (goto-char (point-max)))))
\r
384 + (let ((parent-buffer notmuch-show-parent-buffer))
\r
385 + (kill-this-buffer)
\r
386 + (if parent-buffer
\r
388 + (switch-to-buffer parent-buffer)
\r
390 + (notmuch-search-show-thread)))))
\r
392 +(defun notmuch-show-archive-thread-then-exit ()
\r
393 + "Archive each message in thread, then exit back to search results."
\r
395 + (notmuch-show-archive-thread)
\r
396 + (kill-this-buffer))
\r
398 +(defun notmuch-show-view-raw-message ()
\r
399 + "View the raw email of the current message."
\r
401 + (view-file (notmuch-show-get-filename)))
\r
403 +(defmacro with-current-notmuch-show-message (&rest body)
\r
404 + "Evaluate body with current buffer set to the text of current message"
\r
406 + (let ((filename (notmuch-show-get-filename)))
\r
407 + (let ((buf (generate-new-buffer (concat "*notmuch-msg-" filename "*=
\r
409 + (with-current-buffer buf
\r
410 + (insert-file-contents filename nil nil nil t)
\r
412 + (kill-buffer buf)))))
\r
414 +(defun notmuch-show-view-all-mime-parts ()
\r
415 + "Use external viewers to view all attachments from the current message."
\r
417 + (with-current-notmuch-show-message
\r
418 + ; We ovverride the mm-inline-media-tests to indicate which message
\r
419 + ; parts are already sufficiently handled by the original
\r
420 + ; presentation of the message in notmuch-show mode. These parts
\r
421 + ; will be inserted directly into the temporary buffer of
\r
422 + ; with-current-notmuch-show-message and silently discarded.
\r
424 + ; Any MIME part not explicitly mentioned here will be handled by an
\r
425 + ; external viewer as configured in the various mailcap files.
\r
426 + (let ((mm-inline-media-tests '(
\r
427 + ("text/.*" ignore identity)
\r
428 + ("application/pgp-signature" ignore identity)
\r
429 + ("multipart/alternative" ignore identity)
\r
430 + ("multipart/mixed" ignore identity)
\r
431 + ("multipart/related" ignore identity)
\r
433 + (mm-display-parts (mm-dissect-buffer)))))
\r
435 +(defun notmuch-show-save-attachments ()
\r
436 + "Save all attachments from the current message."
\r
438 + (with-current-notmuch-show-message
\r
439 + (let ((mm-handle (mm-dissect-buffer)))
\r
440 + (notmuch-save-attachments
\r
441 + mm-handle (> (notmuch-count-attachments mm-handle) 1))))
\r
442 + (message "Done"))
\r
444 +(defun notmuch-show-reply ()
\r
445 + "Begin composing a reply to the current message in a new buffer."
\r
447 + (let ((message-id (notmuch-show-get-message-id)))
\r
448 + (notmuch-reply message-id)))
\r
450 +(defun notmuch-show-forward-current ()
\r
451 + "Forward the current message."
\r
453 + (with-current-notmuch-show-message
\r
454 + (message-forward)))
\r
456 +(defun notmuch-show-pipe-message (command)
\r
457 + "Pipe the contents of the current message to the given command.
\r
459 +The given command will be executed with the raw contents of the
\r
460 +current email message as stdin. Anything printed by the command
\r
461 +to stdout or stderr will appear in the *Messages* buffer."
\r
462 + (interactive "sPipe message to command: ")
\r
463 + (apply 'start-process-shell-command "notmuch-pipe-command" "*notmuch-pip=
\r
465 + (list command " < " (shell-quote-argument (notmuch-show-get-filename)))))
\r
467 +(defun notmuch-show-move-to-current-message-summary-line ()
\r
468 + "Move to the beginning of the one-line summary of the current message.
\r
470 +This gives us a stable place to move to and work from since the
\r
471 +summary line is always visible. This is important since moving to
\r
472 +an invisible location is unreliable, (the main command loop moves
\r
473 +point either forward or backward to the next visible character
\r
474 +when a command ends with point on an invisible character).
\r
476 +Emits an error if point is not within a valid message, (that is
\r
477 +no pattern of `notmuch-show-message-begin-regexp' could be found
\r
478 +by searching backward)."
\r
479 + (beginning-of-line)
\r
480 + (if (not (looking-at notmuch-show-message-begin-regexp))
\r
481 + (if (re-search-backward notmuch-show-message-begin-regexp nil t)
\r
483 + (error "Not within a valid message."))
\r
484 + (forward-line 2)))
\r
486 +(defun notmuch-show-last-message-p ()
\r
487 + "Predicate testing whether point is within the last message."
\r
488 + (save-window-excursion
\r
490 + (notmuch-show-move-to-current-message-summary-line)
\r
491 + (not (re-search-forward notmuch-show-message-begin-regexp nil t)))))
\r
493 +(defun notmuch-show-message-unread-p ()
\r
494 + "Predicate testing whether current message is unread."
\r
495 + (member "unread" (notmuch-show-get-tags)))
\r
497 +(defun notmuch-show-message-open-p ()
\r
498 + "Predicate testing whether current message is open (body is visible)."
\r
499 + (let ((btn (previous-button (point) t)))
\r
500 + (while (not (button-has-type-p btn 'notmuch-button-body-toggle-type))
\r
501 + (setq btn (previous-button (button-start btn))))
\r
502 + (not (invisible-p (button-get btn 'invisibility-spec)))))
\r
504 +(defun notmuch-show-next-message-without-marking-read ()
\r
505 + "Advance to the beginning of the next message in the buffer.
\r
507 +Moves to the last visible character of the current message if
\r
508 +already on the last message in the buffer.
\r
510 +Returns nil if already on the last message in the buffer."
\r
511 + (notmuch-show-move-to-current-message-summary-line)
\r
512 + (if (re-search-forward notmuch-show-message-begin-regexp nil t)
\r
514 + (notmuch-show-move-to-current-message-summary-line)
\r
517 + (goto-char (- (point-max) 1))
\r
518 + (while (point-invisible-p)
\r
523 +(defun notmuch-show-next-message ()
\r
524 + "Advance to the next message (whether open or closed)
\r
525 +and remove the unread tag from that message.
\r
527 +Moves to the last visible character of the current message if
\r
528 +already on the last message in the buffer.
\r
530 +Returns nil if already on the last message in the buffer."
\r
532 + (notmuch-show-next-message-without-marking-read)
\r
533 + (notmuch-show-mark-read))
\r
535 +(defun notmuch-show-find-next-message ()
\r
536 + "Returns the position of the next message in the buffer.
\r
538 +Or the position of the last visible character of the current
\r
539 +message if already within the last message in the buffer."
\r
540 + ; save-excursion doesn't save our window position
\r
541 + ; save-window-excursion doesn't save point
\r
542 + ; Looks like we have to use both.
\r
544 + (save-window-excursion
\r
545 + (notmuch-show-next-message-without-marking-read)
\r
548 +(defun notmuch-show-next-unread-message ()
\r
549 + "Advance to the next unread message.
\r
551 +Moves to the last visible character of the current message if
\r
552 +there are no more unread messages past the current point."
\r
553 + (notmuch-show-next-message-without-marking-read)
\r
554 + (while (and (not (notmuch-show-last-message-p))
\r
555 + (not (notmuch-show-message-unread-p)))
\r
556 + (notmuch-show-next-message-without-marking-read))
\r
557 + (if (not (notmuch-show-message-unread-p))
\r
558 + (notmuch-show-next-message-without-marking-read))
\r
559 + (notmuch-show-mark-read))
\r
561 +(defun notmuch-show-next-open-message ()
\r
562 + "Advance to the next open message (that is, body is visible).
\r
564 +Moves to the last visible character of the final message in the buffer
\r
565 +if there are no more open messages."
\r
567 + (while (and (notmuch-show-next-message-without-marking-read)
\r
568 + (not (notmuch-show-message-open-p))))
\r
569 + (notmuch-show-mark-read))
\r
571 +(defun notmuch-show-previous-message-without-marking-read ()
\r
572 + "Backup to the beginning of the previous message in the buffer.
\r
574 +If within a message rather than at the beginning of it, then
\r
575 +simply move to the beginning of the current message.
\r
577 +Returns nil if already on the first message in the buffer."
\r
578 + (let ((start (point)))
\r
579 + (notmuch-show-move-to-current-message-summary-line)
\r
580 + (if (not (< (point) start))
\r
581 + ; Go backward twice to skip the current message's marker
\r
583 + (re-search-backward notmuch-show-message-begin-regexp nil t)
\r
584 + (re-search-backward notmuch-show-message-begin-regexp nil t)
\r
585 + (notmuch-show-move-to-current-message-summary-line)
\r
587 + (if (=3D (point) start)
\r
593 +(defun notmuch-show-previous-message ()
\r
594 + "Backup to the previous message (whether open or closed)
\r
595 +and remove the unread tag from that message.
\r
597 +If within a message rather than at the beginning of it, then
\r
598 +simply move to the beginning of the current message."
\r
600 + (notmuch-show-previous-message-without-marking-read)
\r
601 + (notmuch-show-mark-read))
\r
603 +(defun notmuch-show-find-previous-message ()
\r
604 + "Returns the position of the previous message in the buffer.
\r
606 +Or the position of the beginning of the current message if point
\r
607 +is originally within the message rather than at the beginning of
\r
609 + ; save-excursion doesn't save our window position
\r
610 + ; save-window-excursion doesn't save point
\r
611 + ; Looks like we have to use both.
\r
613 + (save-window-excursion
\r
614 + (notmuch-show-previous-message-without-marking-read)
\r
617 +(defun notmuch-show-previous-open-message ()
\r
618 + "Backup to previous open message (that is, body is visible).
\r
620 +Moves to the first message in the buffer if there are no previous
\r
623 + (while (and (notmuch-show-previous-message-without-marking-read)
\r
624 + (not (notmuch-show-message-open-p))))
\r
625 + (notmuch-show-mark-read))
\r
627 +(defun notmuch-show-rewind ()
\r
628 + "Backup through the thread, (reverse scrolling compared to \\[notmuch-sh=
\r
629 ow-advance-and-archive]).
\r
631 +Specifically, if the beginning of the previous email is fewer
\r
632 +than `window-height' lines from the current point, move to it
\r
633 +just like `notmuch-show-previous-message'.
\r
635 +Otherwise, just scroll down a screenful of the current message.
\r
637 +This command does not modify any message tags, (it does not undo
\r
638 +any effects from previous calls to
\r
639 +`notmuch-show-advance-and-archive'."
\r
641 + (let ((previous (notmuch-show-find-previous-message)))
\r
642 + (if (> (count-lines previous (point)) (- (window-height) next-screen-c=
\r
645 + (condition-case nil
\r
646 + (scroll-down nil)
\r
647 + ((beginning-of-buffer) nil))
\r
648 + (goto-char (window-start))
\r
649 + ; Because count-lines counts invivisible lines, we may have
\r
650 + ; scrolled to far. If so., notice this and fix it up.
\r
651 + (if (< (point) previous)
\r
653 + (goto-char previous)
\r
655 + (notmuch-show-previous-message))))
\r
657 +(defun notmuch-show-advance-and-archive ()
\r
658 + "Advance through thread and archive.
\r
660 +This command is intended to be one of the simplest ways to
\r
661 +process a thread of email. It does the following:
\r
663 +If the current message in the thread is not yet fully visible,
\r
664 +scroll by a near screenful to read more of the message.
\r
666 +Otherwise, (the end of the current message is already within the
\r
667 +current window), advance to the next open message.
\r
669 +Finally, if there is no further message to advance to, and this
\r
670 +last message is already read, then archive the entire current
\r
671 +thread, (remove the \"inbox\" tag from each message). Also kill
\r
672 +this buffer, and display the next thread from the search from
\r
673 +which this thread was originally shown."
\r
675 + (let ((next (notmuch-show-find-next-message))
\r
676 + (unread (notmuch-show-message-unread-p)))
\r
677 + (if (> next (window-end))
\r
679 + (let ((last (notmuch-show-last-message-p)))
\r
680 + (notmuch-show-next-open-message)
\r
682 + (notmuch-show-archive-thread))))))
\r
684 +(defun notmuch-show-next-button ()
\r
685 + "Advance point to the next button in the buffer."
\r
687 + (forward-button 1))
\r
689 +(defun notmuch-show-previous-button ()
\r
690 + "Move point back to the previous button in the buffer."
\r
692 + (backward-button 1))
\r
694 +(defun notmuch-show-toggle-current-body ()
\r
695 + "Toggle the display of the current message body."
\r
698 + (notmuch-show-move-to-current-message-summary-line)
\r
699 + (unless (button-at (point))
\r
700 + (notmuch-show-next-button))
\r
704 +(defun notmuch-show-toggle-current-header ()
\r
705 + "Toggle the display of the current message header."
\r
708 + (notmuch-show-move-to-current-message-summary-line)
\r
710 + (unless (button-at (point))
\r
711 + (notmuch-show-next-button))
\r
715 +(defun notmuch-show-citation-regexp (depth)
\r
716 + "Build a regexp for matching citations at a given DEPTH (indent)"
\r
717 + (let ((line-regexp (format "[[:space:]]\\{%d\\}>.*\n" depth)))
\r
718 + (concat "\\(?:^" line-regexp
\r
719 + "\\(?:[[:space:]]*\n" line-regexp
\r
722 +(defun notmuch-show-region-to-button (beg end type prefix button-text)
\r
723 + "Auxilary function to do the actual making of overlays and buttons
\r
725 +BEG and END are buffer locations. TYPE should a string, either
\r
726 +\"citation\" or \"signature\". PREFIX is some arbitrary text to
\r
727 +insert before the button, probably for indentation. BUTTON-TEXT
\r
728 +is what to put on the button."
\r
730 +;; This uses some slightly tricky conversions between strings and
\r
731 +;; symbols because of the way the button code works. Note that
\r
732 +;; replacing intern-soft with make-symbol will cause this to fail,
\r
733 +;; since the newly created symbol has no plist.
\r
735 + (let ((overlay (make-overlay beg end))
\r
736 + (invis-spec (make-symbol (concat "notmuch-" type "-region")))
\r
737 + (button-type (intern-soft (concat "notmuch-button-"
\r
738 + type "-toggle-type"))))
\r
739 + (add-to-invisibility-spec invis-spec)
\r
740 + (overlay-put overlay 'invisible invis-spec)
\r
741 + (goto-char (1+ end))
\r
743 + (goto-char (1- beg))
\r
745 + (insert-button button-text
\r
746 + 'invisibility-spec invis-spec
\r
747 + :type button-type)
\r
750 +(defun notmuch-show-markup-citations-region (beg end depth)
\r
751 + "Markup citations, and up to one signature in the given region"
\r
752 + ;; it would be nice if the untabify was not required, but
\r
753 + ;; that would require notmuch to indent with spaces.
\r
754 + (untabify beg end)
\r
755 + (let ((citation-regexp (notmuch-show-citation-regexp depth))
\r
756 + (signature-regexp (concat (format "^[[:space:]]\\{%d\\}" depth)
\r
757 + notmuch-show-signature-regexp))
\r
758 + (indent (concat "\n" (make-string depth ? ))))
\r
760 + (beginning-of-line)
\r
761 + (while (and (< (point) end)
\r
762 + (re-search-forward citation-regexp end t))
\r
763 + (let* ((cite-start (match-beginning 0))
\r
764 + (cite-end (match-end 0))
\r
765 + (cite-lines (count-lines cite-start cite-end)))
\r
766 + (when (> cite-lines (1+ notmuch-show-citation-lines-prefix))
\r
767 + (goto-char cite-start)
\r
768 + (forward-line notmuch-show-citation-lines-prefix)
\r
769 + (notmuch-show-region-to-button
\r
773 + (format notmuch-show-citation-button-format
\r
774 + (- cite-lines notmuch-show-citation-lines-prefix))
\r
776 + (if (and (< (point) end)
\r
777 + (re-search-forward signature-regexp end t))
\r
778 + (let* ((sig-start (match-beginning 0))
\r
779 + (sig-end (match-end 0))
\r
780 + (sig-lines (1- (count-lines sig-start end))))
\r
781 + (if (<=3D sig-lines notmuch-show-signature-lines-max)
\r
782 + (notmuch-show-region-to-button
\r
787 + (format notmuch-show-signature-button-format sig-lines)
\r
790 +(defun notmuch-show-markup-part (beg end depth)
\r
791 + (if (re-search-forward notmuch-show-part-begin-regexp nil t)
\r
793 + (let (mime-message mime-type)
\r
795 + (re-search-forward notmuch-show-contentype-regexp end t)
\r
796 + (setq mime-type (car (split-string (buffer-substring
\r
797 + (match-beginning 1) (match=
\r
800 + (if (equal mime-type "text/html")
\r
801 + (let ((filename (notmuch-show-get-filename)))
\r
802 + (with-temp-buffer
\r
803 + (insert-file-contents filename nil nil nil t)
\r
804 + (setq mime-message (mm-dissect-buffer)))))
\r
806 + (let ((beg (point-marker)))
\r
807 + (re-search-forward notmuch-show-part-end-regexp)
\r
808 + (let ((end (copy-marker (match-beginning 0))))
\r
812 + (indent-rigidly beg end depth)
\r
813 + (if (not (eq mime-message nil))
\r
816 + (forward-line -1)
\r
817 + (let ((handle-type (mm-handle-type mime-message))
\r
819 + (if (sequencep (car handle-type))
\r
820 + (setq mime-type (car handle-type))
\r
821 + (setq mime-type (car (car (cdr handle-type))))
\r
823 + (if (equal mime-type "text/html")
\r
824 + (mm-display-part mime-message))))
\r
826 + (notmuch-show-markup-citations-region beg end depth)
\r
827 + ; Advance to the next part (if any) (so the outer loop can
\r
828 + ; determine whether we've left the current message.
\r
829 + (if (re-search-forward notmuch-show-part-begin-regexp nil t)
\r
830 + (beginning-of-line)))))
\r
832 + (goto-char end)))
\r
834 +(defun notmuch-show-markup-parts-region (beg end depth)
\r
837 + (while (< (point) end)
\r
838 + (notmuch-show-markup-part beg end depth))))
\r
840 +(defun notmuch-show-markup-body (depth match btn)
\r
841 + "Markup a message body, (indenting, buttonizing citations,
\r
842 +etc.), and hiding the body itself if the message does not match
\r
843 +the current search.
\r
845 +DEPTH specifies the depth at which this message appears in the
\r
846 +tree of the current thread, (the top-level messages have depth 0
\r
847 +and each reply increases depth by 1). MATCH indicates whether
\r
848 +this message is regarded as matching the current search. BTN is
\r
849 +the button which is used to toggle the visibility of this
\r
852 +When this function is called, point must be within the message, but
\r
853 +before the delimiter marking the beginning of the body."
\r
854 + (re-search-forward notmuch-show-body-begin-regexp)
\r
856 + (let ((beg (point-marker)))
\r
857 + (re-search-forward notmuch-show-body-end-regexp)
\r
858 + (let ((end (copy-marker (match-beginning 0))))
\r
859 + (notmuch-show-markup-parts-region beg end depth)
\r
860 + (let ((invis-spec (make-symbol "notmuch-show-body-read")))
\r
861 + (overlay-put (make-overlay beg end)
\r
862 + 'invisible invis-spec)
\r
863 + (button-put btn 'invisibility-spec invis-spec)
\r
865 + (add-to-invisibility-spec invis-spec)))
\r
866 + (set-marker beg nil)
\r
867 + (set-marker end nil)
\r
870 +(defun notmuch-show-markup-header (message-begin depth)
\r
871 + "Buttonize and decorate faces in a message header.
\r
873 +MESSAGE-BEGIN is the position of the absolute first character in
\r
874 +the message (including all delimiters that will end up being
\r
875 +invisible etc.). This is to allow a button to reliably extend to
\r
876 +the beginning of the message even if point is positioned at an
\r
877 +invisible character (such as the beginning of the buffer).
\r
879 +DEPTH specifies the depth at which this message appears in the
\r
880 +tree of the current thread, (the top-level messages have depth 0
\r
881 +and each reply increases depth by 1)."
\r
882 + (re-search-forward notmuch-show-header-begin-regexp)
\r
884 + (let ((beg (point-marker))
\r
885 + (summary-end (copy-marker (line-beginning-position 2)))
\r
886 + (subject-end (copy-marker (line-end-position 2)))
\r
887 + (invis-spec (make-symbol "notmuch-show-header"))
\r
889 + (re-search-forward notmuch-show-header-end-regexp)
\r
890 + (beginning-of-line)
\r
891 + (let ((end (point-marker)))
\r
892 + (indent-rigidly beg end depth)
\r
894 + (setq btn (make-button message-begin summary-end :type 'notmuch-butt=
\r
895 on-body-toggle-type))
\r
897 + (add-to-invisibility-spec invis-spec)
\r
898 + (overlay-put (make-overlay subject-end end)
\r
899 + 'invisible invis-spec)
\r
900 + (make-button (line-beginning-position) subject-end
\r
901 + 'invisibility-spec invis-spec
\r
902 + :type 'notmuch-button-headers-toggle-type)
\r
903 + (while (looking-at "[[:space:]]*[A-Za-z][-A-Za-z0-9]*:")
\r
904 + (beginning-of-line)
\r
905 + (notmuch-fontify-headers)
\r
910 + (set-marker beg nil)
\r
911 + (set-marker summary-end nil)
\r
912 + (set-marker subject-end nil)
\r
913 + (set-marker end nil)
\r
917 +(defun notmuch-show-markup-message ()
\r
918 + (if (re-search-forward notmuch-show-message-begin-regexp nil t)
\r
919 + (let ((message-begin (match-beginning 0)))
\r
920 + (re-search-forward notmuch-show-depth-match-regexp)
\r
921 + (let ((depth (string-to-number (buffer-substring (match-beginning 1) (mat=
\r
923 + (match (string=3D "1" (buffer-substring (match-beginning 2) (match-=
\r
926 + (setq btn (notmuch-show-markup-header message-begin depth))
\r
927 + (notmuch-show-markup-body depth match btn)))
\r
928 + (goto-char (point-max))))
\r
930 +(defun notmuch-show-hide-markers ()
\r
932 + (goto-char (point-min))
\r
933 + (while (not (eobp))
\r
934 + (if (re-search-forward notmuch-show-marker-regexp nil t)
\r
936 + (overlay-put (make-overlay (match-beginning 0) (+ (match-end 0) 1))
\r
937 + 'invisible 'notmuch-show-marker))
\r
938 + (goto-char (point-max))))))
\r
940 +(defun notmuch-show-markup-messages ()
\r
942 + (goto-char (point-min))
\r
943 + (while (not (eobp))
\r
944 + (notmuch-show-markup-message)))
\r
945 + (notmuch-show-hide-markers))
\r
948 +(defun notmuch-show-mode ()
\r
949 + "Major mode for viewing a thread with notmuch.
\r
951 +This buffer contains the results of the \"notmuch show\" command
\r
952 +for displaying a single thread of email from your email archives.
\r
954 +By default, various components of email messages, (citations,
\r
955 +signatures, already-read messages), are hidden. You can make
\r
956 +these parts visible by clicking with the mouse button or by
\r
957 +pressing RET after positioning the cursor on a hidden part, (for
\r
958 +which \\[notmuch-show-next-button] and \\[notmuch-show-previous-button] ar=
\r
961 +Reading the thread sequentially is well-supported by pressing
\r
962 +\\[notmuch-show-advance-and-archive]. This will
\r
963 +scroll the current message (if necessary), advance to the next
\r
964 +message, or advance to the next thread (if already on the last
\r
965 +message of a thread).
\r
967 +Other commands are available to read or manipulate the thread more
\r
968 +selectively, (such as '\\[notmuch-show-next-message]' and '\\[notmuch-show=
\r
969 -previous-message]' to advance to messages without
\r
970 +removing any tags, and '\\[notmuch-show-archive-thread]' to archive an ent=
\r
972 +scrolling through with \\[notmuch-show-advance-and-archive]).
\r
974 +You can add or remove arbitary tags from the current message with
\r
975 +'\\[notmuch-show-add-tag]' or '\\[notmuch-show-remove-tag]'.
\r
977 +All currently available key bindings:
\r
979 +\\{notmuch-show-mode-map}"
\r
981 + (kill-all-local-variables)
\r
982 + (add-to-invisibility-spec 'notmuch-show-marker)
\r
983 + (use-local-map notmuch-show-mode-map)
\r
984 + (setq major-mode 'notmuch-show-mode
\r
985 + mode-name "notmuch-show")
\r
986 + (setq buffer-read-only t))
\r
988 +(defcustom notmuch-show-hook nil
\r
989 + "List of functions to call when notmuch displays a message."
\r
991 + :options '(goto-address)
\r
994 +(defun notmuch-show-do-stash (text)
\r
996 + (message (concat "Saved: " text)))
\r
998 +(defun notmuch-show-stash-cc ()
\r
999 + "Copy CC field of current message to kill-ring."
\r
1001 + (notmuch-show-do-stash (notmuch-show-get-cc)))
\r
1003 +(defun notmuch-show-stash-date ()
\r
1004 + "Copy date of current message to kill-ring."
\r
1006 + (notmuch-show-do-stash (notmuch-show-get-date)))
\r
1008 +(defun notmuch-show-stash-filename ()
\r
1009 + "Copy filename of current message to kill-ring."
\r
1011 + (notmuch-show-do-stash (notmuch-show-get-filename)))
\r
1013 +(defun notmuch-show-stash-from ()
\r
1014 + "Copy From address of current message to kill-ring."
\r
1016 + (notmuch-show-do-stash (notmuch-show-get-from)))
\r
1018 +(defun notmuch-show-stash-message-id ()
\r
1019 + "Copy message ID of current message to kill-ring."
\r
1021 + (notmuch-show-do-stash (notmuch-show-get-message-id)))
\r
1023 +(defun notmuch-show-stash-subject ()
\r
1024 + "Copy Subject field of current message to kill-ring."
\r
1026 + (notmuch-show-do-stash (notmuch-show-get-subject)))
\r
1028 +(defun notmuch-show-stash-tags ()
\r
1029 + "Copy tags of current message to kill-ring as a comma separated list."
\r
1031 + (notmuch-show-do-stash (mapconcat 'identity (notmuch-show-get-tags) ",")=
\r
1034 +(defun notmuch-show-stash-to ()
\r
1035 + "Copy To address of current message to kill-ring."
\r
1037 + (notmuch-show-do-stash (notmuch-show-get-to)))
\r
1039 +; Make show mode a bit prettier, highlighting URLs and using word wrap
\r
1041 +(defun notmuch-show-mark-read ()
\r
1042 + (notmuch-show-remove-tag "unread"))
\r
1044 +(defun notmuch-show-pretty-hook ()
\r
1045 + (goto-address-mode 1)
\r
1046 + (visual-line-mode))
\r
1048 +(add-hook 'notmuch-show-hook 'notmuch-show-mark-read)
\r
1049 +(add-hook 'notmuch-show-hook 'notmuch-show-pretty-hook)
\r
1050 +(add-hook 'notmuch-search-hook
\r
1052 + (hl-line-mode 1) ))
\r
1054 +(defun notmuch-show (thread-id &optional parent-buffer query-context)
\r
1055 + "Run \"notmuch show\" with the given thread ID and display results.
\r
1057 +The optional PARENT-BUFFER is the notmuch-search buffer from
\r
1058 +which this notmuch-show command was executed, (so that the next
\r
1059 +thread from that buffer can be show when done with this one).
\r
1061 +The optional QUERY-CONTEXT is a notmuch search term. Only messages from th=
\r
1063 +matching this search term are shown if non-nil. "
\r
1064 + (interactive "sNotmuch show: ")
\r
1065 + (let ((buffer (get-buffer-create (concat "*notmuch-show-" thread-id "*")=
\r
1067 + (switch-to-buffer buffer)
\r
1068 + (notmuch-show-mode)
\r
1069 + (set (make-local-variable 'notmuch-show-parent-buffer) parent-buffer)
\r
1070 + (let ((proc (get-buffer-process (current-buffer)))
\r
1071 + (inhibit-read-only t))
\r
1073 + (error "notmuch search process already running for query `%s'" thread-i=
\r
1077 + (goto-char (point-min))
\r
1079 + (let* ((basic-args (list notmuch-command nil t nil "show" "--entire-threa=
\r
1081 + (args (if query-context (append basic-args (list "and (" query-context "=
\r
1082 )")) basic-args)))
\r
1083 + (apply 'call-process args)
\r
1084 + (when (and (eq (buffer-size) 0) query-context)
\r
1085 + (apply 'call-process basic-args)))
\r
1086 + (notmuch-show-markup-messages)
\r
1088 + (run-hooks 'notmuch-show-hook)
\r
1089 + ; Move straight to the first open message
\r
1090 + (if (not (notmuch-show-message-open-p))
\r
1091 + (notmuch-show-next-open-message))
\r
1094 +(provide 'notmuch-show)
\r
1095 diff --git a/emacs/notmuch.el b/emacs/notmuch.el
\r
1096 index 117a365..66990eb 100644
\r
1097 --- a/emacs/notmuch.el
\r
1098 +++ b/emacs/notmuch.el
\r
1099 @@ -51,114 +51,11 @@
\r
1100 (require 'mm-view)
\r
1101 (require 'message)
\r
1103 -(defvar notmuch-show-stash-map
\r
1104 - (let ((map (make-sparse-keymap)))
\r
1105 - (define-key map "c" 'notmuch-show-stash-cc)
\r
1106 - (define-key map "d" 'notmuch-show-stash-date)
\r
1107 - (define-key map "F" 'notmuch-show-stash-filename)
\r
1108 - (define-key map "f" 'notmuch-show-stash-from)
\r
1109 - (define-key map "i" 'notmuch-show-stash-message-id)
\r
1110 - (define-key map "s" 'notmuch-show-stash-subject)
\r
1111 - (define-key map "T" 'notmuch-show-stash-tags)
\r
1112 - (define-key map "t" 'notmuch-show-stash-to)
\r
1114 - "Submap for stash commands"
\r
1117 -(fset 'notmuch-show-stash-map notmuch-show-stash-map)
\r
1119 -(defvar notmuch-show-mode-map
\r
1120 - (let ((map (make-sparse-keymap)))
\r
1121 - (define-key map "?" 'notmuch-help)
\r
1122 - (define-key map "q" 'kill-this-buffer)
\r
1123 - (define-key map (kbd "C-p") 'notmuch-show-previous-line)
\r
1124 - (define-key map (kbd "C-n") 'notmuch-show-next-line)
\r
1125 - (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
\r
1126 - (define-key map (kbd "TAB") 'notmuch-show-next-button)
\r
1127 - (define-key map "s" 'notmuch-search)
\r
1128 - (define-key map "m" 'message-mail)
\r
1129 - (define-key map "f" 'notmuch-show-forward-current)
\r
1130 - (define-key map "r" 'notmuch-show-reply)
\r
1131 - (define-key map "|" 'notmuch-show-pipe-message)
\r
1132 - (define-key map "w" 'notmuch-show-save-attachments)
\r
1133 - (define-key map "V" 'notmuch-show-view-raw-message)
\r
1134 - (define-key map "v" 'notmuch-show-view-all-mime-parts)
\r
1135 - (define-key map "c" 'notmuch-show-stash-map)
\r
1136 - (define-key map "b" 'notmuch-show-toggle-current-body)
\r
1137 - (define-key map "h" 'notmuch-show-toggle-current-header)
\r
1138 - (define-key map "-" 'notmuch-show-remove-tag)
\r
1139 - (define-key map "+" 'notmuch-show-add-tag)
\r
1140 - (define-key map "x" 'notmuch-show-archive-thread-then-exit)
\r
1141 - (define-key map "a" 'notmuch-show-archive-thread)
\r
1142 - (define-key map "P" 'notmuch-show-previous-message)
\r
1143 - (define-key map "N" 'notmuch-show-next-message)
\r
1144 - (define-key map "p" 'notmuch-show-previous-open-message)
\r
1145 - (define-key map "n" 'notmuch-show-next-open-message)
\r
1146 - (define-key map (kbd "DEL") 'notmuch-show-rewind)
\r
1147 - (define-key map " " 'notmuch-show-advance-and-archive)
\r
1149 - "Keymap for \"notmuch show\" buffers.")
\r
1150 -(fset 'notmuch-show-mode-map notmuch-show-mode-map)
\r
1152 -(defvar notmuch-show-signature-regexp "\\(-- ?\\|_+\\)$"
\r
1153 - "Pattern to match a line that separates content from signature.
\r
1155 -The regexp can (and should) include $ to match the end of the
\r
1156 -line, but should not include ^ to match the beginning of the
\r
1157 -line. This is because notmuch may have inserted additional space
\r
1158 -for indentation at the beginning of the line. But notmuch will
\r
1159 -move past the indentation when testing this pattern, (so that the
\r
1160 -pattern can still test against the entire line).")
\r
1162 -(defvar notmuch-show-signature-button-format
\r
1163 - "[ %d-line signature. Click/Enter to toggle visibility. ]"
\r
1164 - "String used to construct button text for hidden signatures
\r
1166 -Can use up to one integer format parameter, i.e. %d")
\r
1168 -(defvar notmuch-show-citation-button-format
\r
1169 - "[ %d more citation lines. Click/Enter to toggle visibility. ]"
\r
1170 - "String used to construct button text for hidden citations.
\r
1172 -Can use up to one integer format parameter, i.e. %d")
\r
1174 -(defvar notmuch-show-signature-lines-max 12
\r
1175 - "Maximum length of signature that will be hidden by default.")
\r
1177 -(defvar notmuch-show-citation-lines-prefix 4
\r
1178 - "Always show at least this many lines of a citation.
\r
1180 -If there is one more line, show that, otherwise collapse
\r
1181 -remaining lines into a button.")
\r
1182 +(require 'notmuch-show)
\r
1184 (defvar notmuch-command "notmuch"
\r
1185 "Command to run the notmuch binary.")
\r
1187 -(defvar notmuch-show-message-begin-regexp "\fmessage{")
\r
1188 -(defvar notmuch-show-message-end-regexp "\fmessage}")
\r
1189 -(defvar notmuch-show-header-begin-regexp "\fheader{")
\r
1190 -(defvar notmuch-show-header-end-regexp "\fheader}")
\r
1191 -(defvar notmuch-show-body-begin-regexp "\fbody{")
\r
1192 -(defvar notmuch-show-body-end-regexp "\fbody}")
\r
1193 -(defvar notmuch-show-attachment-begin-regexp "\fattachment{")
\r
1194 -(defvar notmuch-show-attachment-end-regexp "\fattachment}")
\r
1195 -(defvar notmuch-show-part-begin-regexp "\fpart{")
\r
1196 -(defvar notmuch-show-part-end-regexp "\fpart}")
\r
1197 -(defvar notmuch-show-marker-regexp "\f\\(message\\|header\\|body\\|attachm=
\r
1198 ent\\|part\\)[{}].*$")
\r
1200 -(defvar notmuch-show-id-regexp "\\(id:[^ ]*\\)")
\r
1201 -(defvar notmuch-show-depth-match-regexp " depth:\\([0-9]*\\).*match:\\([01=
\r
1203 -(defvar notmuch-show-filename-regexp "filename:\\(.*\\)$")
\r
1204 -(defvar notmuch-show-contentype-regexp "Content-type: \\(.*\\)")
\r
1206 -(defvar notmuch-show-tags-regexp "(\\([^)]*\\))$")
\r
1208 -(defvar notmuch-show-parent-buffer nil)
\r
1209 -(defvar notmuch-show-body-read-visible nil)
\r
1210 -(defvar notmuch-show-citations-visible nil)
\r
1211 -(defvar notmuch-show-signatures-visible nil)
\r
1212 -(defvar notmuch-show-headers-visible nil)
\r
1214 ; XXX: This should be a generic function in emacs somewhere, not here
\r
1215 (defun point-invisible-p ()
\r
1216 "Return whether the character at point is invisible.
\r
1217 @@ -180,216 +77,6 @@ within the current window."
\r
1218 (apply 'call-process notmuch-command nil t nil "search-tags" search-=
\r
1220 (completing-read prompt (split-string tag-list "\n+" t) nil nil nil)))
\r
1222 -(defun notmuch-show-next-line ()
\r
1223 - "Like builtin `next-line' but ensuring we end on a visible character.
\r
1225 -By advancing forward until reaching a visible character.
\r
1227 -Unlike builtin `next-line' this version accepts no arguments."
\r
1229 - (set 'this-command 'next-line)
\r
1230 - (call-interactively 'next-line)
\r
1231 - (while (point-invisible-p)
\r
1232 - (forward-char)))
\r
1234 -(defun notmuch-show-previous-line ()
\r
1235 - "Like builtin `previous-line' but ensuring we end on a visible character.
\r
1237 -By advancing forward until reaching a visible character.
\r
1239 -Unlike builtin `previous-line' this version accepts no arguments."
\r
1241 - (set 'this-command 'previous-line)
\r
1242 - (call-interactively 'previous-line)
\r
1243 - (while (point-invisible-p)
\r
1244 - (forward-char)))
\r
1246 -(defun notmuch-show-get-message-id ()
\r
1248 - (beginning-of-line)
\r
1249 - (if (not (looking-at notmuch-show-message-begin-regexp))
\r
1250 - (re-search-backward notmuch-show-message-begin-regexp))
\r
1251 - (re-search-forward notmuch-show-id-regexp)
\r
1252 - (buffer-substring-no-properties (match-beginning 1) (match-end 1))))
\r
1254 -(defun notmuch-show-get-filename ()
\r
1256 - (beginning-of-line)
\r
1257 - (if (not (looking-at notmuch-show-message-begin-regexp))
\r
1258 - (re-search-backward notmuch-show-message-begin-regexp))
\r
1259 - (re-search-forward notmuch-show-filename-regexp)
\r
1260 - (buffer-substring-no-properties (match-beginning 1) (match-end 1))))
\r
1262 -(defun notmuch-show-set-tags (tags)
\r
1264 - (beginning-of-line)
\r
1265 - (if (not (looking-at notmuch-show-message-begin-regexp))
\r
1266 - (re-search-backward notmuch-show-message-begin-regexp))
\r
1267 - (re-search-forward notmuch-show-tags-regexp)
\r
1268 - (let ((inhibit-read-only t)
\r
1269 - (beg (match-beginning 1))
\r
1270 - (end (match-end 1)))
\r
1271 - (delete-region beg end)
\r
1273 - (insert (mapconcat 'identity tags " ")))))
\r
1275 -(defun notmuch-show-get-tags ()
\r
1277 - (beginning-of-line)
\r
1278 - (if (not (looking-at notmuch-show-message-begin-regexp))
\r
1279 - (re-search-backward notmuch-show-message-begin-regexp))
\r
1280 - (re-search-forward notmuch-show-tags-regexp)
\r
1281 - (split-string (buffer-substring (match-beginning 1) (match-end 1)))))
\r
1283 -(defun notmuch-show-get-bcc ()
\r
1284 - "Return BCC address(es) of current message"
\r
1285 - (notmuch-show-get-header-field 'bcc))
\r
1287 -(defun notmuch-show-get-cc ()
\r
1288 - "Return CC address(es) of current message"
\r
1289 - (notmuch-show-get-header-field 'cc))
\r
1291 -(defun notmuch-show-get-date ()
\r
1292 - "Return Date of current message"
\r
1293 - (notmuch-show-get-header-field 'date))
\r
1295 -(defun notmuch-show-get-from ()
\r
1296 - "Return From address of current message"
\r
1297 - (notmuch-show-get-header-field 'from))
\r
1299 -(defun notmuch-show-get-subject ()
\r
1300 - "Return Subject of current message"
\r
1301 - (notmuch-show-get-header-field 'subject))
\r
1303 -(defun notmuch-show-get-to ()
\r
1304 - "Return To address(es) of current message"
\r
1305 - (notmuch-show-get-header-field 'to))
\r
1307 -(defun notmuch-show-get-header-field (name)
\r
1308 - "Retrieve the header field NAME from the current message.
\r
1309 -NAME should be a symbol, in lower case, as returned by
\r
1310 -mail-header-extract-no-properties"
\r
1311 - (let* ((result (assoc name (notmuch-show-get-header)))
\r
1312 - (val (and result (cdr result))))
\r
1315 -(defun notmuch-show-get-header ()
\r
1316 - "Retrieve and parse the header from the current message. Returns an alis=
\r
1317 t with of (header . value)
\r
1318 -where header is a symbol and value is a string. The summary from notmuch-=
\r
1319 show is returned as the
\r
1320 -pseudoheader summary"
\r
1321 - (require 'mailheader)
\r
1323 - (beginning-of-line)
\r
1324 - (if (not (looking-at notmuch-show-message-begin-regexp))
\r
1325 - (re-search-backward notmuch-show-message-begin-regexp))
\r
1326 - (re-search-forward (concat notmuch-show-header-begin-regexp "\n[[:spac=
\r
1327 e:]]*\\(.*\\)\n"))
\r
1328 - (let* ((summary (buffer-substring-no-properties (match-beginning 1) (m=
\r
1331 - (re-search-forward notmuch-show-header-end-regexp)
\r
1332 - (let ((text (buffer-substring beg (match-beginning 0))))
\r
1333 - (with-temp-buffer
\r
1335 - (goto-char (point-min))
\r
1336 - (while (looking-at "\\([[:space:]]*\\)[A-Za-z][-A-Za-z0-9]*:")
\r
1337 - (delete-region (match-beginning 1) (match-end 1))
\r
1340 - (goto-char (point-min))
\r
1341 - (cons (cons 'summary summary) (mail-header-extract-no-properties)))))))
\r
1343 -(defun notmuch-show-add-tag (&rest toadd)
\r
1344 - "Add a tag to the current message."
\r
1346 - (list (notmuch-select-tag-with-completion "Tag to add: ")))
\r
1347 - (apply 'notmuch-call-notmuch-process
\r
1348 - (append (cons "tag"
\r
1349 - (mapcar (lambda (s) (concat "+" s)) toadd))
\r
1350 - (cons (notmuch-show-get-message-id) nil)))
\r
1351 - (notmuch-show-set-tags (sort (union toadd (notmuch-show-get-tags) :test =
\r
1352 'string=3D) 'string<)))
\r
1354 -(defun notmuch-show-remove-tag (&rest toremove)
\r
1355 - "Remove a tag from the current message."
\r
1357 - (list (notmuch-select-tag-with-completion "Tag to remove: " (notmuch-sh=
\r
1358 ow-get-message-id))))
\r
1359 - (let ((tags (notmuch-show-get-tags)))
\r
1360 - (if (intersection tags toremove :test 'string=3D)
\r
1362 - (apply 'notmuch-call-notmuch-process
\r
1363 - (append (cons "tag"
\r
1364 - (mapcar (lambda (s) (concat "-" s)) toremove))
\r
1365 - (cons (notmuch-show-get-message-id) nil)))
\r
1366 - (notmuch-show-set-tags (sort (set-difference tags toremove :test 'strin=
\r
1367 g=3D) 'string<))))))
\r
1369 -(defun notmuch-show-archive-thread ()
\r
1370 - "Archive each message in thread, then show next thread from search.
\r
1372 -Archive each message currently shown by removing the \"inbox\"
\r
1373 -tag from each. Then kill this buffer and show the next thread
\r
1374 -from the search from which this thread was originally shown.
\r
1376 -Note: This command is safe from any race condition of new messages
\r
1377 -being delivered to the same thread. It does not archive the
\r
1378 -entire thread, but only the messages shown in the current
\r
1382 - (goto-char (point-min))
\r
1383 - (while (not (eobp))
\r
1384 - (notmuch-show-remove-tag "inbox")
\r
1385 - (if (not (eobp))
\r
1387 - (if (not (re-search-forward notmuch-show-message-begin-regexp nil t))
\r
1388 - (goto-char (point-max)))))
\r
1389 - (let ((parent-buffer notmuch-show-parent-buffer))
\r
1390 - (kill-this-buffer)
\r
1391 - (if parent-buffer
\r
1393 - (switch-to-buffer parent-buffer)
\r
1395 - (notmuch-search-show-thread)))))
\r
1397 -(defun notmuch-show-archive-thread-then-exit ()
\r
1398 - "Archive each message in thread, then exit back to search results."
\r
1400 - (notmuch-show-archive-thread)
\r
1401 - (kill-this-buffer))
\r
1403 -(defun notmuch-show-view-raw-message ()
\r
1404 - "View the raw email of the current message."
\r
1406 - (view-file (notmuch-show-get-filename)))
\r
1408 -(defmacro with-current-notmuch-show-message (&rest body)
\r
1409 - "Evaluate body with current buffer set to the text of current message"
\r
1410 - `(save-excursion
\r
1411 - (let ((filename (notmuch-show-get-filename)))
\r
1412 - (let ((buf (generate-new-buffer (concat "*notmuch-msg-" filename "*=
\r
1414 - (with-current-buffer buf
\r
1415 - (insert-file-contents filename nil nil nil t)
\r
1417 - (kill-buffer buf)))))
\r
1419 -(defun notmuch-show-view-all-mime-parts ()
\r
1420 - "Use external viewers to view all attachments from the current message."
\r
1422 - (with-current-notmuch-show-message
\r
1423 - ; We ovverride the mm-inline-media-tests to indicate which message
\r
1424 - ; parts are already sufficiently handled by the original
\r
1425 - ; presentation of the message in notmuch-show mode. These parts
\r
1426 - ; will be inserted directly into the temporary buffer of
\r
1427 - ; with-current-notmuch-show-message and silently discarded.
\r
1429 - ; Any MIME part not explicitly mentioned here will be handled by an
\r
1430 - ; external viewer as configured in the various mailcap files.
\r
1431 - (let ((mm-inline-media-tests '(
\r
1432 - ("text/.*" ignore identity)
\r
1433 - ("application/pgp-signature" ignore identity)
\r
1434 - ("multipart/alternative" ignore identity)
\r
1435 - ("multipart/mixed" ignore identity)
\r
1436 - ("multipart/related" ignore identity)
\r
1438 - (mm-display-parts (mm-dissect-buffer)))))
\r
1440 (defun notmuch-foreach-mime-part (function mm-handle)
\r
1441 (cond ((stringp (car mm-handle))
\r
1442 (dolist (part (cdr mm-handle))
\r
1443 @@ -426,15 +113,6 @@ buffer."
\r
1444 (mm-save-part p))))
\r
1447 -(defun notmuch-show-save-attachments ()
\r
1448 - "Save all attachments from the current message."
\r
1450 - (with-current-notmuch-show-message
\r
1451 - (let ((mm-handle (mm-dissect-buffer)))
\r
1452 - (notmuch-save-attachments
\r
1453 - mm-handle (> (notmuch-count-attachments mm-handle) 1))))
\r
1454 - (message "Done"))
\r
1456 (defun notmuch-reply (query-string)
\r
1457 (switch-to-buffer (generate-new-buffer "notmuch-draft"))
\r
1458 (call-process notmuch-command nil t nil "reply" query-string)
\r
1459 @@ -446,253 +124,6 @@ buffer."
\r
1463 -(defun notmuch-show-reply ()
\r
1464 - "Begin composing a reply to the current message in a new buffer."
\r
1466 - (let ((message-id (notmuch-show-get-message-id)))
\r
1467 - (notmuch-reply message-id)))
\r
1469 -(defun notmuch-show-forward-current ()
\r
1470 - "Forward the current message."
\r
1472 - (with-current-notmuch-show-message
\r
1473 - (message-forward)))
\r
1475 -(defun notmuch-show-pipe-message (command)
\r
1476 - "Pipe the contents of the current message to the given command.
\r
1478 -The given command will be executed with the raw contents of the
\r
1479 -current email message as stdin. Anything printed by the command
\r
1480 -to stdout or stderr will appear in the *Messages* buffer."
\r
1481 - (interactive "sPipe message to command: ")
\r
1482 - (apply 'start-process-shell-command "notmuch-pipe-command" "*notmuch-pip=
\r
1484 - (list command " < " (shell-quote-argument (notmuch-show-get-filename)))))
\r
1486 -(defun notmuch-show-move-to-current-message-summary-line ()
\r
1487 - "Move to the beginning of the one-line summary of the current message.
\r
1489 -This gives us a stable place to move to and work from since the
\r
1490 -summary line is always visible. This is important since moving to
\r
1491 -an invisible location is unreliable, (the main command loop moves
\r
1492 -point either forward or backward to the next visible character
\r
1493 -when a command ends with point on an invisible character).
\r
1495 -Emits an error if point is not within a valid message, (that is
\r
1496 -no pattern of `notmuch-show-message-begin-regexp' could be found
\r
1497 -by searching backward)."
\r
1498 - (beginning-of-line)
\r
1499 - (if (not (looking-at notmuch-show-message-begin-regexp))
\r
1500 - (if (re-search-backward notmuch-show-message-begin-regexp nil t)
\r
1501 - (forward-line 2)
\r
1502 - (error "Not within a valid message."))
\r
1503 - (forward-line 2)))
\r
1505 -(defun notmuch-show-last-message-p ()
\r
1506 - "Predicate testing whether point is within the last message."
\r
1507 - (save-window-excursion
\r
1509 - (notmuch-show-move-to-current-message-summary-line)
\r
1510 - (not (re-search-forward notmuch-show-message-begin-regexp nil t)))))
\r
1512 -(defun notmuch-show-message-unread-p ()
\r
1513 - "Predicate testing whether current message is unread."
\r
1514 - (member "unread" (notmuch-show-get-tags)))
\r
1516 -(defun notmuch-show-message-open-p ()
\r
1517 - "Predicate testing whether current message is open (body is visible)."
\r
1518 - (let ((btn (previous-button (point) t)))
\r
1519 - (while (not (button-has-type-p btn 'notmuch-button-body-toggle-type))
\r
1520 - (setq btn (previous-button (button-start btn))))
\r
1521 - (not (invisible-p (button-get btn 'invisibility-spec)))))
\r
1523 -(defun notmuch-show-next-message-without-marking-read ()
\r
1524 - "Advance to the beginning of the next message in the buffer.
\r
1526 -Moves to the last visible character of the current message if
\r
1527 -already on the last message in the buffer.
\r
1529 -Returns nil if already on the last message in the buffer."
\r
1530 - (notmuch-show-move-to-current-message-summary-line)
\r
1531 - (if (re-search-forward notmuch-show-message-begin-regexp nil t)
\r
1533 - (notmuch-show-move-to-current-message-summary-line)
\r
1536 - (goto-char (- (point-max) 1))
\r
1537 - (while (point-invisible-p)
\r
1538 - (backward-char))
\r
1542 -(defun notmuch-show-next-message ()
\r
1543 - "Advance to the next message (whether open or closed)
\r
1544 -and remove the unread tag from that message.
\r
1546 -Moves to the last visible character of the current message if
\r
1547 -already on the last message in the buffer.
\r
1549 -Returns nil if already on the last message in the buffer."
\r
1551 - (notmuch-show-next-message-without-marking-read)
\r
1552 - (notmuch-show-mark-read))
\r
1554 -(defun notmuch-show-find-next-message ()
\r
1555 - "Returns the position of the next message in the buffer.
\r
1557 -Or the position of the last visible character of the current
\r
1558 -message if already within the last message in the buffer."
\r
1559 - ; save-excursion doesn't save our window position
\r
1560 - ; save-window-excursion doesn't save point
\r
1561 - ; Looks like we have to use both.
\r
1563 - (save-window-excursion
\r
1564 - (notmuch-show-next-message-without-marking-read)
\r
1567 -(defun notmuch-show-next-unread-message ()
\r
1568 - "Advance to the next unread message.
\r
1570 -Moves to the last visible character of the current message if
\r
1571 -there are no more unread messages past the current point."
\r
1572 - (notmuch-show-next-message-without-marking-read)
\r
1573 - (while (and (not (notmuch-show-last-message-p))
\r
1574 - (not (notmuch-show-message-unread-p)))
\r
1575 - (notmuch-show-next-message-without-marking-read))
\r
1576 - (if (not (notmuch-show-message-unread-p))
\r
1577 - (notmuch-show-next-message-without-marking-read))
\r
1578 - (notmuch-show-mark-read))
\r
1580 -(defun notmuch-show-next-open-message ()
\r
1581 - "Advance to the next open message (that is, body is visible).
\r
1583 -Moves to the last visible character of the final message in the buffer
\r
1584 -if there are no more open messages."
\r
1586 - (while (and (notmuch-show-next-message-without-marking-read)
\r
1587 - (not (notmuch-show-message-open-p))))
\r
1588 - (notmuch-show-mark-read))
\r
1590 -(defun notmuch-show-previous-message-without-marking-read ()
\r
1591 - "Backup to the beginning of the previous message in the buffer.
\r
1593 -If within a message rather than at the beginning of it, then
\r
1594 -simply move to the beginning of the current message.
\r
1596 -Returns nil if already on the first message in the buffer."
\r
1597 - (let ((start (point)))
\r
1598 - (notmuch-show-move-to-current-message-summary-line)
\r
1599 - (if (not (< (point) start))
\r
1600 - ; Go backward twice to skip the current message's marker
\r
1602 - (re-search-backward notmuch-show-message-begin-regexp nil t)
\r
1603 - (re-search-backward notmuch-show-message-begin-regexp nil t)
\r
1604 - (notmuch-show-move-to-current-message-summary-line)
\r
1606 - (if (=3D (point) start)
\r
1612 -(defun notmuch-show-previous-message ()
\r
1613 - "Backup to the previous message (whether open or closed)
\r
1614 -and remove the unread tag from that message.
\r
1616 -If within a message rather than at the beginning of it, then
\r
1617 -simply move to the beginning of the current message."
\r
1619 - (notmuch-show-previous-message-without-marking-read)
\r
1620 - (notmuch-show-mark-read))
\r
1622 -(defun notmuch-show-find-previous-message ()
\r
1623 - "Returns the position of the previous message in the buffer.
\r
1625 -Or the position of the beginning of the current message if point
\r
1626 -is originally within the message rather than at the beginning of
\r
1628 - ; save-excursion doesn't save our window position
\r
1629 - ; save-window-excursion doesn't save point
\r
1630 - ; Looks like we have to use both.
\r
1632 - (save-window-excursion
\r
1633 - (notmuch-show-previous-message-without-marking-read)
\r
1636 -(defun notmuch-show-previous-open-message ()
\r
1637 - "Backup to previous open message (that is, body is visible).
\r
1639 -Moves to the first message in the buffer if there are no previous
\r
1642 - (while (and (notmuch-show-previous-message-without-marking-read)
\r
1643 - (not (notmuch-show-message-open-p))))
\r
1644 - (notmuch-show-mark-read))
\r
1646 -(defun notmuch-show-rewind ()
\r
1647 - "Backup through the thread, (reverse scrolling compared to \\[notmuch-sh=
\r
1648 ow-advance-and-archive]).
\r
1650 -Specifically, if the beginning of the previous email is fewer
\r
1651 -than `window-height' lines from the current point, move to it
\r
1652 -just like `notmuch-show-previous-message'.
\r
1654 -Otherwise, just scroll down a screenful of the current message.
\r
1656 -This command does not modify any message tags, (it does not undo
\r
1657 -any effects from previous calls to
\r
1658 -`notmuch-show-advance-and-archive'."
\r
1660 - (let ((previous (notmuch-show-find-previous-message)))
\r
1661 - (if (> (count-lines previous (point)) (- (window-height) next-screen-c=
\r
1664 - (condition-case nil
\r
1665 - (scroll-down nil)
\r
1666 - ((beginning-of-buffer) nil))
\r
1667 - (goto-char (window-start))
\r
1668 - ; Because count-lines counts invivisible lines, we may have
\r
1669 - ; scrolled to far. If so., notice this and fix it up.
\r
1670 - (if (< (point) previous)
\r
1672 - (goto-char previous)
\r
1674 - (notmuch-show-previous-message))))
\r
1676 -(defun notmuch-show-advance-and-archive ()
\r
1677 - "Advance through thread and archive.
\r
1679 -This command is intended to be one of the simplest ways to
\r
1680 -process a thread of email. It does the following:
\r
1682 -If the current message in the thread is not yet fully visible,
\r
1683 -scroll by a near screenful to read more of the message.
\r
1685 -Otherwise, (the end of the current message is already within the
\r
1686 -current window), advance to the next open message.
\r
1688 -Finally, if there is no further message to advance to, and this
\r
1689 -last message is already read, then archive the entire current
\r
1690 -thread, (remove the \"inbox\" tag from each message). Also kill
\r
1691 -this buffer, and display the next thread from the search from
\r
1692 -which this thread was originally shown."
\r
1694 - (let ((next (notmuch-show-find-next-message))
\r
1695 - (unread (notmuch-show-message-unread-p)))
\r
1696 - (if (> next (window-end))
\r
1698 - (let ((last (notmuch-show-last-message-p)))
\r
1699 - (notmuch-show-next-open-message)
\r
1701 - (notmuch-show-archive-thread))))))
\r
1703 -(defun notmuch-show-next-button ()
\r
1704 - "Advance point to the next button in the buffer."
\r
1706 - (forward-button 1))
\r
1708 -(defun notmuch-show-previous-button ()
\r
1709 - "Move point back to the previous button in the buffer."
\r
1711 - (backward-button 1))
\r
1713 (defun notmuch-toggle-invisible-action (cite-button)
\r
1714 (let ((invis-spec (button-get cite-button 'invisibility-spec)))
\r
1715 (if (invisible-p invis-spec)
\r
1716 @@ -702,27 +133,6 @@ which this thread was originally shown."
\r
1717 (force-window-update)
\r
1720 -(defun notmuch-show-toggle-current-body ()
\r
1721 - "Toggle the display of the current message body."
\r
1724 - (notmuch-show-move-to-current-message-summary-line)
\r
1725 - (unless (button-at (point))
\r
1726 - (notmuch-show-next-button))
\r
1730 -(defun notmuch-show-toggle-current-header ()
\r
1731 - "Toggle the display of the current message header."
\r
1734 - (notmuch-show-move-to-current-message-summary-line)
\r
1736 - (unless (button-at (point))
\r
1737 - (notmuch-show-next-button))
\r
1741 (define-button-type 'notmuch-button-invisibility-toggle-type
\r
1742 'action 'notmuch-toggle-invisible-action
\r
1744 @@ -738,161 +148,6 @@ which this thread was originally shown."
\r
1745 'face 'notmuch-message-summary-face
\r
1746 :supertype 'notmuch-button-invisibility-toggle-type)
\r
1748 -(defun notmuch-show-citation-regexp (depth)
\r
1749 - "Build a regexp for matching citations at a given DEPTH (indent)"
\r
1750 - (let ((line-regexp (format "[[:space:]]\\{%d\\}>.*\n" depth)))
\r
1751 - (concat "\\(?:^" line-regexp
\r
1752 - "\\(?:[[:space:]]*\n" line-regexp
\r
1755 -(defun notmuch-show-region-to-button (beg end type prefix button-text)
\r
1756 - "Auxilary function to do the actual making of overlays and buttons
\r
1758 -BEG and END are buffer locations. TYPE should a string, either
\r
1759 -\"citation\" or \"signature\". PREFIX is some arbitrary text to
\r
1760 -insert before the button, probably for indentation. BUTTON-TEXT
\r
1761 -is what to put on the button."
\r
1763 -;; This uses some slightly tricky conversions between strings and
\r
1764 -;; symbols because of the way the button code works. Note that
\r
1765 -;; replacing intern-soft with make-symbol will cause this to fail,
\r
1766 -;; since the newly created symbol has no plist.
\r
1768 - (let ((overlay (make-overlay beg end))
\r
1769 - (invis-spec (make-symbol (concat "notmuch-" type "-region")))
\r
1770 - (button-type (intern-soft (concat "notmuch-button-"
\r
1771 - type "-toggle-type"))))
\r
1772 - (add-to-invisibility-spec invis-spec)
\r
1773 - (overlay-put overlay 'invisible invis-spec)
\r
1774 - (goto-char (1+ end))
\r
1776 - (goto-char (1- beg))
\r
1778 - (insert-button button-text
\r
1779 - 'invisibility-spec invis-spec
\r
1780 - :type button-type)
\r
1784 -(defun notmuch-show-markup-citations-region (beg end depth)
\r
1785 - "Markup citations, and up to one signature in the given region"
\r
1786 - ;; it would be nice if the untabify was not required, but
\r
1787 - ;; that would require notmuch to indent with spaces.
\r
1788 - (untabify beg end)
\r
1789 - (let ((citation-regexp (notmuch-show-citation-regexp depth))
\r
1790 - (signature-regexp (concat (format "^[[:space:]]\\{%d\\}" depth)
\r
1791 - notmuch-show-signature-regexp))
\r
1792 - (indent (concat "\n" (make-string depth ? ))))
\r
1794 - (beginning-of-line)
\r
1795 - (while (and (< (point) end)
\r
1796 - (re-search-forward citation-regexp end t))
\r
1797 - (let* ((cite-start (match-beginning 0))
\r
1798 - (cite-end (match-end 0))
\r
1799 - (cite-lines (count-lines cite-start cite-end)))
\r
1800 - (when (> cite-lines (1+ notmuch-show-citation-lines-prefix))
\r
1801 - (goto-char cite-start)
\r
1802 - (forward-line notmuch-show-citation-lines-prefix)
\r
1803 - (notmuch-show-region-to-button
\r
1804 - (point) cite-end
\r
1807 - (format notmuch-show-citation-button-format
\r
1808 - (- cite-lines notmuch-show-citation-lines-prefix))
\r
1810 - (if (and (< (point) end)
\r
1811 - (re-search-forward signature-regexp end t))
\r
1812 - (let* ((sig-start (match-beginning 0))
\r
1813 - (sig-end (match-end 0))
\r
1814 - (sig-lines (1- (count-lines sig-start end))))
\r
1815 - (if (<=3D sig-lines notmuch-show-signature-lines-max)
\r
1816 - (notmuch-show-region-to-button
\r
1821 - (format notmuch-show-signature-button-format sig-lines)
\r
1824 -(defun notmuch-show-markup-part (beg end depth)
\r
1825 - (if (re-search-forward notmuch-show-part-begin-regexp nil t)
\r
1827 - (let (mime-message mime-type)
\r
1829 - (re-search-forward notmuch-show-contentype-regexp end t)
\r
1830 - (setq mime-type (car (split-string (buffer-substring
\r
1831 - (match-beginning 1) (match=
\r
1834 - (if (equal mime-type "text/html")
\r
1835 - (let ((filename (notmuch-show-get-filename)))
\r
1836 - (with-temp-buffer
\r
1837 - (insert-file-contents filename nil nil nil t)
\r
1838 - (setq mime-message (mm-dissect-buffer)))))
\r
1840 - (let ((beg (point-marker)))
\r
1841 - (re-search-forward notmuch-show-part-end-regexp)
\r
1842 - (let ((end (copy-marker (match-beginning 0))))
\r
1844 - (if (not (bolp))
\r
1846 - (indent-rigidly beg end depth)
\r
1847 - (if (not (eq mime-message nil))
\r
1850 - (forward-line -1)
\r
1851 - (let ((handle-type (mm-handle-type mime-message))
\r
1853 - (if (sequencep (car handle-type))
\r
1854 - (setq mime-type (car handle-type))
\r
1855 - (setq mime-type (car (car (cdr handle-type))))
\r
1857 - (if (equal mime-type "text/html")
\r
1858 - (mm-display-part mime-message))))
\r
1860 - (notmuch-show-markup-citations-region beg end depth)
\r
1861 - ; Advance to the next part (if any) (so the outer loop can
\r
1862 - ; determine whether we've left the current message.
\r
1863 - (if (re-search-forward notmuch-show-part-begin-regexp nil t)
\r
1864 - (beginning-of-line)))))
\r
1865 - (goto-char end))
\r
1866 - (goto-char end)))
\r
1868 -(defun notmuch-show-markup-parts-region (beg end depth)
\r
1871 - (while (< (point) end)
\r
1872 - (notmuch-show-markup-part beg end depth))))
\r
1874 -(defun notmuch-show-markup-body (depth match btn)
\r
1875 - "Markup a message body, (indenting, buttonizing citations,
\r
1876 -etc.), and hiding the body itself if the message does not match
\r
1877 -the current search.
\r
1879 -DEPTH specifies the depth at which this message appears in the
\r
1880 -tree of the current thread, (the top-level messages have depth 0
\r
1881 -and each reply increases depth by 1). MATCH indicates whether
\r
1882 -this message is regarded as matching the current search. BTN is
\r
1883 -the button which is used to toggle the visibility of this
\r
1886 -When this function is called, point must be within the message, but
\r
1887 -before the delimiter marking the beginning of the body."
\r
1888 - (re-search-forward notmuch-show-body-begin-regexp)
\r
1890 - (let ((beg (point-marker)))
\r
1891 - (re-search-forward notmuch-show-body-end-regexp)
\r
1892 - (let ((end (copy-marker (match-beginning 0))))
\r
1893 - (notmuch-show-markup-parts-region beg end depth)
\r
1894 - (let ((invis-spec (make-symbol "notmuch-show-body-read")))
\r
1895 - (overlay-put (make-overlay beg end)
\r
1896 - 'invisible invis-spec)
\r
1897 - (button-put btn 'invisibility-spec invis-spec)
\r
1899 - (add-to-invisibility-spec invis-spec)))
\r
1900 - (set-marker beg nil)
\r
1901 - (set-marker end nil)
\r
1904 (defun notmuch-fontify-headers ()
\r
1905 (while (looking-at "[[:space:]]")
\r
1907 @@ -921,80 +176,6 @@ before the delimiter marking the beginning of the body=
\r
1909 (overlay-put (make-overlay (point) (re-search-forward ".*$"))
\r
1910 'face 'message-header-other)))))))
\r
1912 -(defun notmuch-show-markup-header (message-begin depth)
\r
1913 - "Buttonize and decorate faces in a message header.
\r
1915 -MESSAGE-BEGIN is the position of the absolute first character in
\r
1916 -the message (including all delimiters that will end up being
\r
1917 -invisible etc.). This is to allow a button to reliably extend to
\r
1918 -the beginning of the message even if point is positioned at an
\r
1919 -invisible character (such as the beginning of the buffer).
\r
1921 -DEPTH specifies the depth at which this message appears in the
\r
1922 -tree of the current thread, (the top-level messages have depth 0
\r
1923 -and each reply increases depth by 1)."
\r
1924 - (re-search-forward notmuch-show-header-begin-regexp)
\r
1926 - (let ((beg (point-marker))
\r
1927 - (summary-end (copy-marker (line-beginning-position 2)))
\r
1928 - (subject-end (copy-marker (line-end-position 2)))
\r
1929 - (invis-spec (make-symbol "notmuch-show-header"))
\r
1931 - (re-search-forward notmuch-show-header-end-regexp)
\r
1932 - (beginning-of-line)
\r
1933 - (let ((end (point-marker)))
\r
1934 - (indent-rigidly beg end depth)
\r
1936 - (setq btn (make-button message-begin summary-end :type 'notmuch-butt=
\r
1937 on-body-toggle-type))
\r
1939 - (add-to-invisibility-spec invis-spec)
\r
1940 - (overlay-put (make-overlay subject-end end)
\r
1941 - 'invisible invis-spec)
\r
1942 - (make-button (line-beginning-position) subject-end
\r
1943 - 'invisibility-spec invis-spec
\r
1944 - :type 'notmuch-button-headers-toggle-type)
\r
1945 - (while (looking-at "[[:space:]]*[A-Za-z][-A-Za-z0-9]*:")
\r
1946 - (beginning-of-line)
\r
1947 - (notmuch-fontify-headers)
\r
1952 - (set-marker beg nil)
\r
1953 - (set-marker summary-end nil)
\r
1954 - (set-marker subject-end nil)
\r
1955 - (set-marker end nil)
\r
1959 -(defun notmuch-show-markup-message ()
\r
1960 - (if (re-search-forward notmuch-show-message-begin-regexp nil t)
\r
1961 - (let ((message-begin (match-beginning 0)))
\r
1962 - (re-search-forward notmuch-show-depth-match-regexp)
\r
1963 - (let ((depth (string-to-number (buffer-substring (match-beginning 1) (mat=
\r
1965 - (match (string=3D "1" (buffer-substring (match-beginning 2) (match-=
\r
1968 - (setq btn (notmuch-show-markup-header message-begin depth))
\r
1969 - (notmuch-show-markup-body depth match btn)))
\r
1970 - (goto-char (point-max))))
\r
1972 -(defun notmuch-show-hide-markers ()
\r
1974 - (goto-char (point-min))
\r
1975 - (while (not (eobp))
\r
1976 - (if (re-search-forward notmuch-show-marker-regexp nil t)
\r
1978 - (overlay-put (make-overlay (match-beginning 0) (+ (match-end 0) 1))
\r
1979 - 'invisible 'notmuch-show-marker))
\r
1980 - (goto-char (point-max))))))
\r
1982 -(defun notmuch-show-markup-messages ()
\r
1984 - (goto-char (point-min))
\r
1985 - (while (not (eobp))
\r
1986 - (notmuch-show-markup-message)))
\r
1987 - (notmuch-show-hide-markers))
\r
1989 (defun notmuch-documentation-first-line (symbol)
\r
1990 "Return the first line of the documentation string for SYMBOL."
\r
1991 (let ((doc (documentation symbol)))
\r
1992 @@ -1065,154 +246,16 @@ For a mouse binding, return nil."
\r
1993 (set-buffer-modified-p nil)
\r
1994 (view-buffer (current-buffer) 'kill-buffer-if-not-modified))))
\r
1997 -(defun notmuch-show-mode ()
\r
1998 - "Major mode for viewing a thread with notmuch.
\r
2000 -This buffer contains the results of the \"notmuch show\" command
\r
2001 -for displaying a single thread of email from your email archives.
\r
2003 -By default, various components of email messages, (citations,
\r
2004 -signatures, already-read messages), are hidden. You can make
\r
2005 -these parts visible by clicking with the mouse button or by
\r
2006 -pressing RET after positioning the cursor on a hidden part, (for
\r
2007 -which \\[notmuch-show-next-button] and \\[notmuch-show-previous-button] ar=
\r
2010 -Reading the thread sequentially is well-supported by pressing
\r
2011 -\\[notmuch-show-advance-and-archive]. This will
\r
2012 -scroll the current message (if necessary), advance to the next
\r
2013 -message, or advance to the next thread (if already on the last
\r
2014 -message of a thread).
\r
2016 -Other commands are available to read or manipulate the thread more
\r
2017 -selectively, (such as '\\[notmuch-show-next-message]' and '\\[notmuch-show=
\r
2018 -previous-message]' to advance to messages without
\r
2019 -removing any tags, and '\\[notmuch-show-archive-thread]' to archive an ent=
\r
2020 ire thread without
\r
2021 -scrolling through with \\[notmuch-show-advance-and-archive]).
\r
2023 -You can add or remove arbitary tags from the current message with
\r
2024 -'\\[notmuch-show-add-tag]' or '\\[notmuch-show-remove-tag]'.
\r
2026 -All currently available key bindings:
\r
2028 -\\{notmuch-show-mode-map}"
\r
2030 - (kill-all-local-variables)
\r
2031 - (add-to-invisibility-spec 'notmuch-show-marker)
\r
2032 - (use-local-map notmuch-show-mode-map)
\r
2033 - (setq major-mode 'notmuch-show-mode
\r
2034 - mode-name "notmuch-show")
\r
2035 - (setq buffer-read-only t))
\r
2037 (defgroup notmuch nil
\r
2038 "Notmuch mail reader for Emacs."
\r
2041 -(defcustom notmuch-show-hook nil
\r
2042 - "List of functions to call when notmuch displays a message."
\r
2044 - :options '(goto-address)
\r
2045 - :group 'notmuch)
\r
2047 (defcustom notmuch-search-hook nil
\r
2048 "List of functions to call when notmuch displays the search results."
\r
2050 :options '(hl-line-mode)
\r
2053 -(defun notmuch-show-do-stash (text)
\r
2055 - (message (concat "Saved: " text)))
\r
2057 -(defun notmuch-show-stash-cc ()
\r
2058 - "Copy CC field of current message to kill-ring."
\r
2060 - (notmuch-show-do-stash (notmuch-show-get-cc)))
\r
2062 -(defun notmuch-show-stash-date ()
\r
2063 - "Copy date of current message to kill-ring."
\r
2065 - (notmuch-show-do-stash (notmuch-show-get-date)))
\r
2067 -(defun notmuch-show-stash-filename ()
\r
2068 - "Copy filename of current message to kill-ring."
\r
2070 - (notmuch-show-do-stash (notmuch-show-get-filename)))
\r
2072 -(defun notmuch-show-stash-from ()
\r
2073 - "Copy From address of current message to kill-ring."
\r
2075 - (notmuch-show-do-stash (notmuch-show-get-from)))
\r
2077 -(defun notmuch-show-stash-message-id ()
\r
2078 - "Copy message ID of current message to kill-ring."
\r
2080 - (notmuch-show-do-stash (notmuch-show-get-message-id)))
\r
2082 -(defun notmuch-show-stash-subject ()
\r
2083 - "Copy Subject field of current message to kill-ring."
\r
2085 - (notmuch-show-do-stash (notmuch-show-get-subject)))
\r
2087 -(defun notmuch-show-stash-tags ()
\r
2088 - "Copy tags of current message to kill-ring as a comma separated list."
\r
2090 - (notmuch-show-do-stash (mapconcat 'identity (notmuch-show-get-tags) ",")=
\r
2093 -(defun notmuch-show-stash-to ()
\r
2094 - "Copy To address of current message to kill-ring."
\r
2096 - (notmuch-show-do-stash (notmuch-show-get-to)))
\r
2098 -; Make show mode a bit prettier, highlighting URLs and using word wrap
\r
2100 -(defun notmuch-show-mark-read ()
\r
2101 - (notmuch-show-remove-tag "unread"))
\r
2103 -(defun notmuch-show-pretty-hook ()
\r
2104 - (goto-address-mode 1)
\r
2105 - (visual-line-mode))
\r
2107 -(add-hook 'notmuch-show-hook 'notmuch-show-mark-read)
\r
2108 -(add-hook 'notmuch-show-hook 'notmuch-show-pretty-hook)
\r
2109 -(add-hook 'notmuch-search-hook
\r
2111 - (hl-line-mode 1) ))
\r
2113 -(defun notmuch-show (thread-id &optional parent-buffer query-context)
\r
2114 - "Run \"notmuch show\" with the given thread ID and display results.
\r
2116 -The optional PARENT-BUFFER is the notmuch-search buffer from
\r
2117 -which this notmuch-show command was executed, (so that the next
\r
2118 -thread from that buffer can be show when done with this one).
\r
2120 -The optional QUERY-CONTEXT is a notmuch search term. Only messages from th=
\r
2122 -matching this search term are shown if non-nil. "
\r
2123 - (interactive "sNotmuch show: ")
\r
2124 - (let ((buffer (get-buffer-create (concat "*notmuch-show-" thread-id "*")=
\r
2126 - (switch-to-buffer buffer)
\r
2127 - (notmuch-show-mode)
\r
2128 - (set (make-local-variable 'notmuch-show-parent-buffer) parent-buffer)
\r
2129 - (let ((proc (get-buffer-process (current-buffer)))
\r
2130 - (inhibit-read-only t))
\r
2132 - (error "notmuch search process already running for query `%s'" thread-i=
\r
2136 - (goto-char (point-min))
\r
2138 - (let* ((basic-args (list notmuch-command nil t nil "show" "--entire-threa=
\r
2140 - (args (if query-context (append basic-args (list "and (" query-context "=
\r
2141 )")) basic-args)))
\r
2142 - (apply 'call-process args)
\r
2143 - (when (and (eq (buffer-size) 0) query-context)
\r
2144 - (apply 'call-process basic-args)))
\r
2145 - (notmuch-show-markup-messages)
\r
2147 - (run-hooks 'notmuch-show-hook)
\r
2148 - ; Move straight to the first open message
\r
2149 - (if (not (notmuch-show-message-open-p))
\r
2150 - (notmuch-show-next-open-message))
\r
2153 (defvar notmuch-search-authors-width 40
\r
2154 "Number of columns to use to display authors in a notmuch-search buffer.=
\r
2157 @@ -1637,7 +680,6 @@ current search results AND that are tagged with the gi=
\r
2159 (list (notmuch-select-tag-with-completion "Filter by tag: ")))
\r
2160 (notmuch-search (concat notmuch-search-query-string " and tag:" tag) not=
\r
2161 much-search-oldest-first))
\r
2166 "Run notmuch to display all mail with tag of 'inbox'"
\r
2173 David Edmondson, http://dme.org
\r