database error
[notmuch-archives.git] / ce / c36207088f681c05ac6efb7ac1c59c98cd18da
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 DD328431FB6\r
6         for <notmuch@notmuchmail.org>; Thu,  5 Jul 2012 01:37:26 -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: -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 H-lCJLaWauyi for <notmuch@notmuchmail.org>;\r
17         Thu,  5 Jul 2012 01:37:26 -0700 (PDT)\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 8CF28431FAE\r
22         for <notmuch@notmuchmail.org>; Thu,  5 Jul 2012 01:37:25 -0700 (PDT)\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 1SmhYx-0001IN-Cm; Thu, 05 Jul 2012 09:37:23 +0100\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 1SmhYw-0003nK-TQ; Thu, 05 Jul 2012 09:37:23 +0100\r
32 From: Mark Walters <markwalters1009@gmail.com>\r
33 To: Austin Clements <amdragon@MIT.EDU>, notmuch@notmuchmail.org\r
34 Subject: Re: [PATCH 8/8] emacs: Switch from text to JSON format for search\r
35         results\r
36 In-Reply-To: <1341354059-29396-9-git-send-email-amdragon@mit.edu>\r
37 References: <1341354059-29396-1-git-send-email-amdragon@mit.edu>\r
38         <1341354059-29396-9-git-send-email-amdragon@mit.edu>\r
39 User-Agent: Notmuch/0.13.2+70~gb6a56e7 (http://notmuchmail.org) Emacs/23.4.1\r
40         (x86_64-pc-linux-gnu)\r
41 Date: Thu, 05 Jul 2012 09:37:19 +0100\r
42 Message-ID: <87y5myeh8g.fsf@qmul.ac.uk>\r
43 MIME-Version: 1.0\r
44 Content-Type: text/plain; charset=us-ascii\r
45 X-Sender-Host-Address: 94.192.233.223\r
46 X-QM-SPAM-Info: Sender has good ham record.  :)\r
47 X-QM-Body-MD5: dee2ed397367a44f97b87e5657d11775 (of first 20000 bytes)\r
48 X-SpamAssassin-Score: -1.8\r
49 X-SpamAssassin-SpamBar: -\r
50 X-SpamAssassin-Report: The QM spam filters have analysed this message to\r
51         determine if it is\r
52         spam. We require at least 5.0 points to mark a message as spam.\r
53         This message scored -1.8 points.\r
54         Summary of the scoring: \r
55         * -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/,\r
56         *      medium trust\r
57         *      [138.37.6.40 listed in list.dnswl.org]\r
58         * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\r
59         provider *      (markwalters1009[at]gmail.com)\r
60         * -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay\r
61         *      domain\r
62         *  0.5 AWL AWL: From: address is in the auto white-list\r
63 X-QM-Scan-Virus: ClamAV says the message is clean\r
64 Cc: tomi.ollila@iki.fi\r
65 X-BeenThere: notmuch@notmuchmail.org\r
66 X-Mailman-Version: 2.1.13\r
67 Precedence: list\r
68 List-Id: "Use and development of the notmuch mail system."\r
69         <notmuch.notmuchmail.org>\r
70 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
71         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
72 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
73 List-Post: <mailto:notmuch@notmuchmail.org>\r
74 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
75 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
76         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
77 X-List-Received-Date: Thu, 05 Jul 2012 08:37:27 -0000\r
78 \r
79 On Tue, 03 Jul 2012, Austin Clements <amdragon@MIT.EDU> wrote:\r
80 > The JSON format eliminates the complex escaping issues that have\r
81 > plagued the text search format.  This uses the incremental JSON parser\r
82 > so that, like the text parser, it can output search results\r
83 > incrementally.\r
84 >\r
85 > This slows down the parser by about ~4X, but puts us in a good\r
86 > position to optimize either by improving the JSON parser (evidence\r
87 > suggests this can reduce the overhead to ~40% over the text format) or\r
88 > by switching to S-expressions (evidence suggests this will more than\r
89 > double performance over the text parser).  [1]\r
90 >\r
91 > This also fixes the incremental search parsing test.\r
92 >\r
93 > [1] id:"20110720205007.GB21316@mit.edu"\r
94 > ---\r
95 >  emacs/notmuch.el |  113 ++++++++++++++++++++++++++++++++----------------------\r
96 >  test/emacs       |    1 -\r
97 >  2 files changed, 67 insertions(+), 47 deletions(-)\r
98 >\r
99 > diff --git a/emacs/notmuch.el b/emacs/notmuch.el\r
100 > index 084cec6..2a09a98 100644\r
101 > --- a/emacs/notmuch.el\r
102 > +++ b/emacs/notmuch.el\r
103 > @@ -60,7 +60,7 @@\r
104 >  (require 'notmuch-message)\r
105 >  \r
106 >  (defcustom notmuch-search-result-format\r
107 > -  `(("date" . "%s ")\r
108 > +  `(("date" . "%12s ")\r
109 >      ("count" . "%-7s ")\r
110 >      ("authors" . "%-20s ")\r
111 >      ("subject" . "%s ")\r
112 > @@ -557,17 +557,14 @@ This function advances the next thread when finished."\r
113 >    (notmuch-search-tag '("-inbox"))\r
114 >    (notmuch-search-next-thread))\r
115 >  \r
116 > -(defvar notmuch-search-process-filter-data nil\r
117 > -  "Data that has not yet been processed.")\r
118 > -(make-variable-buffer-local 'notmuch-search-process-filter-data)\r
119 > -\r
120 >  (defun notmuch-search-process-sentinel (proc msg)\r
121 >    "Add a message to let user know when \"notmuch search\" exits"\r
122 >    (let ((buffer (process-buffer proc))\r
123 >       (status (process-status proc))\r
124 >       (exit-status (process-exit-status proc))\r
125 >       (never-found-target-thread nil))\r
126 > -    (if (memq status '(exit signal))\r
127 > +    (when (memq status '(exit signal))\r
128 > +     (kill-buffer (process-get proc 'parse-buf))\r
129 >       (if (buffer-live-p buffer)\r
130 >           (with-current-buffer buffer\r
131 >             (save-excursion\r
132 > @@ -577,8 +574,6 @@ This function advances the next thread when finished."\r
133 >                 (if (eq status 'signal)\r
134 >                     (insert "Incomplete search results (search process was killed).\n"))\r
135 >                 (when (eq status 'exit)\r
136 > -                 (if notmuch-search-process-filter-data\r
137 > -                     (insert (concat "Error: Unexpected output from notmuch search:\n" notmuch-search-process-filter-data)))\r
138 >                   (insert "End of search results.")\r
139 >                   (unless (= exit-status 0)\r
140 >                     (insert (format " (process returned %d)" exit-status)))\r
141 > @@ -757,45 +752,62 @@ non-authors is found, assume that all of the authors match."\r
142 >      (insert (apply #'format string objects))\r
143 >      (insert "\n")))\r
144 >  \r
145 > +(defvar notmuch-search-process-state nil\r
146 > +  "Parsing state of the search process filter.")\r
147 > +\r
148 > +(defvar notmuch-search-json-parser nil\r
149 > +  "Incremental JSON parser for the search process filter.")\r
150 > +\r
151 >  (defun notmuch-search-process-filter (proc string)\r
152 >    "Process and filter the output of \"notmuch search\""\r
153 > -  (let ((buffer (process-buffer proc)))\r
154 > -    (if (buffer-live-p buffer)\r
155 > -     (with-current-buffer buffer\r
156 > -         (let ((line 0)\r
157 > -               (more t)\r
158 > -               (inhibit-read-only t)\r
159 > -               (string (concat notmuch-search-process-filter-data string)))\r
160 > -           (setq notmuch-search-process-filter-data nil)\r
161 > -           (while more\r
162 > -             (while (and (< line (length string)) (= (elt string line) ?\n))\r
163 > -               (setq line (1+ line)))\r
164 > -             (if (string-match "^thread:\\([0-9A-Fa-f]*\\) \\([^][]*\\) \\[\\([0-9]*\\)/\\([0-9]*\\)\\] \\([^;]*\\); \\(.*\\) (\\([^()]*\\))$" string line)\r
165 > -                 (let* ((thread-id (match-string 1 string))\r
166 > -                        (tags-str (match-string 7 string))\r
167 > -                        (result (list :thread thread-id\r
168 > -                                      :date_relative (match-string 2 string)\r
169 > -                                      :matched (string-to-number\r
170 > -                                                (match-string 3 string))\r
171 > -                                      :total (string-to-number\r
172 > -                                              (match-string 4 string))\r
173 > -                                      :authors (match-string 5 string)\r
174 > -                                      :subject (match-string 6 string)\r
175 > -                                      :tags (if tags-str\r
176 > -                                                (save-match-data\r
177 > -                                                  (split-string tags-str))))))\r
178 > -                   (if (/= (match-beginning 0) line)\r
179 > -                       (notmuch-search-show-error\r
180 > -                        (substring string line (match-beginning 0))))\r
181 > -                   (notmuch-search-show-result result)\r
182 > -                   (set 'line (match-end 0)))\r
183 > -               (set 'more nil)\r
184 > -               (while (and (< line (length string)) (= (elt string line) ?\n))\r
185 > -                 (setq line (1+ line)))\r
186 > -               (if (< line (length string))\r
187 > -                   (setq notmuch-search-process-filter-data (substring string line)))\r
188 > -               ))))\r
189 > -      (delete-process proc))))\r
190 > +  (let ((results-buf (process-buffer proc))\r
191 > +     (parse-buf (process-get proc 'parse-buf))\r
192 > +     (inhibit-read-only t)\r
193 > +     done)\r
194 > +    (if (not (buffer-live-p results-buf))\r
195 > +     (delete-process proc)\r
196 > +      (with-current-buffer parse-buf\r
197 > +     ;; Insert new data\r
198 > +     (save-excursion\r
199 > +       (goto-char (point-max))\r
200 > +       (insert string)))\r
201 > +      (with-current-buffer results-buf\r
202 > +     (while (not done)\r
203 > +       (condition-case nil\r
204 > +           (case notmuch-search-process-state\r
205 > +             ((begin)\r
206 > +              ;; Enter the results list\r
207 > +              (if (eq (notmuch-json-begin-compound\r
208 > +                       notmuch-search-json-parser) 'retry)\r
209 > +                  (setq done t)\r
210 > +                (setq notmuch-search-process-state 'result)))\r
211 > +             ((result)\r
212 > +              ;; Parse a result\r
213 > +              (let ((result (notmuch-json-read notmuch-search-json-parser)))\r
214 > +                (case result\r
215 > +                  ((retry) (setq done t))\r
216 > +                  ((end) (setq notmuch-search-process-state 'end))\r
217 > +                  (otherwise (notmuch-search-show-result result)))))\r
218 > +             ((end)\r
219 > +              ;; Any trailing data is unexpected\r
220 > +              (with-current-buffer parse-buf\r
221 > +                (skip-chars-forward " \t\r\n")\r
222 > +                (if (eobp)\r
223 > +                    (setq done t)\r
224 > +                  (signal 'json-error nil)))))\r
225 \r
226 This looks good to me. Would it make sense to put the "Any trailing\r
227 data" as a tiny function in notmuch-lib? something like \r
228 \r
229 (defun notmuch-json-assert-end-of-data ()\r
230    (skip-chars-forward " \t\r\n") \r
231    (if (eobp)\r
232        (setq done t) \r
233      (signal 'json-error nil)))\r
234 \r
235 Best wishes\r
236 \r
237 Mark\r
238 \r
239 \r
240 > +         (json-error\r
241 > +          ;; Do our best to resynchronize and ensure forward\r
242 > +          ;; progress\r
243 > +          (notmuch-search-show-error\r
244 > +           "%s"\r
245 > +           (with-current-buffer parse-buf\r
246 > +             (let ((bad (buffer-substring (line-beginning-position)\r
247 > +                                          (line-end-position))))\r
248 > +               (forward-line)\r
249 > +               bad))))))\r
250 > +     ;; Clear out what we've parsed\r
251 > +     (with-current-buffer parse-buf\r
252 > +       (delete-region (point-min) (point)))))))\r
253 >  \r
254 >  (defun notmuch-search-tag-all (&optional tag-changes)\r
255 >    "Add/remove tags from all messages in current search buffer.\r
256 > @@ -898,10 +910,19 @@ Other optional parameters are used as follows:\r
257 >       (let ((proc (start-process\r
258 >                    "notmuch-search" buffer\r
259 >                    notmuch-command "search"\r
260 > +                  "--format=json"\r
261 >                    (if oldest-first\r
262 >                        "--sort=oldest-first"\r
263 >                      "--sort=newest-first")\r
264 > -                  query)))\r
265 > +                  query))\r
266 > +           ;; Use a scratch buffer to accumulate partial output.\r
267 > +           ;; This buffer will be killed by the sentinel, which\r
268 > +           ;; should be called no matter how the process dies.\r
269 > +           (parse-buf (generate-new-buffer " *notmuch search parse*")))\r
270 > +       (set (make-local-variable 'notmuch-search-process-state) 'begin)\r
271 > +       (set (make-local-variable 'notmuch-search-json-parser)\r
272 > +            (notmuch-json-create-parser parse-buf))\r
273 > +       (process-put proc 'parse-buf parse-buf)\r
274 >         (set-process-sentinel proc 'notmuch-search-process-sentinel)\r
275 >         (set-process-filter proc 'notmuch-search-process-filter)\r
276 >         (set-process-query-on-exit-flag proc nil))))\r
277 > diff --git a/test/emacs b/test/emacs\r
278 > index 293b12a..afe35ba 100755\r
279 > --- a/test/emacs\r
280 > +++ b/test/emacs\r
281 > @@ -36,7 +36,6 @@ test_emacs '(notmuch-search "tag:inbox")\r
282 >  test_expect_equal_file OUTPUT $EXPECTED/notmuch-search-tag-inbox\r
283 >  \r
284 >  test_begin_subtest "Incremental parsing of search results"\r
285 > -test_subtest_known_broken\r
286 >  test_emacs "(ad-enable-advice 'notmuch-search-process-filter 'around 'pessimal)\r
287 >           (ad-activate 'notmuch-search-process-filter)\r
288 >           (notmuch-search \"tag:inbox\")\r
289 > -- \r
290 > 1.7.10\r
291 >\r
292 > _______________________________________________\r
293 > notmuch mailing list\r
294 > notmuch@notmuchmail.org\r
295 > http://notmuchmail.org/mailman/listinfo/notmuch\r