--- /dev/null
+Return-Path: <dme@dme.org>\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 A2480431FB5\r
+ for <notmuch@notmuchmail.org>; Mon, 29 Nov 2010 02:30:44 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5\r
+ tests=[RCVD_IN_DNSWL_NONE=-0.0001] 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 SgaAQyo4cs5B for <notmuch@notmuchmail.org>;\r
+ Mon, 29 Nov 2010 02:30:43 -0800 (PST)\r
+Received: from mail-wy0-f181.google.com (mail-wy0-f181.google.com\r
+ [74.125.82.181])\r
+ by olra.theworths.org (Postfix) with ESMTP id 713B441A547\r
+ for <notmuch@notmuchmail.org>; Mon, 29 Nov 2010 02:30:43 -0800 (PST)\r
+Received: by wyf22 with SMTP id 22so4656184wyf.26\r
+ for <notmuch@notmuchmail.org>; Mon, 29 Nov 2010 02:30:41 -0800 (PST)\r
+Received: by 10.216.239.199 with SMTP id c49mr4762944wer.12.1291026641780;\r
+ Mon, 29 Nov 2010 02:30:41 -0800 (PST)\r
+Received: from ut.hh.sledj.net (host81-149-164-25.in-addr.btopenworld.com\r
+ [81.149.164.25])\r
+ by mx.google.com with ESMTPS id o43sm2294205weq.47.2010.11.29.02.30.40\r
+ (version=TLSv1/SSLv3 cipher=RC4-MD5);\r
+ Mon, 29 Nov 2010 02:30:40 -0800 (PST)\r
+Received: by ut.hh.sledj.net (Postfix, from userid 1000)\r
+ id E5629594245; Mon, 29 Nov 2010 10:30:07 +0000 (GMT)\r
+From: David Edmondson <dme@dme.org>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 3/3] emacs: Use JSON output for search.\r
+Date: Mon, 29 Nov 2010 10:29:59 +0000\r
+Message-Id: <1291026599-14795-4-git-send-email-dme@dme.org>\r
+X-Mailer: git-send-email 1.7.2.3\r
+In-Reply-To: <1291026599-14795-1-git-send-email-dme@dme.org>\r
+References: <1291026599-14795-1-git-send-email-dme@dme.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, 29 Nov 2010 10:30:44 -0000\r
+\r
+Switch to using the JSON format output of `notmuch search' to avoid\r
+problems parsing the output text. In particular, a comma in the name\r
+of an author would confuse the previous implementation.\r
+---\r
+ emacs/notmuch.el | 114 +++++++++++++++++++++++++++++++++++++-----------------\r
+ 1 files changed, 78 insertions(+), 36 deletions(-)\r
+\r
+diff --git a/emacs/notmuch.el b/emacs/notmuch.el\r
+index 5933747..bde8c47 100644\r
+--- a/emacs/notmuch.el\r
++++ b/emacs/notmuch.el\r
+@@ -50,6 +50,7 @@\r
+ (eval-when-compile (require 'cl))\r
+ (require 'mm-view)\r
+ (require 'message)\r
++(require 'json)\r
+ \r
+ (require 'notmuch-lib)\r
+ (require 'notmuch-show)\r
+@@ -698,40 +699,81 @@ foreground and blue background."\r
+ do (notmuch-search-insert-field field date count authors subject tags)))\r
+ (insert "\n"))\r
+ \r
++(defun notmuch-search-process-insert-object (object)\r
++ (let* ((thread-id (concat "thread:" (cdr (assoc 'thread object))))\r
++ (date (format "%12s" (cdr (assoc 'date_relative object))))\r
++ (count (format "[%d/%d]"\r
++ (cdr (assoc 'matched object))\r
++ (cdr (assoc 'total object))))\r
++ (authors (cdr (assoc 'authors object)))\r
++ (subject (cdr (assoc 'subject object)))\r
++ (tag-list (cdr (assoc 'tags object)))\r
++ (tags (mapconcat 'identity tag-list " "))\r
++ (beg (point-marker)))\r
++ (notmuch-search-show-result date count authors subject tags)\r
++ (notmuch-search-color-line beg (point-marker) tag-list)\r
++ (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id)\r
++ (put-text-property beg (point-marker) 'notmuch-search-authors authors)\r
++ (put-text-property beg (point-marker) 'notmuch-search-subject subject)))\r
++\r
++(defvar notmuch-search-parse-start nil)\r
++(make-variable-buffer-local 'notmuch-show-parse-start)\r
++\r
++(defun notmuch-search-process-insert (proc buffer string)\r
++ (with-current-buffer buffer\r
++ (let ((inhibit-read-only t)\r
++ (inhibit-redisplay t)\r
++ ;; Vectors are not as useful here.\r
++ (json-array-type 'list)\r
++ object)\r
++ (save-excursion\r
++ ;; Insert the text, advancing the process marker\r
++ (goto-char (point-max))\r
++ (insert string)\r
++ (set-marker (process-mark proc) (point)))\r
++\r
++ (save-excursion\r
++ (goto-char notmuch-search-parse-start)\r
++ (condition-case nil\r
++ (while\r
++ (cond\r
++ ;; Opening bracket or comma separator between\r
++ ;; objects.\r
++ ((or (char-equal (json-peek) ?\[)\r
++ (char-equal (json-peek) ?\,))\r
++ (json-advance)\r
++ (delete-region notmuch-search-parse-start (point))\r
++ t)\r
++\r
++ ;; Closing array.\r
++ ((char-equal (json-peek) ?\])\r
++ ;; Consume both the closing bracket and any trailing\r
++ ;; whitespace (typically a carriage return).\r
++ (json-advance)\r
++ (json-skip-whitespace)\r
++ (delete-region notmuch-search-parse-start (point))\r
++ nil)\r
++\r
++ ;; Single object.\r
++ ((setq object (json-read-object))\r
++ ;; Delete the object that we consumed.\r
++ (delete-region notmuch-search-parse-start (point))\r
++ ;; Insert the corresponding results.\r
++ (notmuch-search-process-insert-object object)\r
++ t))\r
++ ;; Consume any white space between terms.\r
++ (let ((p (point)))\r
++ (json-skip-whitespace)\r
++ (delete-region p (point)))\r
++ ;; Remember where we got up to.\r
++ (setq notmuch-search-parse-start (point)))\r
++ (error nil))))))\r
++\r
+ (defun notmuch-search-process-filter (proc string)\r
+- "Process and filter the output of \"notmuch search\""\r
+- (let ((buffer (process-buffer proc))\r
+- (found-target nil))\r
++ "Process and filter the output of `notmuch search'."\r
++ (let ((buffer (process-buffer proc)))\r
+ (if (buffer-live-p buffer)\r
+- (with-current-buffer buffer\r
+- (save-excursion\r
+- (let ((line 0)\r
+- (more t)\r
+- (inhibit-read-only t))\r
+- (while more\r
+- (if (string-match "^\\(thread:[0-9A-Fa-f]*\\) \\([^][]*\\) \\(\\[[0-9/]*\\]\\) \\([^;]*\\); \\(.*\\) (\\([^()]*\\))$" string line)\r
+- (let* ((thread-id (match-string 1 string))\r
+- (date (match-string 2 string))\r
+- (count (match-string 3 string))\r
+- (authors (match-string 4 string))\r
+- (subject (match-string 5 string))\r
+- (tags (match-string 6 string))\r
+- (tag-list (if tags (save-match-data (split-string tags)))))\r
+- (goto-char (point-max))\r
+- (let ((beg (point-marker)))\r
+- (notmuch-search-show-result date count authors subject tags)\r
+- (notmuch-search-color-line beg (point-marker) tag-list)\r
+- (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id)\r
+- (put-text-property beg (point-marker) 'notmuch-search-authors authors)\r
+- (put-text-property beg (point-marker) 'notmuch-search-subject subject)\r
+- (if (string= thread-id notmuch-search-target-thread)\r
+- (progn\r
+- (set 'found-target beg)\r
+- (set 'notmuch-search-target-thread "found"))))\r
+- (set 'line (match-end 0)))\r
+- (set 'more nil)))))\r
+- (if found-target\r
+- (goto-char found-target)))\r
++ (notmuch-search-process-insert proc buffer string)\r
+ (delete-process proc))))\r
+ \r
+ (defun notmuch-search-operate-all (action)\r
+@@ -806,15 +848,15 @@ The optional parameters are used as follows:\r
+ (set 'notmuch-search-continuation continuation)\r
+ (let ((proc (get-buffer-process (current-buffer)))\r
+ (inhibit-read-only t))\r
+- (if proc\r
+- (error "notmuch search process already running for query `%s'" query)\r
+- )\r
++ (when proc\r
++ (error "notmuch search process already running for query `%s'" query))\r
+ (erase-buffer)\r
+- (goto-char (point-min))\r
++ (setq notmuch-search-parse-start (point-min))\r
+ (save-excursion\r
+ (let ((proc (start-process\r
+ "notmuch-search" buffer\r
+ notmuch-command "search"\r
++ "--format=json"\r
+ (if oldest-first\r
+ "--sort=oldest-first"\r
+ "--sort=newest-first")\r
+-- \r
+1.7.2.3\r
+\r