aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--emacs/notmuch.el110
-rwxr-xr-xtest/emacs1
2 files changed, 64 insertions, 47 deletions
diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index dfeaf35..fabb7c0 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -60,7 +60,7 @@
(require 'notmuch-message)
(defcustom notmuch-search-result-format
- `(("date" . "%s ")
+ `(("date" . "%12s ")
("count" . "%-7s ")
("authors" . "%-20s ")
("subject" . "%s ")
@@ -557,17 +557,14 @@ This function advances the next thread when finished."
(notmuch-search-tag '("-inbox"))
(notmuch-search-next-thread))
-(defvar notmuch-search-process-filter-data nil
- "Data that has not yet been processed.")
-(make-variable-buffer-local 'notmuch-search-process-filter-data)
-
(defun notmuch-search-process-sentinel (proc msg)
"Add a message to let user know when \"notmuch search\" exits"
(let ((buffer (process-buffer proc))
(status (process-status proc))
(exit-status (process-exit-status proc))
(never-found-target-thread nil))
- (if (memq status '(exit signal))
+ (when (memq status '(exit signal))
+ (kill-buffer (process-get proc 'parse-buf))
(if (buffer-live-p buffer)
(with-current-buffer buffer
(save-excursion
@@ -577,8 +574,6 @@ This function advances the next thread when finished."
(if (eq status 'signal)
(insert "Incomplete search results (search process was killed).\n"))
(when (eq status 'exit)
- (if notmuch-search-process-filter-data
- (insert (concat "Error: Unexpected output from notmuch search:\n" notmuch-search-process-filter-data)))
(insert "End of search results.")
(unless (= exit-status 0)
(insert (format " (process returned %d)" exit-status)))
@@ -758,45 +753,59 @@ non-authors is found, assume that all of the authors match."
(insert (apply #'format string objects))
(insert "\n")))
+(defvar notmuch-search-process-state nil
+ "Parsing state of the search process filter.")
+
+(defvar notmuch-search-json-parser nil
+ "Incremental JSON parser for the search process filter.")
+
(defun notmuch-search-process-filter (proc string)
"Process and filter the output of \"notmuch search\""
- (let ((buffer (process-buffer proc)))
- (if (buffer-live-p buffer)
- (with-current-buffer buffer
- (let ((line 0)
- (more t)
- (inhibit-read-only t)
- (string (concat notmuch-search-process-filter-data string)))
- (setq notmuch-search-process-filter-data nil)
- (while more
- (while (and (< line (length string)) (= (elt string line) ?\n))
- (setq line (1+ line)))
- (if (string-match "^thread:\\([0-9A-Fa-f]*\\) \\([^][]*\\) \\[\\([0-9]*\\)/\\([0-9]*\\)\\] \\([^;]*\\); \\(.*\\) (\\([^()]*\\))$" string line)
- (let* ((thread-id (match-string 1 string))
- (tags-str (match-string 7 string))
- (result (list :thread thread-id
- :date_relative (match-string 2 string)
- :matched (string-to-number
- (match-string 3 string))
- :total (string-to-number
- (match-string 4 string))
- :authors (match-string 5 string)
- :subject (match-string 6 string)
- :tags (if tags-str
- (save-match-data
- (split-string tags-str))))))
- (if (/= (match-beginning 0) line)
- (notmuch-search-show-error
- (substring string line (match-beginning 0))))
- (notmuch-search-show-result result)
- (set 'line (match-end 0)))
- (set 'more nil)
- (while (and (< line (length string)) (= (elt string line) ?\n))
- (setq line (1+ line)))
- (if (< line (length string))
- (setq notmuch-search-process-filter-data (substring string line)))
- ))))
- (delete-process proc))))
+ (let ((results-buf (process-buffer proc))
+ (parse-buf (process-get proc 'parse-buf))
+ (inhibit-read-only t)
+ done)
+ (if (not (buffer-live-p results-buf))
+ (delete-process proc)
+ (with-current-buffer parse-buf
+ ;; Insert new data
+ (save-excursion
+ (goto-char (point-max))
+ (insert string)))
+ (with-current-buffer results-buf
+ (while (not done)
+ (condition-case nil
+ (case notmuch-search-process-state
+ ((begin)
+ ;; Enter the results list
+ (if (eq (notmuch-json-begin-compound
+ notmuch-search-json-parser) 'retry)
+ (setq done t)
+ (setq notmuch-search-process-state 'result)))
+ ((result)
+ ;; Parse a result
+ (let ((result (notmuch-json-read notmuch-search-json-parser)))
+ (case result
+ ((retry) (setq done t))
+ ((end) (setq notmuch-search-process-state 'end))
+ (otherwise (notmuch-search-show-result result)))))
+ ((end)
+ ;; Any trailing data is unexpected
+ (notmuch-json-eof notmuch-search-json-parser)
+ (setq done t)))
+ (json-error
+ ;; Do our best to resynchronize and ensure forward
+ ;; progress
+ (notmuch-search-show-error
+ "%s"
+ (with-current-buffer parse-buf
+ (let ((bad (buffer-substring (line-beginning-position)
+ (line-end-position))))
+ (forward-line)
+ bad))))))
+ ;; Clear out what we've parsed
+ (with-current-buffer parse-buf
+ (delete-region (point-min) (point)))))))
(defun notmuch-search-tag-all (&optional tag-changes)
"Add/remove tags from all messages in current search buffer.
@@ -899,10 +908,19 @@ Other optional parameters are used as follows:
(let ((proc (start-process
"notmuch-search" buffer
notmuch-command "search"
+ "--format=json"
(if oldest-first
"--sort=oldest-first"
"--sort=newest-first")
- query)))
+ query))
+ ;; Use a scratch buffer to accumulate partial output.
+ ;; This buffer will be killed by the sentinel, which
+ ;; should be called no matter how the process dies.
+ (parse-buf (generate-new-buffer " *notmuch search parse*")))
+ (set (make-local-variable 'notmuch-search-process-state) 'begin)
+ (set (make-local-variable 'notmuch-search-json-parser)
+ (notmuch-json-create-parser parse-buf))
+ (process-put proc 'parse-buf parse-buf)
(set-process-sentinel proc 'notmuch-search-process-sentinel)
(set-process-filter proc 'notmuch-search-process-filter)
(set-process-query-on-exit-flag proc nil))))
diff --git a/test/emacs b/test/emacs
index 293b12a..afe35ba 100755
--- a/test/emacs
+++ b/test/emacs
@@ -36,7 +36,6 @@ test_emacs '(notmuch-search "tag:inbox")
test_expect_equal_file OUTPUT $EXPECTED/notmuch-search-tag-inbox
test_begin_subtest "Incremental parsing of search results"
-test_subtest_known_broken
test_emacs "(ad-enable-advice 'notmuch-search-process-filter 'around 'pessimal)
(ad-activate 'notmuch-search-process-filter)
(notmuch-search \"tag:inbox\")