notmuch.el: Un-break the magic space bar to scroll a long, single message.

Clearly some recent code was very fragile, which I noticed in that the
space bar would no longer scroll a long message if it was the only
message in a thread.

This resulted in a lot of churn, but hopefully things are more robust
now, (for example by using new predicates like
notmuch-show-last-message-p rather than doing heuristics based on
(eobp) or (window-end)).

As usual, the presence of invisible characters complicates the task of
making this stuff robust.
This commit is contained in:
Carl Worth 2009-11-04 13:16:33 -08:00
parent 9fc6f4e60d
commit 1d68e75c21

View file

@ -59,6 +59,20 @@
(defvar notmuch-show-id-regexp "ID: \\(.*\\)$") (defvar notmuch-show-id-regexp "ID: \\(.*\\)$")
(defvar notmuch-show-tags-regexp "(\\([^)]*\\))$") (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-get-message-id () (defun notmuch-show-get-message-id ()
(save-excursion (save-excursion
(beginning-of-line) (beginning-of-line)
@ -147,23 +161,36 @@ by searching backward)."
(error "Not within a valid message.")) (error "Not within a valid message."))
(forward-line 2))) (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 () (defun notmuch-show-next-message ()
"Advance to the beginning of the next message in the buffer. "Advance to the beginning of the next message in the buffer.
Moves to the end of the buffer if already on the last message in Moves to the last visible character of the current message if
the buffer." already on the last message in the buffer."
(interactive) (interactive)
(notmuch-show-move-to-current-message-summary-line) (notmuch-show-move-to-current-message-summary-line)
(if (re-search-forward notmuch-show-message-begin-regexp nil t) (if (re-search-forward notmuch-show-message-begin-regexp nil t)
(notmuch-show-move-to-current-message-summary-line) (notmuch-show-move-to-current-message-summary-line)
(goto-char (point-max))) (goto-char (- (point-max) 1))
(while (point-invisible-p)
(backward-char)))
(recenter 0)) (recenter 0))
(defun notmuch-show-find-next-message () (defun notmuch-show-find-next-message ()
"Returns the position of the next message in the buffer. "Returns the position of the next message in the buffer.
Or the end of the buffer if already within the last message in Or the position of the last visible character of the current
the buffer." message if already within the last message in the buffer."
; save-excursion doesn't save our window position ; save-excursion doesn't save our window position
; save-window-excursion doesn't save point ; save-window-excursion doesn't save point
; Looks like we have to use both. ; Looks like we have to use both.
@ -175,25 +202,15 @@ the buffer."
(defun notmuch-show-next-unread-message () (defun notmuch-show-next-unread-message ()
"Advance to the beginning of the next unread message in the buffer. "Advance to the beginning of the next unread message in the buffer.
Moves to the end of the buffer if there are no more unread Moves to the last visible character of the current message if
messages past the current point." there are no more unread messages past the current point."
(while (and (not (eobp)) (notmuch-show-next-message)
(not (member "unread" (notmuch-show-get-tags)))) (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))) (notmuch-show-next-message)))
(defun notmuch-show-find-next-unread-message ()
"Returns the position of the next message in the buffer.
Or the end of the buffer if there are no more unread messages
past the current point."
; save-excursion doesn't save our window position
; save-window-excursion doesn't save point
; Looks like we have to use both.
(save-excursion
(save-window-excursion
(notmuch-show-next-unread-message)
(point))))
(defun notmuch-show-previous-message () (defun notmuch-show-previous-message ()
"Backup to the beginning of the previous message in the buffer. "Backup to the beginning of the previous message in the buffer.
@ -227,8 +244,8 @@ If the current message in the thread is not yet fully visible,
scroll by a near screenful to read more of the message. scroll by a near screenful to read more of the message.
Otherwise, (the end of the current message is already within the Otherwise, (the end of the current message is already within the
current window), remove the \"unread\" tag from the current current window), remove the \"unread\" tag (if present) from the
message and advance to the next message. current message and advance to the next message.
Finally, if there is no further message to advance to, and this Finally, if there is no further message to advance to, and this
last message is already read, then archive the entire current last message is already read, then archive the entire current
@ -236,15 +253,15 @@ thread, (remove the \"inbox\" tag from each message). Also kill
this buffer, and display the next thread from the search from this buffer, and display the next thread from the search from
which this thread was originally shown." which this thread was originally shown."
(interactive) (interactive)
(let ((next (notmuch-show-find-next-unread-message)) (let ((next (notmuch-show-find-next-message))
(unread (member "unread" (notmuch-show-get-tags)))) (unread (notmuch-show-message-unread-p)))
(if (and (not unread) (if (> next (window-end))
(equal next (point)))
(notmuch-show-archive-thread)
(if (and (> next (window-end))
(< next (point-max)))
(scroll-up nil) (scroll-up nil)
(notmuch-show-mark-read-then-next-unread-message))))) (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) (defun notmuch-show-markup-citations-region (beg end)
(goto-char beg) (goto-char beg)
@ -277,7 +294,7 @@ which this thread was originally shown."
(re-search-forward notmuch-show-body-end-regexp) (re-search-forward notmuch-show-body-end-regexp)
(let ((end (match-beginning 0))) (let ((end (match-beginning 0)))
(notmuch-show-markup-citations-region beg end) (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) (overlay-put (make-overlay beg end)
'invisible 'notmuch-show-body-read))))) 'invisible 'notmuch-show-body-read)))))
@ -422,9 +439,14 @@ thread from that buffer can be show when done with this one)."
(call-process "notmuch" nil t nil "show" thread-id) (call-process "notmuch" nil t nil "show" thread-id)
(notmuch-show-markup-messages) (notmuch-show-markup-messages)
) )
; Move straight to the first unread message
(if (not (notmuch-show-message-unread-p))
(progn
(notmuch-show-next-unread-message) (notmuch-show-next-unread-message)
(if (eobp) ; But if there are no unread messages, go back to the
(goto-char (point-min))) ; beginning of the buffer.
(if (not (notmuch-show-message-unread-p))
(goto-char (point-min)))))
))) )))
(defvar notmuch-search-mode-map (defvar notmuch-search-mode-map