]> git.notmuchmail.org Git - notmuch/blobdiff - emacs/notmuch.el
emacs: notmuch search bugfix
[notmuch] / emacs / notmuch.el
index 69954824ec959cd3cb471406c05918ceae4d8e56..7b61e9be13e868f7a0653bd927c6fee82cbcc26a 100644 (file)
        date, count, authors, subject, tags
 For example:
        (setq notmuch-search-result-format \(\(\"authors\" . \"%-40s\"\)
-                                            \(\"subject\" . \"%s\"\)\)\)"
+                                            \(\"subject\" . \"%s\"\)\)\)
+Line breaks are permitted in format strings (though this is
+currently experimental).  Note that a line break at the end of an
+\"authors\" field will get elided if the authors list is long;
+place it instead at the beginning of the following field.  To
+enter a line break when setting this variable with setq, use \\n.
+To enter a line break in customize, press \\[quoted-insert] C-j."
   :type '(alist :key-type (string) :value-type (string))
   :group 'notmuch-search)
 
@@ -287,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."
@@ -427,25 +440,52 @@ returns nil"
     (next-single-property-change (or pos (point)) 'notmuch-search-result
                                 nil (point-max))))
 
+(defun notmuch-search-foreach-result (beg end function)
+  "Invoke FUNCTION for each result between BEG and END.
+
+FUNCTION should take one argument.  It will be applied to the
+character position of the beginning of each result that overlaps
+the region between points BEG and END.  As a special case, if (=
+BEG END), FUNCTION will be applied to the result containing point
+BEG."
+
+  (lexical-let ((pos (notmuch-search-result-beginning beg))
+               ;; End must be a marker in case function changes the
+               ;; text.
+               (end (copy-marker end))
+               ;; Make sure we examine at least one result, even if
+               ;; (= beg end).
+               (first t))
+    ;; We have to be careful if the region extends beyond the results.
+    ;; In this case, pos could be null or there could be no result at
+    ;; pos.
+    (while (and pos (or (< pos end) first))
+      (when (notmuch-search-get-result pos)
+       (funcall function pos))
+      (setq pos (notmuch-search-result-end pos)
+           first nil))))
+;; Unindent the function argument of notmuch-search-foreach-result so
+;; the indentation of callers doesn't get out of hand.
+(put 'notmuch-search-foreach-result 'lisp-indent-function 2)
+
 (defun notmuch-search-properties-in-region (property beg end)
-  (save-excursion
-    (let ((output nil)
-         (last-line (line-number-at-pos end))
-         (max-line (- (line-number-at-pos (point-max)) 2)))
-      (goto-char beg)
-      (beginning-of-line)
-      (while (<= (line-number-at-pos) (min last-line max-line))
-       (setq output (cons (get-text-property (point) property) output))
-       (forward-line 1))
-      output)))
-
-(defun notmuch-search-find-thread-id ()
-  "Return the thread for the current thread"
-  (get-text-property (point) 'notmuch-search-thread-id))
+  (let (output)
+    (notmuch-search-foreach-result beg end
+      (lambda (pos)
+       (push (plist-get (notmuch-search-get-result pos) property) output)))
+    output))
+
+(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 (unless bare "thread:") thread))))
 
 (defun notmuch-search-find-thread-id-region (beg end)
   "Return a list of threads for the current region"
-  (notmuch-search-properties-in-region 'notmuch-search-thread-id beg end))
+  (mapcar (lambda (thread) (concat "thread:" thread))
+         (notmuch-search-properties-in-region :thread beg end)))
 
 (defun notmuch-search-find-thread-id-region-search (beg end)
   "Return a search string for threads for the current region"
@@ -453,19 +493,19 @@ returns nil"
 
 (defun notmuch-search-find-authors ()
   "Return the authors for the current thread"
-  (get-text-property (point) 'notmuch-search-authors))
+  (plist-get (notmuch-search-get-result) :authors))
 
 (defun notmuch-search-find-authors-region (beg end)
   "Return a list of authors for the current region"
-  (notmuch-search-properties-in-region 'notmuch-search-authors beg end))
+  (notmuch-search-properties-in-region :authors beg end))
 
 (defun notmuch-search-find-subject ()
   "Return the subject for the current thread"
-  (get-text-property (point) 'notmuch-search-subject))
+  (plist-get (notmuch-search-get-result) :subject))
 
 (defun notmuch-search-find-subject-region (beg end)
   "Return a list of authors for the current region"
-  (notmuch-search-properties-in-region 'notmuch-search-subject beg end))
+  (notmuch-search-properties-in-region :subject beg end))
 
 (defun notmuch-search-show-thread ()
   "Display the currently selected thread."
@@ -517,28 +557,21 @@ and will also appear in a buffer named \"*Notmuch errors*\"."
   (plist-get (notmuch-search-get-result pos) :tags))
 
 (defun notmuch-search-get-tags-region (beg end)
-  (save-excursion
-    (let ((output nil)
-         (last-line (line-number-at-pos end))
-         (max-line (- (line-number-at-pos (point-max)) 2)))
-      (goto-char beg)
-      (while (<= (line-number-at-pos) (min last-line max-line))
-       (setq output (append output (notmuch-search-get-tags)))
-       (forward-line 1))
-      output)))
+  (let (output)
+    (notmuch-search-foreach-result beg end
+      (lambda (pos)
+       (setq output (append output (notmuch-search-get-tags pos)))))
+    output))
 
 (defun notmuch-search-tag-region (beg end &optional tag-changes)
   "Change tags for threads in the given region."
   (let ((search-string (notmuch-search-find-thread-id-region-search beg end)))
     (setq tag-changes (funcall 'notmuch-tag search-string tag-changes))
-    (save-excursion
-      (let ((last-line (line-number-at-pos end))
-           (max-line (- (line-number-at-pos (point-max)) 2)))
-       (goto-char beg)
-       (while (<= (line-number-at-pos) (min last-line max-line))
-         (notmuch-search-set-tags
-          (notmuch-update-tags (notmuch-search-get-tags) tag-changes))
-         (forward-line))))))
+    (notmuch-search-foreach-result beg end
+      (lambda (pos)
+       (notmuch-search-set-tags
+        (notmuch-update-tags (notmuch-search-get-tags pos) tag-changes)
+        pos)))))
 
 (defun notmuch-search-tag (&optional tag-changes)
   "Change tags for the currently selected thread or region.
@@ -751,11 +784,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."
@@ -768,13 +799,7 @@ non-authors is found, assume that all of the authors match."
          (notmuch-search-insert-field (car spec) (cdr spec) result))
        (insert "\n")
        (notmuch-search-color-line beg (point) (plist-get result :tags))
-       (put-text-property beg (point) 'notmuch-search-result result)
-       (put-text-property beg (point) 'notmuch-search-thread-id
-                          (concat "thread:" (plist-get result :thread)))
-       (put-text-property beg (point) 'notmuch-search-authors
-                          (plist-get result :authors))
-       (put-text-property beg (point) 'notmuch-search-subject
-                          (plist-get result :subject)))
+       (put-text-property beg (point) 'notmuch-search-result result))
       (when (string= (plist-get result :thread) notmuch-search-target-thread)
        (setq notmuch-search-target-thread "found")
        (goto-char beg)))))
@@ -913,7 +938,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."
@@ -970,7 +995,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)