Author: rinrab
Date: Sat May 16 15:32:17 2026
New Revision: 1934261
Log:
svnbrowse: Display file information if a file is opened. This prevents it from
crashing when each time when we face a file. Yet it's not perfect but at least
we have state handling logic and the looks can be improved later.
* subversion/svnbrowse/model.c
(state_from_file, state_from_dir): New functions; state_from_dir does the
same thing as svn_browse__state_create() was before.
(svn_browse__state_create): Forward logic to either state_from_file or
state_from_dir. Currently try directory first and in case of an error treat
it as a file.
* subversion/svnbrowse/svnbrowse.c
(#include): Add svn_time.h.
(view_on_event): Check state before proceeding to handle keystrokes.
(view_get_list_height): New function.
(view_draw_footer): Use view_get_list_height() to measure size of the
current list.
(dir_draw, file_draw): New functions.
(view_draw): Call either dir_draw() or file_draw() based on the current
state.
* subversion/svnbrowse/svnbrowse.h
(svn_browse__state_type_e): New enum.
(svn_browse__state_t): Add info for node if it was a file and update some
comments.
Modified:
subversion/trunk/subversion/svnbrowse/model.c
subversion/trunk/subversion/svnbrowse/svnbrowse.c
subversion/trunk/subversion/svnbrowse/svnbrowse.h
Modified: subversion/trunk/subversion/svnbrowse/model.c
==============================================================================
--- subversion/trunk/subversion/svnbrowse/model.c Sat May 16 14:27:11
2026 (r1934260)
+++ subversion/trunk/subversion/svnbrowse/model.c Sat May 16 15:32:17
2026 (r1934261)
@@ -85,12 +85,12 @@ sort_item_comparison_func(const void *le
}
svn_error_t *
-svn_browse__state_create(svn_browse__state_t **state_p,
- svn_ra_session_t *session,
- const char *relpath,
- svn_revnum_t revision,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+state_from_dir(svn_browse__state_t **state_p,
+ svn_ra_session_t *session,
+ const char *relpath,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_browse__state_t *state = apr_pcalloc(result_pool, sizeof(*state));
svn_revnum_t fetched_revnum;
@@ -100,6 +100,7 @@ svn_browse__state_create(svn_browse__sta
SVN_ERR(svn_ra_get_dir2(session, &dirents, &fetched_revnum, NULL, relpath,
revision, SVN_DIRENT_ALL, scratch_pool));
+ state->type = svn_browse__state_dir;
state->relpath = apr_pstrdup(result_pool, relpath);
state->revision = fetched_revnum;
state->selection = 0;
@@ -129,6 +130,51 @@ svn_browse__state_create(svn_browse__sta
}
svn_error_t *
+state_from_file(svn_browse__state_t **state_p,
+ svn_ra_session_t *session,
+ const char *relpath,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_browse__state_t *state = apr_pcalloc(result_pool, sizeof(*state));
+ svn_revnum_t fetched_revnum;
+ apr_hash_index_t *hi;
+ svn_dirent_t *dirent;
+
+ SVN_ERR(svn_ra_stat(session, relpath, revision, &dirent, scratch_pool));
+
+ state->type = svn_browse__state_file;
+ state->relpath = apr_pstrdup(result_pool, relpath);
+ state->revision = fetched_revnum;
+ state->this_dirent = svn_dirent_dup(dirent, result_pool);
+ state->pool = result_pool;
+
+ *state_p = state;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_browse__state_create(svn_browse__state_t **state_p,
+ svn_ra_session_t *session,
+ const char *relpath,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+
+ err = state_from_dir(state_p, session, relpath, revision, result_pool,
+ scratch_pool);
+
+ if (err && err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)
+ return svn_error_trace(state_from_file(state_p, session, relpath, revision,
+ result_pool, scratch_pool));
+ else
+ return svn_error_trace(err);
+}
+
+svn_error_t *
svn_browse__model_enter_path(svn_browse__model_t *ctx, const char *relpath,
apr_pool_t *scratch_pool)
{
Modified: subversion/trunk/subversion/svnbrowse/svnbrowse.c
==============================================================================
--- subversion/trunk/subversion/svnbrowse/svnbrowse.c Sat May 16 14:27:11
2026 (r1934260)
+++ subversion/trunk/subversion/svnbrowse/svnbrowse.c Sat May 16 15:32:17
2026 (r1934261)
@@ -37,6 +37,7 @@
#include <curses.h>
#include "svn_private_config.h"
+#include "svn_time.h"
#include "svnbrowse.h"
/* Option codes and descriptions for the command line client.
@@ -263,22 +264,9 @@ view_on_event(svn_browse__view_t *view,
* 3. The rest of keys remain as their equivalents on the current layout.
* 4. If shift is held, they just become uppercased.
*/
+
switch (ch)
{
- 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':
- case '\r':
- SVN_ERR(svn_browse__model_go_enter(view->model, scratch_pool));
- break;
case KEY_BACKSPACE:
case '-':
case 'u':
@@ -286,45 +274,72 @@ view_on_event(svn_browse__view_t *view,
view->model->current->scroller_offset =
view->model->current->selection - scrollsize / 2;
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);
+ default:
+ break;
}
- SVN_ERR(svn_browse__model_scroll_in_view(view->model, scrollsize));
+ if (view->model->current->type == svn_browse__state_dir)
+ {
+ switch (ch)
+ {
+ 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':
+ case '\r':
+ SVN_ERR(svn_browse__model_go_enter(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;
+ }
+
+ SVN_ERR(svn_browse__model_scroll_in_view(view->model, scrollsize));
+ }
return SVN_NO_ERROR;
}
@@ -494,6 +509,15 @@ format_percentage_scroll(int scroll, int
}
}
+static int
+view_get_list_height(const svn_browse__state_t *state)
+{
+ if (state->type == svn_browse__state_dir)
+ return state->list->nelts;
+ else
+ return 0;
+}
+
static void
view_draw_footer(svn_browse__view_t *view, WINDOW *win,
apr_pool_t *scratch_pool)
@@ -508,26 +532,24 @@ view_draw_footer(svn_browse__view_t *vie
getmaxx(win) - 4 - strlen(brand) - 16,
scratch_pool));
waddstr(win, brand);
+
waddstr(win, leftpad(apr_psprintf(scratch_pool, "%d/%d",
- state->selection + 1, state->list->nelts),
+ state->selection + 1,
+ view_get_list_height(state)),
8, scratch_pool));
waddstr(win, leftpad(format_percentage_scroll(state->scroller_offset,
- state->list->nelts,
+ view_get_list_height(state),
getmaxy(view->list),
scratch_pool),
8, scratch_pool));
waddstr(win, " ");
}
-
static void
-view_draw(svn_browse__view_t *view, apr_pool_t *pool)
+dir_draw(svn_browse__view_t *view, apr_pool_t *pool)
{
int i;
- view_draw_header(view, view->header, pool);
- view_draw_footer(view, view->footer, pool);
-
for (i = 0; i < view->model->current->list->nelts; i++)
{
svn_browse__item_t *item = APR_ARRAY_IDX(view->model->current->list, i,
@@ -540,6 +562,39 @@ view_draw(svn_browse__view_t *view, apr_
}
}
+static void
+file_draw(svn_browse__view_t *view, apr_pool_t *pool)
+{
+ const svn_browse__state_t *state = view->model->current;
+
+ mvwprintw(view->list, 0, 0, " File: %s",
+ svn_relpath_basename(state->relpath, pool));
+
+ mvwprintw(view->list, 1, 0, " Last Changed Rev: %ld",
+ state->this_dirent->created_rev);
+ mvwprintw(view->list, 2, 0, " Last Changed Author: %s",
+ state->this_dirent->last_author);
+ mvwprintw(view->list, 3, 0, " Last Changed Date: %s",
+ svn_time_to_human_cstring(state->this_dirent->time, pool));
+}
+
+static void
+view_draw(svn_browse__view_t *view, apr_pool_t *pool)
+{
+ view_draw_header(view, view->header, pool);
+ view_draw_footer(view, view->footer, pool);
+
+ switch (view->model->current->type)
+ {
+ case svn_browse__state_dir:
+ dir_draw(view, pool);
+ break;
+ case svn_browse__state_file:
+ file_draw(view, pool);
+ break;
+ }
+}
+
static svn_error_t *
show_usage(apr_pool_t *scratch_pool)
{
Modified: subversion/trunk/subversion/svnbrowse/svnbrowse.h
==============================================================================
--- subversion/trunk/subversion/svnbrowse/svnbrowse.h Sat May 16 14:27:11
2026 (r1934260)
+++ subversion/trunk/subversion/svnbrowse/svnbrowse.h Sat May 16 15:32:17
2026 (r1934261)
@@ -68,15 +68,26 @@ typedef struct svn_browse__item_t {
const svn_dirent_t *dirent;
} svn_browse__item_t;
+typedef enum svn_browse__state_type_e {
+ svn_browse__state_dir,
+ svn_browse__state_file,
+} svn_browse__state_type_e;
+
/* a state of a single directory */
typedef struct svn_browse__state_t {
+ svn_browse__state_type_e type;
+
/* information about this node */
const char *relpath;
svn_revnum_t revision;
- /* stores the list of nodes in this state; an array of svn_browse__item_t */
+ /* stores the list of nodes in this state; an array of svn_browse__item_t or
+ * NULL if 'type' is set to svn_browse__state_file */
apr_array_header_t *list;
+ /* only available for files */
+ const svn_dirent_t *this_dirent;
+
/* the index of hovered item */
int selection;
int scroller_offset;