Re: [PATCH] test: canonicalize content-type in "Sending a message via (fake) SMTP"
[notmuch-archives.git] / ad / 1c05bb2afe746ed5df2764130d07de698ff003
1 Return-Path: <amdragon@mit.edu>\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 5A4D5431FC2\r
6         for <notmuch@notmuchmail.org>; Thu,  5 Jul 2012 13:52:47 -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.7\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id NMz-pghLFrIF for <notmuch@notmuchmail.org>;\r
16         Thu,  5 Jul 2012 13:52:45 -0700 (PDT)\r
17 Received: from dmz-mailsec-scanner-4.mit.edu (DMZ-MAILSEC-SCANNER-4.MIT.EDU\r
18         [18.9.25.15])\r
19         by olra.theworths.org (Postfix) with ESMTP id 84012431FC3\r
20         for <notmuch@notmuchmail.org>; Thu,  5 Jul 2012 13:52:43 -0700 (PDT)\r
21 X-AuditID: 1209190f-b7f306d0000008b4-ec-4ff5fe9b1ede\r
22 Received: from mailhub-auth-1.mit.edu ( [18.9.21.35])\r
23         by dmz-mailsec-scanner-4.mit.edu (Symantec Messaging Gateway) with SMTP\r
24         id FC.18.02228.B9EF5FF4; Thu,  5 Jul 2012 16:52:43 -0400 (EDT)\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
26         by mailhub-auth-1.mit.edu (8.13.8/8.9.2) with ESMTP id q65Kqg4T024134; \r
27         Thu, 5 Jul 2012 16:52:42 -0400\r
28 Received: from drake.dyndns.org (26-4-182.dynamic.csail.mit.edu [18.26.4.182])\r
29         (authenticated bits=0)\r
30         (User authenticated as amdragon@ATHENA.MIT.EDU)\r
31         by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q65Kqbd9027241\r
32         (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
33         Thu, 5 Jul 2012 16:52:39 -0400 (EDT)\r
34 Received: from amthrax by drake.dyndns.org with local (Exim 4.77)\r
35         (envelope-from <amdragon@mit.edu>)\r
36         id 1Smt2T-0004Xq-Mi; Thu, 05 Jul 2012 16:52:37 -0400\r
37 From: Austin Clements <amdragon@MIT.EDU>\r
38 To: notmuch@notmuchmail.org\r
39 Subject: [PATCH v2 8/9] emacs: Switch from text to JSON format for search\r
40         results\r
41 Date: Thu,  5 Jul 2012 16:52:26 -0400\r
42 Message-Id: <1341521547-15502-9-git-send-email-amdragon@mit.edu>\r
43 X-Mailer: git-send-email 1.7.10\r
44 In-Reply-To: <1341521547-15502-1-git-send-email-amdragon@mit.edu>\r
45 References: <1341354059-29396-1-git-send-email-amdragon@mit.edu>\r
46         <1341521547-15502-1-git-send-email-amdragon@mit.edu>\r
47 X-Brightmail-Tracker:\r
48  H4sIAAAAAAAAA+NgFjrGIsWRmVeSWpSXmKPExsUixCmqrDv731d/g8alwhar5/JYXL85k9ni\r
49         zcp5rA7MHjtn3WX3OPx1IYvHs1W3mAOYo7hsUlJzMstSi/TtErgy9j0yL7hkXzFr5xGmBsb5\r
50         Rl2MnBwSAiYSH5Y/ZYGwxSQu3FvP1sXIxSEksI9R4uWnX1DOekaJD3vvMEE4J5kkNsy7yQrh\r
51         zGWU+DN9FyNIP5uAhsS2/cvBbBEBaYmdd2ezgtjMAnESW6b8B4sLCwRKLJo0GWwfi4CqxP8l\r
52         e5hBbF4BB4l9V45C3SEv8fR+HxuIzSngKHFh4mKwXiGBcok/S/6xTGDkX8DIsIpRNiW3Sjc3\r
53         MTOnODVZtzg5MS8vtUjXRC83s0QvNaV0EyM4uCT5dzB+O6h0iFGAg1GJh9cw94u/EGtiWXFl\r
54         7iFGSQ4mJVHext9f/YX4kvJTKjMSizPii0pzUosPMUpwMCuJ8PZmAOV4UxIrq1KL8mFS0hws\r
55         SuK8V1Nu+gsJpCeWpGanphakFsFkZTg4lCR41YFRJCRYlJqeWpGWmVOCkGbi4AQZzgM0XAOk\r
56         hre4IDG3ODMdIn+KUVFKnFcaJCEAksgozYPrhUX/K0ZxoFeEeT/9BariASYOuO5XQIOZgAbn\r
57         Lf4EMrgkESEl1cDYWLvOq3FH9ZfA59qLr/n8FuYofZXnq8a5tOT71Z+tcxZl9x7bWxOZc6e+\r
58         PfBETtzHQibf/fui+FsvRnd8d75+MuT7jSK20jNdF5gyP5w9rfZgIl9IW57Zir0cy1UWbZUr\r
59         l/8ck6PsZqgie75G9MDugmvG5+84aqpf5JrHYq76++2NbvZPfTVKLMUZiYZazEXFiQCAXJ2F\r
60         2QIAAA==\r
61 Cc: tomi.ollila@iki.fi\r
62 X-BeenThere: notmuch@notmuchmail.org\r
63 X-Mailman-Version: 2.1.13\r
64 Precedence: list\r
65 List-Id: "Use and development of the notmuch mail system."\r
66         <notmuch.notmuchmail.org>\r
67 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
68         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
69 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
70 List-Post: <mailto:notmuch@notmuchmail.org>\r
71 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
72 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
73         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
74 X-List-Received-Date: Thu, 05 Jul 2012 20:52:47 -0000\r
75 \r
76 The JSON format eliminates the complex escaping issues that have\r
77 plagued the text search format.  This uses the incremental JSON parser\r
78 so that, like the text parser, it can output search results\r
79 incrementally.\r
80 \r
81 This slows down the parser by about ~4X, but puts us in a good\r
82 position to optimize either by improving the JSON parser (evidence\r
83 suggests this can reduce the overhead to ~40% over the text format) or\r
84 by switching to S-expressions (evidence suggests this will more than\r
85 double performance over the text parser).  [1]\r
86 \r
87 This also fixes the incremental search parsing test.\r
88 \r
89 This has one minor side-effect on search result formatting.\r
90 Previously, the date field was always padded to a fixed width of 12\r
91 characters because of how the text parser's regexp was written.  The\r
92 JSON format doesn't do this.  We could pad it out in Emacs before\r
93 formatting it, but, since all of the other fields are variable width,\r
94 we instead fix notmuch-search-result-format to take the variable-width\r
95 field and pad it out.  For users who have customized this variable,\r
96 we'll mention in the NEWS how to fix this slight format change.\r
97 \r
98 [1] id:"20110720205007.GB21316@mit.edu"\r
99 ---\r
100  emacs/notmuch.el |  110 +++++++++++++++++++++++++++++++-----------------------\r
101  test/emacs       |    1 -\r
102  2 files changed, 64 insertions(+), 47 deletions(-)\r
103 \r
104 diff --git a/emacs/notmuch.el b/emacs/notmuch.el\r
105 index dfeaf35..fabb7c0 100644\r
106 --- a/emacs/notmuch.el\r
107 +++ b/emacs/notmuch.el\r
108 @@ -60,7 +60,7 @@\r
109  (require 'notmuch-message)\r
110  \r
111  (defcustom notmuch-search-result-format\r
112 -  `(("date" . "%s ")\r
113 +  `(("date" . "%12s ")\r
114      ("count" . "%-7s ")\r
115      ("authors" . "%-20s ")\r
116      ("subject" . "%s ")\r
117 @@ -557,17 +557,14 @@ This function advances the next thread when finished."\r
118    (notmuch-search-tag '("-inbox"))\r
119    (notmuch-search-next-thread))\r
120  \r
121 -(defvar notmuch-search-process-filter-data nil\r
122 -  "Data that has not yet been processed.")\r
123 -(make-variable-buffer-local 'notmuch-search-process-filter-data)\r
124 -\r
125  (defun notmuch-search-process-sentinel (proc msg)\r
126    "Add a message to let user know when \"notmuch search\" exits"\r
127    (let ((buffer (process-buffer proc))\r
128         (status (process-status proc))\r
129         (exit-status (process-exit-status proc))\r
130         (never-found-target-thread nil))\r
131 -    (if (memq status '(exit signal))\r
132 +    (when (memq status '(exit signal))\r
133 +       (kill-buffer (process-get proc 'parse-buf))\r
134         (if (buffer-live-p buffer)\r
135             (with-current-buffer buffer\r
136               (save-excursion\r
137 @@ -577,8 +574,6 @@ This function advances the next thread when finished."\r
138                   (if (eq status 'signal)\r
139                       (insert "Incomplete search results (search process was killed).\n"))\r
140                   (when (eq status 'exit)\r
141 -                   (if notmuch-search-process-filter-data\r
142 -                       (insert (concat "Error: Unexpected output from notmuch search:\n" notmuch-search-process-filter-data)))\r
143                     (insert "End of search results.")\r
144                     (unless (= exit-status 0)\r
145                       (insert (format " (process returned %d)" exit-status)))\r
146 @@ -758,45 +753,59 @@ non-authors is found, assume that all of the authors match."\r
147      (insert (apply #'format string objects))\r
148      (insert "\n")))\r
149  \r
150 +(defvar notmuch-search-process-state nil\r
151 +  "Parsing state of the search process filter.")\r
152 +\r
153 +(defvar notmuch-search-json-parser nil\r
154 +  "Incremental JSON parser for the search process filter.")\r
155 +\r
156  (defun notmuch-search-process-filter (proc string)\r
157    "Process and filter the output of \"notmuch search\""\r
158 -  (let ((buffer (process-buffer proc)))\r
159 -    (if (buffer-live-p buffer)\r
160 -       (with-current-buffer buffer\r
161 -           (let ((line 0)\r
162 -                 (more t)\r
163 -                 (inhibit-read-only t)\r
164 -                 (string (concat notmuch-search-process-filter-data string)))\r
165 -             (setq notmuch-search-process-filter-data nil)\r
166 -             (while more\r
167 -               (while (and (< line (length string)) (= (elt string line) ?\n))\r
168 -                 (setq line (1+ line)))\r
169 -               (if (string-match "^thread:\\([0-9A-Fa-f]*\\) \\([^][]*\\) \\[\\([0-9]*\\)/\\([0-9]*\\)\\] \\([^;]*\\); \\(.*\\) (\\([^()]*\\))$" string line)\r
170 -                   (let* ((thread-id (match-string 1 string))\r
171 -                          (tags-str (match-string 7 string))\r
172 -                          (result (list :thread thread-id\r
173 -                                        :date_relative (match-string 2 string)\r
174 -                                        :matched (string-to-number\r
175 -                                                  (match-string 3 string))\r
176 -                                        :total (string-to-number\r
177 -                                                (match-string 4 string))\r
178 -                                        :authors (match-string 5 string)\r
179 -                                        :subject (match-string 6 string)\r
180 -                                        :tags (if tags-str\r
181 -                                                  (save-match-data\r
182 -                                                    (split-string tags-str))))))\r
183 -                     (if (/= (match-beginning 0) line)\r
184 -                         (notmuch-search-show-error\r
185 -                          (substring string line (match-beginning 0))))\r
186 -                     (notmuch-search-show-result result)\r
187 -                     (set 'line (match-end 0)))\r
188 -                 (set 'more nil)\r
189 -                 (while (and (< line (length string)) (= (elt string line) ?\n))\r
190 -                   (setq line (1+ line)))\r
191 -                 (if (< line (length string))\r
192 -                     (setq notmuch-search-process-filter-data (substring string line)))\r
193 -                 ))))\r
194 -      (delete-process proc))))\r
195 +  (let ((results-buf (process-buffer proc))\r
196 +       (parse-buf (process-get proc 'parse-buf))\r
197 +       (inhibit-read-only t)\r
198 +       done)\r
199 +    (if (not (buffer-live-p results-buf))\r
200 +       (delete-process proc)\r
201 +      (with-current-buffer parse-buf\r
202 +       ;; Insert new data\r
203 +       (save-excursion\r
204 +         (goto-char (point-max))\r
205 +         (insert string)))\r
206 +      (with-current-buffer results-buf\r
207 +       (while (not done)\r
208 +         (condition-case nil\r
209 +             (case notmuch-search-process-state\r
210 +               ((begin)\r
211 +                ;; Enter the results list\r
212 +                (if (eq (notmuch-json-begin-compound\r
213 +                         notmuch-search-json-parser) 'retry)\r
214 +                    (setq done t)\r
215 +                  (setq notmuch-search-process-state 'result)))\r
216 +               ((result)\r
217 +                ;; Parse a result\r
218 +                (let ((result (notmuch-json-read notmuch-search-json-parser)))\r
219 +                  (case result\r
220 +                    ((retry) (setq done t))\r
221 +                    ((end) (setq notmuch-search-process-state 'end))\r
222 +                    (otherwise (notmuch-search-show-result result)))))\r
223 +               ((end)\r
224 +                ;; Any trailing data is unexpected\r
225 +                (notmuch-json-eof notmuch-search-json-parser)\r
226 +                (setq done t)))\r
227 +           (json-error\r
228 +            ;; Do our best to resynchronize and ensure forward\r
229 +            ;; progress\r
230 +            (notmuch-search-show-error\r
231 +             "%s"\r
232 +             (with-current-buffer parse-buf\r
233 +               (let ((bad (buffer-substring (line-beginning-position)\r
234 +                                            (line-end-position))))\r
235 +                 (forward-line)\r
236 +                 bad))))))\r
237 +       ;; Clear out what we've parsed\r
238 +       (with-current-buffer parse-buf\r
239 +         (delete-region (point-min) (point)))))))\r
240  \r
241  (defun notmuch-search-tag-all (&optional tag-changes)\r
242    "Add/remove tags from all messages in current search buffer.\r
243 @@ -899,10 +908,19 @@ Other optional parameters are used as follows:\r
244         (let ((proc (start-process\r
245                      "notmuch-search" buffer\r
246                      notmuch-command "search"\r
247 +                    "--format=json"\r
248                      (if oldest-first\r
249                          "--sort=oldest-first"\r
250                        "--sort=newest-first")\r
251 -                    query)))\r
252 +                    query))\r
253 +             ;; Use a scratch buffer to accumulate partial output.\r
254 +             ;; This buffer will be killed by the sentinel, which\r
255 +             ;; should be called no matter how the process dies.\r
256 +             (parse-buf (generate-new-buffer " *notmuch search parse*")))\r
257 +         (set (make-local-variable 'notmuch-search-process-state) 'begin)\r
258 +         (set (make-local-variable 'notmuch-search-json-parser)\r
259 +              (notmuch-json-create-parser parse-buf))\r
260 +         (process-put proc 'parse-buf parse-buf)\r
261           (set-process-sentinel proc 'notmuch-search-process-sentinel)\r
262           (set-process-filter proc 'notmuch-search-process-filter)\r
263           (set-process-query-on-exit-flag proc nil))))\r
264 diff --git a/test/emacs b/test/emacs\r
265 index 293b12a..afe35ba 100755\r
266 --- a/test/emacs\r
267 +++ b/test/emacs\r
268 @@ -36,7 +36,6 @@ test_emacs '(notmuch-search "tag:inbox")\r
269  test_expect_equal_file OUTPUT $EXPECTED/notmuch-search-tag-inbox\r
270  \r
271  test_begin_subtest "Incremental parsing of search results"\r
272 -test_subtest_known_broken\r
273  test_emacs "(ad-enable-advice 'notmuch-search-process-filter 'around 'pessimal)\r
274             (ad-activate 'notmuch-search-process-filter)\r
275             (notmuch-search \"tag:inbox\")\r
276 -- \r
277 1.7.10\r
278 \r