Re: [PATCH 9/9] add has: query prefix to search for specific properties
[notmuch-archives.git] / f8 / 0261ce8c92b35308938d8083cd4d7c004f09a6
1 Return-Path: <m.walters@qmul.ac.uk>\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 AA00F431E82\r
6         for <notmuch@notmuchmail.org>; Wed, 15 Feb 2012 01:26:04 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -1.098\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-1.098 tagged_above=-999 required=5\r
12         tests=[DKIM_ADSP_CUSTOM_MED=0.001, FREEMAIL_FROM=0.001,\r
13         NML_ADSP_CUSTOM_MED=1.2, RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled\r
14 Received: from olra.theworths.org ([127.0.0.1])\r
15         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
16         with ESMTP id uTOuW1awQfrP for <notmuch@notmuchmail.org>;\r
17         Wed, 15 Feb 2012 01:26:03 -0800 (PST)\r
18 Received: from mail2.qmul.ac.uk (mail2.qmul.ac.uk [138.37.6.6])\r
19         (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id 3B049431E62\r
22         for <notmuch@notmuchmail.org>; Wed, 15 Feb 2012 01:26:03 -0800 (PST)\r
23 Received: from smtp.qmul.ac.uk ([138.37.6.40])\r
24         by mail2.qmul.ac.uk with esmtp (Exim 4.71)\r
25         (envelope-from <m.walters@qmul.ac.uk>)\r
26         id 1Rxb7e-0001MZ-9d; Wed, 15 Feb 2012 09:25:58 +0000\r
27 Received: from 94-192-233-223.zone6.bethere.co.uk ([94.192.233.223]\r
28         helo=localhost)\r
29         by smtp.qmul.ac.uk with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69)\r
30         (envelope-from <m.walters@qmul.ac.uk>)\r
31         id 1Rxb7d-0003Ob-O6; Wed, 15 Feb 2012 09:25:58 +0000\r
32 From: Mark Walters <markwalters1009@gmail.com>\r
33 To: Austin Clements <amdragon@MIT.EDU>\r
34 Subject: Re: [RFC PATCH v3 00/11] notmuch-pick: an emacs threaded message view\r
35         with split-pane\r
36 In-Reply-To: <20120214152114.GQ27039@mit.edu>\r
37 References: <1329072579-27340-1-git-send-email-markwalters1009@gmail.com>\r
38         <1329096015-8078-1-git-send-email-markwalters1009@gmail.com>\r
39         <87haytbnun.fsf@qmul.ac.uk> <20120214152114.GQ27039@mit.edu>\r
40 User-Agent: Notmuch/0.11.1+206~g3b67774 (http://notmuchmail.org) Emacs/23.2.1\r
41         (i486-pc-linux-gnu)\r
42 Date: Wed, 15 Feb 2012 09:27:21 +0000\r
43 Message-ID: <87fwecbg5i.fsf@qmul.ac.uk>\r
44 MIME-Version: 1.0\r
45 Content-Type: multipart/mixed; boundary="=-=-="\r
46 X-Sender-Host-Address: 94.192.233.223\r
47 X-QM-SPAM-Info: Sender has good ham record.  :)\r
48 X-QM-Body-MD5: 842cd08fc917f2a3e061ed51a47e85d2 (of first 20000 bytes)\r
49 X-SpamAssassin-Score: -1.8\r
50 X-SpamAssassin-SpamBar: -\r
51 X-SpamAssassin-Report: The QM spam filters have analysed this message to\r
52         determine if it is\r
53         spam. We require at least 5.0 points to mark a message as spam.\r
54         This message scored -1.8 points.\r
55         Summary of the scoring: \r
56         * -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/,\r
57         *      medium trust\r
58         *      [138.37.6.40 listed in list.dnswl.org]\r
59         * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\r
60         provider *      (markwalters1009[at]gmail.com)\r
61         * -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay\r
62         *      domain\r
63         *  0.0 T_TVD_MIME_NO_HEADERS BODY: T_TVD_MIME_NO_HEADERS\r
64         *  0.5 AWL AWL: From: address is in the auto white-list\r
65 X-QM-Scan-Virus: ClamAV says the message is clean\r
66 Cc: notmuch@notmuchmail.org\r
67 X-BeenThere: notmuch@notmuchmail.org\r
68 X-Mailman-Version: 2.1.13\r
69 Precedence: list\r
70 List-Id: "Use and development of the notmuch mail system."\r
71         <notmuch.notmuchmail.org>\r
72 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
73         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
74 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
75 List-Post: <mailto:notmuch@notmuchmail.org>\r
76 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
77 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
78         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
79 X-List-Received-Date: Wed, 15 Feb 2012 09:26:04 -0000\r
80 \r
81 --=-=-=\r
82 \r
83 On Tue, 14 Feb 2012 10:21:14 -0500, Austin Clements <amdragon@MIT.EDU> wrote:\r
84 > Quoth Mark Walters on Feb 14 at 12:28 pm:\r
85 > > Finally, if notmuch-pick were able to do work asynchronously (as\r
86 > > notmuch-search does now) then I think all the speed concerns would go\r
87 > > away. However, I am not sure how to do incremental json parsing.\r
88\r
89 > For JSON search, at least, I think we've concluded that it should put\r
90 > newlines at strategic places in the JSON output (and never anywhere\r
91 > else) so that it's easy for a consumer to know when it has a complete\r
92 > JSON object and hand it to the JSON parser.  E.g.,\r
93\r
94 > [{"thread":"X", timestamp: 42, ...}\r
95 > ,{"thread":"Y", timestamp: 24, ...}\r
96 > ]\r
97\r
98 > This "framed JSON" works really well for the flat output of search.\r
99 > It's obviously trickier to apply to show's hierarchical output.  But,\r
100 > perhaps this will inspire you.\r
101\r
102 > (One possibility: we could rework show's output to be non-hierarchical\r
103 > and use the above framing; that is, it could be a sequence of message\r
104 > objects that each indicate which earlier message object they're a\r
105 > reply to, probably restricted to DFS order.)\r
106 \r
107 Here is a (very early version) of an async notmuch-pick based on the\r
108 above idea. This is on top of the previous patch series. There are two\r
109 attached patches: the first is a very small patch to notmuch-show.c to\r
110 print newlines between threads (since this is sufficient framing for\r
111 notmuch-pick; obviously it does not help notmuch-show at all).\r
112 \r
113 The second patch implements the async handling in notmuch-pick.el. This\r
114 is probably very inefficient, and doubtless has significant bugs, but it\r
115 seems to mostly work and does display the first page of results almost\r
116 immediately even on very large queries so I think this is the right way\r
117 to go.\r
118 \r
119 I am mostly posting it so people can play with the functionality but\r
120 cleanups, bugfixes, bug reports etc are gratefully received!\r
121 \r
122 Best wishes\r
123 \r
124 Mark\r
125 \r
126 \r
127 --=-=-=\r
128 Content-Type: text/x-diff\r
129 Content-Disposition: inline;\r
130  filename=0001-cli-notmuch-show-with-framing-newlines-between-threa.patch\r
131 \r
132 >From 5622a3ed7b486edc360ffa764756ce76f69ac032 Mon Sep 17 00:00:00 2001\r
133 From: Mark Walters <markwalters1009@gmail.com>\r
134 Date: Tue, 14 Feb 2012 19:33:56 +0000\r
135 Subject: [PATCH 1/2] cli: notmuch-show with framing newlines between threads in JSON\r
136 \r
137 Add newlines between complete threads to make asynchronous parsing\r
138 of the JSON easier\r
139 ---\r
140  notmuch-show.c |    4 ++++\r
141  1 files changed, 4 insertions(+), 0 deletions(-)\r
142 \r
143 diff --git a/notmuch-show.c b/notmuch-show.c\r
144 index 3f8618b..a7cc684 100644\r
145 --- a/notmuch-show.c\r
146 +++ b/notmuch-show.c\r
147 @@ -912,6 +912,7 @@ do_show_messages (void *ctx,\r
148      int first_set = 1;\r
149  \r
150      fputs (format->message_set_start, stdout);\r
151 +    fputs ("\n", stdout);\r
152      messages = notmuch_query_search_messages (query);\r
153  \r
154      for (;\r
155 @@ -939,6 +940,7 @@ do_show_messages (void *ctx,\r
156  \r
157         fputs (format->message_set_end, stdout);\r
158         fputs (format->message_set_end, stdout);\r
159 +       fputs ("\n", stdout);\r
160      }\r
161  \r
162      fputs (format->message_set_end, stdout);\r
163 @@ -1029,6 +1031,7 @@ do_show (void *ctx,\r
164      int first_toplevel = 1;\r
165  \r
166      fputs (format->message_set_start, stdout);\r
167 +    fputs ("\n", stdout);\r
168  \r
169      for (threads = notmuch_query_search_threads (query);\r
170          notmuch_threads_valid (threads);\r
171 @@ -1047,6 +1050,7 @@ do_show (void *ctx,\r
172         first_toplevel = 0;\r
173  \r
174         show_messages (ctx, format, messages, 0, params);\r
175 +       fputs ("\n", stdout);\r
176  \r
177         notmuch_thread_destroy (thread);\r
178  \r
179 -- \r
180 1.7.2.3\r
181 \r
182 \r
183 --=-=-=\r
184 Content-Type: text/x-diff\r
185 Content-Disposition: inline;\r
186  filename=0002-emacs-a-semi-working-async-pick.patch\r
187 \r
188 >From c3bcd7d11b67a15a1760505c036e760e7028b125 Mon Sep 17 00:00:00 2001\r
189 From: Mark Walters <markwalters1009@gmail.com>\r
190 Date: Mon, 13 Feb 2012 23:31:45 +0000\r
191 Subject: [PATCH 2/2] emacs: a  semi-working async pick\r
192 \r
193 ---\r
194  emacs/notmuch-pick.el |  111 ++++++++++++++++++++++++++++++++++++++-----------\r
195  1 files changed, 87 insertions(+), 24 deletions(-)\r
196 \r
197 diff --git a/emacs/notmuch-pick.el b/emacs/notmuch-pick.el\r
198 index 46eb720..f944d79 100644\r
199 --- a/emacs/notmuch-pick.el\r
200 +++ b/emacs/notmuch-pick.el\r
201 @@ -287,7 +287,7 @@ This command toggles the sort order for the current search."\r
202         (query-context notmuch-pick-query-context)\r
203         (buffer-name notmuch-pick-buffer-name))\r
204      (erase-buffer)\r
205 -    (notmuch-pick-worker thread-id  query-context buffer-name)))\r
206 +    (notmuch-pick-worker thread-id  query-context (get-buffer buffer-name))))\r
207  \r
208  (defun notmuch-pick-toggle-view ()\r
209    "Toggle showing threads or as isolated messages."\r
210 @@ -531,34 +531,97 @@ Complete list of currently available key bindings:\r
211    (setq buffer-read-only t\r
212         truncate-lines t))\r
213  \r
214 -(defun notmuch-pick-worker (thread-id &optional query-context buffer-name)\r
215 +(defvar notmuch-pick-process-filter-data nil\r
216 +  "Data that has not yet been processed.")\r
217 +(make-variable-buffer-local 'notmuch-pick-process-filter-data)\r
218 +\r
219 +(defun notmuch-pick-process-sentinel (proc msg)\r
220 +  "Add a message to let user know when \"notmuch pick\" exits"\r
221 +  (let ((buffer (process-buffer proc))\r
222 +       (status (process-status proc))\r
223 +       (exit-status (process-exit-status proc))\r
224 +       (never-found-target-thread nil))\r
225 +    (if (memq status '(exit signal))\r
226 +       (if (buffer-live-p buffer)\r
227 +           (with-current-buffer buffer\r
228 +             (save-excursion\r
229 +               (let ((inhibit-read-only t)\r
230 +                     (atbob (bobp)))\r
231 +                 (goto-char (point-max))\r
232 +                 (if (eq status 'signal)\r
233 +                     (insert "Incomplete search results (pick process was killed).\n"))\r
234 +                 (when (eq status 'exit)\r
235 +                   (if (not (string= notmuch-pick-process-filter-data "\n]"))\r
236 +                       (insert (concat "Error: Unexpected output from notmuch pick:\n"\r
237 +                                       notmuch-pick-process-filter-data)))\r
238 +                   (insert "End of search results.")\r
239 +                   (unless (= exit-status 0)\r
240 +                     (insert (format " (process returned %d)" exit-status)))\r
241 +                   (insert "\n")))))))))\r
242 +\r
243 +(defun notmuch-pick-process-filter (proc string)\r
244 +  "Process and filter the output of \"notmuch show\" (for pick)"\r
245 +  (let ((buffer (process-buffer proc)))\r
246 +    (if (buffer-live-p buffer)\r
247 +       (with-current-buffer buffer\r
248 +         (save-excursion\r
249 +           (let ((line 0)\r
250 +                 (more t)\r
251 +                 (inhibit-read-only t)\r
252 +                 (string (concat notmuch-pick-process-filter-data string)))\r
253 +             (while (string-match "\n.*\n" string)\r
254 +               (let ((frame (match-string 0 string))\r
255 +                     (frame-end (match-end 0)))\r
256 +                 (when (or (= (elt frame 1) ?\[) (= (elt frame 1) ?,))\r
257 +                   (let (json)\r
258 +                     (with-temp-buffer\r
259 +                       (let ((json-object-type 'plist)\r
260 +                             (json-array-type 'list)\r
261 +                             (json-false 'nil))\r
262 +                         (insert frame)\r
263 +                         (goto-char (point-min))\r
264 +                         (search-forward "[")\r
265 +                         (backward-char)\r
266 +                         (setq json (json-read))))\r
267 +                     (goto-char (point-max))\r
268 +                     (notmuch-pick-insert-forest (list json))))\r
269 +                 (setq string (substring string (- frame-end 1)))))\r
270 +             (setq notmuch-pick-process-filter-data string)))))))\r
271 +\r
272 +(defun notmuch-pick-worker (thread-id &optional query-context buffer)\r
273    (interactive)\r
274    (notmuch-pick-mode)\r
275    (setq notmuch-pick-thread-id thread-id)\r
276    (setq notmuch-pick-query-context query-context)\r
277 -  (setq notmuch-pick-buffer-name buffer-name)\r
278 +  (setq notmuch-pick-buffer-name (buffer-name buffer))\r
279  \r
280    (erase-buffer)\r
281    (goto-char (point-min))\r
282 -  (save-excursion\r
283 -    (let* ((basic-args (list thread-id))\r
284 -          (args (if query-context\r
285 -                    (append (list "\'") basic-args (list "and (" query-context ")\'"))\r
286 -                  (append (list "\'") basic-args (list "\'"))))\r
287 -          (message-arg (if notmuch-pick-view-just-messages\r
288 -                           "--thread=none"\r
289 -                         "--thread=entire"))\r
290 -          (sort-arg (if notmuch-pick-oldest-first\r
291 -                         "--sort=oldest-first"\r
292 -                       "--sort=newest-first")))\r
293 -\r
294 -      (notmuch-pick-insert-forest (notmuch-query-get-threads args "--headers-only" message-arg sort-arg))\r
295 -      ;; If the query context reduced the results to nothing, run\r
296 -      ;; the basic query.\r
297 -      (when (and (eq (buffer-size) 0)\r
298 -                query-context)\r
299 -       (notmuch-pick-insert-forest\r
300 -        (notmuch-query-get-threads basic-args message-arg sort-arg))))))\r
301 +  (let* (args\r
302 +        (basic-args thread-id)\r
303 +        (search-args (concat\r
304 +                      basic-args (if query-context (concat " and (" query-context ")"))))\r
305 +        (message-arg (if notmuch-pick-view-just-messages\r
306 +                         "--thread=none"\r
307 +                       "--thread=entire"))\r
308 +        (sort-arg (if notmuch-pick-oldest-first\r
309 +                      "--sort=oldest-first"\r
310 +                    "--sort=newest-first"))\r
311 +        (proc (start-process\r
312 +               "notmuch-pick" buffer\r
313 +               notmuch-command "show" "--headers-only" "--format=json"\r
314 +               message-arg sort-arg search-args)))\r
315 +    (set-process-sentinel proc 'notmuch-pick-process-sentinel)\r
316 +    (set-process-filter proc 'notmuch-pick-process-filter)\r
317 +    (set-process-query-on-exit-flag proc nil)))\r
318 +;;      (notmuch-pick-insert-forest (notmuch-query-get-threads args "--headers-only" message-arg sort-arg))\r
319 +;;     (message "time3 (end forest): %s" (current-time))\r
320 +;; If the query context reduced the results to nothing, run\r
321 +;; the basic query.\r
322 +;;      (when (and (eq (buffer-size) 0)\r
323 +;;              query-context)\r
324 +;;     (notmuch-pick-insert-forest\r
325 +;;      (notmuch-query-get-threads basic-args message-arg sort-arg))))))\r
326  \r
327  (defun notmuch-pick (&optional query query-context buffer-name)\r
328    "Run notmuch pick with the given `query' and display the results"\r
329 @@ -567,14 +630,14 @@ Complete list of currently available key bindings:\r
330        (setq query (notmuch-read-query "Notmuch pick: ")))\r
331    (let ((buffer (get-buffer-create (generate-new-buffer-name\r
332                                     (or buffer-name\r
333 -                                       (concat "*notmuch-" query "*")))))\r
334 +                                       (concat "*notmuch-pick-" query "*")))))\r
335         (inhibit-read-only t))\r
336  \r
337      (switch-to-buffer buffer)\r
338      ;; Don't track undo information for this buffer\r
339      (set 'buffer-undo-list t)\r
340  \r
341 -    (notmuch-pick-worker query query-context buffer-name)\r
342 +    (notmuch-pick-worker query query-context buffer)\r
343  \r
344      (setq truncate-lines t)))\r
345  \r
346 -- \r
347 1.7.2.3\r
348 \r
349 \r
350 --=-=-=--\r