--- /dev/null
+Return-Path: <m.walters@qmul.ac.uk>\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 AA00F431E82\r
+ for <notmuch@notmuchmail.org>; Wed, 15 Feb 2012 01:26:04 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -1.098\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-1.098 tagged_above=-999 required=5\r
+ tests=[DKIM_ADSP_CUSTOM_MED=0.001, FREEMAIL_FROM=0.001,\r
+ NML_ADSP_CUSTOM_MED=1.2, RCVD_IN_DNSWL_MED=-2.3] 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 uTOuW1awQfrP for <notmuch@notmuchmail.org>;\r
+ Wed, 15 Feb 2012 01:26:03 -0800 (PST)\r
+Received: from mail2.qmul.ac.uk (mail2.qmul.ac.uk [138.37.6.6])\r
+ (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+ (No client certificate requested)\r
+ by olra.theworths.org (Postfix) with ESMTPS id 3B049431E62\r
+ for <notmuch@notmuchmail.org>; Wed, 15 Feb 2012 01:26:03 -0800 (PST)\r
+Received: from smtp.qmul.ac.uk ([138.37.6.40])\r
+ by mail2.qmul.ac.uk with esmtp (Exim 4.71)\r
+ (envelope-from <m.walters@qmul.ac.uk>)\r
+ id 1Rxb7e-0001MZ-9d; Wed, 15 Feb 2012 09:25:58 +0000\r
+Received: from 94-192-233-223.zone6.bethere.co.uk ([94.192.233.223]\r
+ helo=localhost)\r
+ by smtp.qmul.ac.uk with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69)\r
+ (envelope-from <m.walters@qmul.ac.uk>)\r
+ id 1Rxb7d-0003Ob-O6; Wed, 15 Feb 2012 09:25:58 +0000\r
+From: Mark Walters <markwalters1009@gmail.com>\r
+To: Austin Clements <amdragon@MIT.EDU>\r
+Subject: Re: [RFC PATCH v3 00/11] notmuch-pick: an emacs threaded message view\r
+ with split-pane\r
+In-Reply-To: <20120214152114.GQ27039@mit.edu>\r
+References: <1329072579-27340-1-git-send-email-markwalters1009@gmail.com>\r
+ <1329096015-8078-1-git-send-email-markwalters1009@gmail.com>\r
+ <87haytbnun.fsf@qmul.ac.uk> <20120214152114.GQ27039@mit.edu>\r
+User-Agent: Notmuch/0.11.1+206~g3b67774 (http://notmuchmail.org) Emacs/23.2.1\r
+ (i486-pc-linux-gnu)\r
+Date: Wed, 15 Feb 2012 09:27:21 +0000\r
+Message-ID: <87fwecbg5i.fsf@qmul.ac.uk>\r
+MIME-Version: 1.0\r
+Content-Type: multipart/mixed; boundary="=-=-="\r
+X-Sender-Host-Address: 94.192.233.223\r
+X-QM-SPAM-Info: Sender has good ham record. :)\r
+X-QM-Body-MD5: 842cd08fc917f2a3e061ed51a47e85d2 (of first 20000 bytes)\r
+X-SpamAssassin-Score: -1.8\r
+X-SpamAssassin-SpamBar: -\r
+X-SpamAssassin-Report: The QM spam filters have analysed this message to\r
+ determine if it is\r
+ spam. We require at least 5.0 points to mark a message as spam.\r
+ This message scored -1.8 points.\r
+ Summary of the scoring: \r
+ * -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/,\r
+ * medium trust\r
+ * [138.37.6.40 listed in list.dnswl.org]\r
+ * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\r
+ provider * (markwalters1009[at]gmail.com)\r
+ * -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay\r
+ * domain\r
+ * 0.0 T_TVD_MIME_NO_HEADERS BODY: T_TVD_MIME_NO_HEADERS\r
+ * 0.5 AWL AWL: From: address is in the auto white-list\r
+X-QM-Scan-Virus: ClamAV says the message is clean\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: Wed, 15 Feb 2012 09:26:04 -0000\r
+\r
+--=-=-=\r
+\r
+On Tue, 14 Feb 2012 10:21:14 -0500, Austin Clements <amdragon@MIT.EDU> wrote:\r
+> Quoth Mark Walters on Feb 14 at 12:28 pm:\r
+> > Finally, if notmuch-pick were able to do work asynchronously (as\r
+> > notmuch-search does now) then I think all the speed concerns would go\r
+> > away. However, I am not sure how to do incremental json parsing.\r
+> \r
+> For JSON search, at least, I think we've concluded that it should put\r
+> newlines at strategic places in the JSON output (and never anywhere\r
+> else) so that it's easy for a consumer to know when it has a complete\r
+> JSON object and hand it to the JSON parser. E.g.,\r
+> \r
+> [{"thread":"X", timestamp: 42, ...}\r
+> ,{"thread":"Y", timestamp: 24, ...}\r
+> ]\r
+> \r
+> This "framed JSON" works really well for the flat output of search.\r
+> It's obviously trickier to apply to show's hierarchical output. But,\r
+> perhaps this will inspire you.\r
+> \r
+> (One possibility: we could rework show's output to be non-hierarchical\r
+> and use the above framing; that is, it could be a sequence of message\r
+> objects that each indicate which earlier message object they're a\r
+> reply to, probably restricted to DFS order.)\r
+\r
+Here is a (very early version) of an async notmuch-pick based on the\r
+above idea. This is on top of the previous patch series. There are two\r
+attached patches: the first is a very small patch to notmuch-show.c to\r
+print newlines between threads (since this is sufficient framing for\r
+notmuch-pick; obviously it does not help notmuch-show at all).\r
+\r
+The second patch implements the async handling in notmuch-pick.el. This\r
+is probably very inefficient, and doubtless has significant bugs, but it\r
+seems to mostly work and does display the first page of results almost\r
+immediately even on very large queries so I think this is the right way\r
+to go.\r
+\r
+I am mostly posting it so people can play with the functionality but\r
+cleanups, bugfixes, bug reports etc are gratefully received!\r
+\r
+Best wishes\r
+\r
+Mark\r
+\r
+\r
+--=-=-=\r
+Content-Type: text/x-diff\r
+Content-Disposition: inline;\r
+ filename=0001-cli-notmuch-show-with-framing-newlines-between-threa.patch\r
+\r
+>From 5622a3ed7b486edc360ffa764756ce76f69ac032 Mon Sep 17 00:00:00 2001\r
+From: Mark Walters <markwalters1009@gmail.com>\r
+Date: Tue, 14 Feb 2012 19:33:56 +0000\r
+Subject: [PATCH 1/2] cli: notmuch-show with framing newlines between threads in JSON\r
+\r
+Add newlines between complete threads to make asynchronous parsing\r
+of the JSON easier\r
+---\r
+ notmuch-show.c | 4 ++++\r
+ 1 files changed, 4 insertions(+), 0 deletions(-)\r
+\r
+diff --git a/notmuch-show.c b/notmuch-show.c\r
+index 3f8618b..a7cc684 100644\r
+--- a/notmuch-show.c\r
++++ b/notmuch-show.c\r
+@@ -912,6 +912,7 @@ do_show_messages (void *ctx,\r
+ int first_set = 1;\r
+ \r
+ fputs (format->message_set_start, stdout);\r
++ fputs ("\n", stdout);\r
+ messages = notmuch_query_search_messages (query);\r
+ \r
+ for (;\r
+@@ -939,6 +940,7 @@ do_show_messages (void *ctx,\r
+ \r
+ fputs (format->message_set_end, stdout);\r
+ fputs (format->message_set_end, stdout);\r
++ fputs ("\n", stdout);\r
+ }\r
+ \r
+ fputs (format->message_set_end, stdout);\r
+@@ -1029,6 +1031,7 @@ do_show (void *ctx,\r
+ int first_toplevel = 1;\r
+ \r
+ fputs (format->message_set_start, stdout);\r
++ fputs ("\n", stdout);\r
+ \r
+ for (threads = notmuch_query_search_threads (query);\r
+ notmuch_threads_valid (threads);\r
+@@ -1047,6 +1050,7 @@ do_show (void *ctx,\r
+ first_toplevel = 0;\r
+ \r
+ show_messages (ctx, format, messages, 0, params);\r
++ fputs ("\n", stdout);\r
+ \r
+ notmuch_thread_destroy (thread);\r
+ \r
+-- \r
+1.7.2.3\r
+\r
+\r
+--=-=-=\r
+Content-Type: text/x-diff\r
+Content-Disposition: inline;\r
+ filename=0002-emacs-a-semi-working-async-pick.patch\r
+\r
+>From c3bcd7d11b67a15a1760505c036e760e7028b125 Mon Sep 17 00:00:00 2001\r
+From: Mark Walters <markwalters1009@gmail.com>\r
+Date: Mon, 13 Feb 2012 23:31:45 +0000\r
+Subject: [PATCH 2/2] emacs: a semi-working async pick\r
+\r
+---\r
+ emacs/notmuch-pick.el | 111 ++++++++++++++++++++++++++++++++++++++-----------\r
+ 1 files changed, 87 insertions(+), 24 deletions(-)\r
+\r
+diff --git a/emacs/notmuch-pick.el b/emacs/notmuch-pick.el\r
+index 46eb720..f944d79 100644\r
+--- a/emacs/notmuch-pick.el\r
++++ b/emacs/notmuch-pick.el\r
+@@ -287,7 +287,7 @@ This command toggles the sort order for the current search."\r
+ (query-context notmuch-pick-query-context)\r
+ (buffer-name notmuch-pick-buffer-name))\r
+ (erase-buffer)\r
+- (notmuch-pick-worker thread-id query-context buffer-name)))\r
++ (notmuch-pick-worker thread-id query-context (get-buffer buffer-name))))\r
+ \r
+ (defun notmuch-pick-toggle-view ()\r
+ "Toggle showing threads or as isolated messages."\r
+@@ -531,34 +531,97 @@ Complete list of currently available key bindings:\r
+ (setq buffer-read-only t\r
+ truncate-lines t))\r
+ \r
+-(defun notmuch-pick-worker (thread-id &optional query-context buffer-name)\r
++(defvar notmuch-pick-process-filter-data nil\r
++ "Data that has not yet been processed.")\r
++(make-variable-buffer-local 'notmuch-pick-process-filter-data)\r
++\r
++(defun notmuch-pick-process-sentinel (proc msg)\r
++ "Add a message to let user know when \"notmuch pick\" exits"\r
++ (let ((buffer (process-buffer proc))\r
++ (status (process-status proc))\r
++ (exit-status (process-exit-status proc))\r
++ (never-found-target-thread nil))\r
++ (if (memq status '(exit signal))\r
++ (if (buffer-live-p buffer)\r
++ (with-current-buffer buffer\r
++ (save-excursion\r
++ (let ((inhibit-read-only t)\r
++ (atbob (bobp)))\r
++ (goto-char (point-max))\r
++ (if (eq status 'signal)\r
++ (insert "Incomplete search results (pick process was killed).\n"))\r
++ (when (eq status 'exit)\r
++ (if (not (string= notmuch-pick-process-filter-data "\n]"))\r
++ (insert (concat "Error: Unexpected output from notmuch pick:\n"\r
++ notmuch-pick-process-filter-data)))\r
++ (insert "End of search results.")\r
++ (unless (= exit-status 0)\r
++ (insert (format " (process returned %d)" exit-status)))\r
++ (insert "\n")))))))))\r
++\r
++(defun notmuch-pick-process-filter (proc string)\r
++ "Process and filter the output of \"notmuch show\" (for pick)"\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
++ (string (concat notmuch-pick-process-filter-data string)))\r
++ (while (string-match "\n.*\n" string)\r
++ (let ((frame (match-string 0 string))\r
++ (frame-end (match-end 0)))\r
++ (when (or (= (elt frame 1) ?\[) (= (elt frame 1) ?,))\r
++ (let (json)\r
++ (with-temp-buffer\r
++ (let ((json-object-type 'plist)\r
++ (json-array-type 'list)\r
++ (json-false 'nil))\r
++ (insert frame)\r
++ (goto-char (point-min))\r
++ (search-forward "[")\r
++ (backward-char)\r
++ (setq json (json-read))))\r
++ (goto-char (point-max))\r
++ (notmuch-pick-insert-forest (list json))))\r
++ (setq string (substring string (- frame-end 1)))))\r
++ (setq notmuch-pick-process-filter-data string)))))))\r
++\r
++(defun notmuch-pick-worker (thread-id &optional query-context buffer)\r
+ (interactive)\r
+ (notmuch-pick-mode)\r
+ (setq notmuch-pick-thread-id thread-id)\r
+ (setq notmuch-pick-query-context query-context)\r
+- (setq notmuch-pick-buffer-name buffer-name)\r
++ (setq notmuch-pick-buffer-name (buffer-name buffer))\r
+ \r
+ (erase-buffer)\r
+ (goto-char (point-min))\r
+- (save-excursion\r
+- (let* ((basic-args (list thread-id))\r
+- (args (if query-context\r
+- (append (list "\'") basic-args (list "and (" query-context ")\'"))\r
+- (append (list "\'") basic-args (list "\'"))))\r
+- (message-arg (if notmuch-pick-view-just-messages\r
+- "--thread=none"\r
+- "--thread=entire"))\r
+- (sort-arg (if notmuch-pick-oldest-first\r
+- "--sort=oldest-first"\r
+- "--sort=newest-first")))\r
+-\r
+- (notmuch-pick-insert-forest (notmuch-query-get-threads args "--headers-only" message-arg sort-arg))\r
+- ;; If the query context reduced the results to nothing, run\r
+- ;; the basic query.\r
+- (when (and (eq (buffer-size) 0)\r
+- query-context)\r
+- (notmuch-pick-insert-forest\r
+- (notmuch-query-get-threads basic-args message-arg sort-arg))))))\r
++ (let* (args\r
++ (basic-args thread-id)\r
++ (search-args (concat\r
++ basic-args (if query-context (concat " and (" query-context ")"))))\r
++ (message-arg (if notmuch-pick-view-just-messages\r
++ "--thread=none"\r
++ "--thread=entire"))\r
++ (sort-arg (if notmuch-pick-oldest-first\r
++ "--sort=oldest-first"\r
++ "--sort=newest-first"))\r
++ (proc (start-process\r
++ "notmuch-pick" buffer\r
++ notmuch-command "show" "--headers-only" "--format=json"\r
++ message-arg sort-arg search-args)))\r
++ (set-process-sentinel proc 'notmuch-pick-process-sentinel)\r
++ (set-process-filter proc 'notmuch-pick-process-filter)\r
++ (set-process-query-on-exit-flag proc nil)))\r
++;; (notmuch-pick-insert-forest (notmuch-query-get-threads args "--headers-only" message-arg sort-arg))\r
++;; (message "time3 (end forest): %s" (current-time))\r
++;; If the query context reduced the results to nothing, run\r
++;; the basic query.\r
++;; (when (and (eq (buffer-size) 0)\r
++;; query-context)\r
++;; (notmuch-pick-insert-forest\r
++;; (notmuch-query-get-threads basic-args message-arg sort-arg))))))\r
+ \r
+ (defun notmuch-pick (&optional query query-context buffer-name)\r
+ "Run notmuch pick with the given `query' and display the results"\r
+@@ -567,14 +630,14 @@ Complete list of currently available key bindings:\r
+ (setq query (notmuch-read-query "Notmuch pick: ")))\r
+ (let ((buffer (get-buffer-create (generate-new-buffer-name\r
+ (or buffer-name\r
+- (concat "*notmuch-" query "*")))))\r
++ (concat "*notmuch-pick-" query "*")))))\r
+ (inhibit-read-only t))\r
+ \r
+ (switch-to-buffer buffer)\r
+ ;; Don't track undo information for this buffer\r
+ (set 'buffer-undo-list t)\r
+ \r
+- (notmuch-pick-worker query query-context buffer-name)\r
++ (notmuch-pick-worker query query-context buffer)\r
+ \r
+ (setq truncate-lines t)))\r
+ \r
+-- \r
+1.7.2.3\r
+\r
+\r
+--=-=-=--\r