[PATCH 8/8] emacs: Switch from text to JSON format for search results

Austin Clements amdragon at MIT.EDU
Thu Jul 5 11:58:26 PDT 2012


Quoth Mark Walters on Jul 05 at  9:37 am:
> On Tue, 03 Jul 2012, Austin Clements <amdragon at MIT.EDU> wrote:
> > The JSON format eliminates the complex escaping issues that have
> > plagued the text search format.  This uses the incremental JSON parser
> > so that, like the text parser, it can output search results
> > incrementally.
> >
> > This slows down the parser by about ~4X, but puts us in a good
> > position to optimize either by improving the JSON parser (evidence
> > suggests this can reduce the overhead to ~40% over the text format) or
> > by switching to S-expressions (evidence suggests this will more than
> > double performance over the text parser).  [1]
> >
> > This also fixes the incremental search parsing test.
> >
> > [1] id:"20110720205007.GB21316 at mit.edu"
> > ---
> >  emacs/notmuch.el |  113 ++++++++++++++++++++++++++++++++----------------------
> >  test/emacs       |    1 -
> >  2 files changed, 67 insertions(+), 47 deletions(-)
> >
> > diff --git a/emacs/notmuch.el b/emacs/notmuch.el
> > index 084cec6..2a09a98 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)))
> > @@ -757,45 +752,62 @@ 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
> > +		 (with-current-buffer parse-buf
> > +		   (skip-chars-forward " \t\r\n")
> > +		   (if (eobp)
> > +		       (setq done t)
> > +		     (signal 'json-error nil)))))
> 
> This looks good to me. Would it make sense to put the "Any trailing
> data" as a tiny function in notmuch-lib? something like 
> 
> (defun notmuch-json-assert-end-of-data ()
>    (skip-chars-forward " \t\r\n") 
>    (if (eobp)
>        (setq done t) 
>      (signal 'json-error nil)))

Also a good idea.

Thanks for the review!  I'll be sending v2 shortly.

> Best wishes
> 
> Mark


More information about the notmuch mailing list