Re: message-default-mail-headers not working in notmuch 0.22
[notmuch-archives.git] / 1f / e3bf5a149656a772fbedddefecd20ef3430f6f
1 Return-Path: <markwalters1009@gmail.com>\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 8492B431FDB\r
6         for <notmuch@notmuchmail.org>; Sun, 20 Oct 2013 01:28:51 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: 0.201\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=0.201 tagged_above=-999 required=5\r
12         tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,\r
13         FREEMAIL_ENVFROM_END_DIGIT=1, FREEMAIL_FROM=0.001,\r
14         RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
15 Received: from olra.theworths.org ([127.0.0.1])\r
16         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
17         with ESMTP id oF4PLm6nrr0h for <notmuch@notmuchmail.org>;\r
18         Sun, 20 Oct 2013 01:28:33 -0700 (PDT)\r
19 Received: from mail-wg0-f53.google.com (mail-wg0-f53.google.com\r
20  [74.125.82.53])        (using TLSv1 with cipher RC4-SHA (128/128 bits))        (No client\r
21  certificate requested) by olra.theworths.org (Postfix) with ESMTPS id\r
22  C02CF431FC0    for <notmuch@notmuchmail.org>; Sun, 20 Oct 2013 01:28:27 -0700\r
23  (PDT)\r
24 Received: by mail-wg0-f53.google.com with SMTP id y10so5365775wgg.20\r
25         for <notmuch@notmuchmail.org>; Sun, 20 Oct 2013 01:28:25 -0700 (PDT)\r
26 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113;\r
27         h=from:to:cc:subject:date:message-id:in-reply-to:references\r
28         :mime-version:content-type:content-transfer-encoding;\r
29         bh=ae3bpNvams7wy2zVrG3y4Xy0+W9DCyT/VybeiR2/1oY=;\r
30         b=cU00DEzCq6PhLumQvMqrrDFVYaPiJ+DpnENaZi7bKJgXjqT91aAh+H9yxhJmxbcwdk\r
31         kXgjWpI75hYo3GednWqtaBROBrMZNpcoIldWtF7b2RIIG3k50IKrs400fMcSy0JxFSm9\r
32         RNRtHJBML7aI4PGB2JPbbvpE8OGQm2gzmtuyjaBqvv7MWotBFvleFyNx37PHrAyF/eaf\r
33         FUnMJg5NKRViI0g9ZoBT+N8gvKGvjRC5tzJiYQj8d7cu3iIzytZyk/qHejhJ2RiUrofj\r
34         SOGrvjU+iuGp8nAlManOSCvGKEtcZwx4aW+4L5CfRSGM409UUavLqGPveCHKBwmbG8jP\r
35         NsrQ==\r
36 X-Received: by 10.180.9.134 with SMTP id z6mr5449925wia.9.1382257705168;\r
37         Sun, 20 Oct 2013 01:28:25 -0700 (PDT)\r
38 Received: from localhost (93-97-24-31.zone5.bethere.co.uk. [93.97.24.31])\r
39         by mx.google.com with ESMTPSA id gp9sm43729420wib.8.2013.10.20.01.28.23\r
40         for <multiple recipients>\r
41         (version=TLSv1.2 cipher=RC4-SHA bits=128/128);\r
42         Sun, 20 Oct 2013 01:28:24 -0700 (PDT)\r
43 From: Mark Walters <markwalters1009@gmail.com>\r
44 To: notmuch@notmuchmail.org\r
45 Subject: [PATCH 1/5] emacs: move pick from contrib into mainline\r
46 Date: Sun, 20 Oct 2013 09:28:15 +0100\r
47 Message-Id: <1382257699-29860-2-git-send-email-markwalters1009@gmail.com>\r
48 X-Mailer: git-send-email 1.7.9.1\r
49 In-Reply-To: <1382257699-29860-1-git-send-email-markwalters1009@gmail.com>\r
50 References: <1382257699-29860-1-git-send-email-markwalters1009@gmail.com>\r
51 MIME-Version: 1.0\r
52 Content-Type: text/plain; charset=UTF-8\r
53 Content-Transfer-Encoding: 8bit\r
54 X-BeenThere: notmuch@notmuchmail.org\r
55 X-Mailman-Version: 2.1.13\r
56 Precedence: list\r
57 List-Id: "Use and development of the notmuch mail system."\r
58         <notmuch.notmuchmail.org>\r
59 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
60         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
61 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
62 List-Post: <mailto:notmuch@notmuchmail.org>\r
63 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
64 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
65         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
66 X-List-Received-Date: Sun, 20 Oct 2013 08:28:52 -0000\r
67 \r
68 This moves the notmuch-pick.el file into the main emacs directory. The\r
69 file is not changed in the move.\r
70 ---\r
71  contrib/notmuch-pick/notmuch-pick.el |  946 ----------------------------------\r
72  emacs/notmuch-pick.el                |  946 ++++++++++++++++++++++++++++++++++\r
73  2 files changed, 946 insertions(+), 946 deletions(-)\r
74  delete mode 100644 contrib/notmuch-pick/notmuch-pick.el\r
75  create mode 100644 emacs/notmuch-pick.el\r
76 \r
77 diff --git a/contrib/notmuch-pick/notmuch-pick.el b/contrib/notmuch-pick/notmuch-pick.el\r
78 deleted file mode 100644\r
79 index a492214..0000000\r
80 --- a/contrib/notmuch-pick/notmuch-pick.el\r
81 +++ /dev/null\r
82 @@ -1,946 +0,0 @@\r
83 -;; notmuch-pick.el --- displaying notmuch forests.\r
84 -;;\r
85 -;; Copyright © Carl Worth\r
86 -;; Copyright © David Edmondson\r
87 -;; Copyright © Mark Walters\r
88 -;;\r
89 -;; This file is part of Notmuch.\r
90 -;;\r
91 -;; Notmuch is free software: you can redistribute it and/or modify it\r
92 -;; under the terms of the GNU General Public License as published by\r
93 -;; the Free Software Foundation, either version 3 of the License, or\r
94 -;; (at your option) any later version.\r
95 -;;\r
96 -;; Notmuch is distributed in the hope that it will be useful, but\r
97 -;; WITHOUT ANY WARRANTY; without even the implied warranty of\r
98 -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
99 -;; General Public License for more details.\r
100 -;;\r
101 -;; You should have received a copy of the GNU General Public License\r
102 -;; along with Notmuch.  If not, see <http://www.gnu.org/licenses/>.\r
103 -;;\r
104 -;; Authors: David Edmondson <dme@dme.org>\r
105 -;;          Mark Walters <markwalters1009@gmail.com>\r
106 -\r
107 -(require 'mail-parse)\r
108 -\r
109 -(require 'notmuch-lib)\r
110 -(require 'notmuch-query)\r
111 -(require 'notmuch-show)\r
112 -(require 'notmuch) ;; XXX ATM, as notmuch-search-mode-map is defined here\r
113 -\r
114 -(eval-when-compile (require 'cl))\r
115 -\r
116 -(declare-function notmuch-call-notmuch-process "notmuch" (&rest args))\r
117 -(declare-function notmuch-show "notmuch-show" (&rest args))\r
118 -(declare-function notmuch-tag "notmuch" (query &rest tags))\r
119 -(declare-function notmuch-show-strip-re "notmuch-show" (subject))\r
120 -(declare-function notmuch-show-spaces-n "notmuch-show" (n))\r
121 -(declare-function notmuch-read-query "notmuch" (prompt))\r
122 -(declare-function notmuch-read-tag-changes "notmuch" (&optional initial-input &rest search-terms))\r
123 -(declare-function notmuch-update-tags "notmuch" (current-tags tag-changes))\r
124 -(declare-function notmuch-hello-trim "notmuch-hello" (search))\r
125 -(declare-function notmuch-search-find-thread-id "notmuch" ())\r
126 -(declare-function notmuch-search-find-subject "notmuch" ())\r
127 -\r
128 -;; the following variable is defined in notmuch.el\r
129 -(defvar notmuch-search-query-string)\r
130 -\r
131 -(defgroup notmuch-pick nil\r
132 -  "Showing message and thread structure."\r
133 -  :group 'notmuch)\r
134 -\r
135 -(defcustom notmuch-pick-show-out nil\r
136 -  "View selected messages in new window rather than split-pane."\r
137 -  :type 'boolean\r
138 -  :group 'notmuch-pick)\r
139 -\r
140 -(defcustom notmuch-pick-result-format\r
141 -  `(("date" . "%12s  ")\r
142 -    ("authors" . "%-20s")\r
143 -    ((("tree" . "%s")("subject" . "%s")) ." %-54s ")\r
144 -    ("tags" . "(%s)"))\r
145 -  "Result formatting for Pick. Supported fields are: date,\r
146 -        authors, subject, tree, tags.  Tree means the thread tree\r
147 -        box graphics. The field may also be a list in which case\r
148 -        the formatting rules are applied recursively and then the\r
149 -        output of all the fields in the list is inserted\r
150 -        according to format-string.\r
151 -\r
152 -Note the author string should not contain\r
153 -        whitespace (put it in the neighbouring fields instead).\r
154 -        For example:\r
155 -        (setq notmuch-pick-result-format \(\(\"authors\" . \"%-40s\"\)\r
156 -                                             \(\"subject\" . \"%s\"\)\)\)"\r
157 -  :type '(alist :key-type (string) :value-type (string))\r
158 -  :group 'notmuch-pick)\r
159 -\r
160 -;; Faces for messages that match the query.\r
161 -(defface notmuch-pick-match-date-face\r
162 -  '((t :inherit default))\r
163 -  "Face used in pick mode for the date in messages matching the query."\r
164 -  :group 'notmuch-pick\r
165 -  :group 'notmuch-faces)\r
166 -\r
167 -(defface notmuch-pick-match-author-face\r
168 -  '((((class color)\r
169 -      (background dark))\r
170 -     (:foreground "OliveDrab1"))\r
171 -    (((class color)\r
172 -      (background light))\r
173 -     (:foreground "dark blue"))\r
174 -    (t\r
175 -     (:bold t)))\r
176 -  "Face used in pick mode for the date in messages matching the query."\r
177 -  :group 'notmuch-pick\r
178 -  :group 'notmuch-faces)\r
179 -\r
180 -(defface notmuch-pick-match-subject-face\r
181 -  '((t :inherit default))\r
182 -  "Face used in pick mode for the subject in messages matching the query."\r
183 -  :group 'notmuch-pick\r
184 -  :group 'notmuch-faces)\r
185 -\r
186 -(defface notmuch-pick-match-tree-face\r
187 -  '((t :inherit default))\r
188 -  "Face used in pick mode for the thread tree block graphics in messages matching the query."\r
189 -  :group 'notmuch-pick\r
190 -  :group 'notmuch-faces)\r
191 -\r
192 -(defface notmuch-pick-match-tag-face\r
193 -  '((((class color)\r
194 -      (background dark))\r
195 -     (:foreground "OliveDrab1"))\r
196 -    (((class color)\r
197 -      (background light))\r
198 -     (:foreground "navy blue" :bold t))\r
199 -    (t\r
200 -     (:bold t)))\r
201 -  "Face used in pick mode for tags in messages matching the query."\r
202 -  :group 'notmuch-pick\r
203 -  :group 'notmuch-faces)\r
204 -\r
205 -;; Faces for messages that do not match the query.\r
206 -(defface notmuch-pick-no-match-date-face\r
207 -  '((t (:foreground "gray")))\r
208 -  "Face used in pick mode for non-matching dates."\r
209 -  :group 'notmuch-pick\r
210 -  :group 'notmuch-faces)\r
211 -\r
212 -(defface notmuch-pick-no-match-subject-face\r
213 -  '((t (:foreground "gray")))\r
214 -  "Face used in pick mode for non-matching subjects."\r
215 -  :group 'notmuch-pick\r
216 -  :group 'notmuch-faces)\r
217 -\r
218 -(defface notmuch-pick-no-match-tree-face\r
219 -  '((t (:foreground "gray")))\r
220 -  "Face used in pick mode for the thread tree block graphics in messages matching the query."\r
221 -  :group 'notmuch-pick\r
222 -  :group 'notmuch-faces)\r
223 -\r
224 -(defface notmuch-pick-no-match-author-face\r
225 -  '((t (:foreground "gray")))\r
226 -  "Face used in pick mode for the date in messages matching the query."\r
227 -  :group 'notmuch-pick\r
228 -  :group 'notmuch-faces)\r
229 -\r
230 -(defface notmuch-pick-no-match-tag-face\r
231 -  '((t (:foreground "gray")))\r
232 -  "Face used in pick mode face for non-matching tags."\r
233 -  :group 'notmuch-pick\r
234 -  :group 'notmuch-faces)\r
235 -\r
236 -(defvar notmuch-pick-previous-subject\r
237 -  "The subject of the most recent result shown during the async display")\r
238 -(make-variable-buffer-local 'notmuch-pick-previous-subject)\r
239 -\r
240 -(defvar notmuch-pick-basic-query nil\r
241 -  "A buffer local copy of argument query to the function notmuch-pick")\r
242 -(make-variable-buffer-local 'notmuch-pick-basic-query)\r
243 -\r
244 -(defvar notmuch-pick-query-context nil\r
245 -  "A buffer local copy of argument query-context to the function notmuch-pick")\r
246 -(make-variable-buffer-local 'notmuch-pick-query-context)\r
247 -\r
248 -(defvar notmuch-pick-target-msg nil\r
249 -  "A buffer local copy of argument target to the function notmuch-pick")\r
250 -(make-variable-buffer-local 'notmuch-pick-target-msg)\r
251 -\r
252 -(defvar notmuch-pick-open-target nil\r
253 -  "A buffer local copy of argument open-target to the function notmuch-pick")\r
254 -(make-variable-buffer-local 'notmuch-pick-open-target)\r
255 -\r
256 -(defvar notmuch-pick-message-window nil\r
257 -  "The window of the message pane.\r
258 -\r
259 -It is set in both the pick buffer and the child show buffer. It\r
260 -is used to try and close the message pane when quitting pick or\r
261 -the child show buffer.")\r
262 -(make-variable-buffer-local 'notmuch-pick-message-window)\r
263 -(put 'notmuch-pick-message-window 'permanent-local t)\r
264 -\r
265 -(defvar notmuch-pick-message-buffer nil\r
266 -  "The buffer name of the show buffer in the message pane.\r
267 -\r
268 -This is used to try and make sure we don't close the message pane\r
269 -if the user has loaded a different buffer in that window.")\r
270 -(make-variable-buffer-local 'notmuch-pick-message-buffer)\r
271 -(put 'notmuch-pick-message-buffer 'permanent-local t)\r
272 -\r
273 -(defun notmuch-pick-to-message-pane (func)\r
274 -  "Execute FUNC in message pane.\r
275 -\r
276 -This function returns a function (so can be used as a keybinding)\r
277 -which executes function FUNC in the message pane if it is\r
278 -open (if the message pane is closed it does nothing)."\r
279 -  `(lambda ()\r
280 -      ,(concat "(In message pane) " (documentation func t))\r
281 -     (interactive)\r
282 -     (when (window-live-p notmuch-pick-message-window)\r
283 -       (with-selected-window notmuch-pick-message-window\r
284 -        (call-interactively #',func)))))\r
285 -\r
286 -(defun notmuch-pick-button-activate (&optional button)\r
287 -  "Activate BUTTON or button at point\r
288 -\r
289 -This function does not give an error if there is no button."\r
290 -  (interactive)\r
291 -  (let ((button (or button (button-at (point)))))\r
292 -    (when button (button-activate button))))\r
293 -\r
294 -(defun notmuch-pick-close-message-pane-and (func)\r
295 -  "Close message pane and execute FUNC.\r
296 -\r
297 -This function returns a function (so can be used as a keybinding)\r
298 -which closes the message pane if open and then executes function\r
299 -FUNC."\r
300 -  `(lambda ()\r
301 -      ,(concat "(Close message pane and) " (documentation func t))\r
302 -     (interactive)\r
303 -     (notmuch-pick-close-message-window)\r
304 -     (call-interactively #',func)))\r
305 -\r
306 -(defvar notmuch-pick-mode-map\r
307 -  (let ((map (make-sparse-keymap)))\r
308 -    (set-keymap-parent map notmuch-common-keymap)\r
309 -    ;; The following override the global keymap.\r
310 -    ;; Override because we want to close message pane first.\r
311 -    (define-key map "?" (notmuch-pick-close-message-pane-and #'notmuch-help))\r
312 -    ;; Override because we first close message pane and then close pick buffer.\r
313 -    (define-key map "q" 'notmuch-pick-quit)\r
314 -    ;; Override because we close message pane after the search query is entered.\r
315 -    (define-key map "s" 'notmuch-pick-to-search)\r
316 -    ;; Override because we want to close message pane first.\r
317 -    (define-key map "m" (notmuch-pick-close-message-pane-and #'notmuch-mua-new-mail))\r
318 -\r
319 -    ;; these use notmuch-show functions directly\r
320 -    (define-key map "|" 'notmuch-show-pipe-message)\r
321 -    (define-key map "w" 'notmuch-show-save-attachments)\r
322 -    (define-key map "v" 'notmuch-show-view-all-mime-parts)\r
323 -    (define-key map "c" 'notmuch-show-stash-map)\r
324 -\r
325 -    ;; these apply to the message pane\r
326 -    (define-key map (kbd "M-TAB") (notmuch-pick-to-message-pane #'notmuch-show-previous-button))\r
327 -    (define-key map (kbd "<backtab>")  (notmuch-pick-to-message-pane #'notmuch-show-previous-button))\r
328 -    (define-key map (kbd "TAB") (notmuch-pick-to-message-pane #'notmuch-show-next-button))\r
329 -    (define-key map "e" (notmuch-pick-to-message-pane #'notmuch-pick-button-activate))\r
330 -\r
331 -    ;; bindings from show (or elsewhere) but we close the message pane first.\r
332 -    (define-key map "f" (notmuch-pick-close-message-pane-and #'notmuch-show-forward-message))\r
333 -    (define-key map "r" (notmuch-pick-close-message-pane-and #'notmuch-show-reply-sender))\r
334 -    (define-key map "R" (notmuch-pick-close-message-pane-and #'notmuch-show-reply))\r
335 -    (define-key map "V" (notmuch-pick-close-message-pane-and #'notmuch-show-view-raw-message))\r
336 -\r
337 -    ;; The main pick bindings\r
338 -    (define-key map (kbd "RET") 'notmuch-pick-show-message)\r
339 -    (define-key map [mouse-1] 'notmuch-pick-show-message)\r
340 -    (define-key map "x" 'notmuch-pick-quit)\r
341 -    (define-key map "A" 'notmuch-pick-archive-thread)\r
342 -    (define-key map "a" 'notmuch-pick-archive-message-then-next)\r
343 -    (define-key map "=" 'notmuch-pick-refresh-view)\r
344 -    (define-key map "z" 'notmuch-pick-to-pick)\r
345 -    (define-key map "n" 'notmuch-pick-next-matching-message)\r
346 -    (define-key map "p" 'notmuch-pick-prev-matching-message)\r
347 -    (define-key map "N" 'notmuch-pick-next-message)\r
348 -    (define-key map "P" 'notmuch-pick-prev-message)\r
349 -    (define-key map (kbd "M-p") 'notmuch-pick-prev-thread)\r
350 -    (define-key map (kbd "M-n") 'notmuch-pick-next-thread)\r
351 -    (define-key map "-" 'notmuch-pick-remove-tag)\r
352 -    (define-key map "+" 'notmuch-pick-add-tag)\r
353 -    (define-key map "*" 'notmuch-pick-tag-thread)\r
354 -    (define-key map " " 'notmuch-pick-scroll-or-next)\r
355 -    (define-key map "b" 'notmuch-pick-scroll-message-window-back)\r
356 -    map))\r
357 -(fset 'notmuch-pick-mode-map notmuch-pick-mode-map)\r
358 -\r
359 -(defun notmuch-pick-get-message-properties ()\r
360 -  "Return the properties of the current message as a plist.\r
361 -\r
362 -Some useful entries are:\r
363 -:headers - Property list containing the headers :Date, :Subject, :From, etc.\r
364 -:tags - Tags for this message"\r
365 -  (save-excursion\r
366 -    (beginning-of-line)\r
367 -    (get-text-property (point) :notmuch-message-properties)))\r
368 -\r
369 -;; XXX This should really be a lib function but we are trying to\r
370 -;; reduce impact on the code base.\r
371 -(defun notmuch-show-get-prop (prop &optional props)\r
372 -  "This is a pick overridden version of notmuch-show-get-prop\r
373 -\r
374 -It gets property PROP from PROPS or, if PROPS is nil, the current\r
375 -message in either pick or show. This means that several functions\r
376 -in notmuch-show now work unchanged in pick as they just need the\r
377 -correct message properties."\r
378 -  (let ((props (or props\r
379 -                  (cond ((eq major-mode 'notmuch-show-mode)\r
380 -                         (notmuch-show-get-message-properties))\r
381 -                        ((eq major-mode 'notmuch-pick-mode)\r
382 -                         (notmuch-pick-get-message-properties))))))\r
383 -    (plist-get props prop)))\r
384 -\r
385 -(defun notmuch-pick-set-message-properties (props)\r
386 -  (save-excursion\r
387 -    (beginning-of-line)\r
388 -    (put-text-property (point) (+ (point) 1) :notmuch-message-properties props)))\r
389 -\r
390 -(defun notmuch-pick-set-prop (prop val &optional props)\r
391 -  (let ((inhibit-read-only t)\r
392 -       (props (or props\r
393 -                  (notmuch-pick-get-message-properties))))\r
394 -    (plist-put props prop val)\r
395 -    (notmuch-pick-set-message-properties props)))\r
396 -\r
397 -(defun notmuch-pick-get-prop (prop &optional props)\r
398 -  (let ((props (or props\r
399 -                  (notmuch-pick-get-message-properties))))\r
400 -    (plist-get props prop)))\r
401 -\r
402 -(defun notmuch-pick-set-tags (tags)\r
403 -  "Set the tags of the current message."\r
404 -  (notmuch-pick-set-prop :tags tags))\r
405 -\r
406 -(defun notmuch-pick-get-tags ()\r
407 -  "Return the tags of the current message."\r
408 -  (notmuch-pick-get-prop :tags))\r
409 -\r
410 -(defun notmuch-pick-get-message-id ()\r
411 -  "Return the message id of the current message."\r
412 -  (let ((id (notmuch-pick-get-prop :id)))\r
413 -    (if id\r
414 -       (notmuch-id-to-query id)\r
415 -      nil)))\r
416 -\r
417 -(defun notmuch-pick-get-match ()\r
418 -  "Return whether the current message is a match."\r
419 -  (interactive)\r
420 -  (notmuch-pick-get-prop :match))\r
421 -\r
422 -(defun notmuch-pick-refresh-result ()\r
423 -  "Redisplay the current message line.\r
424 -\r
425 -This redisplays the current line based on the messages\r
426 -properties (as they are now). This is used when tags are\r
427 -updated."\r
428 -  (let ((init-point (point))\r
429 -       (end (line-end-position))\r
430 -       (msg (notmuch-pick-get-message-properties))\r
431 -       (inhibit-read-only t))\r
432 -    (beginning-of-line)\r
433 -    ;; This is a little tricky: we override\r
434 -    ;; notmuch-pick-previous-subject to get the decision between\r
435 -    ;; ... and a subject right and it stops notmuch-pick-insert-msg\r
436 -    ;; from overwriting the buffer local copy of\r
437 -    ;; notmuch-pick-previous-subject if this is called while the\r
438 -    ;; buffer is displaying.\r
439 -    (let ((notmuch-pick-previous-subject (notmuch-pick-get-prop :previous-subject)))\r
440 -      (delete-region (point) (1+ (line-end-position)))\r
441 -      (notmuch-pick-insert-msg msg))\r
442 -    (let ((new-end (line-end-position)))\r
443 -      (goto-char (if (= init-point end)\r
444 -                    new-end\r
445 -                  (min init-point (- new-end 1)))))))\r
446 -\r
447 -(defun notmuch-pick-tag-update-display (&optional tag-changes)\r
448 -  "Update display for TAG-CHANGES to current message.\r
449 -\r
450 -Does NOT change the database."\r
451 -  (let* ((current-tags (notmuch-pick-get-tags))\r
452 -        (new-tags (notmuch-update-tags current-tags tag-changes)))\r
453 -    (unless (equal current-tags new-tags)\r
454 -      (notmuch-pick-set-tags new-tags)\r
455 -      (notmuch-pick-refresh-result))))\r
456 -\r
457 -(defun notmuch-pick-tag (&optional tag-changes)\r
458 -  "Change tags for the current message"\r
459 -  (interactive)\r
460 -  (setq tag-changes (notmuch-tag (notmuch-pick-get-message-id) tag-changes))\r
461 -  (notmuch-pick-tag-update-display tag-changes))\r
462 -\r
463 -(defun notmuch-pick-add-tag ()\r
464 -  "Same as `notmuch-pick-tag' but sets initial input to '+'."\r
465 -  (interactive)\r
466 -  (notmuch-pick-tag "+"))\r
467 -\r
468 -(defun notmuch-pick-remove-tag ()\r
469 -  "Same as `notmuch-pick-tag' but sets initial input to '-'."\r
470 -  (interactive)\r
471 -  (notmuch-pick-tag "-"))\r
472 -\r
473 -;; The next two functions close the message window before searching or\r
474 -;; picking but they do so after the user has entered the query (in\r
475 -;; case the user was basing the query on something in the message\r
476 -;; window).\r
477 -\r
478 -(defun notmuch-pick-to-search ()\r
479 -  "Run \"notmuch search\" with the given `query' and display results."\r
480 -  (interactive)\r
481 -  (let ((query (notmuch-read-query "Notmuch search: ")))\r
482 -    (notmuch-pick-close-message-window)\r
483 -    (notmuch-search query)))\r
484 -\r
485 -(defun notmuch-pick-to-pick ()\r
486 -  "Run a query and display results in experimental notmuch-pick mode"\r
487 -  (interactive)\r
488 -  (let ((query (notmuch-read-query "Notmuch pick: ")))\r
489 -    (notmuch-pick-close-message-window)\r
490 -    (notmuch-pick query)))\r
491 -\r
492 -;; This function should be in notmuch-show.el but be we trying to\r
493 -;; minimise impact on the rest of the codebase.\r
494 -(defun notmuch-pick-from-show-current-query ()\r
495 -  "Call notmuch pick with the current query"\r
496 -  (interactive)\r
497 -  (notmuch-pick notmuch-show-thread-id\r
498 -               notmuch-show-query-context\r
499 -               (notmuch-show-get-message-id)))\r
500 -\r
501 -;; This function should be in notmuch.el but be we trying to minimise\r
502 -;; impact on the rest of the codebase.\r
503 -(defun notmuch-pick-from-search-current-query ()\r
504 -  "Call notmuch pick with the current query"\r
505 -  (interactive)\r
506 -  (notmuch-pick notmuch-search-query-string))\r
507 -\r
508 -;; This function should be in notmuch.el but be we trying to minimise\r
509 -;; impact on the rest of the codebase.\r
510 -(defun notmuch-pick-from-search-thread ()\r
511 -  "Show the selected thread with notmuch-pick"\r
512 -  (interactive)\r
513 -  (notmuch-pick (notmuch-search-find-thread-id)\r
514 -                notmuch-search-query-string\r
515 -               nil\r
516 -                (notmuch-prettify-subject (notmuch-search-find-subject))\r
517 -               t))\r
518 -\r
519 -(defun notmuch-pick-message-window-kill-hook ()\r
520 -  "Close the message pane when exiting the show buffer."\r
521 -  (let ((buffer (current-buffer)))\r
522 -    (when (and (window-live-p notmuch-pick-message-window)\r
523 -              (eq (window-buffer notmuch-pick-message-window) buffer))\r
524 -      ;; We do not want an error if this is the sole window in the\r
525 -      ;; frame and I do not know how to test for that in emacs pre\r
526 -      ;; 24. Hence we just ignore-errors.\r
527 -      (ignore-errors\r
528 -       (delete-window notmuch-pick-message-window)))))\r
529 -\r
530 -(defun notmuch-pick-show-message-in ()\r
531 -  "Show the current message (in split-pane)."\r
532 -  (interactive)\r
533 -  (let ((id (notmuch-pick-get-message-id))\r
534 -       (inhibit-read-only t)\r
535 -       buffer)\r
536 -    (when id\r
537 -      ;; We close and reopen the window to kill off un-needed buffers\r
538 -      ;; this might cause flickering but seems ok.\r
539 -      (notmuch-pick-close-message-window)\r
540 -      (setq notmuch-pick-message-window\r
541 -           (split-window-vertically (/ (window-height) 4)))\r
542 -      (with-selected-window notmuch-pick-message-window\r
543 -       ;; Since we are only displaying one message do not indent.\r
544 -       (let ((notmuch-show-indent-messages-width 0)\r
545 -             (notmuch-show-only-matching-messages t))\r
546 -         (setq buffer (notmuch-show id))))\r
547 -      ;; We need the `let' as notmuch-pick-message-window is buffer local.\r
548 -      (let ((window notmuch-pick-message-window))\r
549 -       (with-current-buffer buffer\r
550 -         (setq notmuch-pick-message-window window)\r
551 -         (add-hook 'kill-buffer-hook 'notmuch-pick-message-window-kill-hook)))\r
552 -      (when notmuch-show-mark-read-tags\r
553 -       (notmuch-pick-tag-update-display notmuch-show-mark-read-tags))\r
554 -      (setq notmuch-pick-message-buffer buffer))))\r
555 -\r
556 -(defun notmuch-pick-show-message-out ()\r
557 -  "Show the current message (in whole window)."\r
558 -  (interactive)\r
559 -  (let ((id (notmuch-pick-get-message-id))\r
560 -       (inhibit-read-only t)\r
561 -       buffer)\r
562 -    (when id\r
563 -      ;; We close the window to kill off un-needed buffers.\r
564 -      (notmuch-pick-close-message-window)\r
565 -      (notmuch-show id))))\r
566 -\r
567 -(defun notmuch-pick-show-message (arg)\r
568 -  "Show the current message.\r
569 -\r
570 -Shows in split pane or whole window according to value of\r
571 -`notmuch-pick-show-out'. A prefix argument reverses the choice."\r
572 -  (interactive "P")\r
573 -  (if (or (and notmuch-pick-show-out  (not arg))\r
574 -         (and (not notmuch-pick-show-out) arg))\r
575 -      (notmuch-pick-show-message-out)\r
576 -    (notmuch-pick-show-message-in)))\r
577 -\r
578 -(defun notmuch-pick-scroll-message-window ()\r
579 -  "Scroll the message window (if it exists)"\r
580 -  (interactive)\r
581 -  (when (window-live-p notmuch-pick-message-window)\r
582 -    (with-selected-window notmuch-pick-message-window\r
583 -      (if (pos-visible-in-window-p (point-max))\r
584 -         t\r
585 -       (scroll-up)))))\r
586 -\r
587 -(defun notmuch-pick-scroll-message-window-back ()\r
588 -  "Scroll the message window back(if it exists)"\r
589 -  (interactive)\r
590 -  (when (window-live-p notmuch-pick-message-window)\r
591 -    (with-selected-window notmuch-pick-message-window\r
592 -      (if (pos-visible-in-window-p (point-min))\r
593 -         t\r
594 -       (scroll-down)))))\r
595 -\r
596 -(defun notmuch-pick-scroll-or-next ()\r
597 -  "Scroll the message window. If it at end go to next message."\r
598 -  (interactive)\r
599 -  (when (notmuch-pick-scroll-message-window)\r
600 -    (notmuch-pick-next-matching-message)))\r
601 -\r
602 -(defun notmuch-pick-quit ()\r
603 -  "Close the split view or exit pick."\r
604 -  (interactive)\r
605 -  (unless (notmuch-pick-close-message-window)\r
606 -    (kill-buffer (current-buffer))))\r
607 -\r
608 -(defun notmuch-pick-close-message-window ()\r
609 -  "Close the message-window. Return t if close succeeds."\r
610 -  (interactive)\r
611 -  (when (and (window-live-p notmuch-pick-message-window)\r
612 -            (eq (window-buffer notmuch-pick-message-window) notmuch-pick-message-buffer))\r
613 -    (delete-window notmuch-pick-message-window)\r
614 -    (unless (get-buffer-window-list notmuch-pick-message-buffer)\r
615 -      (kill-buffer notmuch-pick-message-buffer))\r
616 -    t))\r
617 -\r
618 -(defun notmuch-pick-archive-message (&optional unarchive)\r
619 -  "Archive the current message.\r
620 -\r
621 -Archive the current message by applying the tag changes in\r
622 -`notmuch-archive-tags' to it. If a prefix argument is given, the\r
623 -message will be \"unarchived\", i.e. the tag changes in\r
624 -`notmuch-archive-tags' will be reversed."\r
625 -  (interactive "P")\r
626 -  (when notmuch-archive-tags\r
627 -    (apply 'notmuch-pick-tag\r
628 -          (notmuch-tag-change-list notmuch-archive-tags unarchive))))\r
629 -\r
630 -(defun notmuch-pick-archive-message-then-next (&optional unarchive)\r
631 -  "Archive the current message and move to next matching message."\r
632 -  (interactive "P")\r
633 -  (notmuch-pick-archive-message unarchive)\r
634 -  (notmuch-pick-next-matching-message))\r
635 -\r
636 -(defun notmuch-pick-next-message ()\r
637 -  "Move to next message."\r
638 -  (interactive)\r
639 -  (forward-line)\r
640 -  (when (window-live-p notmuch-pick-message-window)\r
641 -    (notmuch-pick-show-message-in)))\r
642 -\r
643 -(defun notmuch-pick-prev-message ()\r
644 -  "Move to previous message."\r
645 -  (interactive)\r
646 -  (forward-line -1)\r
647 -  (when (window-live-p notmuch-pick-message-window)\r
648 -    (notmuch-pick-show-message-in)))\r
649 -\r
650 -(defun notmuch-pick-prev-matching-message ()\r
651 -  "Move to previous matching message."\r
652 -  (interactive)\r
653 -  (forward-line -1)\r
654 -  (while (and (not (bobp)) (not (notmuch-pick-get-match)))\r
655 -    (forward-line -1))\r
656 -  (when (window-live-p notmuch-pick-message-window)\r
657 -    (notmuch-pick-show-message-in)))\r
658 -\r
659 -(defun notmuch-pick-next-matching-message ()\r
660 -  "Move to next matching message."\r
661 -  (interactive)\r
662 -  (forward-line)\r
663 -  (while (and (not (eobp)) (not (notmuch-pick-get-match)))\r
664 -    (forward-line))\r
665 -  (when (window-live-p notmuch-pick-message-window)\r
666 -    (notmuch-pick-show-message-in)))\r
667 -\r
668 -(defun notmuch-pick-refresh-view ()\r
669 -  "Refresh view."\r
670 -  (interactive)\r
671 -  (let ((inhibit-read-only t)\r
672 -       (basic-query notmuch-pick-basic-query)\r
673 -       (query-context notmuch-pick-query-context)\r
674 -       (target (notmuch-pick-get-message-id)))\r
675 -    (erase-buffer)\r
676 -    (notmuch-pick-worker basic-query\r
677 -                        query-context\r
678 -                        target)))\r
679 -\r
680 -(defun notmuch-pick-thread-top ()\r
681 -  (when (notmuch-pick-get-message-properties)\r
682 -    (while (not (or (notmuch-pick-get-prop :first) (eobp)))\r
683 -      (forward-line -1))))\r
684 -\r
685 -(defun notmuch-pick-prev-thread ()\r
686 -  (interactive)\r
687 -  (forward-line -1)\r
688 -  (notmuch-pick-thread-top))\r
689 -\r
690 -(defun notmuch-pick-next-thread ()\r
691 -  (interactive)\r
692 -  (forward-line 1)\r
693 -  (while (not (or (notmuch-pick-get-prop :first) (eobp)))\r
694 -    (forward-line 1)))\r
695 -\r
696 -(defun notmuch-pick-thread-mapcar (function)\r
697 -  "Iterate through all messages in the current thread\r
698 - and call FUNCTION for side effects."\r
699 -  (save-excursion\r
700 -    (notmuch-pick-thread-top)\r
701 -    (loop collect (funcall function)\r
702 -         do (forward-line)\r
703 -         while (and (notmuch-pick-get-message-properties)\r
704 -                    (not (notmuch-pick-get-prop :first))))))\r
705 -\r
706 -(defun notmuch-pick-get-messages-ids-thread-search ()\r
707 -  "Return a search string for all message ids of messages in the current thread."\r
708 -  (mapconcat 'identity\r
709 -            (notmuch-pick-thread-mapcar 'notmuch-pick-get-message-id)\r
710 -            " or "))\r
711 -\r
712 -(defun notmuch-pick-tag-thread (&optional tag-changes)\r
713 -  "Tag all messages in the current thread"\r
714 -  (interactive)\r
715 -  (when (notmuch-pick-get-message-properties)\r
716 -    (let ((tag-changes (notmuch-tag (notmuch-pick-get-messages-ids-thread-search) tag-changes)))\r
717 -      (notmuch-pick-thread-mapcar\r
718 -       (lambda () (notmuch-pick-tag-update-display tag-changes))))))\r
719 -\r
720 -(defun notmuch-pick-archive-thread (&optional unarchive)\r
721 -  "Archive each message in thread.\r
722 -\r
723 -Archive each message currently shown by applying the tag changes\r
724 -in `notmuch-archive-tags' to each. If a prefix argument is given,\r
725 -the messages will be \"unarchived\", i.e. the tag changes in\r
726 -`notmuch-archive-tags' will be reversed.\r
727 -\r
728 -Note: This command is safe from any race condition of new messages\r
729 -being delivered to the same thread. It does not archive the\r
730 -entire thread, but only the messages shown in the current\r
731 -buffer."\r
732 -  (interactive "P")\r
733 -  (when notmuch-archive-tags\r
734 -    (notmuch-pick-tag-thread\r
735 -     (notmuch-tag-change-list notmuch-archive-tags unarchive))))\r
736 -\r
737 -;; Functions below here display the pick buffer itself.\r
738 -\r
739 -(defun notmuch-pick-clean-address (address)\r
740 -  "Try to clean a single email ADDRESS for display. Return\r
741 -AUTHOR_NAME if present, otherwise return AUTHOR_EMAIL. Return\r
742 -unchanged ADDRESS if parsing fails."\r
743 -  (let* ((clean-address (notmuch-clean-address address))\r
744 -        (p-address (car clean-address))\r
745 -        (p-name (cdr clean-address)))\r
746 -\r
747 -    ;; If we have a name return that otherwise return the address.\r
748 -    (or p-name p-address)))\r
749 -\r
750 -(defun notmuch-pick-format-field (field format-string msg)\r
751 -  "Format a FIELD of MSG according to FORMAT-STRING and return string"\r
752 -  (let* ((headers (plist-get msg :headers))\r
753 -        (match (plist-get msg :match)))\r
754 -    (cond\r
755 -     ((listp field)\r
756 -      (format format-string (notmuch-pick-format-field-list field msg)))\r
757 -\r
758 -     ((string-equal field "date")\r
759 -      (let ((face (if match\r
760 -                     'notmuch-pick-match-date-face\r
761 -                   'notmuch-pick-no-match-date-face)))\r
762 -       (propertize (format format-string (plist-get msg :date_relative)) 'face face)))\r
763 -\r
764 -     ((string-equal field "tree")\r
765 -      (let ((tree-status (plist-get msg :tree-status))\r
766 -           (face (if match\r
767 -                     'notmuch-pick-match-tree-face\r
768 -                   'notmuch-pick-no-match-tree-face)))\r
769 -\r
770 -       (propertize (format format-string\r
771 -                           (mapconcat #'identity (reverse tree-status) ""))\r
772 -                   'face face)))\r
773 -\r
774 -     ((string-equal field "subject")\r
775 -      (let ((bare-subject (notmuch-show-strip-re (plist-get headers :Subject)))\r
776 -           (previous-subject notmuch-pick-previous-subject)\r
777 -           (face (if match\r
778 -                     'notmuch-pick-match-subject-face\r
779 -                   'notmuch-pick-no-match-subject-face)))\r
780 -\r
781 -       (setq notmuch-pick-previous-subject bare-subject)\r
782 -       (propertize (format format-string\r
783 -                           (if (string= previous-subject bare-subject)\r
784 -                               " ..."\r
785 -                             bare-subject))\r
786 -                   'face face)))\r
787 -\r
788 -     ((string-equal field "authors")\r
789 -      (let ((author (notmuch-pick-clean-address (plist-get headers :From)))\r
790 -           (len (length (format format-string "")))\r
791 -           (face (if match\r
792 -                     'notmuch-pick-match-author-face\r
793 -                   'notmuch-pick-no-match-author-face)))\r
794 -       (when (> (length author) len)\r
795 -         (setq author (substring author 0 len)))\r
796 -       (propertize (format format-string author) 'face face)))\r
797 -\r
798 -     ((string-equal field "tags")\r
799 -      (let ((tags (plist-get msg :tags))\r
800 -           (face (if match\r
801 -                     'notmuch-pick-match-tag-face\r
802 -                   'notmuch-pick-no-match-tag-face)))\r
803 -       (propertize (format format-string\r
804 -                           (mapconcat #'identity tags ", "))\r
805 -                   'face face))))))\r
806 -\r
807 -\r
808 -(defun notmuch-pick-format-field-list (field-list msg)\r
809 -  "Format fields of MSG according to FIELD-LIST and return string"\r
810 -  (let (result-string)\r
811 -    (dolist (spec field-list result-string)\r
812 -      (let ((field-string (notmuch-pick-format-field (car spec) (cdr spec) msg)))\r
813 -       (setq result-string (concat result-string field-string))))))\r
814 -\r
815 -(defun notmuch-pick-insert-msg (msg)\r
816 -  "Insert the message MSG according to notmuch-pick-result-format"\r
817 -  ;; We need to save the previous subject as it will get overwritten\r
818 -  ;; by the insert-field calls.\r
819 -  (let ((previous-subject notmuch-pick-previous-subject))\r
820 -    (insert (notmuch-pick-format-field-list notmuch-pick-result-format msg))\r
821 -    (notmuch-pick-set-message-properties msg)\r
822 -    (notmuch-pick-set-prop :previous-subject previous-subject)\r
823 -    (insert "\n")))\r
824 -\r
825 -(defun notmuch-pick-goto-and-insert-msg (msg)\r
826 -  "Insert msg at the end of the buffer. Move point to msg if it is the target"\r
827 -  (save-excursion\r
828 -    (goto-char (point-max))\r
829 -    (notmuch-pick-insert-msg msg))\r
830 -  (let ((msg-id (notmuch-id-to-query (plist-get msg :id)))\r
831 -       (target notmuch-pick-target-msg))\r
832 -    (when (or (and (not target) (plist-get msg :match))\r
833 -             (string= msg-id target))\r
834 -      (setq notmuch-pick-target-msg "found")\r
835 -      (goto-char (point-max))\r
836 -      (forward-line -1)\r
837 -      (when notmuch-pick-open-target\r
838 -       (notmuch-pick-show-message-in)))))\r
839 -\r
840 -(defun notmuch-pick-insert-tree (tree depth tree-status first last)\r
841 -  "Insert the message tree TREE at depth DEPTH in the current thread.\r
842 -\r
843 -A message tree is another name for a single sub-thread: i.e., a\r
844 -message together with all its descendents."\r
845 -  (let ((msg (car tree))\r
846 -       (replies (cadr tree)))\r
847 -\r
848 -      (cond\r
849 -       ((and (< 0 depth) (not last))\r
850 -       (push "├" tree-status))\r
851 -       ((and (< 0 depth) last)\r
852 -       (push "╰" tree-status))\r
853 -       ((and (eq 0 depth) first last)\r
854 -;;       (push "─" tree-status)) choice between this and next line is matter of taste.\r
855 -       (push " " tree-status))\r
856 -       ((and (eq 0 depth) first (not last))\r
857 -         (push "┬" tree-status))\r
858 -       ((and (eq 0 depth) (not first) last)\r
859 -       (push "╰" tree-status))\r
860 -       ((and (eq 0 depth) (not first) (not last))\r
861 -       (push "├" tree-status)))\r
862 -\r
863 -      (push (concat (if replies "┬" "─") "►") tree-status)\r
864 -      (plist-put msg :first (and first (eq 0 depth)))\r
865 -      (notmuch-pick-goto-and-insert-msg (plist-put msg :tree-status tree-status))\r
866 -      (pop tree-status)\r
867 -      (pop tree-status)\r
868 -\r
869 -      (if last\r
870 -         (push " " tree-status)\r
871 -       (push "│" tree-status))\r
872 -\r
873 -    (notmuch-pick-insert-thread replies (1+ depth) tree-status)))\r
874 -\r
875 -(defun notmuch-pick-insert-thread (thread depth tree-status)\r
876 -  "Insert the collection of sibling sub-threads THREAD at depth DEPTH in the current forest."\r
877 -  (let ((n (length thread)))\r
878 -    (loop for tree in thread\r
879 -         for count from 1 to n\r
880 -\r
881 -         do (notmuch-pick-insert-tree tree depth tree-status (eq count 1) (eq count n)))))\r
882 -\r
883 -(defun notmuch-pick-insert-forest-thread (forest-thread)\r
884 -  "Insert a single complete thread."\r
885 -  (let (tree-status)\r
886 -    ;; Reset at the start of each main thread.\r
887 -    (setq notmuch-pick-previous-subject nil)\r
888 -    (notmuch-pick-insert-thread forest-thread 0 tree-status)))\r
889 -\r
890 -(defun notmuch-pick-insert-forest (forest)\r
891 -  "Insert a forest of threads.\r
892 -\r
893 -This function inserts a collection of several complete threads as\r
894 -passed to it by notmuch-pick-process-filter."\r
895 -  (mapc 'notmuch-pick-insert-forest-thread forest))\r
896 -\r
897 -(defun notmuch-pick-mode ()\r
898 -  "Major mode displaying messages (as opposed to threads) of of a notmuch search.\r
899 -\r
900 -This buffer contains the results of a \"notmuch pick\" of your\r
901 -email archives. Each line in the buffer represents a single\r
902 -message giving the relative date, the author, subject, and any\r
903 -tags.\r
904 -\r
905 -Pressing \\[notmuch-pick-show-message] on any line displays that message.\r
906 -\r
907 -Complete list of currently available key bindings:\r
908 -\r
909 -\\{notmuch-pick-mode-map}"\r
910 -\r
911 -  (interactive)\r
912 -  (kill-all-local-variables)\r
913 -  (setq notmuch-buffer-refresh-function #'notmuch-pick-refresh-view)\r
914 -  (use-local-map notmuch-pick-mode-map)\r
915 -  (setq major-mode 'notmuch-pick-mode\r
916 -       mode-name "notmuch-pick")\r
917 -  (hl-line-mode 1)\r
918 -  (setq buffer-read-only t\r
919 -       truncate-lines t))\r
920 -\r
921 -(defun notmuch-pick-process-sentinel (proc msg)\r
922 -  "Add a message to let user know when \"notmuch pick\" exits"\r
923 -  (let ((buffer (process-buffer proc))\r
924 -       (status (process-status proc))\r
925 -       (exit-status (process-exit-status proc))\r
926 -       (never-found-target-thread nil))\r
927 -    (when (memq status '(exit signal))\r
928 -        (kill-buffer (process-get proc 'parse-buf))\r
929 -       (if (buffer-live-p buffer)\r
930 -           (with-current-buffer buffer\r
931 -             (save-excursion\r
932 -               (let ((inhibit-read-only t)\r
933 -                     (atbob (bobp)))\r
934 -                 (goto-char (point-max))\r
935 -                 (if (eq status 'signal)\r
936 -                     (insert "Incomplete search results (pick process was killed).\n"))\r
937 -                 (when (eq status 'exit)\r
938 -                   (insert "End of search results.")\r
939 -                   (unless (= exit-status 0)\r
940 -                     (insert (format " (process returned %d)" exit-status)))\r
941 -                   (insert "\n")))))))))\r
942 -\r
943 -(defun notmuch-pick-process-filter (proc string)\r
944 -  "Process and filter the output of \"notmuch show\" (for pick)"\r
945 -  (let ((results-buf (process-buffer proc))\r
946 -        (parse-buf (process-get proc 'parse-buf))\r
947 -        (inhibit-read-only t)\r
948 -        done)\r
949 -    (if (not (buffer-live-p results-buf))\r
950 -        (delete-process proc)\r
951 -      (with-current-buffer parse-buf\r
952 -        ;; Insert new data\r
953 -        (save-excursion\r
954 -          (goto-char (point-max))\r
955 -          (insert string))\r
956 -       (notmuch-sexp-parse-partial-list 'notmuch-pick-insert-forest-thread\r
957 -                                        results-buf)))))\r
958 -\r
959 -(defun notmuch-pick-worker (basic-query &optional query-context target open-target)\r
960 -  "Insert the actual pick search in the current buffer.\r
961 -\r
962 -This is is a helper function for notmuch-pick. The arguments are\r
963 -the same as for the function notmuch-pick."\r
964 -  (interactive)\r
965 -  (notmuch-pick-mode)\r
966 -  (setq notmuch-pick-basic-query basic-query)\r
967 -  (setq notmuch-pick-query-context query-context)\r
968 -  (setq notmuch-pick-target-msg target)\r
969 -  (setq notmuch-pick-open-target open-target)\r
970 -\r
971 -  (erase-buffer)\r
972 -  (goto-char (point-min))\r
973 -  (let* ((search-args (concat basic-query\r
974 -                      (if query-context (concat " and (" query-context ")"))\r
975 -                      ))\r
976 -        (message-arg "--entire-thread"))\r
977 -    (if (equal (car (process-lines notmuch-command "count" search-args)) "0")\r
978 -       (setq search-args basic-query))\r
979 -    (let ((proc (notmuch-start-notmuch\r
980 -                "notmuch-pick" (current-buffer) #'notmuch-pick-process-sentinel\r
981 -                "show" "--body=false" "--format=sexp"\r
982 -                message-arg search-args))\r
983 -         ;; Use a scratch buffer to accumulate partial output.\r
984 -         ;; This buffer will be killed by the sentinel, which\r
985 -         ;; should be called no matter how the process dies.\r
986 -         (parse-buf (generate-new-buffer " *notmuch pick parse*")))\r
987 -      (process-put proc 'parse-buf parse-buf)\r
988 -      (set-process-filter proc 'notmuch-pick-process-filter)\r
989 -      (set-process-query-on-exit-flag proc nil))))\r
990 -\r
991 -(defun notmuch-pick (&optional query query-context target buffer-name open-target)\r
992 -  "Run notmuch pick with the given `query' and display the results.\r
993 -\r
994 -The arguments are:\r
995 -  QUERY: the main query. This can be any query but in many cases will be\r
996 -      a single thread. If nil this is read interactively from the minibuffer.\r
997 -  QUERY-CONTEXT: is an additional term for the query. The query used\r
998 -      is QUERY and QUERY-CONTEXT unless that does not match any messages\r
999 -      in which case we fall back to just QUERY.\r
1000 -  TARGET: A message ID (with the id: prefix) that will be made\r
1001 -      current if it appears in the pick results.\r
1002 -  BUFFER-NAME: the name of the buffer to show the pick tree. If\r
1003 -      it is nil \"*notmuch-pick\" followed by QUERY is used.\r
1004 -  OPEN-TARGET: If TRUE open the target message in the message pane."\r
1005 -  (interactive)\r
1006 -  (if (null query)\r
1007 -      (setq query (notmuch-read-query "Notmuch pick: ")))\r
1008 -  (let ((buffer (get-buffer-create (generate-new-buffer-name\r
1009 -                                   (or buffer-name\r
1010 -                                       (concat "*notmuch-pick-" query "*")))))\r
1011 -       (inhibit-read-only t))\r
1012 -\r
1013 -    (switch-to-buffer buffer))\r
1014 -  ;; Don't track undo information for this buffer\r
1015 -  (set 'buffer-undo-list t)\r
1016 -\r
1017 -  (notmuch-pick-worker query query-context target open-target)\r
1018 -\r
1019 -  (setq truncate-lines t))\r
1020 -\r
1021 -\r
1022 -;; Set up key bindings from the rest of notmuch.\r
1023 -(define-key notmuch-common-keymap "z" 'notmuch-pick)\r
1024 -(define-key notmuch-search-mode-map "Z" 'notmuch-pick-from-search-current-query)\r
1025 -(define-key notmuch-show-mode-map "Z" 'notmuch-pick-from-show-current-query)\r
1026 -(message "Initialised notmuch-pick")\r
1027 -\r
1028 -(provide 'notmuch-pick)\r
1029 diff --git a/emacs/notmuch-pick.el b/emacs/notmuch-pick.el\r
1030 new file mode 100644\r
1031 index 0000000..a492214\r
1032 --- /dev/null\r
1033 +++ b/emacs/notmuch-pick.el\r
1034 @@ -0,0 +1,946 @@\r
1035 +;; notmuch-pick.el --- displaying notmuch forests.\r
1036 +;;\r
1037 +;; Copyright © Carl Worth\r
1038 +;; Copyright © David Edmondson\r
1039 +;; Copyright © Mark Walters\r
1040 +;;\r
1041 +;; This file is part of Notmuch.\r
1042 +;;\r
1043 +;; Notmuch is free software: you can redistribute it and/or modify it\r
1044 +;; under the terms of the GNU General Public License as published by\r
1045 +;; the Free Software Foundation, either version 3 of the License, or\r
1046 +;; (at your option) any later version.\r
1047 +;;\r
1048 +;; Notmuch is distributed in the hope that it will be useful, but\r
1049 +;; WITHOUT ANY WARRANTY; without even the implied warranty of\r
1050 +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
1051 +;; General Public License for more details.\r
1052 +;;\r
1053 +;; You should have received a copy of the GNU General Public License\r
1054 +;; along with Notmuch.  If not, see <http://www.gnu.org/licenses/>.\r
1055 +;;\r
1056 +;; Authors: David Edmondson <dme@dme.org>\r
1057 +;;          Mark Walters <markwalters1009@gmail.com>\r
1058 +\r
1059 +(require 'mail-parse)\r
1060 +\r
1061 +(require 'notmuch-lib)\r
1062 +(require 'notmuch-query)\r
1063 +(require 'notmuch-show)\r
1064 +(require 'notmuch) ;; XXX ATM, as notmuch-search-mode-map is defined here\r
1065 +\r
1066 +(eval-when-compile (require 'cl))\r
1067 +\r
1068 +(declare-function notmuch-call-notmuch-process "notmuch" (&rest args))\r
1069 +(declare-function notmuch-show "notmuch-show" (&rest args))\r
1070 +(declare-function notmuch-tag "notmuch" (query &rest tags))\r
1071 +(declare-function notmuch-show-strip-re "notmuch-show" (subject))\r
1072 +(declare-function notmuch-show-spaces-n "notmuch-show" (n))\r
1073 +(declare-function notmuch-read-query "notmuch" (prompt))\r
1074 +(declare-function notmuch-read-tag-changes "notmuch" (&optional initial-input &rest search-terms))\r
1075 +(declare-function notmuch-update-tags "notmuch" (current-tags tag-changes))\r
1076 +(declare-function notmuch-hello-trim "notmuch-hello" (search))\r
1077 +(declare-function notmuch-search-find-thread-id "notmuch" ())\r
1078 +(declare-function notmuch-search-find-subject "notmuch" ())\r
1079 +\r
1080 +;; the following variable is defined in notmuch.el\r
1081 +(defvar notmuch-search-query-string)\r
1082 +\r
1083 +(defgroup notmuch-pick nil\r
1084 +  "Showing message and thread structure."\r
1085 +  :group 'notmuch)\r
1086 +\r
1087 +(defcustom notmuch-pick-show-out nil\r
1088 +  "View selected messages in new window rather than split-pane."\r
1089 +  :type 'boolean\r
1090 +  :group 'notmuch-pick)\r
1091 +\r
1092 +(defcustom notmuch-pick-result-format\r
1093 +  `(("date" . "%12s  ")\r
1094 +    ("authors" . "%-20s")\r
1095 +    ((("tree" . "%s")("subject" . "%s")) ." %-54s ")\r
1096 +    ("tags" . "(%s)"))\r
1097 +  "Result formatting for Pick. Supported fields are: date,\r
1098 +        authors, subject, tree, tags.  Tree means the thread tree\r
1099 +        box graphics. The field may also be a list in which case\r
1100 +        the formatting rules are applied recursively and then the\r
1101 +        output of all the fields in the list is inserted\r
1102 +        according to format-string.\r
1103 +\r
1104 +Note the author string should not contain\r
1105 +        whitespace (put it in the neighbouring fields instead).\r
1106 +        For example:\r
1107 +        (setq notmuch-pick-result-format \(\(\"authors\" . \"%-40s\"\)\r
1108 +                                             \(\"subject\" . \"%s\"\)\)\)"\r
1109 +  :type '(alist :key-type (string) :value-type (string))\r
1110 +  :group 'notmuch-pick)\r
1111 +\r
1112 +;; Faces for messages that match the query.\r
1113 +(defface notmuch-pick-match-date-face\r
1114 +  '((t :inherit default))\r
1115 +  "Face used in pick mode for the date in messages matching the query."\r
1116 +  :group 'notmuch-pick\r
1117 +  :group 'notmuch-faces)\r
1118 +\r
1119 +(defface notmuch-pick-match-author-face\r
1120 +  '((((class color)\r
1121 +      (background dark))\r
1122 +     (:foreground "OliveDrab1"))\r
1123 +    (((class color)\r
1124 +      (background light))\r
1125 +     (:foreground "dark blue"))\r
1126 +    (t\r
1127 +     (:bold t)))\r
1128 +  "Face used in pick mode for the date in messages matching the query."\r
1129 +  :group 'notmuch-pick\r
1130 +  :group 'notmuch-faces)\r
1131 +\r
1132 +(defface notmuch-pick-match-subject-face\r
1133 +  '((t :inherit default))\r
1134 +  "Face used in pick mode for the subject in messages matching the query."\r
1135 +  :group 'notmuch-pick\r
1136 +  :group 'notmuch-faces)\r
1137 +\r
1138 +(defface notmuch-pick-match-tree-face\r
1139 +  '((t :inherit default))\r
1140 +  "Face used in pick mode for the thread tree block graphics in messages matching the query."\r
1141 +  :group 'notmuch-pick\r
1142 +  :group 'notmuch-faces)\r
1143 +\r
1144 +(defface notmuch-pick-match-tag-face\r
1145 +  '((((class color)\r
1146 +      (background dark))\r
1147 +     (:foreground "OliveDrab1"))\r
1148 +    (((class color)\r
1149 +      (background light))\r
1150 +     (:foreground "navy blue" :bold t))\r
1151 +    (t\r
1152 +     (:bold t)))\r
1153 +  "Face used in pick mode for tags in messages matching the query."\r
1154 +  :group 'notmuch-pick\r
1155 +  :group 'notmuch-faces)\r
1156 +\r
1157 +;; Faces for messages that do not match the query.\r
1158 +(defface notmuch-pick-no-match-date-face\r
1159 +  '((t (:foreground "gray")))\r
1160 +  "Face used in pick mode for non-matching dates."\r
1161 +  :group 'notmuch-pick\r
1162 +  :group 'notmuch-faces)\r
1163 +\r
1164 +(defface notmuch-pick-no-match-subject-face\r
1165 +  '((t (:foreground "gray")))\r
1166 +  "Face used in pick mode for non-matching subjects."\r
1167 +  :group 'notmuch-pick\r
1168 +  :group 'notmuch-faces)\r
1169 +\r
1170 +(defface notmuch-pick-no-match-tree-face\r
1171 +  '((t (:foreground "gray")))\r
1172 +  "Face used in pick mode for the thread tree block graphics in messages matching the query."\r
1173 +  :group 'notmuch-pick\r
1174 +  :group 'notmuch-faces)\r
1175 +\r
1176 +(defface notmuch-pick-no-match-author-face\r
1177 +  '((t (:foreground "gray")))\r
1178 +  "Face used in pick mode for the date in messages matching the query."\r
1179 +  :group 'notmuch-pick\r
1180 +  :group 'notmuch-faces)\r
1181 +\r
1182 +(defface notmuch-pick-no-match-tag-face\r
1183 +  '((t (:foreground "gray")))\r
1184 +  "Face used in pick mode face for non-matching tags."\r
1185 +  :group 'notmuch-pick\r
1186 +  :group 'notmuch-faces)\r
1187 +\r
1188 +(defvar notmuch-pick-previous-subject\r
1189 +  "The subject of the most recent result shown during the async display")\r
1190 +(make-variable-buffer-local 'notmuch-pick-previous-subject)\r
1191 +\r
1192 +(defvar notmuch-pick-basic-query nil\r
1193 +  "A buffer local copy of argument query to the function notmuch-pick")\r
1194 +(make-variable-buffer-local 'notmuch-pick-basic-query)\r
1195 +\r
1196 +(defvar notmuch-pick-query-context nil\r
1197 +  "A buffer local copy of argument query-context to the function notmuch-pick")\r
1198 +(make-variable-buffer-local 'notmuch-pick-query-context)\r
1199 +\r
1200 +(defvar notmuch-pick-target-msg nil\r
1201 +  "A buffer local copy of argument target to the function notmuch-pick")\r
1202 +(make-variable-buffer-local 'notmuch-pick-target-msg)\r
1203 +\r
1204 +(defvar notmuch-pick-open-target nil\r
1205 +  "A buffer local copy of argument open-target to the function notmuch-pick")\r
1206 +(make-variable-buffer-local 'notmuch-pick-open-target)\r
1207 +\r
1208 +(defvar notmuch-pick-message-window nil\r
1209 +  "The window of the message pane.\r
1210 +\r
1211 +It is set in both the pick buffer and the child show buffer. It\r
1212 +is used to try and close the message pane when quitting pick or\r
1213 +the child show buffer.")\r
1214 +(make-variable-buffer-local 'notmuch-pick-message-window)\r
1215 +(put 'notmuch-pick-message-window 'permanent-local t)\r
1216 +\r
1217 +(defvar notmuch-pick-message-buffer nil\r
1218 +  "The buffer name of the show buffer in the message pane.\r
1219 +\r
1220 +This is used to try and make sure we don't close the message pane\r
1221 +if the user has loaded a different buffer in that window.")\r
1222 +(make-variable-buffer-local 'notmuch-pick-message-buffer)\r
1223 +(put 'notmuch-pick-message-buffer 'permanent-local t)\r
1224 +\r
1225 +(defun notmuch-pick-to-message-pane (func)\r
1226 +  "Execute FUNC in message pane.\r
1227 +\r
1228 +This function returns a function (so can be used as a keybinding)\r
1229 +which executes function FUNC in the message pane if it is\r
1230 +open (if the message pane is closed it does nothing)."\r
1231 +  `(lambda ()\r
1232 +      ,(concat "(In message pane) " (documentation func t))\r
1233 +     (interactive)\r
1234 +     (when (window-live-p notmuch-pick-message-window)\r
1235 +       (with-selected-window notmuch-pick-message-window\r
1236 +        (call-interactively #',func)))))\r
1237 +\r
1238 +(defun notmuch-pick-button-activate (&optional button)\r
1239 +  "Activate BUTTON or button at point\r
1240 +\r
1241 +This function does not give an error if there is no button."\r
1242 +  (interactive)\r
1243 +  (let ((button (or button (button-at (point)))))\r
1244 +    (when button (button-activate button))))\r
1245 +\r
1246 +(defun notmuch-pick-close-message-pane-and (func)\r
1247 +  "Close message pane and execute FUNC.\r
1248 +\r
1249 +This function returns a function (so can be used as a keybinding)\r
1250 +which closes the message pane if open and then executes function\r
1251 +FUNC."\r
1252 +  `(lambda ()\r
1253 +      ,(concat "(Close message pane and) " (documentation func t))\r
1254 +     (interactive)\r
1255 +     (notmuch-pick-close-message-window)\r
1256 +     (call-interactively #',func)))\r
1257 +\r
1258 +(defvar notmuch-pick-mode-map\r
1259 +  (let ((map (make-sparse-keymap)))\r
1260 +    (set-keymap-parent map notmuch-common-keymap)\r
1261 +    ;; The following override the global keymap.\r
1262 +    ;; Override because we want to close message pane first.\r
1263 +    (define-key map "?" (notmuch-pick-close-message-pane-and #'notmuch-help))\r
1264 +    ;; Override because we first close message pane and then close pick buffer.\r
1265 +    (define-key map "q" 'notmuch-pick-quit)\r
1266 +    ;; Override because we close message pane after the search query is entered.\r
1267 +    (define-key map "s" 'notmuch-pick-to-search)\r
1268 +    ;; Override because we want to close message pane first.\r
1269 +    (define-key map "m" (notmuch-pick-close-message-pane-and #'notmuch-mua-new-mail))\r
1270 +\r
1271 +    ;; these use notmuch-show functions directly\r
1272 +    (define-key map "|" 'notmuch-show-pipe-message)\r
1273 +    (define-key map "w" 'notmuch-show-save-attachments)\r
1274 +    (define-key map "v" 'notmuch-show-view-all-mime-parts)\r
1275 +    (define-key map "c" 'notmuch-show-stash-map)\r
1276 +\r
1277 +    ;; these apply to the message pane\r
1278 +    (define-key map (kbd "M-TAB") (notmuch-pick-to-message-pane #'notmuch-show-previous-button))\r
1279 +    (define-key map (kbd "<backtab>")  (notmuch-pick-to-message-pane #'notmuch-show-previous-button))\r
1280 +    (define-key map (kbd "TAB") (notmuch-pick-to-message-pane #'notmuch-show-next-button))\r
1281 +    (define-key map "e" (notmuch-pick-to-message-pane #'notmuch-pick-button-activate))\r
1282 +\r
1283 +    ;; bindings from show (or elsewhere) but we close the message pane first.\r
1284 +    (define-key map "f" (notmuch-pick-close-message-pane-and #'notmuch-show-forward-message))\r
1285 +    (define-key map "r" (notmuch-pick-close-message-pane-and #'notmuch-show-reply-sender))\r
1286 +    (define-key map "R" (notmuch-pick-close-message-pane-and #'notmuch-show-reply))\r
1287 +    (define-key map "V" (notmuch-pick-close-message-pane-and #'notmuch-show-view-raw-message))\r
1288 +\r
1289 +    ;; The main pick bindings\r
1290 +    (define-key map (kbd "RET") 'notmuch-pick-show-message)\r
1291 +    (define-key map [mouse-1] 'notmuch-pick-show-message)\r
1292 +    (define-key map "x" 'notmuch-pick-quit)\r
1293 +    (define-key map "A" 'notmuch-pick-archive-thread)\r
1294 +    (define-key map "a" 'notmuch-pick-archive-message-then-next)\r
1295 +    (define-key map "=" 'notmuch-pick-refresh-view)\r
1296 +    (define-key map "z" 'notmuch-pick-to-pick)\r
1297 +    (define-key map "n" 'notmuch-pick-next-matching-message)\r
1298 +    (define-key map "p" 'notmuch-pick-prev-matching-message)\r
1299 +    (define-key map "N" 'notmuch-pick-next-message)\r
1300 +    (define-key map "P" 'notmuch-pick-prev-message)\r
1301 +    (define-key map (kbd "M-p") 'notmuch-pick-prev-thread)\r
1302 +    (define-key map (kbd "M-n") 'notmuch-pick-next-thread)\r
1303 +    (define-key map "-" 'notmuch-pick-remove-tag)\r
1304 +    (define-key map "+" 'notmuch-pick-add-tag)\r
1305 +    (define-key map "*" 'notmuch-pick-tag-thread)\r
1306 +    (define-key map " " 'notmuch-pick-scroll-or-next)\r
1307 +    (define-key map "b" 'notmuch-pick-scroll-message-window-back)\r
1308 +    map))\r
1309 +(fset 'notmuch-pick-mode-map notmuch-pick-mode-map)\r
1310 +\r
1311 +(defun notmuch-pick-get-message-properties ()\r
1312 +  "Return the properties of the current message as a plist.\r
1313 +\r
1314 +Some useful entries are:\r
1315 +:headers - Property list containing the headers :Date, :Subject, :From, etc.\r
1316 +:tags - Tags for this message"\r
1317 +  (save-excursion\r
1318 +    (beginning-of-line)\r
1319 +    (get-text-property (point) :notmuch-message-properties)))\r
1320 +\r
1321 +;; XXX This should really be a lib function but we are trying to\r
1322 +;; reduce impact on the code base.\r
1323 +(defun notmuch-show-get-prop (prop &optional props)\r
1324 +  "This is a pick overridden version of notmuch-show-get-prop\r
1325 +\r
1326 +It gets property PROP from PROPS or, if PROPS is nil, the current\r
1327 +message in either pick or show. This means that several functions\r
1328 +in notmuch-show now work unchanged in pick as they just need the\r
1329 +correct message properties."\r
1330 +  (let ((props (or props\r
1331 +                  (cond ((eq major-mode 'notmuch-show-mode)\r
1332 +                         (notmuch-show-get-message-properties))\r
1333 +                        ((eq major-mode 'notmuch-pick-mode)\r
1334 +                         (notmuch-pick-get-message-properties))))))\r
1335 +    (plist-get props prop)))\r
1336 +\r
1337 +(defun notmuch-pick-set-message-properties (props)\r
1338 +  (save-excursion\r
1339 +    (beginning-of-line)\r
1340 +    (put-text-property (point) (+ (point) 1) :notmuch-message-properties props)))\r
1341 +\r
1342 +(defun notmuch-pick-set-prop (prop val &optional props)\r
1343 +  (let ((inhibit-read-only t)\r
1344 +       (props (or props\r
1345 +                  (notmuch-pick-get-message-properties))))\r
1346 +    (plist-put props prop val)\r
1347 +    (notmuch-pick-set-message-properties props)))\r
1348 +\r
1349 +(defun notmuch-pick-get-prop (prop &optional props)\r
1350 +  (let ((props (or props\r
1351 +                  (notmuch-pick-get-message-properties))))\r
1352 +    (plist-get props prop)))\r
1353 +\r
1354 +(defun notmuch-pick-set-tags (tags)\r
1355 +  "Set the tags of the current message."\r
1356 +  (notmuch-pick-set-prop :tags tags))\r
1357 +\r
1358 +(defun notmuch-pick-get-tags ()\r
1359 +  "Return the tags of the current message."\r
1360 +  (notmuch-pick-get-prop :tags))\r
1361 +\r
1362 +(defun notmuch-pick-get-message-id ()\r
1363 +  "Return the message id of the current message."\r
1364 +  (let ((id (notmuch-pick-get-prop :id)))\r
1365 +    (if id\r
1366 +       (notmuch-id-to-query id)\r
1367 +      nil)))\r
1368 +\r
1369 +(defun notmuch-pick-get-match ()\r
1370 +  "Return whether the current message is a match."\r
1371 +  (interactive)\r
1372 +  (notmuch-pick-get-prop :match))\r
1373 +\r
1374 +(defun notmuch-pick-refresh-result ()\r
1375 +  "Redisplay the current message line.\r
1376 +\r
1377 +This redisplays the current line based on the messages\r
1378 +properties (as they are now). This is used when tags are\r
1379 +updated."\r
1380 +  (let ((init-point (point))\r
1381 +       (end (line-end-position))\r
1382 +       (msg (notmuch-pick-get-message-properties))\r
1383 +       (inhibit-read-only t))\r
1384 +    (beginning-of-line)\r
1385 +    ;; This is a little tricky: we override\r
1386 +    ;; notmuch-pick-previous-subject to get the decision between\r
1387 +    ;; ... and a subject right and it stops notmuch-pick-insert-msg\r
1388 +    ;; from overwriting the buffer local copy of\r
1389 +    ;; notmuch-pick-previous-subject if this is called while the\r
1390 +    ;; buffer is displaying.\r
1391 +    (let ((notmuch-pick-previous-subject (notmuch-pick-get-prop :previous-subject)))\r
1392 +      (delete-region (point) (1+ (line-end-position)))\r
1393 +      (notmuch-pick-insert-msg msg))\r
1394 +    (let ((new-end (line-end-position)))\r
1395 +      (goto-char (if (= init-point end)\r
1396 +                    new-end\r
1397 +                  (min init-point (- new-end 1)))))))\r
1398 +\r
1399 +(defun notmuch-pick-tag-update-display (&optional tag-changes)\r
1400 +  "Update display for TAG-CHANGES to current message.\r
1401 +\r
1402 +Does NOT change the database."\r
1403 +  (let* ((current-tags (notmuch-pick-get-tags))\r
1404 +        (new-tags (notmuch-update-tags current-tags tag-changes)))\r
1405 +    (unless (equal current-tags new-tags)\r
1406 +      (notmuch-pick-set-tags new-tags)\r
1407 +      (notmuch-pick-refresh-result))))\r
1408 +\r
1409 +(defun notmuch-pick-tag (&optional tag-changes)\r
1410 +  "Change tags for the current message"\r
1411 +  (interactive)\r
1412 +  (setq tag-changes (notmuch-tag (notmuch-pick-get-message-id) tag-changes))\r
1413 +  (notmuch-pick-tag-update-display tag-changes))\r
1414 +\r
1415 +(defun notmuch-pick-add-tag ()\r
1416 +  "Same as `notmuch-pick-tag' but sets initial input to '+'."\r
1417 +  (interactive)\r
1418 +  (notmuch-pick-tag "+"))\r
1419 +\r
1420 +(defun notmuch-pick-remove-tag ()\r
1421 +  "Same as `notmuch-pick-tag' but sets initial input to '-'."\r
1422 +  (interactive)\r
1423 +  (notmuch-pick-tag "-"))\r
1424 +\r
1425 +;; The next two functions close the message window before searching or\r
1426 +;; picking but they do so after the user has entered the query (in\r
1427 +;; case the user was basing the query on something in the message\r
1428 +;; window).\r
1429 +\r
1430 +(defun notmuch-pick-to-search ()\r
1431 +  "Run \"notmuch search\" with the given `query' and display results."\r
1432 +  (interactive)\r
1433 +  (let ((query (notmuch-read-query "Notmuch search: ")))\r
1434 +    (notmuch-pick-close-message-window)\r
1435 +    (notmuch-search query)))\r
1436 +\r
1437 +(defun notmuch-pick-to-pick ()\r
1438 +  "Run a query and display results in experimental notmuch-pick mode"\r
1439 +  (interactive)\r
1440 +  (let ((query (notmuch-read-query "Notmuch pick: ")))\r
1441 +    (notmuch-pick-close-message-window)\r
1442 +    (notmuch-pick query)))\r
1443 +\r
1444 +;; This function should be in notmuch-show.el but be we trying to\r
1445 +;; minimise impact on the rest of the codebase.\r
1446 +(defun notmuch-pick-from-show-current-query ()\r
1447 +  "Call notmuch pick with the current query"\r
1448 +  (interactive)\r
1449 +  (notmuch-pick notmuch-show-thread-id\r
1450 +               notmuch-show-query-context\r
1451 +               (notmuch-show-get-message-id)))\r
1452 +\r
1453 +;; This function should be in notmuch.el but be we trying to minimise\r
1454 +;; impact on the rest of the codebase.\r
1455 +(defun notmuch-pick-from-search-current-query ()\r
1456 +  "Call notmuch pick with the current query"\r
1457 +  (interactive)\r
1458 +  (notmuch-pick notmuch-search-query-string))\r
1459 +\r
1460 +;; This function should be in notmuch.el but be we trying to minimise\r
1461 +;; impact on the rest of the codebase.\r
1462 +(defun notmuch-pick-from-search-thread ()\r
1463 +  "Show the selected thread with notmuch-pick"\r
1464 +  (interactive)\r
1465 +  (notmuch-pick (notmuch-search-find-thread-id)\r
1466 +                notmuch-search-query-string\r
1467 +               nil\r
1468 +                (notmuch-prettify-subject (notmuch-search-find-subject))\r
1469 +               t))\r
1470 +\r
1471 +(defun notmuch-pick-message-window-kill-hook ()\r
1472 +  "Close the message pane when exiting the show buffer."\r
1473 +  (let ((buffer (current-buffer)))\r
1474 +    (when (and (window-live-p notmuch-pick-message-window)\r
1475 +              (eq (window-buffer notmuch-pick-message-window) buffer))\r
1476 +      ;; We do not want an error if this is the sole window in the\r
1477 +      ;; frame and I do not know how to test for that in emacs pre\r
1478 +      ;; 24. Hence we just ignore-errors.\r
1479 +      (ignore-errors\r
1480 +       (delete-window notmuch-pick-message-window)))))\r
1481 +\r
1482 +(defun notmuch-pick-show-message-in ()\r
1483 +  "Show the current message (in split-pane)."\r
1484 +  (interactive)\r
1485 +  (let ((id (notmuch-pick-get-message-id))\r
1486 +       (inhibit-read-only t)\r
1487 +       buffer)\r
1488 +    (when id\r
1489 +      ;; We close and reopen the window to kill off un-needed buffers\r
1490 +      ;; this might cause flickering but seems ok.\r
1491 +      (notmuch-pick-close-message-window)\r
1492 +      (setq notmuch-pick-message-window\r
1493 +           (split-window-vertically (/ (window-height) 4)))\r
1494 +      (with-selected-window notmuch-pick-message-window\r
1495 +       ;; Since we are only displaying one message do not indent.\r
1496 +       (let ((notmuch-show-indent-messages-width 0)\r
1497 +             (notmuch-show-only-matching-messages t))\r
1498 +         (setq buffer (notmuch-show id))))\r
1499 +      ;; We need the `let' as notmuch-pick-message-window is buffer local.\r
1500 +      (let ((window notmuch-pick-message-window))\r
1501 +       (with-current-buffer buffer\r
1502 +         (setq notmuch-pick-message-window window)\r
1503 +         (add-hook 'kill-buffer-hook 'notmuch-pick-message-window-kill-hook)))\r
1504 +      (when notmuch-show-mark-read-tags\r
1505 +       (notmuch-pick-tag-update-display notmuch-show-mark-read-tags))\r
1506 +      (setq notmuch-pick-message-buffer buffer))))\r
1507 +\r
1508 +(defun notmuch-pick-show-message-out ()\r
1509 +  "Show the current message (in whole window)."\r
1510 +  (interactive)\r
1511 +  (let ((id (notmuch-pick-get-message-id))\r
1512 +       (inhibit-read-only t)\r
1513 +       buffer)\r
1514 +    (when id\r
1515 +      ;; We close the window to kill off un-needed buffers.\r
1516 +      (notmuch-pick-close-message-window)\r
1517 +      (notmuch-show id))))\r
1518 +\r
1519 +(defun notmuch-pick-show-message (arg)\r
1520 +  "Show the current message.\r
1521 +\r
1522 +Shows in split pane or whole window according to value of\r
1523 +`notmuch-pick-show-out'. A prefix argument reverses the choice."\r
1524 +  (interactive "P")\r
1525 +  (if (or (and notmuch-pick-show-out  (not arg))\r
1526 +         (and (not notmuch-pick-show-out) arg))\r
1527 +      (notmuch-pick-show-message-out)\r
1528 +    (notmuch-pick-show-message-in)))\r
1529 +\r
1530 +(defun notmuch-pick-scroll-message-window ()\r
1531 +  "Scroll the message window (if it exists)"\r
1532 +  (interactive)\r
1533 +  (when (window-live-p notmuch-pick-message-window)\r
1534 +    (with-selected-window notmuch-pick-message-window\r
1535 +      (if (pos-visible-in-window-p (point-max))\r
1536 +         t\r
1537 +       (scroll-up)))))\r
1538 +\r
1539 +(defun notmuch-pick-scroll-message-window-back ()\r
1540 +  "Scroll the message window back(if it exists)"\r
1541 +  (interactive)\r
1542 +  (when (window-live-p notmuch-pick-message-window)\r
1543 +    (with-selected-window notmuch-pick-message-window\r
1544 +      (if (pos-visible-in-window-p (point-min))\r
1545 +         t\r
1546 +       (scroll-down)))))\r
1547 +\r
1548 +(defun notmuch-pick-scroll-or-next ()\r
1549 +  "Scroll the message window. If it at end go to next message."\r
1550 +  (interactive)\r
1551 +  (when (notmuch-pick-scroll-message-window)\r
1552 +    (notmuch-pick-next-matching-message)))\r
1553 +\r
1554 +(defun notmuch-pick-quit ()\r
1555 +  "Close the split view or exit pick."\r
1556 +  (interactive)\r
1557 +  (unless (notmuch-pick-close-message-window)\r
1558 +    (kill-buffer (current-buffer))))\r
1559 +\r
1560 +(defun notmuch-pick-close-message-window ()\r
1561 +  "Close the message-window. Return t if close succeeds."\r
1562 +  (interactive)\r
1563 +  (when (and (window-live-p notmuch-pick-message-window)\r
1564 +            (eq (window-buffer notmuch-pick-message-window) notmuch-pick-message-buffer))\r
1565 +    (delete-window notmuch-pick-message-window)\r
1566 +    (unless (get-buffer-window-list notmuch-pick-message-buffer)\r
1567 +      (kill-buffer notmuch-pick-message-buffer))\r
1568 +    t))\r
1569 +\r
1570 +(defun notmuch-pick-archive-message (&optional unarchive)\r
1571 +  "Archive the current message.\r
1572 +\r
1573 +Archive the current message by applying the tag changes in\r
1574 +`notmuch-archive-tags' to it. If a prefix argument is given, the\r
1575 +message will be \"unarchived\", i.e. the tag changes in\r
1576 +`notmuch-archive-tags' will be reversed."\r
1577 +  (interactive "P")\r
1578 +  (when notmuch-archive-tags\r
1579 +    (apply 'notmuch-pick-tag\r
1580 +          (notmuch-tag-change-list notmuch-archive-tags unarchive))))\r
1581 +\r
1582 +(defun notmuch-pick-archive-message-then-next (&optional unarchive)\r
1583 +  "Archive the current message and move to next matching message."\r
1584 +  (interactive "P")\r
1585 +  (notmuch-pick-archive-message unarchive)\r
1586 +  (notmuch-pick-next-matching-message))\r
1587 +\r
1588 +(defun notmuch-pick-next-message ()\r
1589 +  "Move to next message."\r
1590 +  (interactive)\r
1591 +  (forward-line)\r
1592 +  (when (window-live-p notmuch-pick-message-window)\r
1593 +    (notmuch-pick-show-message-in)))\r
1594 +\r
1595 +(defun notmuch-pick-prev-message ()\r
1596 +  "Move to previous message."\r
1597 +  (interactive)\r
1598 +  (forward-line -1)\r
1599 +  (when (window-live-p notmuch-pick-message-window)\r
1600 +    (notmuch-pick-show-message-in)))\r
1601 +\r
1602 +(defun notmuch-pick-prev-matching-message ()\r
1603 +  "Move to previous matching message."\r
1604 +  (interactive)\r
1605 +  (forward-line -1)\r
1606 +  (while (and (not (bobp)) (not (notmuch-pick-get-match)))\r
1607 +    (forward-line -1))\r
1608 +  (when (window-live-p notmuch-pick-message-window)\r
1609 +    (notmuch-pick-show-message-in)))\r
1610 +\r
1611 +(defun notmuch-pick-next-matching-message ()\r
1612 +  "Move to next matching message."\r
1613 +  (interactive)\r
1614 +  (forward-line)\r
1615 +  (while (and (not (eobp)) (not (notmuch-pick-get-match)))\r
1616 +    (forward-line))\r
1617 +  (when (window-live-p notmuch-pick-message-window)\r
1618 +    (notmuch-pick-show-message-in)))\r
1619 +\r
1620 +(defun notmuch-pick-refresh-view ()\r
1621 +  "Refresh view."\r
1622 +  (interactive)\r
1623 +  (let ((inhibit-read-only t)\r
1624 +       (basic-query notmuch-pick-basic-query)\r
1625 +       (query-context notmuch-pick-query-context)\r
1626 +       (target (notmuch-pick-get-message-id)))\r
1627 +    (erase-buffer)\r
1628 +    (notmuch-pick-worker basic-query\r
1629 +                        query-context\r
1630 +                        target)))\r
1631 +\r
1632 +(defun notmuch-pick-thread-top ()\r
1633 +  (when (notmuch-pick-get-message-properties)\r
1634 +    (while (not (or (notmuch-pick-get-prop :first) (eobp)))\r
1635 +      (forward-line -1))))\r
1636 +\r
1637 +(defun notmuch-pick-prev-thread ()\r
1638 +  (interactive)\r
1639 +  (forward-line -1)\r
1640 +  (notmuch-pick-thread-top))\r
1641 +\r
1642 +(defun notmuch-pick-next-thread ()\r
1643 +  (interactive)\r
1644 +  (forward-line 1)\r
1645 +  (while (not (or (notmuch-pick-get-prop :first) (eobp)))\r
1646 +    (forward-line 1)))\r
1647 +\r
1648 +(defun notmuch-pick-thread-mapcar (function)\r
1649 +  "Iterate through all messages in the current thread\r
1650 + and call FUNCTION for side effects."\r
1651 +  (save-excursion\r
1652 +    (notmuch-pick-thread-top)\r
1653 +    (loop collect (funcall function)\r
1654 +         do (forward-line)\r
1655 +         while (and (notmuch-pick-get-message-properties)\r
1656 +                    (not (notmuch-pick-get-prop :first))))))\r
1657 +\r
1658 +(defun notmuch-pick-get-messages-ids-thread-search ()\r
1659 +  "Return a search string for all message ids of messages in the current thread."\r
1660 +  (mapconcat 'identity\r
1661 +            (notmuch-pick-thread-mapcar 'notmuch-pick-get-message-id)\r
1662 +            " or "))\r
1663 +\r
1664 +(defun notmuch-pick-tag-thread (&optional tag-changes)\r
1665 +  "Tag all messages in the current thread"\r
1666 +  (interactive)\r
1667 +  (when (notmuch-pick-get-message-properties)\r
1668 +    (let ((tag-changes (notmuch-tag (notmuch-pick-get-messages-ids-thread-search) tag-changes)))\r
1669 +      (notmuch-pick-thread-mapcar\r
1670 +       (lambda () (notmuch-pick-tag-update-display tag-changes))))))\r
1671 +\r
1672 +(defun notmuch-pick-archive-thread (&optional unarchive)\r
1673 +  "Archive each message in thread.\r
1674 +\r
1675 +Archive each message currently shown by applying the tag changes\r
1676 +in `notmuch-archive-tags' to each. If a prefix argument is given,\r
1677 +the messages will be \"unarchived\", i.e. the tag changes in\r
1678 +`notmuch-archive-tags' will be reversed.\r
1679 +\r
1680 +Note: This command is safe from any race condition of new messages\r
1681 +being delivered to the same thread. It does not archive the\r
1682 +entire thread, but only the messages shown in the current\r
1683 +buffer."\r
1684 +  (interactive "P")\r
1685 +  (when notmuch-archive-tags\r
1686 +    (notmuch-pick-tag-thread\r
1687 +     (notmuch-tag-change-list notmuch-archive-tags unarchive))))\r
1688 +\r
1689 +;; Functions below here display the pick buffer itself.\r
1690 +\r
1691 +(defun notmuch-pick-clean-address (address)\r
1692 +  "Try to clean a single email ADDRESS for display. Return\r
1693 +AUTHOR_NAME if present, otherwise return AUTHOR_EMAIL. Return\r
1694 +unchanged ADDRESS if parsing fails."\r
1695 +  (let* ((clean-address (notmuch-clean-address address))\r
1696 +        (p-address (car clean-address))\r
1697 +        (p-name (cdr clean-address)))\r
1698 +\r
1699 +    ;; If we have a name return that otherwise return the address.\r
1700 +    (or p-name p-address)))\r
1701 +\r
1702 +(defun notmuch-pick-format-field (field format-string msg)\r
1703 +  "Format a FIELD of MSG according to FORMAT-STRING and return string"\r
1704 +  (let* ((headers (plist-get msg :headers))\r
1705 +        (match (plist-get msg :match)))\r
1706 +    (cond\r
1707 +     ((listp field)\r
1708 +      (format format-string (notmuch-pick-format-field-list field msg)))\r
1709 +\r
1710 +     ((string-equal field "date")\r
1711 +      (let ((face (if match\r
1712 +                     'notmuch-pick-match-date-face\r
1713 +                   'notmuch-pick-no-match-date-face)))\r
1714 +       (propertize (format format-string (plist-get msg :date_relative)) 'face face)))\r
1715 +\r
1716 +     ((string-equal field "tree")\r
1717 +      (let ((tree-status (plist-get msg :tree-status))\r
1718 +           (face (if match\r
1719 +                     'notmuch-pick-match-tree-face\r
1720 +                   'notmuch-pick-no-match-tree-face)))\r
1721 +\r
1722 +       (propertize (format format-string\r
1723 +                           (mapconcat #'identity (reverse tree-status) ""))\r
1724 +                   'face face)))\r
1725 +\r
1726 +     ((string-equal field "subject")\r
1727 +      (let ((bare-subject (notmuch-show-strip-re (plist-get headers :Subject)))\r
1728 +           (previous-subject notmuch-pick-previous-subject)\r
1729 +           (face (if match\r
1730 +                     'notmuch-pick-match-subject-face\r
1731 +                   'notmuch-pick-no-match-subject-face)))\r
1732 +\r
1733 +       (setq notmuch-pick-previous-subject bare-subject)\r
1734 +       (propertize (format format-string\r
1735 +                           (if (string= previous-subject bare-subject)\r
1736 +                               " ..."\r
1737 +                             bare-subject))\r
1738 +                   'face face)))\r
1739 +\r
1740 +     ((string-equal field "authors")\r
1741 +      (let ((author (notmuch-pick-clean-address (plist-get headers :From)))\r
1742 +           (len (length (format format-string "")))\r
1743 +           (face (if match\r
1744 +                     'notmuch-pick-match-author-face\r
1745 +                   'notmuch-pick-no-match-author-face)))\r
1746 +       (when (> (length author) len)\r
1747 +         (setq author (substring author 0 len)))\r
1748 +       (propertize (format format-string author) 'face face)))\r
1749 +\r
1750 +     ((string-equal field "tags")\r
1751 +      (let ((tags (plist-get msg :tags))\r
1752 +           (face (if match\r
1753 +                     'notmuch-pick-match-tag-face\r
1754 +                   'notmuch-pick-no-match-tag-face)))\r
1755 +       (propertize (format format-string\r
1756 +                           (mapconcat #'identity tags ", "))\r
1757 +                   'face face))))))\r
1758 +\r
1759 +\r
1760 +(defun notmuch-pick-format-field-list (field-list msg)\r
1761 +  "Format fields of MSG according to FIELD-LIST and return string"\r
1762 +  (let (result-string)\r
1763 +    (dolist (spec field-list result-string)\r
1764 +      (let ((field-string (notmuch-pick-format-field (car spec) (cdr spec) msg)))\r
1765 +       (setq result-string (concat result-string field-string))))))\r
1766 +\r
1767 +(defun notmuch-pick-insert-msg (msg)\r
1768 +  "Insert the message MSG according to notmuch-pick-result-format"\r
1769 +  ;; We need to save the previous subject as it will get overwritten\r
1770 +  ;; by the insert-field calls.\r
1771 +  (let ((previous-subject notmuch-pick-previous-subject))\r
1772 +    (insert (notmuch-pick-format-field-list notmuch-pick-result-format msg))\r
1773 +    (notmuch-pick-set-message-properties msg)\r
1774 +    (notmuch-pick-set-prop :previous-subject previous-subject)\r
1775 +    (insert "\n")))\r
1776 +\r
1777 +(defun notmuch-pick-goto-and-insert-msg (msg)\r
1778 +  "Insert msg at the end of the buffer. Move point to msg if it is the target"\r
1779 +  (save-excursion\r
1780 +    (goto-char (point-max))\r
1781 +    (notmuch-pick-insert-msg msg))\r
1782 +  (let ((msg-id (notmuch-id-to-query (plist-get msg :id)))\r
1783 +       (target notmuch-pick-target-msg))\r
1784 +    (when (or (and (not target) (plist-get msg :match))\r
1785 +             (string= msg-id target))\r
1786 +      (setq notmuch-pick-target-msg "found")\r
1787 +      (goto-char (point-max))\r
1788 +      (forward-line -1)\r
1789 +      (when notmuch-pick-open-target\r
1790 +       (notmuch-pick-show-message-in)))))\r
1791 +\r
1792 +(defun notmuch-pick-insert-tree (tree depth tree-status first last)\r
1793 +  "Insert the message tree TREE at depth DEPTH in the current thread.\r
1794 +\r
1795 +A message tree is another name for a single sub-thread: i.e., a\r
1796 +message together with all its descendents."\r
1797 +  (let ((msg (car tree))\r
1798 +       (replies (cadr tree)))\r
1799 +\r
1800 +      (cond\r
1801 +       ((and (< 0 depth) (not last))\r
1802 +       (push "├" tree-status))\r
1803 +       ((and (< 0 depth) last)\r
1804 +       (push "╰" tree-status))\r
1805 +       ((and (eq 0 depth) first last)\r
1806 +;;       (push "─" tree-status)) choice between this and next line is matter of taste.\r
1807 +       (push " " tree-status))\r
1808 +       ((and (eq 0 depth) first (not last))\r
1809 +         (push "┬" tree-status))\r
1810 +       ((and (eq 0 depth) (not first) last)\r
1811 +       (push "╰" tree-status))\r
1812 +       ((and (eq 0 depth) (not first) (not last))\r
1813 +       (push "├" tree-status)))\r
1814 +\r
1815 +      (push (concat (if replies "┬" "─") "►") tree-status)\r
1816 +      (plist-put msg :first (and first (eq 0 depth)))\r
1817 +      (notmuch-pick-goto-and-insert-msg (plist-put msg :tree-status tree-status))\r
1818 +      (pop tree-status)\r
1819 +      (pop tree-status)\r
1820 +\r
1821 +      (if last\r
1822 +         (push " " tree-status)\r
1823 +       (push "│" tree-status))\r
1824 +\r
1825 +    (notmuch-pick-insert-thread replies (1+ depth) tree-status)))\r
1826 +\r
1827 +(defun notmuch-pick-insert-thread (thread depth tree-status)\r
1828 +  "Insert the collection of sibling sub-threads THREAD at depth DEPTH in the current forest."\r
1829 +  (let ((n (length thread)))\r
1830 +    (loop for tree in thread\r
1831 +         for count from 1 to n\r
1832 +\r
1833 +         do (notmuch-pick-insert-tree tree depth tree-status (eq count 1) (eq count n)))))\r
1834 +\r
1835 +(defun notmuch-pick-insert-forest-thread (forest-thread)\r
1836 +  "Insert a single complete thread."\r
1837 +  (let (tree-status)\r
1838 +    ;; Reset at the start of each main thread.\r
1839 +    (setq notmuch-pick-previous-subject nil)\r
1840 +    (notmuch-pick-insert-thread forest-thread 0 tree-status)))\r
1841 +\r
1842 +(defun notmuch-pick-insert-forest (forest)\r
1843 +  "Insert a forest of threads.\r
1844 +\r
1845 +This function inserts a collection of several complete threads as\r
1846 +passed to it by notmuch-pick-process-filter."\r
1847 +  (mapc 'notmuch-pick-insert-forest-thread forest))\r
1848 +\r
1849 +(defun notmuch-pick-mode ()\r
1850 +  "Major mode displaying messages (as opposed to threads) of of a notmuch search.\r
1851 +\r
1852 +This buffer contains the results of a \"notmuch pick\" of your\r
1853 +email archives. Each line in the buffer represents a single\r
1854 +message giving the relative date, the author, subject, and any\r
1855 +tags.\r
1856 +\r
1857 +Pressing \\[notmuch-pick-show-message] on any line displays that message.\r
1858 +\r
1859 +Complete list of currently available key bindings:\r
1860 +\r
1861 +\\{notmuch-pick-mode-map}"\r
1862 +\r
1863 +  (interactive)\r
1864 +  (kill-all-local-variables)\r
1865 +  (setq notmuch-buffer-refresh-function #'notmuch-pick-refresh-view)\r
1866 +  (use-local-map notmuch-pick-mode-map)\r
1867 +  (setq major-mode 'notmuch-pick-mode\r
1868 +       mode-name "notmuch-pick")\r
1869 +  (hl-line-mode 1)\r
1870 +  (setq buffer-read-only t\r
1871 +       truncate-lines t))\r
1872 +\r
1873 +(defun notmuch-pick-process-sentinel (proc msg)\r
1874 +  "Add a message to let user know when \"notmuch pick\" exits"\r
1875 +  (let ((buffer (process-buffer proc))\r
1876 +       (status (process-status proc))\r
1877 +       (exit-status (process-exit-status proc))\r
1878 +       (never-found-target-thread nil))\r
1879 +    (when (memq status '(exit signal))\r
1880 +        (kill-buffer (process-get proc 'parse-buf))\r
1881 +       (if (buffer-live-p buffer)\r
1882 +           (with-current-buffer buffer\r
1883 +             (save-excursion\r
1884 +               (let ((inhibit-read-only t)\r
1885 +                     (atbob (bobp)))\r
1886 +                 (goto-char (point-max))\r
1887 +                 (if (eq status 'signal)\r
1888 +                     (insert "Incomplete search results (pick process was killed).\n"))\r
1889 +                 (when (eq status 'exit)\r
1890 +                   (insert "End of search results.")\r
1891 +                   (unless (= exit-status 0)\r
1892 +                     (insert (format " (process returned %d)" exit-status)))\r
1893 +                   (insert "\n")))))))))\r
1894 +\r
1895 +(defun notmuch-pick-process-filter (proc string)\r
1896 +  "Process and filter the output of \"notmuch show\" (for pick)"\r
1897 +  (let ((results-buf (process-buffer proc))\r
1898 +        (parse-buf (process-get proc 'parse-buf))\r
1899 +        (inhibit-read-only t)\r
1900 +        done)\r
1901 +    (if (not (buffer-live-p results-buf))\r
1902 +        (delete-process proc)\r
1903 +      (with-current-buffer parse-buf\r
1904 +        ;; Insert new data\r
1905 +        (save-excursion\r
1906 +          (goto-char (point-max))\r
1907 +          (insert string))\r
1908 +       (notmuch-sexp-parse-partial-list 'notmuch-pick-insert-forest-thread\r
1909 +                                        results-buf)))))\r
1910 +\r
1911 +(defun notmuch-pick-worker (basic-query &optional query-context target open-target)\r
1912 +  "Insert the actual pick search in the current buffer.\r
1913 +\r
1914 +This is is a helper function for notmuch-pick. The arguments are\r
1915 +the same as for the function notmuch-pick."\r
1916 +  (interactive)\r
1917 +  (notmuch-pick-mode)\r
1918 +  (setq notmuch-pick-basic-query basic-query)\r
1919 +  (setq notmuch-pick-query-context query-context)\r
1920 +  (setq notmuch-pick-target-msg target)\r
1921 +  (setq notmuch-pick-open-target open-target)\r
1922 +\r
1923 +  (erase-buffer)\r
1924 +  (goto-char (point-min))\r
1925 +  (let* ((search-args (concat basic-query\r
1926 +                      (if query-context (concat " and (" query-context ")"))\r
1927 +                      ))\r
1928 +        (message-arg "--entire-thread"))\r
1929 +    (if (equal (car (process-lines notmuch-command "count" search-args)) "0")\r
1930 +       (setq search-args basic-query))\r
1931 +    (let ((proc (notmuch-start-notmuch\r
1932 +                "notmuch-pick" (current-buffer) #'notmuch-pick-process-sentinel\r
1933 +                "show" "--body=false" "--format=sexp"\r
1934 +                message-arg search-args))\r
1935 +         ;; Use a scratch buffer to accumulate partial output.\r
1936 +         ;; This buffer will be killed by the sentinel, which\r
1937 +         ;; should be called no matter how the process dies.\r
1938 +         (parse-buf (generate-new-buffer " *notmuch pick parse*")))\r
1939 +      (process-put proc 'parse-buf parse-buf)\r
1940 +      (set-process-filter proc 'notmuch-pick-process-filter)\r
1941 +      (set-process-query-on-exit-flag proc nil))))\r
1942 +\r
1943 +(defun notmuch-pick (&optional query query-context target buffer-name open-target)\r
1944 +  "Run notmuch pick with the given `query' and display the results.\r
1945 +\r
1946 +The arguments are:\r
1947 +  QUERY: the main query. This can be any query but in many cases will be\r
1948 +      a single thread. If nil this is read interactively from the minibuffer.\r
1949 +  QUERY-CONTEXT: is an additional term for the query. The query used\r
1950 +      is QUERY and QUERY-CONTEXT unless that does not match any messages\r
1951 +      in which case we fall back to just QUERY.\r
1952 +  TARGET: A message ID (with the id: prefix) that will be made\r
1953 +      current if it appears in the pick results.\r
1954 +  BUFFER-NAME: the name of the buffer to show the pick tree. If\r
1955 +      it is nil \"*notmuch-pick\" followed by QUERY is used.\r
1956 +  OPEN-TARGET: If TRUE open the target message in the message pane."\r
1957 +  (interactive)\r
1958 +  (if (null query)\r
1959 +      (setq query (notmuch-read-query "Notmuch pick: ")))\r
1960 +  (let ((buffer (get-buffer-create (generate-new-buffer-name\r
1961 +                                   (or buffer-name\r
1962 +                                       (concat "*notmuch-pick-" query "*")))))\r
1963 +       (inhibit-read-only t))\r
1964 +\r
1965 +    (switch-to-buffer buffer))\r
1966 +  ;; Don't track undo information for this buffer\r
1967 +  (set 'buffer-undo-list t)\r
1968 +\r
1969 +  (notmuch-pick-worker query query-context target open-target)\r
1970 +\r
1971 +  (setq truncate-lines t))\r
1972 +\r
1973 +\r
1974 +;; Set up key bindings from the rest of notmuch.\r
1975 +(define-key notmuch-common-keymap "z" 'notmuch-pick)\r
1976 +(define-key notmuch-search-mode-map "Z" 'notmuch-pick-from-search-current-query)\r
1977 +(define-key notmuch-show-mode-map "Z" 'notmuch-pick-from-show-current-query)\r
1978 +(message "Initialised notmuch-pick")\r
1979 +\r
1980 +(provide 'notmuch-pick)\r
1981 -- \r
1982 1.7.9.1\r
1983 \r