--- /dev/null
+Return-Path: <amdragon@mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id 5A293431FAF\r
+ for <notmuch@notmuchmail.org>; Sun, 29 Jul 2012 18:35:16 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+ tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id SSDr-+mrrIuW for <notmuch@notmuchmail.org>;\r
+ Sun, 29 Jul 2012 18:35:12 -0700 (PDT)\r
+Received: from dmz-mailsec-scanner-6.mit.edu (DMZ-MAILSEC-SCANNER-6.MIT.EDU\r
+ [18.7.68.35])\r
+ by olra.theworths.org (Postfix) with ESMTP id 354A3431FAE\r
+ for <notmuch@notmuchmail.org>; Sun, 29 Jul 2012 18:35:12 -0700 (PDT)\r
+X-AuditID: 12074423-b7f396d0000008f4-7a-5015e4cd8a59\r
+Received: from mailhub-auth-3.mit.edu ( [18.9.21.43])\r
+ by dmz-mailsec-scanner-6.mit.edu (Symantec Messaging Gateway) with SMTP\r
+ id 07.13.02292.DC4E5105; Sun, 29 Jul 2012 21:35:09 -0400 (EDT)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+ by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id q6U1Z8nq005853; \r
+ Sun, 29 Jul 2012 21:35:09 -0400\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+ (authenticated bits=0)\r
+ (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+ by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q6U1Z7C6029430\r
+ (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+ Sun, 29 Jul 2012 21:35:08 -0400 (EDT)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)\r
+ (envelope-from <amdragon@MIT.EDU>)\r
+ id 1Svet0-0000Gh-Vu; Sun, 29 Jul 2012 21:35:07 -0400\r
+Date: Sun, 29 Jul 2012 21:35:06 -0400\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: Mark Walters <markwalters1009@gmail.com>\r
+Subject: Re: [PATCH] emacs: move async json parser to its own function\r
+Message-ID: <20120730013506.GF8502@mit.edu>\r
+References: <87k3xo85tv.fsf@qmul.ac.uk>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=us-ascii\r
+Content-Disposition: inline\r
+In-Reply-To: <87k3xo85tv.fsf@qmul.ac.uk>\r
+User-Agent: Mutt/1.5.21 (2010-09-15)\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFmpkleLIzCtJLcpLzFFi42IR4hTV1j37RDTA4OEWYYvVc3ksrt+cyezA\r
+ 5LFz1l12j2erbjEHMEVx2aSk5mSWpRbp2yVwZWzum8tW8FGnYs+8DcwNjHOVuxg5OCQETCSm\r
+ T/HtYuQEMsUkLtxbz9bFyMUhJLCPUeJv81RGCGcDo0Tj4fcsEM5JJolzNw9DOUsYJZ7OecMM\r
+ 0s8ioCox90kHE4jNJqAhsW3/ckYQW0RAR+L2oQXsIDazgLTEt9/NTCCrhQXcJD4vDQYJ8wpo\r
+ S/x9dB6sREhAXeLTnFnsEHFBiZMzn7BAtGpJ3Pj3EqwVZMzyfxwgYU6gTfe+3WADsUUFVCSm\r
+ nNzGNoFRaBaS7llIumchdC9gZF7FKJuSW6Wbm5iZU5yarFucnJiXl1qka6aXm1mil5pSuokR\r
+ FNLsLso7GP8cVDrEKMDBqMTD23lVNECINbGsuDL3EKMkB5OSKG/LDaAQX1J+SmVGYnFGfFFp\r
+ TmrxIUYJDmYlEd5r54ByvCmJlVWpRfkwKWkOFiVx3mspN/2FBNITS1KzU1MLUotgsjIcHEoS\r
+ vAHA2BUSLEpNT61Iy8wpQUgzcXCCDOcBGm4CUsNbXJCYW5yZDpE/xagoJc775TFQQgAkkVGa\r
+ B9cLSzmvGMWBXhHm9QRp5wGmK7juV0CDmYAGW0QLgQwuSURISTUwbvp5dIty+TrXStuKb91v\r
+ zfZdiTv37OMhpQ3KX8uSI/x9Vj9QfV45VUu/90tC72bzlA0T3EJ/i91oEmFQmMIuH1eYaxn1\r
+ fWeRrLywQIrv/Pnh3x9vKj3wJGJjUN0Ub95VDzfuFdrivvzpem2dnX6tJ7nqDyz/Jr/jf7KA\r
+ mFXHnNufpH7VTwpdqsRSnJFoqMVcVJwIAHYDyncUAwAA\r
+Cc: notmuch@notmuchmail.org\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Mon, 30 Jul 2012 01:35:16 -0000\r
+\r
+This seems like a good idea, but as a generic interface, this seems\r
+suboptimal. In particular, it seems odd that a parsing function would\r
+have to know about a process and require the caller to set up various\r
+process properties and buffer-local variables. What about something\r
+dedicated to incrementally parsing lists, like the async parser but\r
+more specialized? Something along the lines of,\r
+\r
+(defun notmuch-json-parse-partial-list (result-function error-function \r
+ &optional buffer)\r
+ "Parse a partial JSON list from BUFFER (or the current buffer).\r
+\r
+This function consumes a JSON list from BUFFER, applying\r
+RESULT-FUNCTION to each complete value in the list. It operates\r
+incrementally and should be called whenever the buffer has been\r
+extended with additional data.\r
+\r
+If there is a syntax error, this will attempt to resynchronize with\r
+the input and will apply ERROR-FUNCTION to any input that was\r
+skipped.\r
+\r
+This calls RESULT-FUNCTION and ERROR-FUNCTION with the same current\r
+buffer as this function is called with (that is, this function does\r
+not visibly switch to BUFFER)."\r
+ ...)\r
+\r
+This could use buffer-local variables for tracking the async parser\r
+state as well its own state. It could even set these up automatically\r
+when called for the first time in a buffer without them set, making it\r
+very DWIM. It would clearly require a little more help from the\r
+caller for process management than your patch does (and a little less\r
+for parser setup), but I think the genericity would be worth it.\r
+\r
+Quoth Mark Walters on Jul 28 at 12:48 pm:\r
+> \r
+> We separate out the json parser into its own function. \r
+> ---\r
+> \r
+> Hi\r
+> \r
+> Notmuch pick uses the new asynchronous json parser and the code to do so\r
+> is almost identical to that for the search mode. Thus separate out the\r
+> parsing in search mode into a more general function that can easily be\r
+> used by both pick and search.\r
+> \r
+> This saves nearly 50 lines of duplicated code in notmuch-pick.el.\r
+> \r
+> The function notmuch-json-async-parse should probably be move in\r
+> notmuch-lib but that can be a follow on patch.\r
+> \r
+> Best wishes\r
+> \r
+> Mark\r
+> \r
+> emacs/notmuch.el | 46 ++++++++++++++++++++++++++++++++++++----------\r
+> 1 files changed, 36 insertions(+), 10 deletions(-)\r
+> \r
+> diff --git a/emacs/notmuch.el b/emacs/notmuch.el\r
+> index fd1836f..ee01028 100644\r
+> --- a/emacs/notmuch.el\r
+> +++ b/emacs/notmuch.el\r
+> @@ -816,7 +816,32 @@ non-authors is found, assume that all of the authors match."\r
+> "Incremental JSON parser for the search process filter.")\r
+> \r
+> (defun notmuch-search-process-filter (proc string)\r
+> - "Process and filter the output of \"notmuch search\""\r
+> + "Process and filter the output of \"notmuch search\" using the asynchronous parser."\r
+> + (setq notmuch-search-process-state\r
+> + (notmuch-json-async-parse proc\r
+> + string\r
+> + notmuch-search-process-state\r
+> + notmuch-search-json-parser\r
+> + 'notmuch-search-show-result\r
+> + 'notmuch-search-show-error)))\r
+> +\r
+> +(defun notmuch-json-async-parse (proc string process-state parser result-function error-function)\r
+> + "Process and filter the output using the asynchronous parser.\r
+> +\r
+> +This function steps into the first level of JSON nesting and then\r
+> +applies RESULT-FUNCTION to each complete JSON object as it comes\r
+> +in.\r
+> +\r
+> +PROC is the process: it should have a results buffer as\r
+> +process-buffer and a 'parse-buf for the incoming json.\r
+> +PROCESS-STATE the current state of filter process\r
+> +STRING the incoming data\r
+> +PARSER the parser\r
+> +RESULT-FUNCTION a function to call on complete pieces of json\r
+> +ERROR-FUNCTION the function to call on errors\r
+> +\r
+> +The function returns the new PROCESS-STATE"\r
+> +\r
+> (let ((results-buf (process-buffer proc))\r
+> (parse-buf (process-get proc 'parse-buf))\r
+> (inhibit-read-only t)\r
+> @@ -831,28 +856,28 @@ non-authors is found, assume that all of the authors match."\r
+> (with-current-buffer results-buf\r
+> (while (not done)\r
+> (condition-case nil\r
+> - (case notmuch-search-process-state\r
+> + (case process-state\r
+> ((begin)\r
+> ;; Enter the results list\r
+> (if (eq (notmuch-json-begin-compound\r
+> - notmuch-search-json-parser) 'retry)\r
+> + parser) 'retry)\r
+> (setq done t)\r
+> - (setq notmuch-search-process-state 'result)))\r
+> + (setq process-state 'result)))\r
+> ((result)\r
+> ;; Parse a result\r
+> - (let ((result (notmuch-json-read notmuch-search-json-parser)))\r
+> + (let ((result (notmuch-json-read parser)))\r
+> (case result\r
+> ((retry) (setq done t))\r
+> - ((end) (setq notmuch-search-process-state 'end))\r
+> - (otherwise (notmuch-search-show-result result)))))\r
+> + ((end) (setq process-state 'end))\r
+> + (otherwise (funcall result-function result)))))\r
+> ((end)\r
+> ;; Any trailing data is unexpected\r
+> - (notmuch-json-eof notmuch-search-json-parser)\r
+> + (notmuch-json-eof parser)\r
+> (setq done t)))\r
+> (json-error\r
+> ;; Do our best to resynchronize and ensure forward\r
+> ;; progress\r
+> - (notmuch-search-show-error\r
+> + (funcall error-function\r
+> "%s"\r
+> (with-current-buffer parse-buf\r
+> (let ((bad (buffer-substring (line-beginning-position)\r
+> @@ -861,7 +886,8 @@ non-authors is found, assume that all of the authors match."\r
+> bad))))))\r
+> ;; Clear out what we've parsed\r
+> (with-current-buffer parse-buf\r
+> - (delete-region (point-min) (point)))))))\r
+> + (delete-region (point-min) (point))))\r
+> + process-state)))\r
+> \r
+> (defun notmuch-search-tag-all (&optional tag-changes)\r
+> "Add/remove tags from all messages in current search buffer.\r