X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=notmuch.el;h=de995ecaa014294ddf0f1cd0caeac6cfc6708c2d;hp=16ca037ed5573757280116f82e04b72503c856ed;hb=c6aae1561a51ba12f716d25e3731019d16115ab4;hpb=c5672ca12a1694a64376141f1ac9f65312d7ccb0 diff --git a/notmuch.el b/notmuch.el index 16ca037e..de995eca 100644 --- a/notmuch.el +++ b/notmuch.el @@ -32,8 +32,10 @@ (define-key map "b" 'notmuch-show-toggle-body-read-visible) (define-key map "c" 'notmuch-show-toggle-citations-visible) (define-key map "h" 'notmuch-show-toggle-headers-visible) - (define-key map "n" 'notmuch-show-mark-read-then-next-message) + (define-key map "n" 'notmuch-show-next-message) (define-key map "p" 'notmuch-show-previous-message) + (define-key map (kbd "C-n") 'notmuch-show-next-line) + (define-key map (kbd "C-p") 'notmuch-show-previous-line) (define-key map "q" 'kill-this-buffer) (define-key map "s" 'notmuch-show-toggle-signatures-visible) (define-key map "x" 'kill-this-buffer) @@ -59,6 +61,44 @@ (defvar notmuch-show-id-regexp "ID: \\(.*\\)$") (defvar notmuch-show-tags-regexp "(\\([^)]*\\))$") +; XXX: This should be a generic function in emacs somewhere, not here +(defun point-invisible-p () + "Return whether the character at point is invisible. + +Here visibility is determined by `buffer-invisibility-spec' and +the invisible property of any overlays for point. It doesn't have +anything to do with whether point is currently being displayed +within the current window." + (let ((prop (get-char-property (point) 'invisible))) + (if (eq buffer-invisibility-spec t) + prop + (or (memq prop buffer-invisibility-spec) + (assq prop buffer-invisibility-spec))))) + +(defun notmuch-show-next-line () + "Like builtin `next-line' but ensuring we end on a visible character. + +By advancing forward until reaching a visible character. + +Unlike builtin `next-line' this version accepts no arguments." + (interactive) + (set 'this-command 'next-line) + (call-interactively 'next-line) + (while (point-invisible-p) + (forward-char))) + +(defun notmuch-show-previous-line () + "Like builtin `previous-line' but ensuring we end on a visible character. + +By advancing forward until reaching a visible character. + +Unlike builtin `next-line' this version accepts no arguments." + (interactive) + (set 'this-command 'previous-line) + (call-interactively 'previous-line) + (while (point-invisible-p) + (forward-char))) + (defun notmuch-show-get-message-id () (save-excursion (beginning-of-line) @@ -147,22 +187,36 @@ by searching backward)." (error "Not within a valid message.")) (forward-line 2))) +(defun notmuch-show-last-message-p () + "Predicate testing whether point is within the last message." + (save-window-excursion + (save-excursion + (notmuch-show-move-to-current-message-summary-line) + (not (re-search-forward notmuch-show-message-begin-regexp nil t))))) + +(defun notmuch-show-message-unread-p () + "Preficate testing whether current message is unread." + (member "unread" (notmuch-show-get-tags))) + (defun notmuch-show-next-message () "Advance to the beginning of the next message in the buffer. -Moves to the beginning of the current message if already on the -last message in the buffer." +Moves to the last visible character of the current message if +already on the last message in the buffer." (interactive) (notmuch-show-move-to-current-message-summary-line) - (re-search-forward notmuch-show-message-begin-regexp nil t) - (notmuch-show-move-to-current-message-summary-line) + (if (re-search-forward notmuch-show-message-begin-regexp nil t) + (notmuch-show-move-to-current-message-summary-line) + (goto-char (- (point-max) 1)) + (while (point-invisible-p) + (backward-char))) (recenter 0)) (defun notmuch-show-find-next-message () "Returns the position of the next message in the buffer. -Or the beginning of the current message if already within the last -message in the buffer." +Or the position of the last visible character of the current +message if already within the last message in the buffer." ; save-excursion doesn't save our window position ; save-window-excursion doesn't save point ; Looks like we have to use both. @@ -171,6 +225,18 @@ message in the buffer." (notmuch-show-next-message) (point)))) +(defun notmuch-show-next-unread-message () + "Advance to the beginning of the next unread message in the buffer. + +Moves to the last visible character of the current message if +there are no more unread messages past the current point." + (notmuch-show-next-message) + (while (and (not (notmuch-show-last-message-p)) + (not (notmuch-show-message-unread-p))) + (notmuch-show-next-message)) + (if (not (notmuch-show-message-unread-p)) + (notmuch-show-next-message))) + (defun notmuch-show-previous-message () "Backup to the beginning of the previous message in the buffer. @@ -188,11 +254,11 @@ simply move to the beginning of the current message." )) (recenter 0))) -(defun notmuch-show-mark-read-then-next-message () - "Remove unread tag from current message, then advance to next message." +(defun notmuch-show-mark-read-then-next-unread-message () + "Remove unread tag from current message, then advance to next unread message." (interactive) (notmuch-show-remove-tag "unread") - (notmuch-show-next-message)) + (notmuch-show-next-unread-message)) (defun notmuch-show-advance-marking-read-and-archiving () "Advance through buffer, marking read and archiving. @@ -204,8 +270,8 @@ If the current message in the thread is not yet fully visible, scroll by a near screenful to read more of the message. Otherwise, (the end of the current message is already within the -current window), remove the \"unread\" tag from the current -message and advance to the next message. +current window), remove the \"unread\" tag (if present) from the +current message and advance to the next message. Finally, if there is no further message to advance to, and this last message is already read, then archive the entire current @@ -214,13 +280,14 @@ this buffer, and display the next thread from the search from which this thread was originally shown." (interactive) (let ((next (notmuch-show-find-next-message)) - (unread (member "unread" (notmuch-show-get-tags)))) - (if (and (not unread) - (equal next (point))) - (notmuch-show-archive-thread) - (if (< (notmuch-show-find-next-message) (window-end)) - (notmuch-show-mark-read-then-next-message) - (scroll-up nil))))) + (unread (notmuch-show-message-unread-p))) + (if (> next (window-end)) + (scroll-up nil) + (if unread + (notmuch-show-mark-read-then-next-unread-message) + (if (notmuch-show-last-message-p) + (notmuch-show-archive-thread) + (notmuch-show-next-unread-message)))))) (defun notmuch-show-markup-citations-region (beg end) (goto-char beg) @@ -253,7 +320,7 @@ which this thread was originally shown." (re-search-forward notmuch-show-body-end-regexp) (let ((end (match-beginning 0))) (notmuch-show-markup-citations-region beg end) - (if (not (member "unread" (notmuch-show-get-tags))) + (if (not (notmuch-show-message-unread-p)) (overlay-put (make-overlay beg end) 'invisible 'notmuch-show-body-read))))) @@ -398,12 +465,20 @@ thread from that buffer can be show when done with this one)." (call-process "notmuch" nil t nil "show" thread-id) (notmuch-show-markup-messages) ) + ; Move straight to the first unread message + (if (not (notmuch-show-message-unread-p)) + (progn + (notmuch-show-next-unread-message) + ; But if there are no unread messages, go back to the + ; beginning of the buffer. + (if (not (notmuch-show-message-unread-p)) + (goto-char (point-min))))) ))) (defvar notmuch-search-mode-map (let ((map (make-sparse-keymap))) (define-key map "a" 'notmuch-search-archive-thread) - (define-key map "b" 'scroll-down) + (define-key map "b" 'notmuch-search-scroll-down) (define-key map "f" 'notmuch-search-filter) (define-key map "n" 'next-line) (define-key map "p" 'previous-line) @@ -418,12 +493,34 @@ thread from that buffer can be show when done with this one)." (define-key map ">" 'notmuch-search-goto-last-thread) (define-key map "=" 'notmuch-search-refresh-view) (define-key map "\M->" 'notmuch-search-goto-last-thread) - (define-key map " " 'scroll-up) - (define-key map (kbd "") 'scroll-down) + (define-key map " " 'notmuch-search-scroll-up) + (define-key map (kbd "") 'notmuch-search-scroll-down) map) "Keymap for \"notmuch search\" buffers.") (fset 'notmuch-search-mode-map notmuch-search-mode-map) +(defun notmuch-search-scroll-up () + "Scroll up, moving point to last message in thread if at end." + (interactive) + (condition-case nil + (scroll-up nil) + ((end-of-buffer) (notmuch-search-goto-last-thread)))) + +(defun notmuch-search-scroll-down () + "Scroll down, moving point to first message in thread if at beginning." + (interactive) + ; I don't know why scroll-down doesn't signal beginning-of-buffer + ; the way that scroll-up signals end-of-buffer, but c'est la vie. + ; + ; So instead of trapping a signal we instead check whether the + ; window begins on the first line of the buffer and if so, move + ; directly to that position. (We have to count lines since the + ; window-start position is not the same as point-min due to the + ; invisible thread-ID characters on the first line. + (if (equal (count-lines (point-min) (window-start)) 1) + (goto-char (window-start)) + (scroll-down nil))) + (defun notmuch-search-goto-last-thread (&optional arg) "Move point to the last thread in the buffer." (interactive "^P")