]> git.notmuchmail.org Git - notmuch/blobdiff - emacs/notmuch.el
emacs: Rename incremental JSON internal variables
[notmuch] / emacs / notmuch.el
index 7180b9d94a74038f9e82b053ada71aa9390849c6..428b0117fadcba0a00b0526a685a3ba09e142292 100644 (file)
@@ -293,18 +293,25 @@ For a mouse binding, return nil."
 (defun notmuch-search-next-thread ()
   "Select the next thread in the search results."
   (interactive)
-  (forward-line 1))
+  (when (notmuch-search-get-result)
+    (goto-char (notmuch-search-result-end))))
 
 (defun notmuch-search-previous-thread ()
   "Select the previous thread in the search results."
   (interactive)
-  (forward-line -1))
+  (if (notmuch-search-get-result)
+      (unless (bobp)
+       (goto-char (notmuch-search-result-beginning (- (point) 1))))
+    ;; We must be past the end; jump to the last result
+    (notmuch-search-last-thread)))
 
 (defun notmuch-search-last-thread ()
   "Select the last thread in the search results."
   (interactive)
   (goto-char (point-max))
-  (forward-line -2))
+  (forward-line -2)
+  (let ((beg (notmuch-search-result-beginning)))
+    (when beg (goto-char beg))))
 
 (defun notmuch-search-first-thread ()
   "Select the first thread in the search results."
@@ -381,8 +388,9 @@ any tags).
 Pressing \\[notmuch-search-show-thread] on any line displays that thread. The '\\[notmuch-search-add-tag]' and '\\[notmuch-search-remove-tag]'
 keys can be used to add or remove tags from a thread. The '\\[notmuch-search-archive-thread]' key
 is a convenience for archiving a thread (removing the \"inbox\"
-tag). The '\\[notmuch-search-tag-all]' key can be used to add or remove a tag from all
-threads in the current buffer.
+tag). The '\\[notmuch-search-tag-all]' key can be used to add and/or remove tags from all
+messages (as opposed to threads) that match the current query.  Use with caution, as this
+will also tag matching messages that arrived *after* constructing the buffer.
 
 Other useful commands are '\\[notmuch-search-filter]' for filtering the current search
 based on an additional query string, '\\[notmuch-search-filter-by-tag]' for filtering to include
@@ -468,10 +476,12 @@ BEG."
        (push (plist-get (notmuch-search-get-result pos) property) output)))
     output))
 
-(defun notmuch-search-find-thread-id ()
-  "Return the thread for the current thread"
+(defun notmuch-search-find-thread-id (&optional bare)
+  "Return the thread for the current thread
+
+If BARE is set then do not prefix with \"thread:\""
   (let ((thread (plist-get (notmuch-search-get-result) :thread)))
-    (when thread (concat "thread:" thread))))
+    (when thread (concat (unless bare "thread:") thread))))
 
 (defun notmuch-search-find-thread-id-region (beg end)
   "Return a list of threads for the current region"
@@ -583,12 +593,20 @@ See `notmuch-tag' for information on the format of TAG-CHANGES."
   (interactive)
   (notmuch-search-tag "-"))
 
-(defun notmuch-search-archive-thread ()
-  "Archive the currently selected thread (remove its \"inbox\" tag).
+(defun notmuch-search-archive-thread (&optional unarchive)
+  "Archive the currently selected thread.
+
+Archive each message in the currently selected thread by applying
+the tag changes in `notmuch-archive-tags' to each (remove the
+\"inbox\" tag by default). If a prefix argument is given, the
+messages will be \"unarchived\" (i.e. the tag changes in
+`notmuch-archive-tags' will be reversed).
 
 This function advances the next thread when finished."
-  (interactive)
-  (notmuch-search-tag '("-inbox"))
+  (interactive "P")
+  (when notmuch-archive-tags
+    (notmuch-search-tag
+     (notmuch-tag-change-list notmuch-archive-tags unarchive)))
   (notmuch-search-next-thread))
 
 (defun notmuch-search-update-result (result &optional pos)
@@ -775,11 +793,9 @@ non-authors is found, assume that all of the authors match."
     (notmuch-search-insert-authors format-string (plist-get result :authors)))
 
    ((string-equal field "tags")
-    ;; Ignore format-string here because notmuch-search-set-tags
-    ;; depends on the format of this
-    (insert (concat "(" (propertize
-                        (mapconcat 'identity (plist-get result :tags) " ")
-                        'font-lock-face 'notmuch-tag-face) ")")))))
+    (let ((tags-str (mapconcat 'identity (plist-get result :tags) " ")))
+      (insert (propertize (format format-string tags-str)
+                         'face 'notmuch-tag-face))))))
 
 (defun notmuch-search-show-result (result &optional pos)
   "Insert RESULT at POS or the end of the buffer if POS is null."
@@ -804,11 +820,15 @@ 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.")
+;; These two variables are internal variables to the parsing
+;; routines. They are always used buffer local but need to be declared
+;; globally to avoid compiler warnings.
+
+(defvar notmuch-json-state nil
+  "Internal incremental JSON parser object: local to the buffer being parsed.")
 
-(defvar notmuch-search-json-parser nil
-  "Incremental JSON parser for the search process filter.")
+(defvar notmuch-json-parser nil
+  "State of the internal JSON parser: local to the buffer being parsed.")
 
 (defun notmuch-search-process-filter (proc string)
   "Process and filter the output of \"notmuch search\""
@@ -822,41 +842,63 @@ non-authors is found, assume that all of the authors match."
        ;; 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
+         (insert string))
+       (notmuch-json-parse-partial-list 'notmuch-search-show-result
+                                        'notmuch-search-show-error
+                                        results-buf)))))
+
+(defun notmuch-json-parse-partial-list (result-function error-function results-buf)
+  "Parse a partial JSON list from current buffer.
+
+This function consumes a JSON list from the current buffer,
+applying RESULT-FUNCTION in buffer RESULT-BUFFER to each complete
+value in the list.  It operates incrementally and should be
+called whenever the buffer has been extended with additional
+data.
+
+If there is a syntax error, this will attempt to resynchronize
+with the input and will apply ERROR-FUNCTION in buffer
+RESULT-BUFFER to any input that was skipped.
+
+It sets up all the needed internal variables: the caller just
+needs to call it with point in the same place that the parser
+left it."
+  (let (done)
+    (unless (local-variable-p 'notmuch-json-parser)
+      (set (make-local-variable 'notmuch-json-parser)
+          (notmuch-json-create-parser (current-buffer)))
+      (set (make-local-variable 'notmuch-json-state) 'begin))
+    (while (not done)
+      (condition-case nil
+         (case notmuch-json-state
                ((begin)
                 ;; Enter the results list
                 (if (eq (notmuch-json-begin-compound
-                         notmuch-search-json-parser) 'retry)
+                         notmuch-json-parser) 'retry)
                     (setq done t)
-                  (setq notmuch-search-process-state 'result)))
+                  (setq notmuch-json-state 'result)))
                ((result)
                 ;; Parse a result
-                (let ((result (notmuch-json-read notmuch-search-json-parser)))
+                (let ((result (notmuch-json-read notmuch-json-parser)))
                   (case result
-                    ((retry) (setq done t))
-                    ((end) (setq notmuch-search-process-state 'end))
-                    (otherwise (notmuch-search-show-result result)))))
+                        ((retry) (setq done t))
+                        ((end) (setq notmuch-json-state 'end))
+                        (otherwise (with-current-buffer results-buf
+                                     (funcall result-function result))))))
                ((end)
                 ;; Any trailing data is unexpected
-                (notmuch-json-eof notmuch-search-json-parser)
+                (notmuch-json-eof notmuch-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)))))))
+       (json-error
+        ;; Do our best to resynchronize and ensure forward
+        ;; progress
+        (let ((bad (buffer-substring (line-beginning-position)
+                                     (line-end-position))))
+          (forward-line)
+          (with-current-buffer results-buf
+            (funcall error-function "%s" bad))))))
+    ;; Clear out what we've parsed
+    (delete-region (point-min) (point))))
 
 (defun notmuch-search-tag-all (&optional tag-changes)
   "Add/remove tags from all messages in current search buffer.
@@ -901,7 +943,7 @@ PROMPT is the string to prompt with."
        (append (list "folder:" "thread:" "id:" "date:" "from:" "to:"
                      "subject:" "attachment:")
                (mapcar (lambda (tag)
-                         (concat "tag:" tag))
+                         (concat "tag:" (notmuch-escape-boolean-term tag)))
                        (process-lines notmuch-command "search" "--output=tags" "*")))))
     (let ((keymap (copy-keymap minibuffer-local-map))
          (minibuffer-completion-table
@@ -931,7 +973,7 @@ If `query' is nil, it is read interactively from the minibuffer.
 Other optional parameters are used as follows:
 
   oldest-first: A Boolean controlling the sort order of returned threads
-  target-thread: A thread ID (with the thread: prefix) that will be made
+  target-thread: A thread ID (without the thread: prefix) that will be made
                  current if it appears in the search results.
   target-line: The line number to move to if the target thread does not
                appear in the search results."
@@ -968,9 +1010,6 @@ Other optional parameters are used as follows:
              ;; 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)
@@ -988,7 +1027,7 @@ same relative position within the new buffer."
   (interactive)
   (let ((target-line (line-number-at-pos))
        (oldest-first notmuch-search-oldest-first)
-       (target-thread (notmuch-search-find-thread-id))
+       (target-thread (notmuch-search-find-thread-id 'bare))
        (query notmuch-search-query-string)
        (continuation notmuch-search-continuation))
     (notmuch-kill-this-buffer)