Modify the `notmuch search' JSON output to remove the outer list wrappers and item separators between elements. Add `date_relative' to the JSON search output. Replace the emacs use of the text based search output with the modified JSON output, including incremental parsing of the individual threads in the JSON. --- emacs/notmuch.el | 70 ++++++++++++++++++++++++++++++----------------------- notmuch-search.c | 8 ++++-- 2 files changed, 45 insertions(+), 33 deletions(-) Proper incremental parsing of the JSON output was hard, so I punted and got rid of the array wrappings around the search output. This may well break some other tools as a result (hence 'RFC'). Display of the thread can be a bit 'flashy' due to the incremental implementation, but it doesn't seem too off-putting on reasonable hardware - it may well be annoying on slower systems. diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 5933747..0bbdf16 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -698,40 +698,50 @@ foreground and blue background." do (notmuch-search-insert-field field date count authors subject tags))) (insert "\n")) +(defvar notmuch-search-parse-start nil) +(make-variable-buffer-local 'notmuch-show-parse-start) + (defun notmuch-search-process-filter (proc string) - "Process and filter the output of \"notmuch search\"" + "Process and filter the output of `notmuch search'." + (let ((buffer (process-buffer proc)) - (found-target nil)) + object) (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))) + (let ((inhibit-read-only t) + (inhibit-redisplay t)) + (save-excursion + ;; Insert the text, advancing the process marker + (goto-char (point-max)) + (insert string) + (set-marker (process-mark proc) (point-max))) + + (save-excursion + (condition-case nil + (progn + (goto-char notmuch-search-parse-start) + (while (and (not (eobp)) + (setq object (json-read-object))) + (forward-char) + (delete-region notmuch-search-parse-start (point)) + + (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) - (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))) + (put-text-property beg (point-marker) 'notmuch-search-subject subject)) + (setq notmuch-search-parse-start (point)))) + (error nil))))) (delete-process proc)))) (defun notmuch-search-operate-all (action) @@ -806,15 +816,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") diff --git a/notmuch-search.c b/notmuch-search.c index c628b36..1b07aa7 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -89,15 +89,15 @@ format_thread_json (const void *ctx, const char *authors, const char *subject); static const search_format_t format_json = { - "[", + "", "{", format_item_id_json, format_thread_json, "\"tags\": [", "\"%s\"", ", ", - "]", ",\n", + "]", "\n", "}", - "]\n", + "\n", }; static void @@ -152,12 +152,14 @@ format_thread_json (const void *ctx, printf ("\"thread\": %s,\n" "\"timestamp\": %ld,\n" + "\"date_relative\": %s,\n" "\"matched\": %d,\n" "\"total\": %d,\n" "\"authors\": %s,\n" "\"subject\": %s,\n", json_quote_str (ctx_quote, thread_id), date, + json_quote_str (ctx_quote, notmuch_time_relative_date (ctx, date)), matched, total, json_quote_str (ctx_quote, authors), -- 1.7.2.3