sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit 8044d63cdefd47b1b900c0be54038ea1e06f9665
parent dc62829249629046dee28d2df521894346be1130
Author: Dan Callaghan <djc@djc.id.au>
Date:   Sun,  5 Apr 2026 14:54:24 +1000

fix cursor position handling for half-page-{up,down}

Diffstat:
M lib/sup/modes/line_cursor_mode.rb | 10 ++++++++++
M test/unit/test_line_cursor_mode.rb | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+), 0 deletions(-)
diff --git a/lib/sup/modes/line_cursor_mode.rb b/lib/sup/modes/line_cursor_mode.rb
@@ -162,6 +162,11 @@ protected
     end
   end
 
+  def half_page_up
+    super
+    set_cursor_pos [botline, @curpos].min
+  end
+
   def page_down
     relpos = @curpos - topline
     super
@@ -169,6 +174,11 @@ protected
     call_load_more_callbacks buffer.content_height if lines < topline + buffer.content_height
   end
 
+  def half_page_down
+    super
+    set_cursor_pos [topline, @curpos].max
+  end
+
   def jump_to_start
     super
     set_cursor_pos @cursor_top
diff --git a/test/unit/test_line_cursor_mode.rb b/test/unit/test_line_cursor_mode.rb
@@ -140,6 +140,60 @@ class TestLineCursorMode < Minitest::Test
     assert_equal 80, mode.topline
   end
 
+  def test_half_page_down
+    mode = make_mode
+    expect_load_more 41
+
+    mode.handle_input Ncurses::CharCode.character("\C-d")
+    assert_equal 20, mode.curpos
+    assert_equal 20, mode.topline
+
+    mode.handle_input Ncurses::CharCode.character("\C-d")
+    assert_equal 40, mode.curpos
+    assert_equal 40, mode.topline
+  end
+
+  def test_half_page_down_when_fully_populated
+    mode = make_mode
+    expect_load_more 41
+    (0...119).map { |i| @lines << "more line #{i}" }  # enough for 4 full pages
+
+    mode.handle_input Ncurses::CharCode.character("\C-d")
+    assert_equal 20, mode.curpos
+    assert_equal 20, mode.topline
+
+    25.times do
+      mode.handle_input Ncurses::CharCode.character('j')
+    end
+    assert_equal 45, mode.curpos
+    assert_equal 20, mode.topline
+    mode.handle_input Ncurses::CharCode.character("\C-d")
+    assert_equal 45, mode.curpos
+    assert_equal 40, mode.topline
+  end
+
+  def test_half_page_up
+    mode = make_mode
+    expect_load_more 41
+    (0...119).map { |i| @lines << "more line #{i}" }  # enough for 4 full pages
+
+    mode.handle_input Ncurses::CharCode.keycode(Ncurses::KEY_NPAGE)
+    assert_equal 40, mode.curpos
+    assert_equal 40, mode.topline
+
+    mode.handle_input Ncurses::CharCode.character("\C-u")
+    assert_equal 40, mode.curpos
+    assert_equal 20, mode.topline
+
+    mode.handle_input Ncurses::CharCode.character('j')
+    assert_equal 41, mode.curpos
+    assert_equal 20, mode.topline
+
+    mode.handle_input Ncurses::CharCode.character("\C-u")
+    assert_equal 40, mode.curpos
+    assert_equal 0, mode.topline
+  end
+
   def test_jump_to_end
     mode = make_mode
     expect_load_more 41