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