]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch.el
emacs: Fix refresh of search results to leave cursor on current thread.
[notmuch] / notmuch.el
index 7723730f3e92f3da198df9353ea296c6298154aa..5b553bbf55050be5b11a61221a28fb7c855e6ed7 100644 (file)
     (define-key map "+" 'notmuch-show-add-tag)
     (define-key map "x" 'notmuch-show-archive-thread-then-exit)
     (define-key map "a" 'notmuch-show-archive-thread)
-    (define-key map "p" 'notmuch-show-previous-message)
-    (define-key map "n" 'notmuch-show-next-message)
+    (define-key map "P" 'notmuch-show-previous-message)
+    (define-key map "N" 'notmuch-show-next-message)
+    (define-key map "p" 'notmuch-show-previous-open-message)
+    (define-key map "n" 'notmuch-show-next-open-message)
     (define-key map (kbd "DEL") 'notmuch-show-rewind)
     (define-key map " " 'notmuch-show-advance-and-archive)
     map)
@@ -523,7 +525,7 @@ Returns nil if already on the last message in the buffer."
     nil))
 
 (defun notmuch-show-next-message ()
-  "Advance to the beginning of the next message in the buffer.
+  "Advance to the next message (whether open or closed)
 and remove the unread tag from that message.
 
 Moves to the last visible character of the current message if
@@ -548,7 +550,7 @@ message if already within the last message in the buffer."
       (point))))
 
 (defun notmuch-show-next-unread-message ()
-  "Advance to the beginning of the next unread message in the buffer.
+  "Advance to the next unread message.
 
 Moves to the last visible character of the current message if
 there are no more unread messages past the current point."
@@ -561,17 +563,22 @@ there are no more unread messages past the current point."
   (notmuch-show-mark-read))
 
 (defun notmuch-show-next-open-message ()
-  "Advance to the next open message (that is, body is not invisible)."
+  "Advance to the next open message (that is, body is visible).
+
+Moves to the last visible character of the final message in the buffer
+if there are no more open messages."
+  (interactive)
   (while (and (notmuch-show-next-message-without-marking-read)
              (not (notmuch-show-message-open-p))))
   (notmuch-show-mark-read))
 
-(defun notmuch-show-previous-message ()
+(defun notmuch-show-previous-message-without-marking-read ()
   "Backup to the beginning of the previous message in the buffer.
 
 If within a message rather than at the beginning of it, then
-simply move to the beginning of the current message."
-  (interactive)
+simply move to the beginning of the current message.
+
+Returns nil if already on the first message in the buffer."
   (let ((start (point)))
     (notmuch-show-move-to-current-message-summary-line)
     (if (not (< (point) start))
@@ -580,8 +587,22 @@ simply move to the beginning of the current message."
          (re-search-backward notmuch-show-message-begin-regexp nil t)
          (re-search-backward notmuch-show-message-begin-regexp nil t)
          (notmuch-show-move-to-current-message-summary-line)
-         ))
-    (recenter 0)))
+         (recenter 0)
+         (if (= (point) start)
+             nil
+           t))
+      (recenter 0)
+      nil)))
+
+(defun notmuch-show-previous-message ()
+  "Backup to the previous message (whether open or closed)
+and remove the unread tag from that message.
+
+If within a message rather than at the beginning of it, then
+simply move to the beginning of the current message."
+  (interactive)
+  (notmuch-show-previous-message-without-marking-read)
+  (notmuch-show-mark-read))
 
 (defun notmuch-show-find-previous-message ()
   "Returns the position of the previous message in the buffer.
@@ -594,9 +615,19 @@ it."
   ; Looks like we have to use both.
   (save-excursion
     (save-window-excursion
-      (notmuch-show-previous-message)
+      (notmuch-show-previous-message-without-marking-read)
       (point))))
 
+(defun notmuch-show-previous-open-message ()
+  "Backup to previous open message (that is, body is visible).
+
+Moves to the first message in the buffer if there are no previous
+open messages."
+  (interactive)
+  (while (and (notmuch-show-previous-message-without-marking-read)
+             (not (notmuch-show-message-open-p))))
+  (notmuch-show-mark-read))
+
 (defun notmuch-show-rewind ()
   "Backup through the thread, (reverse scrolling compared to \\[notmuch-show-advance-and-archive]).
 
@@ -616,7 +647,13 @@ any effects from previous calls to
          (condition-case nil
              (scroll-down nil)
            ((beginning-of-buffer) nil))
-         (goto-char (window-start)))
+         (goto-char (window-start))
+         ; Because count-lines counts invivisible lines, we may have
+         ; scrolled to far. If so., notice this and fix it up.
+         (if (< (point) previous)
+             (progn
+               (goto-char previous)
+               (recenter 0))))
       (notmuch-show-previous-message))))
 
 (defun notmuch-show-advance-and-archive ()
@@ -828,8 +865,8 @@ is what to put on the button."
 
 (defun notmuch-show-markup-body (depth match btn)
   "Markup a message body, (indenting, buttonizing citations,
-etc.), and conditionally hiding the body itself if the message
-has been read and does not match the current search.
+etc.), and hiding the body itself if the message does not match
+the current search.
 
 DEPTH specifies the depth at which this message appears in the
 tree of the current thread, (the top-level messages have depth 0
@@ -850,7 +887,7 @@ before the delimiter marking the beginning of the body."
         (overlay-put (make-overlay beg end)
                      'invisible invis-spec)
         (button-put btn 'invisibility-spec invis-spec)
-        (if (not (or (notmuch-show-message-unread-p) match))
+        (if (not match)
             (add-to-invisibility-spec invis-spec)))
       (set-marker beg nil)
       (set-marker end nil)
@@ -1209,6 +1246,8 @@ matching this search term are shown if non-nil. "
 (fset 'notmuch-search-mode-map notmuch-search-mode-map)
 
 (defvar notmuch-search-query-string)
+(defvar notmuch-search-target-thread)
+(defvar notmuch-search-target-position)
 (defvar notmuch-search-oldest-first t
   "Show the oldest mail first in the search-mode")
 
@@ -1309,6 +1348,8 @@ Complete list of currently available key bindings:
   (kill-all-local-variables)
   (make-local-variable 'notmuch-search-query-string)
   (make-local-variable 'notmuch-search-oldest-first)
+  (make-local-variable 'notmuch-search-target-thread)
+  (make-local-variable 'notmuch-search-target-position)
   (set (make-local-variable 'scroll-preserve-screen-position) t)
   (add-to-invisibility-spec 'notmuch-search)
   (use-local-map notmuch-search-mode-map)
@@ -1405,8 +1446,7 @@ which match the current search terms."
 (defun notmuch-search-remove-tag (tag)
   "Remove a tag from the currently selected thread.
 
-The tag is removed from messages in the currently selected thread
-which match the current search terms."
+The tag is removed from all messages in the currently selected thread."
   (interactive
    (list (notmuch-select-tag-with-completion "Tag to remove: " (notmuch-search-find-thread-id))))
   (notmuch-call-notmuch-process "tag" (concat "-" tag) (notmuch-search-find-thread-id))
@@ -1424,12 +1464,14 @@ This function advances the next thread when finished."
   "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)))
+       (exit-status (process-exit-status proc))
+       (never-found-target-thread nil))
     (if (memq status '(exit signal))
        (if (buffer-live-p buffer)
            (with-current-buffer buffer
              (save-excursion
-               (let ((inhibit-read-only t))
+               (let ((inhibit-read-only t)
+                     (atbob (bobp)))
                  (goto-char (point-max))
                  (if (eq status 'signal)
                      (insert "Incomplete search results (search process was killed).\n"))
@@ -1438,11 +1480,17 @@ This function advances the next thread when finished."
                        (insert "End of search results.")
                        (if (not (= exit-status 0))
                            (insert (format " (process returned %d)" exit-status)))
-                       (insert "\n"))))))))))
+                       (insert "\n")
+                       (if (and atbob
+                                notmuch-search-target-thread)
+                           (set 'never-found-target-thread t))))))
+             (if never-found-target-thread
+                 (goto-char notmuch-search-target-position)))))))
 
 (defun notmuch-search-process-filter (proc string)
   "Process and filter the output of \"notmuch search\""
-  (let ((buffer (process-buffer proc)))
+  (let ((buffer (process-buffer proc))
+       (found-target nil))
     (if (buffer-live-p buffer)
        (with-current-buffer buffer
          (save-excursion
@@ -1465,9 +1513,16 @@ This function advances the next thread when finished."
                        (insert (format "%s %-7s %-40s %s (%s)\n" date count authors subject tags))
                        (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))
+                       (put-text-property beg (point-marker) 'notmuch-search-subject subject)
+                       (if (and notmuch-search-target-thread
+                                (string= thread-id notmuch-search-target-thread))
+                           (progn
+                             (set 'found-target beg)
+                             (set 'notmuch-search-target-thread nil))))
                      (set 'line (match-end 0)))
-                 (set 'more nil))))))
+                 (set 'more nil)))))
+         (if found-target
+             (goto-char found-target)))
       (delete-process proc))))
 
 (defun notmuch-search-operate-all (action)
@@ -1494,14 +1549,27 @@ characters as well as `_.+-'.
           (append action-split (list notmuch-search-query-string) nil))))
 
 ;;;###autoload
-(defun notmuch-search (query &optional oldest-first)
-  "Run \"notmuch search\" with the given query string and display results."
+(defun notmuch-search (query &optional oldest-first target-thread target-position)
+  "Run \"notmuch search\" with the given query string and display results.
+
+The 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
+                 current if it appears in the search results.
+  saved-position: If the search results complete, and the target thread is
+                  not found in the results, and the point is still at the
+                  beginning of the buffer, then the point will be moved to
+                  the saved position.
+"
   (interactive "sNotmuch search: ")
   (let ((buffer (get-buffer-create (concat "*notmuch-search-" query "*"))))
     (switch-to-buffer buffer)
     (notmuch-search-mode)
     (set 'notmuch-search-query-string query)
     (set 'notmuch-search-oldest-first oldest-first)
+    (set 'notmuch-search-target-thread target-thread)
+    (set 'notmuch-search-target-position target-position)
     (let ((proc (get-buffer-process (current-buffer)))
          (inhibit-read-only t))
       (if proc
@@ -1532,11 +1600,9 @@ same relative position within the new buffer."
        (thread (notmuch-search-find-thread-id))
        (query notmuch-search-query-string))
     (kill-this-buffer)
-    (notmuch-search query oldest-first)
+    (notmuch-search query oldest-first thread here)
     (goto-char (point-min))
-    (if (re-search-forward (concat "^" thread) nil t)
-       (beginning-of-line)
-      (goto-char here))))
+    ))
 
 (defun notmuch-search-toggle-order ()
   "Toggle the current search order.