Re: [PATCH v4 08/16] reorganize indexing of multipart/signed and multipart/encrypted
[notmuch-archives.git] / 88 / 114373696bcd3ad700a971af88dc8e6fd60ff7
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
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -2.317\r
10 X-Spam-Level: \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
18         [209.85.218.210])\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
39 MIME-Version: 1.0\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
45 Precedence: list\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
56 \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
59 \r
60 Signed-off-by: David Edmondson <dme@dme.org>\r
61 ---\r
62  emacs/Makefile.local  |    2 +-\r
63  emacs/notmuch-show.el |  982 +++++++++++++++++++++++++++++++++++++++++++++=\r
64 ++++\r
65  emacs/notmuch.el      |  960 +--------------------------------------------=\r
66 ---\r
67  3 files changed, 984 insertions(+), 960 deletions(-)\r
68  create mode 100644 emacs/notmuch-show.el\r
69 \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
74 @@ -1,5 +1,5 @@\r
75  dir :=3D emacs\r
76 -emacs_sources :=3D $(dir)/notmuch.el\r
77 +emacs_sources :=3D $(dir)/notmuch.el $(dir)/notmuch-show.el\r
78 =20\r
79  emacs_bytecode :=3D $(subst .el,.elc,$(emacs_sources))\r
80 =20\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
84 --- /dev/null\r
85 +++ b/emacs/notmuch-show.el\r
86 @@ -0,0 +1,982 @@\r
87 +;; notmuch-show.el --- display notmuch messages within emacs\r
88 +;;\r
89 +;; Copyright =C2=A9 Carl Worth\r
90 +;;\r
91 +;; This file is part of Notmuch.\r
92 +;;\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
97 +;;\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
102 +;;\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
105 +;;\r
106 +;; Authors: Carl Worth <cworth@cworth.org>\r
107 +\r
108 +;; This is an part of an emacs-based interface to the notmuch mail system.\r
109 +\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
120 +    map)\r
121 +  "Submap for stash commands"\r
122 +  )\r
123 +\r
124 +(fset 'notmuch-show-stash-map notmuch-show-stash-map)\r
125 +\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
155 +    map)\r
156 +  "Keymap for \"notmuch show\" buffers.")\r
157 +(fset 'notmuch-show-mode-map notmuch-show-mode-map)\r
158 +\r
159 +(defvar notmuch-show-signature-regexp "\\(-- ?\\|_+\\)$"\r
160 +  "Pattern to match a line that separates content from signature.\r
161 +\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
168 +\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
172 +\r
173 +Can use up to one integer format parameter, i.e. %d")\r
174 +\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
178 +\r
179 +Can use up to one integer format parameter, i.e. %d")\r
180 +\r
181 +(defvar notmuch-show-signature-lines-max 12\r
182 +  "Maximum length of signature that will be hidden by default.")\r
183 +\r
184 +(defvar notmuch-show-citation-lines-prefix 4\r
185 +  "Always show at least this many lines of a citation.\r
186 +\r
187 +If there is one more line, show that, otherwise collapse\r
188 +remaining lines into a button.")\r
189 +\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
202 +\r
203 +(defvar notmuch-show-id-regexp "\\(id:[^ ]*\\)")\r
204 +(defvar notmuch-show-depth-match-regexp " depth:\\([0-9]*\\).*match:\\([01=\r
205 ]\\) ")\r
206 +(defvar notmuch-show-filename-regexp "filename:\\(.*\\)$")\r
207 +(defvar notmuch-show-contentype-regexp "Content-type: \\(.*\\)")\r
208 +\r
209 +(defvar notmuch-show-tags-regexp "(\\([^)]*\\))$")\r
210 +\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
216 +\r
217 +(defun notmuch-show-next-line ()\r
218 +  "Like builtin `next-line' but ensuring we end on a visible character.\r
219 +\r
220 +By advancing forward until reaching a visible character.\r
221 +\r
222 +Unlike builtin `next-line' this version accepts no arguments."\r
223 +  (interactive)\r
224 +  (set 'this-command 'next-line)\r
225 +  (call-interactively 'next-line)\r
226 +  (while (point-invisible-p)\r
227 +    (forward-char)))\r
228 +\r
229 +(defun notmuch-show-previous-line ()\r
230 +  "Like builtin `previous-line' but ensuring we end on a visible character.\r
231 +\r
232 +By advancing forward until reaching a visible character.\r
233 +\r
234 +Unlike builtin `previous-line' this version accepts no arguments."\r
235 +  (interactive)\r
236 +  (set 'this-command 'previous-line)\r
237 +  (call-interactively 'previous-line)\r
238 +  (while (point-invisible-p)\r
239 +    (forward-char)))\r
240 +\r
241 +(defun notmuch-show-get-message-id ()\r
242 +  (save-excursion\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
248 +\r
249 +(defun notmuch-show-get-filename ()\r
250 +  (save-excursion\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
256 +\r
257 +(defun notmuch-show-set-tags (tags)\r
258 +  (save-excursion\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
267 +      (goto-char beg)\r
268 +      (insert (mapconcat 'identity tags " ")))))\r
269 +\r
270 +(defun notmuch-show-get-tags ()\r
271 +  (save-excursion\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
277 +\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
281 +\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
285 +\r
286 +(defun notmuch-show-get-date ()\r
287 +  "Return Date of current message"\r
288 +  (notmuch-show-get-header-field 'date))\r
289 +\r
290 +(defun notmuch-show-get-from ()\r
291 +  "Return From address of current message"\r
292 +  (notmuch-show-get-header-field 'from))\r
293 +\r
294 +(defun notmuch-show-get-subject ()\r
295 +  "Return Subject of current message"\r
296 +  (notmuch-show-get-header-field 'subject))\r
297 +\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
301 +\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
308 +    val))\r
309 +\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
317 +  (save-excursion\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
322 e:]]*\\(.*\\)\n"))\r
323 +    (let* ((summary (buffer-substring-no-properties (match-beginning 1) (m=\r
324 atch-end 1)))\r
325 +         (beg (point)))\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
329 +         (insert text)\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
333 +           (forward-line)\r
334 +           )\r
335 +         (goto-char (point-min))\r
336 +         (cons (cons 'summary summary) (mail-header-extract-no-properties)))))))\r
337 +\r
338 +(defun notmuch-show-add-tag (&rest toadd)\r
339 +  "Add a tag to the current message."\r
340 +  (interactive\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
348 +\r
349 +(defun notmuch-show-remove-tag (&rest toremove)\r
350 +  "Remove a tag from the current message."\r
351 +  (interactive\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
356 +       (progn\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
363 +\r
364 +(defun notmuch-show-archive-thread ()\r
365 +  "Archive each message in thread, then show next thread from search.\r
366 +\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
370 +\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
374 +buffer."\r
375 +  (interactive)\r
376 +  (save-excursion\r
377 +    (goto-char (point-min))\r
378 +    (while (not (eobp))\r
379 +      (notmuch-show-remove-tag "inbox")\r
380 +      (if (not (eobp))\r
381 +         (forward-char))\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
387 +       (progn\r
388 +         (switch-to-buffer parent-buffer)\r
389 +         (forward-line)\r
390 +         (notmuch-search-show-thread)))))\r
391 +\r
392 +(defun notmuch-show-archive-thread-then-exit ()\r
393 +  "Archive each message in thread, then exit back to search results."\r
394 +  (interactive)\r
395 +  (notmuch-show-archive-thread)\r
396 +  (kill-this-buffer))\r
397 +\r
398 +(defun notmuch-show-view-raw-message ()\r
399 +  "View the raw email of the current message."\r
400 +  (interactive)\r
401 +  (view-file (notmuch-show-get-filename)))\r
402 +\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
405 +  `(save-excursion\r
406 +     (let ((filename (notmuch-show-get-filename)))\r
407 +       (let ((buf (generate-new-buffer (concat "*notmuch-msg-" filename "*=\r
408 "))))\r
409 +         (with-current-buffer buf\r
410 +           (insert-file-contents filename nil nil nil t)\r
411 +           ,@body)\r
412 +        (kill-buffer buf)))))\r
413 +\r
414 +(defun notmuch-show-view-all-mime-parts ()\r
415 +  "Use external viewers to view all attachments from the current message."\r
416 +  (interactive)\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
423 +   ;\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
432 +                                )))\r
433 +     (mm-display-parts (mm-dissect-buffer)))))\r
434 +\r
435 +(defun notmuch-show-save-attachments ()\r
436 +  "Save all attachments from the current message."\r
437 +  (interactive)\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
443 +\r
444 +(defun notmuch-show-reply ()\r
445 +  "Begin composing a reply to the current message in a new buffer."\r
446 +  (interactive)\r
447 +  (let ((message-id (notmuch-show-get-message-id)))\r
448 +    (notmuch-reply message-id)))\r
449 +\r
450 +(defun notmuch-show-forward-current ()\r
451 +  "Forward the current message."\r
452 +  (interactive)\r
453 +  (with-current-notmuch-show-message\r
454 +   (message-forward)))\r
455 +\r
456 +(defun notmuch-show-pipe-message (command)\r
457 +  "Pipe the contents of the current message to the given command.\r
458 +\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
464 e*"\r
465 +        (list command " < " (shell-quote-argument (notmuch-show-get-filename)))))\r
466 +\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
469 +\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
475 +\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
482 +         (forward-line 2)\r
483 +       (error "Not within a valid message."))\r
484 +    (forward-line 2)))\r
485 +\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
489 +    (save-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
492 +\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
496 +\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
503 +\r
504 +(defun notmuch-show-next-message-without-marking-read ()\r
505 +  "Advance to the beginning of the next message in the buffer.\r
506 +\r
507 +Moves to the last visible character of the current message if\r
508 +already on the last message in the buffer.\r
509 +\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
513 +      (progn\r
514 +       (notmuch-show-move-to-current-message-summary-line)\r
515 +       (recenter 0)\r
516 +       t)\r
517 +    (goto-char (- (point-max) 1))\r
518 +    (while (point-invisible-p)\r
519 +      (backward-char))\r
520 +    (recenter 0)\r
521 +    nil))\r
522 +\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
526 +\r
527 +Moves to the last visible character of the current message if\r
528 +already on the last message in the buffer.\r
529 +\r
530 +Returns nil if already on the last message in the buffer."\r
531 +  (interactive)\r
532 +  (notmuch-show-next-message-without-marking-read)\r
533 +  (notmuch-show-mark-read))\r
534 +\r
535 +(defun notmuch-show-find-next-message ()\r
536 +  "Returns the position of the next message in the buffer.\r
537 +\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
543 +  (save-excursion\r
544 +    (save-window-excursion\r
545 +      (notmuch-show-next-message-without-marking-read)\r
546 +      (point))))\r
547 +\r
548 +(defun notmuch-show-next-unread-message ()\r
549 +  "Advance to the next unread message.\r
550 +\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
560 +\r
561 +(defun notmuch-show-next-open-message ()\r
562 +  "Advance to the next open message (that is, body is visible).\r
563 +\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
566 +  (interactive)\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
570 +\r
571 +(defun notmuch-show-previous-message-without-marking-read ()\r
572 +  "Backup to the beginning of the previous message in the buffer.\r
573 +\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
576 +\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
582 +       (progn\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
586 +         (recenter 0)\r
587 +         (if (=3D (point) start)\r
588 +             nil\r
589 +           t))\r
590 +      (recenter 0)\r
591 +      nil)))\r
592 +\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
596 +\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
599 +  (interactive)\r
600 +  (notmuch-show-previous-message-without-marking-read)\r
601 +  (notmuch-show-mark-read))\r
602 +\r
603 +(defun notmuch-show-find-previous-message ()\r
604 +  "Returns the position of the previous message in the buffer.\r
605 +\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
608 +it."\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
612 +  (save-excursion\r
613 +    (save-window-excursion\r
614 +      (notmuch-show-previous-message-without-marking-read)\r
615 +      (point))))\r
616 +\r
617 +(defun notmuch-show-previous-open-message ()\r
618 +  "Backup to previous open message (that is, body is visible).\r
619 +\r
620 +Moves to the first message in the buffer if there are no previous\r
621 +open messages."\r
622 +  (interactive)\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
626 +\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
630 +\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
634 +\r
635 +Otherwise, just scroll down a screenful of the current message.\r
636 +\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
640 +  (interactive)\r
641 +  (let ((previous (notmuch-show-find-previous-message)))\r
642 +    (if (> (count-lines previous (point)) (- (window-height) next-screen-c=\r
643 ontext-lines))\r
644 +       (progn\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
652 +             (progn\r
653 +               (goto-char previous)\r
654 +               (recenter 0))))\r
655 +      (notmuch-show-previous-message))))\r
656 +\r
657 +(defun notmuch-show-advance-and-archive ()\r
658 +  "Advance through thread and archive.\r
659 +\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
662 +\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
665 +\r
666 +Otherwise, (the end of the current message is already within the\r
667 +current window), advance to the next open message.\r
668 +\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
674 +  (interactive)\r
675 +  (let ((next (notmuch-show-find-next-message))\r
676 +       (unread (notmuch-show-message-unread-p)))\r
677 +    (if (> next (window-end))\r
678 +       (scroll-up nil)\r
679 +      (let ((last (notmuch-show-last-message-p)))\r
680 +       (notmuch-show-next-open-message)\r
681 +       (if last\r
682 +           (notmuch-show-archive-thread))))))\r
683 +\r
684 +(defun notmuch-show-next-button ()\r
685 +  "Advance point to the next button in the buffer."\r
686 +  (interactive)\r
687 +  (forward-button 1))\r
688 +\r
689 +(defun notmuch-show-previous-button ()\r
690 +  "Move point back to the previous button in the buffer."\r
691 +  (interactive)\r
692 +  (backward-button 1))\r
693 +\r
694 +(defun notmuch-show-toggle-current-body ()\r
695 +  "Toggle the display of the current message body."\r
696 +  (interactive)\r
697 +  (save-excursion\r
698 +    (notmuch-show-move-to-current-message-summary-line)\r
699 +    (unless (button-at (point))\r
700 +      (notmuch-show-next-button))\r
701 +    (push-button))\r
702 +  )\r
703 +\r
704 +(defun notmuch-show-toggle-current-header ()\r
705 +  "Toggle the display of the current message header."\r
706 +  (interactive)\r
707 +  (save-excursion\r
708 +    (notmuch-show-move-to-current-message-summary-line)\r
709 +    (forward-line)\r
710 +    (unless (button-at (point))\r
711 +      (notmuch-show-next-button))\r
712 +    (push-button))\r
713 +  )\r
714 +\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
720 +           "\\)?\\)+")))\r
721 +\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
724 +\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
729 +\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
734 +\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
742 +    (save-excursion\r
743 +      (goto-char (1- beg))\r
744 +      (insert prefix)\r
745 +      (insert-button button-text\r
746 +                    'invisibility-spec invis-spec\r
747 +                    :type button-type)\r
748 +      )))\r
749 +\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
759 +    (goto-char beg)\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
770 +          (point) cite-end\r
771 +          "citation"\r
772 +          indent\r
773 +          (format notmuch-show-citation-button-format\r
774 +                  (- cite-lines notmuch-show-citation-lines-prefix))\r
775 +          ))))\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
783 +              sig-start\r
784 +              end\r
785 +              "signature"\r
786 +              indent\r
787 +              (format notmuch-show-signature-button-format sig-lines)\r
788 +              ))))))\r
789 +\r
790 +(defun notmuch-show-markup-part (beg end depth)\r
791 +  (if (re-search-forward notmuch-show-part-begin-regexp nil t)\r
792 +      (progn\r
793 +        (let (mime-message mime-type)\r
794 +          (save-excursion\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
798 -end 1))))))\r
799 +\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
805 +          (forward-line)\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
809 +              (goto-char end)\r
810 +              (if (not (bolp))\r
811 +                  (insert "\n"))\r
812 +              (indent-rigidly beg end depth)\r
813 +              (if (not (eq mime-message nil))\r
814 +                  (save-excursion\r
815 +                    (goto-char beg)\r
816 +                    (forward-line -1)\r
817 +                    (let ((handle-type (mm-handle-type mime-message))\r
818 +                          mime-type)\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
822 +                        )\r
823 +                      (if (equal mime-type "text/html")\r
824 +                          (mm-display-part mime-message))))\r
825 +                )\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
831 +        (goto-char end))\r
832 +    (goto-char end)))\r
833 +\r
834 +(defun notmuch-show-markup-parts-region (beg end depth)\r
835 +  (save-excursion\r
836 +    (goto-char beg)\r
837 +    (while (< (point) end)\r
838 +      (notmuch-show-markup-part beg end depth))))\r
839 +\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
844 +\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
850 +message.\r
851 +\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
855 +  (forward-line)\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
864 +        (if (not match)\r
865 +            (add-to-invisibility-spec invis-spec)))\r
866 +      (set-marker beg nil)\r
867 +      (set-marker end nil)\r
868 +      )))\r
869 +\r
870 +(defun notmuch-show-markup-header (message-begin depth)\r
871 +  "Buttonize and decorate faces in a message header.\r
872 +\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
878 +\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
883 +  (forward-line)\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
888 +        (btn nil))\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
893 +      (goto-char beg)\r
894 +      (setq btn (make-button message-begin summary-end :type 'notmuch-butt=\r
895 on-body-toggle-type))\r
896 +      (forward-line)\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
906 +       (forward-line)\r
907 +       )\r
908 +      (goto-char end)\r
909 +      (insert "\n")\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
914 +      )\r
915 +  btn))\r
916 +\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
922 ch-end 1))))\r
923 +             (match (string=3D "1" (buffer-substring (match-beginning 2) (match-=\r
924 end 2))))\r
925 +              (btn nil))\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
929 +\r
930 +(defun notmuch-show-hide-markers ()\r
931 +  (save-excursion\r
932 +    (goto-char (point-min))\r
933 +    (while (not (eobp))\r
934 +      (if (re-search-forward notmuch-show-marker-regexp nil t)\r
935 +         (progn\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
939 +\r
940 +(defun notmuch-show-markup-messages ()\r
941 +  (save-excursion\r
942 +    (goto-char (point-min))\r
943 +    (while (not (eobp))\r
944 +      (notmuch-show-markup-message)))\r
945 +  (notmuch-show-hide-markers))\r
946 +\r
947 +;;;###autoload\r
948 +(defun notmuch-show-mode ()\r
949 +  "Major mode for viewing a thread with notmuch.\r
950 +\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
953 +\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
959 e helpful).\r
960 +\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
966 +\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
971 ire thread without\r
972 +scrolling through with \\[notmuch-show-advance-and-archive]).\r
973 +\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
976 +\r
977 +All currently available key bindings:\r
978 +\r
979 +\\{notmuch-show-mode-map}"\r
980 +  (interactive)\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
987 +\r
988 +(defcustom notmuch-show-hook nil\r
989 +  "List of functions to call when notmuch displays a message."\r
990 +  :type 'hook\r
991 +  :options '(goto-address)\r
992 +  :group 'notmuch)\r
993 +\r
994 +(defun notmuch-show-do-stash (text)\r
995 +    (kill-new text)\r
996 +    (message (concat "Saved: " text)))\r
997 +\r
998 +(defun notmuch-show-stash-cc ()\r
999 +  "Copy CC field of current message to kill-ring."\r
1000 +  (interactive)\r
1001 +  (notmuch-show-do-stash (notmuch-show-get-cc)))\r
1002 +\r
1003 +(defun notmuch-show-stash-date ()\r
1004 +  "Copy date of current message to kill-ring."\r
1005 +  (interactive)\r
1006 +  (notmuch-show-do-stash (notmuch-show-get-date)))\r
1007 +\r
1008 +(defun notmuch-show-stash-filename ()\r
1009 +  "Copy filename of current message to kill-ring."\r
1010 +  (interactive)\r
1011 +  (notmuch-show-do-stash (notmuch-show-get-filename)))\r
1012 +\r
1013 +(defun notmuch-show-stash-from ()\r
1014 +  "Copy From address of current message to kill-ring."\r
1015 +  (interactive)\r
1016 +  (notmuch-show-do-stash (notmuch-show-get-from)))\r
1017 +\r
1018 +(defun notmuch-show-stash-message-id ()\r
1019 +  "Copy message ID of current message to kill-ring."\r
1020 +  (interactive)\r
1021 +  (notmuch-show-do-stash (notmuch-show-get-message-id)))\r
1022 +\r
1023 +(defun notmuch-show-stash-subject ()\r
1024 +  "Copy Subject field of current message to kill-ring."\r
1025 +  (interactive)\r
1026 +  (notmuch-show-do-stash (notmuch-show-get-subject)))\r
1027 +\r
1028 +(defun notmuch-show-stash-tags ()\r
1029 +  "Copy tags of current message to kill-ring as a comma separated list."\r
1030 +  (interactive)\r
1031 +  (notmuch-show-do-stash (mapconcat 'identity (notmuch-show-get-tags) ",")=\r
1032 ))\r
1033 +\r
1034 +(defun notmuch-show-stash-to ()\r
1035 +  "Copy To address of current message to kill-ring."\r
1036 +  (interactive)\r
1037 +  (notmuch-show-do-stash (notmuch-show-get-to)))\r
1038 +\r
1039 +; Make show mode a bit prettier, highlighting URLs and using word wrap\r
1040 +\r
1041 +(defun notmuch-show-mark-read ()\r
1042 +  (notmuch-show-remove-tag "unread"))\r
1043 +\r
1044 +(defun notmuch-show-pretty-hook ()\r
1045 +  (goto-address-mode 1)\r
1046 +  (visual-line-mode))\r
1047 +\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
1051 +         (lambda()\r
1052 +           (hl-line-mode 1) ))\r
1053 +\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
1056 +\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
1060 +\r
1061 +The optional QUERY-CONTEXT is a notmuch search term. Only messages from th=\r
1062 e thread\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
1066 )))\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
1072 +      (if proc\r
1073 +         (error "notmuch search process already running for query `%s'" thread-i=\r
1074 d)\r
1075 +       )\r
1076 +      (erase-buffer)\r
1077 +      (goto-char (point-min))\r
1078 +      (save-excursion\r
1079 +       (let* ((basic-args (list notmuch-command nil t nil "show" "--entire-threa=\r
1080 d" thread-id))\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
1087 +       )\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
1092 +      )))\r
1093 +\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
1102 =20\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
1113 -    map)\r
1114 -  "Submap for stash commands"\r
1115 -  )\r
1116 -\r
1117 -(fset 'notmuch-show-stash-map notmuch-show-stash-map)\r
1118 -\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
1148 -    map)\r
1149 -  "Keymap for \"notmuch show\" buffers.")\r
1150 -(fset 'notmuch-show-mode-map notmuch-show-mode-map)\r
1151 -\r
1152 -(defvar notmuch-show-signature-regexp "\\(-- ?\\|_+\\)$"\r
1153 -  "Pattern to match a line that separates content from signature.\r
1154 -\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
1161 -\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
1165 -\r
1166 -Can use up to one integer format parameter, i.e. %d")\r
1167 -\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
1171 -\r
1172 -Can use up to one integer format parameter, i.e. %d")\r
1173 -\r
1174 -(defvar notmuch-show-signature-lines-max 12\r
1175 -  "Maximum length of signature that will be hidden by default.")\r
1176 -\r
1177 -(defvar notmuch-show-citation-lines-prefix 4\r
1178 -  "Always show at least this many lines of a citation.\r
1179 -\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
1183 =20\r
1184  (defvar notmuch-command "notmuch"\r
1185    "Command to run the notmuch binary.")\r
1186 =20\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
1199 -\r
1200 -(defvar notmuch-show-id-regexp "\\(id:[^ ]*\\)")\r
1201 -(defvar notmuch-show-depth-match-regexp " depth:\\([0-9]*\\).*match:\\([01=\r
1202 ]\\) ")\r
1203 -(defvar notmuch-show-filename-regexp "filename:\\(.*\\)$")\r
1204 -(defvar notmuch-show-contentype-regexp "Content-type: \\(.*\\)")\r
1205 -\r
1206 -(defvar notmuch-show-tags-regexp "(\\([^)]*\\))$")\r
1207 -\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
1213 -\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
1219 terms)))))\r
1220      (completing-read prompt (split-string tag-list "\n+" t) nil nil nil)))\r
1221 =20\r
1222 -(defun notmuch-show-next-line ()\r
1223 -  "Like builtin `next-line' but ensuring we end on a visible character.\r
1224 -\r
1225 -By advancing forward until reaching a visible character.\r
1226 -\r
1227 -Unlike builtin `next-line' this version accepts no arguments."\r
1228 -  (interactive)\r
1229 -  (set 'this-command 'next-line)\r
1230 -  (call-interactively 'next-line)\r
1231 -  (while (point-invisible-p)\r
1232 -    (forward-char)))\r
1233 -\r
1234 -(defun notmuch-show-previous-line ()\r
1235 -  "Like builtin `previous-line' but ensuring we end on a visible character.\r
1236 -\r
1237 -By advancing forward until reaching a visible character.\r
1238 -\r
1239 -Unlike builtin `previous-line' this version accepts no arguments."\r
1240 -  (interactive)\r
1241 -  (set 'this-command 'previous-line)\r
1242 -  (call-interactively 'previous-line)\r
1243 -  (while (point-invisible-p)\r
1244 -    (forward-char)))\r
1245 -\r
1246 -(defun notmuch-show-get-message-id ()\r
1247 -  (save-excursion\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
1253 -\r
1254 -(defun notmuch-show-get-filename ()\r
1255 -  (save-excursion\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
1261 -\r
1262 -(defun notmuch-show-set-tags (tags)\r
1263 -  (save-excursion\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
1272 -      (goto-char beg)\r
1273 -      (insert (mapconcat 'identity tags " ")))))\r
1274 -\r
1275 -(defun notmuch-show-get-tags ()\r
1276 -  (save-excursion\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
1282 -\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
1286 -\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
1290 -\r
1291 -(defun notmuch-show-get-date ()\r
1292 -  "Return Date of current message"\r
1293 -  (notmuch-show-get-header-field 'date))\r
1294 -\r
1295 -(defun notmuch-show-get-from ()\r
1296 -  "Return From address of current message"\r
1297 -  (notmuch-show-get-header-field 'from))\r
1298 -\r
1299 -(defun notmuch-show-get-subject ()\r
1300 -  "Return Subject of current message"\r
1301 -  (notmuch-show-get-header-field 'subject))\r
1302 -\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
1306 -\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
1313 -    val))\r
1314 -\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
1322 -  (save-excursion\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
1329 atch-end 1)))\r
1330 -         (beg (point)))\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
1334 -         (insert text)\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
1338 -           (forward-line)\r
1339 -           )\r
1340 -         (goto-char (point-min))\r
1341 -         (cons (cons 'summary summary) (mail-header-extract-no-properties)))))))\r
1342 -\r
1343 -(defun notmuch-show-add-tag (&rest toadd)\r
1344 -  "Add a tag to the current message."\r
1345 -  (interactive\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
1353 -\r
1354 -(defun notmuch-show-remove-tag (&rest toremove)\r
1355 -  "Remove a tag from the current message."\r
1356 -  (interactive\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
1361 -       (progn\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
1368 -\r
1369 -(defun notmuch-show-archive-thread ()\r
1370 -  "Archive each message in thread, then show next thread from search.\r
1371 -\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
1375 -\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
1379 -buffer."\r
1380 -  (interactive)\r
1381 -  (save-excursion\r
1382 -    (goto-char (point-min))\r
1383 -    (while (not (eobp))\r
1384 -      (notmuch-show-remove-tag "inbox")\r
1385 -      (if (not (eobp))\r
1386 -         (forward-char))\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
1392 -       (progn\r
1393 -         (switch-to-buffer parent-buffer)\r
1394 -         (forward-line)\r
1395 -         (notmuch-search-show-thread)))))\r
1396 -\r
1397 -(defun notmuch-show-archive-thread-then-exit ()\r
1398 -  "Archive each message in thread, then exit back to search results."\r
1399 -  (interactive)\r
1400 -  (notmuch-show-archive-thread)\r
1401 -  (kill-this-buffer))\r
1402 -\r
1403 -(defun notmuch-show-view-raw-message ()\r
1404 -  "View the raw email of the current message."\r
1405 -  (interactive)\r
1406 -  (view-file (notmuch-show-get-filename)))\r
1407 -\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
1413 "))))\r
1414 -         (with-current-buffer buf\r
1415 -           (insert-file-contents filename nil nil nil t)\r
1416 -           ,@body)\r
1417 -        (kill-buffer buf)))))\r
1418 -\r
1419 -(defun notmuch-show-view-all-mime-parts ()\r
1420 -  "Use external viewers to view all attachments from the current message."\r
1421 -  (interactive)\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
1428 -   ;\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
1437 -                                )))\r
1438 -     (mm-display-parts (mm-dissect-buffer)))))\r
1439 -\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
1445     mm-handle))\r
1446 =20\r
1447 -(defun notmuch-show-save-attachments ()\r
1448 -  "Save all attachments from the current message."\r
1449 -  (interactive)\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
1455 -\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
1460         (forward-line)))\r
1461    (message-mode))\r
1462 =20\r
1463 -(defun notmuch-show-reply ()\r
1464 -  "Begin composing a reply to the current message in a new buffer."\r
1465 -  (interactive)\r
1466 -  (let ((message-id (notmuch-show-get-message-id)))\r
1467 -    (notmuch-reply message-id)))\r
1468 -\r
1469 -(defun notmuch-show-forward-current ()\r
1470 -  "Forward the current message."\r
1471 -  (interactive)\r
1472 -  (with-current-notmuch-show-message\r
1473 -   (message-forward)))\r
1474 -\r
1475 -(defun notmuch-show-pipe-message (command)\r
1476 -  "Pipe the contents of the current message to the given command.\r
1477 -\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
1483 e*"\r
1484 -        (list command " < " (shell-quote-argument (notmuch-show-get-filename)))))\r
1485 -\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
1488 -\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
1494 -\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
1504 -\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
1508 -    (save-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
1511 -\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
1515 -\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
1522 -\r
1523 -(defun notmuch-show-next-message-without-marking-read ()\r
1524 -  "Advance to the beginning of the next message in the buffer.\r
1525 -\r
1526 -Moves to the last visible character of the current message if\r
1527 -already on the last message in the buffer.\r
1528 -\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
1532 -      (progn\r
1533 -       (notmuch-show-move-to-current-message-summary-line)\r
1534 -       (recenter 0)\r
1535 -       t)\r
1536 -    (goto-char (- (point-max) 1))\r
1537 -    (while (point-invisible-p)\r
1538 -      (backward-char))\r
1539 -    (recenter 0)\r
1540 -    nil))\r
1541 -\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
1545 -\r
1546 -Moves to the last visible character of the current message if\r
1547 -already on the last message in the buffer.\r
1548 -\r
1549 -Returns nil if already on the last message in the buffer."\r
1550 -  (interactive)\r
1551 -  (notmuch-show-next-message-without-marking-read)\r
1552 -  (notmuch-show-mark-read))\r
1553 -\r
1554 -(defun notmuch-show-find-next-message ()\r
1555 -  "Returns the position of the next message in the buffer.\r
1556 -\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
1562 -  (save-excursion\r
1563 -    (save-window-excursion\r
1564 -      (notmuch-show-next-message-without-marking-read)\r
1565 -      (point))))\r
1566 -\r
1567 -(defun notmuch-show-next-unread-message ()\r
1568 -  "Advance to the next unread message.\r
1569 -\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
1579 -\r
1580 -(defun notmuch-show-next-open-message ()\r
1581 -  "Advance to the next open message (that is, body is visible).\r
1582 -\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
1585 -  (interactive)\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
1589 -\r
1590 -(defun notmuch-show-previous-message-without-marking-read ()\r
1591 -  "Backup to the beginning of the previous message in the buffer.\r
1592 -\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
1595 -\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
1601 -       (progn\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
1605 -         (recenter 0)\r
1606 -         (if (=3D (point) start)\r
1607 -             nil\r
1608 -           t))\r
1609 -      (recenter 0)\r
1610 -      nil)))\r
1611 -\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
1615 -\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
1618 -  (interactive)\r
1619 -  (notmuch-show-previous-message-without-marking-read)\r
1620 -  (notmuch-show-mark-read))\r
1621 -\r
1622 -(defun notmuch-show-find-previous-message ()\r
1623 -  "Returns the position of the previous message in the buffer.\r
1624 -\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
1627 -it."\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
1631 -  (save-excursion\r
1632 -    (save-window-excursion\r
1633 -      (notmuch-show-previous-message-without-marking-read)\r
1634 -      (point))))\r
1635 -\r
1636 -(defun notmuch-show-previous-open-message ()\r
1637 -  "Backup to previous open message (that is, body is visible).\r
1638 -\r
1639 -Moves to the first message in the buffer if there are no previous\r
1640 -open messages."\r
1641 -  (interactive)\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
1645 -\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
1649 -\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
1653 -\r
1654 -Otherwise, just scroll down a screenful of the current message.\r
1655 -\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
1659 -  (interactive)\r
1660 -  (let ((previous (notmuch-show-find-previous-message)))\r
1661 -    (if (> (count-lines previous (point)) (- (window-height) next-screen-c=\r
1662 ontext-lines))\r
1663 -       (progn\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
1671 -             (progn\r
1672 -               (goto-char previous)\r
1673 -               (recenter 0))))\r
1674 -      (notmuch-show-previous-message))))\r
1675 -\r
1676 -(defun notmuch-show-advance-and-archive ()\r
1677 -  "Advance through thread and archive.\r
1678 -\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
1681 -\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
1684 -\r
1685 -Otherwise, (the end of the current message is already within the\r
1686 -current window), advance to the next open message.\r
1687 -\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
1693 -  (interactive)\r
1694 -  (let ((next (notmuch-show-find-next-message))\r
1695 -       (unread (notmuch-show-message-unread-p)))\r
1696 -    (if (> next (window-end))\r
1697 -       (scroll-up nil)\r
1698 -      (let ((last (notmuch-show-last-message-p)))\r
1699 -       (notmuch-show-next-open-message)\r
1700 -       (if last\r
1701 -           (notmuch-show-archive-thread))))))\r
1702 -\r
1703 -(defun notmuch-show-next-button ()\r
1704 -  "Advance point to the next button in the buffer."\r
1705 -  (interactive)\r
1706 -  (forward-button 1))\r
1707 -\r
1708 -(defun notmuch-show-previous-button ()\r
1709 -  "Move point back to the previous button in the buffer."\r
1710 -  (interactive)\r
1711 -  (backward-button 1))\r
1712 -\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
1718    (redisplay t))\r
1719 =20\r
1720 -(defun notmuch-show-toggle-current-body ()\r
1721 -  "Toggle the display of the current message body."\r
1722 -  (interactive)\r
1723 -  (save-excursion\r
1724 -    (notmuch-show-move-to-current-message-summary-line)\r
1725 -    (unless (button-at (point))\r
1726 -      (notmuch-show-next-button))\r
1727 -    (push-button))\r
1728 -  )\r
1729 -\r
1730 -(defun notmuch-show-toggle-current-header ()\r
1731 -  "Toggle the display of the current message header."\r
1732 -  (interactive)\r
1733 -  (save-excursion\r
1734 -    (notmuch-show-move-to-current-message-summary-line)\r
1735 -    (forward-line)\r
1736 -    (unless (button-at (point))\r
1737 -      (notmuch-show-next-button))\r
1738 -    (push-button))\r
1739 -  )\r
1740 -\r
1741  (define-button-type 'notmuch-button-invisibility-toggle-type\r
1742    'action 'notmuch-toggle-invisible-action\r
1743    'follow-link t\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
1747 =20\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
1753 -           "\\)?\\)+")))\r
1754 -\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
1757 -\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
1762 -\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
1767 -\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
1775 -    (save-excursion\r
1776 -      (goto-char (1- beg))\r
1777 -      (insert prefix)\r
1778 -      (insert-button button-text\r
1779 -                    'invisibility-spec invis-spec\r
1780 -                    :type button-type)\r
1781 -      )))\r
1782 -\r
1783 -\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
1793 -    (goto-char beg)\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
1805 -          "citation"\r
1806 -          indent\r
1807 -          (format notmuch-show-citation-button-format\r
1808 -                  (- cite-lines notmuch-show-citation-lines-prefix))\r
1809 -          ))))\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
1817 -              sig-start\r
1818 -              end\r
1819 -              "signature"\r
1820 -              indent\r
1821 -              (format notmuch-show-signature-button-format sig-lines)\r
1822 -              ))))))\r
1823 -\r
1824 -(defun notmuch-show-markup-part (beg end depth)\r
1825 -  (if (re-search-forward notmuch-show-part-begin-regexp nil t)\r
1826 -      (progn\r
1827 -        (let (mime-message mime-type)\r
1828 -          (save-excursion\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
1832 -end 1))))))\r
1833 -\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
1839 -          (forward-line)\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
1843 -              (goto-char end)\r
1844 -              (if (not (bolp))\r
1845 -                  (insert "\n"))\r
1846 -              (indent-rigidly beg end depth)\r
1847 -              (if (not (eq mime-message nil))\r
1848 -                  (save-excursion\r
1849 -                    (goto-char beg)\r
1850 -                    (forward-line -1)\r
1851 -                    (let ((handle-type (mm-handle-type mime-message))\r
1852 -                          mime-type)\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
1856 -                        )\r
1857 -                      (if (equal mime-type "text/html")\r
1858 -                          (mm-display-part mime-message))))\r
1859 -                )\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
1867 -\r
1868 -(defun notmuch-show-markup-parts-region (beg end depth)\r
1869 -  (save-excursion\r
1870 -    (goto-char beg)\r
1871 -    (while (< (point) end)\r
1872 -      (notmuch-show-markup-part beg end depth))))\r
1873 -\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
1878 -\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
1884 -message.\r
1885 -\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
1889 -  (forward-line)\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
1898 -        (if (not match)\r
1899 -            (add-to-invisibility-spec invis-spec)))\r
1900 -      (set-marker beg nil)\r
1901 -      (set-marker end nil)\r
1902 -      )))\r
1903 -\r
1904  (defun notmuch-fontify-headers ()\r
1905    (while (looking-at "[[:space:]]")\r
1906      (forward-char))\r
1907 @@ -921,80 +176,6 @@ before the delimiter marking the beginning of the body=\r
1908 ."\r
1909               (overlay-put (make-overlay (point) (re-search-forward ".*$"))\r
1910                            'face 'message-header-other)))))))\r
1911 =20\r
1912 -(defun notmuch-show-markup-header (message-begin depth)\r
1913 -  "Buttonize and decorate faces in a message header.\r
1914 -\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
1920 -\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
1925 -  (forward-line)\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
1930 -        (btn nil))\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
1935 -      (goto-char beg)\r
1936 -      (setq btn (make-button message-begin summary-end :type 'notmuch-butt=\r
1937 on-body-toggle-type))\r
1938 -      (forward-line)\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
1948 -       (forward-line)\r
1949 -       )\r
1950 -      (goto-char end)\r
1951 -      (insert "\n")\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
1956 -      )\r
1957 -  btn))\r
1958 -\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
1964 ch-end 1))))\r
1965 -             (match (string=3D "1" (buffer-substring (match-beginning 2) (match-=\r
1966 end 2))))\r
1967 -              (btn nil))\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
1971 -\r
1972 -(defun notmuch-show-hide-markers ()\r
1973 -  (save-excursion\r
1974 -    (goto-char (point-min))\r
1975 -    (while (not (eobp))\r
1976 -      (if (re-search-forward notmuch-show-marker-regexp nil t)\r
1977 -         (progn\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
1981 -\r
1982 -(defun notmuch-show-markup-messages ()\r
1983 -  (save-excursion\r
1984 -    (goto-char (point-min))\r
1985 -    (while (not (eobp))\r
1986 -      (notmuch-show-markup-message)))\r
1987 -  (notmuch-show-hide-markers))\r
1988 -\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
1995 =20\r
1996 -;;;###autoload\r
1997 -(defun notmuch-show-mode ()\r
1998 -  "Major mode for viewing a thread with notmuch.\r
1999 -\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
2002 -\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
2008 e helpful).\r
2009 -\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
2015 -\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
2022 -\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
2025 -\r
2026 -All currently available key bindings:\r
2027 -\r
2028 -\\{notmuch-show-mode-map}"\r
2029 -  (interactive)\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
2036 -\r
2037  (defgroup notmuch nil\r
2038    "Notmuch mail reader for Emacs."\r
2039    :group 'mail)\r
2040 =20\r
2041 -(defcustom notmuch-show-hook nil\r
2042 -  "List of functions to call when notmuch displays a message."\r
2043 -  :type 'hook\r
2044 -  :options '(goto-address)\r
2045 -  :group 'notmuch)\r
2046 -\r
2047  (defcustom notmuch-search-hook nil\r
2048    "List of functions to call when notmuch displays the search results."\r
2049    :type 'hook\r
2050    :options '(hl-line-mode)\r
2051    :group 'notmuch)\r
2052 =20\r
2053 -(defun notmuch-show-do-stash (text)\r
2054 -    (kill-new text)\r
2055 -    (message (concat "Saved: " text)))\r
2056 -\r
2057 -(defun notmuch-show-stash-cc ()\r
2058 -  "Copy CC field of current message to kill-ring."\r
2059 -  (interactive)\r
2060 -  (notmuch-show-do-stash (notmuch-show-get-cc)))\r
2061 -\r
2062 -(defun notmuch-show-stash-date ()\r
2063 -  "Copy date of current message to kill-ring."\r
2064 -  (interactive)\r
2065 -  (notmuch-show-do-stash (notmuch-show-get-date)))\r
2066 -\r
2067 -(defun notmuch-show-stash-filename ()\r
2068 -  "Copy filename of current message to kill-ring."\r
2069 -  (interactive)\r
2070 -  (notmuch-show-do-stash (notmuch-show-get-filename)))\r
2071 -\r
2072 -(defun notmuch-show-stash-from ()\r
2073 -  "Copy From address of current message to kill-ring."\r
2074 -  (interactive)\r
2075 -  (notmuch-show-do-stash (notmuch-show-get-from)))\r
2076 -\r
2077 -(defun notmuch-show-stash-message-id ()\r
2078 -  "Copy message ID of current message to kill-ring."\r
2079 -  (interactive)\r
2080 -  (notmuch-show-do-stash (notmuch-show-get-message-id)))\r
2081 -\r
2082 -(defun notmuch-show-stash-subject ()\r
2083 -  "Copy Subject field of current message to kill-ring."\r
2084 -  (interactive)\r
2085 -  (notmuch-show-do-stash (notmuch-show-get-subject)))\r
2086 -\r
2087 -(defun notmuch-show-stash-tags ()\r
2088 -  "Copy tags of current message to kill-ring as a comma separated list."\r
2089 -  (interactive)\r
2090 -  (notmuch-show-do-stash (mapconcat 'identity (notmuch-show-get-tags) ",")=\r
2091 ))\r
2092 -\r
2093 -(defun notmuch-show-stash-to ()\r
2094 -  "Copy To address of current message to kill-ring."\r
2095 -  (interactive)\r
2096 -  (notmuch-show-do-stash (notmuch-show-get-to)))\r
2097 -\r
2098 -; Make show mode a bit prettier, highlighting URLs and using word wrap\r
2099 -\r
2100 -(defun notmuch-show-mark-read ()\r
2101 -  (notmuch-show-remove-tag "unread"))\r
2102 -\r
2103 -(defun notmuch-show-pretty-hook ()\r
2104 -  (goto-address-mode 1)\r
2105 -  (visual-line-mode))\r
2106 -\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
2110 -         (lambda()\r
2111 -           (hl-line-mode 1) ))\r
2112 -\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
2115 -\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
2119 -\r
2120 -The optional QUERY-CONTEXT is a notmuch search term. Only messages from th=\r
2121 e thread\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
2125 )))\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
2131 -      (if proc\r
2132 -         (error "notmuch search process already running for query `%s'" thread-i=\r
2133 d)\r
2134 -       )\r
2135 -      (erase-buffer)\r
2136 -      (goto-char (point-min))\r
2137 -      (save-excursion\r
2138 -       (let* ((basic-args (list notmuch-command nil t nil "show" "--entire-threa=\r
2139 d" thread-id))\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
2146 -       )\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
2151 -      )))\r
2152 -\r
2153  (defvar notmuch-search-authors-width 40\r
2154    "Number of columns to use to display authors in a notmuch-search buffer.=\r
2155 ")\r
2156 =20\r
2157 @@ -1637,7 +680,6 @@ current search results AND that are tagged with the gi=\r
2158 ven tag."\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
2162 =20\r
2163 -\r
2164  ;;;###autoload\r
2165  (defun notmuch ()\r
2166    "Run notmuch to display all mail with tag of 'inbox'"\r
2167 --=20\r
2168 1.7.0\r
2169 \r
2170 \r
2171 dme.\r
2172 --=20\r
2173 David Edmondson, http://dme.org\r