Author: rinrab
Date: Thu Apr  9 14:49:00 2026
New Revision: 1932925

Log:
svnbrowse: More shortcuts for scrolling and moving around the list.

### Current controls ###

- kj, arrows - up/down
- CTRL-P, CTRL-N - also up/down, common in vim list views
- enter - enter, Unix terminals also produce '\n' via CTRL+M.
- backspace, u, '-' - up a directory. I think backspace would be pretty
  intuative as the default shortcut. u and '-' are stolen from vim's netrw.
  Well technically u is defined as "Change to recently-visited directory" but
  it's almost the same as going up and we're not going to implement history
  anytime soon.
- CTRL+Y/CTRL+E - scrolls one line up/down
- CTRL+U/CTRL+D - scrolls half-page up/down
- CTRL+B/CTRL+F (forward/backwards), page-up/page-down - scrolls whole page
  up/down
- g/HOME - select first element 
- G/END - select last element 
- z - center selection
- q/ESC - get out of here

TODO: Add repetition by entering a number+action that can be repeated (like
movement by one line). Also CTRL+U/CTRL+D should remember it for usages without
a number. (do we need all of that?)

Note: According to [1], it's a bad practise to use shortcuts like CTRL+arrow or
CTRL+SHIFT+KEY - TUI apps should only use basic combinations or
CTRL+alphabetical symbol.

If there is a useful keybind that I missed, please feel free to extend the
code.

* subversion/svnbrowse/svnbrowse.c
  (view_on_event): Factor out scrollsize local variable and implement movement
   from above.
* subversion/svnbrowse/model.c
  (svn_browse__model_scroll_in_view): Forbid scrolling into negatives.

[1] https://invisible-island.net/ncurses/ncurses.faq.html#modified_keys

Modified:
   subversion/trunk/subversion/svnbrowse/model.c
   subversion/trunk/subversion/svnbrowse/svnbrowse.c

Modified: subversion/trunk/subversion/svnbrowse/model.c
==============================================================================
--- subversion/trunk/subversion/svnbrowse/model.c       Thu Apr  9 13:51:29 
2026        (r1932924)
+++ subversion/trunk/subversion/svnbrowse/model.c       Thu Apr  9 14:49:00 
2026        (r1932925)
@@ -144,6 +144,9 @@ svn_browse__model_scroll_in_view(svn_bro
   state->scroller_offset = min(state->scroller_offset,
                                state->selection);
 
+  /* forbid scrolling into negatives */
+  state->scroller_offset = max(state->scroller_offset, 0);
+
   return SVN_NO_ERROR;
 }
 

Modified: subversion/trunk/subversion/svnbrowse/svnbrowse.c
==============================================================================
--- subversion/trunk/subversion/svnbrowse/svnbrowse.c   Thu Apr  9 13:51:29 
2026        (r1932924)
+++ subversion/trunk/subversion/svnbrowse/svnbrowse.c   Thu Apr  9 14:49:00 
2026        (r1932925)
@@ -126,6 +126,9 @@ view_make(svn_browse__model_t *model, ap
 static svn_error_t *
 view_on_event(svn_browse__view_t *view, int ch, apr_pool_t *scratch_pool)
 {
+  /* scrollable height is one row less than the whole view */
+  int scrollsize = getmaxy(stdscr) - 1;
+
   /* ch is received from getch() which would read the next character/key with
    * the following additional rules:
    * 1. as we configured it to use keypad(), arrows and other special keys
@@ -139,10 +142,12 @@ view_on_event(svn_browse__view_t *view,
     {
       case KEY_UP:
       case 'k':
+      case CTRL('p'):
         SVN_ERR(svn_browse__model_move_selection(view->model, -1));
         break;
       case KEY_DOWN:
       case 'j':
+      case CTRL('n'):
         SVN_ERR(svn_browse__model_move_selection(view->model, 1));
         break;
       case '\n':
@@ -154,13 +159,45 @@ view_on_event(svn_browse__view_t *view,
       case 'u':
         SVN_ERR(svn_browse__model_go_up(view->model, scratch_pool));
         break;
+      case CTRL('e'):
+        view->model->current->scroller_offset += 1;
+        break;
+      case CTRL('y'):
+        view->model->current->scroller_offset -= 1;
+        break;
+      case CTRL('d'):
+        SVN_ERR(svn_browse__model_move_selection(view->model, scrollsize / 2));
+        break;
+      case CTRL('f'):
+      case KEY_NPAGE:
+        SVN_ERR(svn_browse__model_move_selection(view->model, scrollsize));
+        break;
+      case CTRL('u'):
+        SVN_ERR(svn_browse__model_move_selection(view->model, -scrollsize / 
2));
+        break;
+      case CTRL('b'):
+      case KEY_PPAGE:
+        SVN_ERR(svn_browse__model_move_selection(view->model, -scrollsize));
+        break;
+      case 'g':
+      case KEY_HOME:
+        view->model->current->selection = 0;
+        break;
+      case 'G':
+      case KEY_END:
+        view->model->current->selection =
+            view->model->current->list->nelts - 1;
+        break;
+      case 'z':
+        view->model->current->scroller_offset =
+            view->model->current->selection - scrollsize / 2;
+        break;
       case 'q':
       case KEY_ESC:
         return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
     }
 
-  /* scrollable height is one row less than the whole view */
-  SVN_ERR(svn_browse__model_scroll_in_view(view->model, getmaxy(stdscr) - 1));
+  SVN_ERR(svn_browse__model_scroll_in_view(view->model, scrollsize));
 
   return SVN_NO_ERROR;
 }

Reply via email to