From abd829511e50535987a0a4afd2bdf8a257715a9b Mon Sep 17 00:00:00 2001
From: Kristoffer Balintona <krisbalintona@gmail.com>
Date: Tue, 18 Nov 2025 14:08:56 -0600
Subject: [PATCH 1/1] emacs: Message scrolling commands now accept a prefix
 argument

There are several commands with special behavior used to scroll
message buffers. In `notmuch-show-mode` buffers:
- `notmuch-show-advance`,
- `notmuch-show-advance-and-archive`
- `notmuch-show-rewind`
And in `notmuch-tree-mode' buffers:
- `notmuch-tree-scroll-message-window`
- `notmuch-tree-scroll-message-window-back`
- `notmuch-tree-scroll-or-next`
These commands now accept an optional argument, ARG (interactively,
the prefix argument), such that their behavior aligns with the
built-in Emacs scrolling commands: `scroll-up-command` and
`scroll-down-command`.

Specifically, when ARG is nil, the previous behavior of the given
command is retained. However, when ARG is non-nil, it will cause that
command to only scroll up or down (depending on the command) ARG
number of lines in the message buffer (and nothing else).

For example, `notmuch-show-advance-and-archive` is bound to SPC in
`notmuch-show-mode` buffers. Now, when the user presses C-u 5 SPC, the
screen will scroll 5 lines upward.

Finally, I have updated these commands' docstrings to reflect their
new behavior while enhancing the existing behavior they expressed.
---
 emacs/notmuch-show.el | 94 +++++++++++++++++++++++--------------------
 emacs/notmuch-tree.el | 41 +++++++++++++------
 2 files changed, 80 insertions(+), 55 deletions(-)

diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
index 14e3c698..04d2a57c 100644
--- a/emacs/notmuch-show.el
+++ b/emacs/notmuch-show.el
@@ -1971,15 +1971,17 @@ Reshows the current thread with matches defined by the new query-string."
 
 ;;; Commands typically bound to keys.
 
-(defun notmuch-show-advance ()
+(defun notmuch-show-advance (&optional arg)
   "Advance through thread.
 
-If the current message in the thread is not yet fully visible,
-scroll by a near screenful to read more of the message.
+If ARG is non-nil (interactively, the prefix argument), scroll up that
+many lines. If ARG is nil and 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), advance to the next open message."
-  (interactive)
+Otherwise (when ARG is nil and the end of the current message is already
+within the current window), advance to the next open message."
+  (interactive "P")
   (let* ((end-of-this-message (notmuch-show-message-bottom))
 	 (visible-end-of-this-message (1- end-of-this-message))
 	 (ret nil))
@@ -1992,10 +1994,11 @@ current window), advance to the next open message."
      ;; Ideally we would test `end-of-this-message' against the result
      ;; of `window-end', but that doesn't account for the fact that
      ;; the end of the message might be hidden.
-     ((and visible-end-of-this-message
-	   (> visible-end-of-this-message (window-end)))
-      ;; The bottom of this message is not visible - scroll.
-      (scroll-up nil))
+     ((or arg (and visible-end-of-this-message
+		   (> visible-end-of-this-message (window-end))))
+      ;; ARG is non-nil or the bottom of this message is not visible -
+      ;; scroll.
+      (scroll-up arg))
      ((not (= end-of-this-message (point-max)))
       ;; This is not the last message - move to the next visible one.
       (notmuch-show-next-open-message))
@@ -2008,53 +2011,58 @@ current window), advance to the next open message."
       (setq ret t)))
     ret))
 
-(defun notmuch-show-advance-and-archive ()
+(defun notmuch-show-advance-and-archive (&optional arg)
   "Advance through thread and archive.
 
-This command is intended to be one of the simplest ways to
-process a thread of email. It works exactly like
-notmuch-show-advance, in that it scrolls through messages in a
-show buffer, except that when it gets to the end of the buffer it
-archives the entire current thread, (apply changes in
-`notmuch-archive-tags'), kills the buffer, and displays the next
-thread from the search from which this thread was originally
-shown."
-  (interactive)
-  (when (notmuch-show-advance)
+This command is intended to be one of the simplest ways to process a
+thread of email.
+
+It works exactly like `notmuch-show-advance' in that it scrolls through
+messages in the current show buffer according to ARG (the prefix
+argument when called interactively). However, when it gets to the end of
+the buffer, it archives the entire current thread (apply changes in
+`notmuch-archive-tags'), kills the buffer, and displays the next thread
+from the search from which this thread was originally shown."
+  (interactive "P")
+  (when (notmuch-show-advance arg)
     (notmuch-show-archive-thread-then-next)))
 
-(defun notmuch-show-rewind ()
+(defun notmuch-show-rewind (&optional arg)
   "Backup through the thread (reverse scrolling compared to \
 \\[notmuch-show-advance-and-archive]).
 
-Specifically, if the beginning of the previous email is fewer
-than `window-height' lines from the current point, move to it
-just like `notmuch-show-previous-message'.
+Specifically, when ARG (interactively, the prefix argument) is non-nil,
+scroll down that many lines. When ARG is nil, if the beginning of the
+previous email is fewer than `window-height' lines from the current
+point, move to it just like `notmuch-show-previous-message'.
 
 Otherwise, just scroll down a screenful of the current message.
 
-This command does not modify any message tags, (it does not undo
-any effects from previous calls to
-`notmuch-show-advance-and-archive'."
-  (interactive)
+This command does not modify any message tags, (it does not undo any
+effects from previous calls to `notmuch-show-advance-and-archive'."
+  (interactive "P")
   (let ((start-of-message (notmuch-show-message-top))
 	(start-of-window (window-start)))
     (cond
-     ;; Either this message is properly aligned with the start of the
-     ;; window or the start of this message is not visible on the
-     ;; screen - scroll.
-     ((or (= start-of-message start-of-window)
+     ;; Either (1) ARG is non-nil, (2) this message is properly
+     ;; aligned with the start of the window, or (3) the start of this
+     ;; message is not visible on the screen - scroll.
+     ((or arg
+	  (= start-of-message start-of-window)
 	  (< start-of-message start-of-window))
-      (scroll-down)
-      ;; If a small number of lines from the previous message are
-      ;; visible, realign so that the top of the current message is at
-      ;; the top of the screen.
-      (when (<= (count-screen-lines (window-start) start-of-message)
-		next-screen-context-lines)
-	(goto-char (notmuch-show-message-top))
-	(notmuch-show-message-adjust))
-      ;; Move to the top left of the window.
-      (goto-char (window-start)))
+      (scroll-down arg)
+      ;; Only move point when ARG is nil (i.e., when the user only
+      ;; wants to scroll)
+      (unless arg
+	;; If a small number of lines from the previous message are
+	;; visible, realign so that the top of the current message is
+	;; at the top of the screen.
+	(when (<= (count-screen-lines (window-start) start-of-message)
+		  next-screen-context-lines)
+	  (goto-char (notmuch-show-message-top))
+	  (notmuch-show-message-adjust))
+	;; Move to the top left of the window.
+	(goto-char (window-start))))
      (t
       ;; Move to the previous message.
       (notmuch-show-previous-message)))))
diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el
index faec89c4..0c178c03 100644
--- a/emacs/notmuch-tree.el
+++ b/emacs/notmuch-tree.el
@@ -668,29 +668,46 @@ Shows in split pane or whole window according to value of
       (notmuch-tree-show-message-out)
     (notmuch-tree-show-message-in)))
 
-(defun notmuch-tree-scroll-message-window ()
-  "Scroll the message window (if it exists)."
-  (interactive)
+(defun notmuch-tree-scroll-message-window (&optional arg)
+  "Scroll the message window (if it exists) ARG lines.
+
+If ARG is non-nil, scroll the screen that many lines upward.
+
+If ARG is nil, scroll upward by a near full screen. However, when ARG is
+nil and the end of the message window is already visible, do nothing and
+return non-nil instead of scrolling."
+  (interactive "P")
   (when (window-live-p notmuch-tree-message-window)
     (with-selected-window notmuch-tree-message-window
       (if (pos-visible-in-window-p (point-max))
 	  t
-	(scroll-up)))))
+	(scroll-up arg)))))
 
-(defun notmuch-tree-scroll-message-window-back ()
-  "Scroll the message window back (if it exists)."
-  (interactive)
+(defun notmuch-tree-scroll-message-window-back (&optional arg)
+  "Scroll the message window back (if it exists) ARG lines.
+
+If ARG is non-nil, scroll the screen that many lines down.
+
+If ARG is nil, scroll downward by a near full screen. However, when ARG
+is nil and the beginning of the message window is already visible, do
+nothing and return non-nil instead of scrolling."
+  (interactive "P")
   (when (window-live-p notmuch-tree-message-window)
     (with-selected-window notmuch-tree-message-window
       (if (pos-visible-in-window-p (point-min))
 	  t
-	(scroll-down)))))
+	(scroll-down arg)))))
 
-(defun notmuch-tree-scroll-or-next ()
+(defun notmuch-tree-scroll-or-next (&optional arg)
   "Scroll the message window.
-If it at end go to next message."
-  (interactive)
-  (when (notmuch-tree-scroll-message-window)
+
+If ARG is non-nil, scroll the screen that many lines upward.
+
+If ARG is nil, scroll upward by a near full screen. However, when ARG is
+nil and the end of the buffer is already visible, go to next message
+instead."
+  (interactive "P")
+  (when (notmuch-tree-scroll-message-window arg)
     (notmuch-tree-next-matching-message)))
 
 (defun notmuch-tree-quit (&optional kill-both)
-- 
2.52.0

