Switch to using the JSON format output of `notmuch search' to avoid problems parsing the output text. In particular, a comma in the name of an author would confuse the previous implementation. --- emacs/notmuch.el | 114 +++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 78 insertions(+), 36 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 5933747..bde8c47 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -50,6 +50,7 @@ (eval-when-compile (require 'cl)) (require 'mm-view) (require 'message) +(require 'json) (require 'notmuch-lib) (require 'notmuch-show) @@ -698,40 +699,81 @@ foreground and blue background." do (notmuch-search-insert-field field date count authors subject tags))) (insert "\n")) +(defun notmuch-search-process-insert-object (object) + (let* ((thread-id (concat "thread:" (cdr (assoc 'thread object)))) + (date (format "%12s" (cdr (assoc 'date_relative object)))) + (count (format "[%d/%d]" + (cdr (assoc 'matched object)) + (cdr (assoc 'total object)))) + (authors (cdr (assoc 'authors object))) + (subject (cdr (assoc 'subject object))) + (tag-list (cdr (assoc 'tags object))) + (tags (mapconcat 'identity tag-list " ")) + (beg (point-marker))) + (notmuch-search-show-result date count authors subject tags) + (notmuch-search-color-line beg (point-marker) tag-list) + (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id) + (put-text-property beg (point-marker) 'notmuch-search-authors authors) + (put-text-property beg (point-marker) 'notmuch-search-subject subject))) + +(defvar notmuch-search-parse-start nil) +(make-variable-buffer-local 'notmuch-show-parse-start) + +(defun notmuch-search-process-insert (proc buffer string) + (with-current-buffer buffer + (let ((inhibit-read-only t) + (inhibit-redisplay t) + ;; Vectors are not as useful here. + (json-array-type 'list) + object) + (save-excursion + ;; Insert the text, advancing the process marker + (goto-char (point-max)) + (insert string) + (set-marker (process-mark proc) (point))) + + (save-excursion + (goto-char notmuch-search-parse-start) + (condition-case nil + (while + (cond + ;; Opening bracket or comma separator between + ;; objects. + ((or (char-equal (json-peek) ?\[) + (char-equal (json-peek) ?\,)) + (json-advance) + (delete-region notmuch-search-parse-start (point)) + t) + + ;; Closing array. + ((char-equal (json-peek) ?\]) + ;; Consume both the closing bracket and any trailing + ;; whitespace (typically a carriage return). + (json-advance) + (json-skip-whitespace) + (delete-region notmuch-search-parse-start (point)) + nil) + + ;; Single object. + ((setq object (json-read-object)) + ;; Delete the object that we consumed. + (delete-region notmuch-search-parse-start (point)) + ;; Insert the corresponding results. + (notmuch-search-process-insert-object object) + t)) + ;; Consume any white space between terms. + (let ((p (point))) + (json-skip-whitespace) + (delete-region p (point))) + ;; Remember where we got up to. + (setq notmuch-search-parse-start (point))) + (error nil)))))) + (defun notmuch-search-process-filter (proc string) - "Process and filter the output of \"notmuch search\"" - (let ((buffer (process-buffer proc)) - (found-target nil)) + "Process and filter the output of `notmuch search'." + (let ((buffer (process-buffer proc))) (if (buffer-live-p buffer) - (with-current-buffer buffer - (save-excursion - (let ((line 0) - (more t) - (inhibit-read-only t)) - (while more - (if (string-match "^\\(thread:[0-9A-Fa-f]*\\) \\([^][]*\\) \\(\\[[0-9/]*\\]\\) \\([^;]*\\); \\(.*\\) (\\([^()]*\\))$" string line) - (let* ((thread-id (match-string 1 string)) - (date (match-string 2 string)) - (count (match-string 3 string)) - (authors (match-string 4 string)) - (subject (match-string 5 string)) - (tags (match-string 6 string)) - (tag-list (if tags (save-match-data (split-string tags))))) - (goto-char (point-max)) - (let ((beg (point-marker))) - (notmuch-search-show-result date count authors subject tags) - (notmuch-search-color-line beg (point-marker) tag-list) - (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id) - (put-text-property beg (point-marker) 'notmuch-search-authors authors) - (put-text-property beg (point-marker) 'notmuch-search-subject subject) - (if (string= thread-id notmuch-search-target-thread) - (progn - (set 'found-target beg) - (set 'notmuch-search-target-thread "found")))) - (set 'line (match-end 0))) - (set 'more nil))))) - (if found-target - (goto-char found-target))) + (notmuch-search-process-insert proc buffer string) (delete-process proc)))) (defun notmuch-search-operate-all (action) @@ -806,15 +848,15 @@ The optional parameters are used as follows: (set 'notmuch-search-continuation continuation) (let ((proc (get-buffer-process (current-buffer))) (inhibit-read-only t)) - (if proc - (error "notmuch search process already running for query `%s'" query) - ) + (when proc + (error "notmuch search process already running for query `%s'" query)) (erase-buffer) - (goto-char (point-min)) + (setq notmuch-search-parse-start (point-min)) (save-excursion (let ((proc (start-process "notmuch-search" buffer notmuch-command "search" + "--format=json" (if oldest-first "--sort=oldest-first" "--sort=newest-first") -- 1.7.2.3