Download raw body.
tog: nG key map like vi(1) and less(1)
This implements the nG key map--functionally the same as vi(1) and
less(1)--to navigate to line n in the diff and blame views.
As the diff view is a pager, we make nG behave like less(1) such that
line n will be the first line on the page.
And because the blame view is more like an editor with navigation, we
make nG behave like vi(1) such that line n is centered in the middle of
the page--except for when n < view->nlines / 2. In this case, line
n will be the nth line on the page (excluding the header).
The alternative is to make them both either place line n at the top
[like less(1)] or the middle [like vi(1)] of the page. If we go that
route, I'm partial to centering the line.
I didn't think it made sense to add this to the log, tree, and ref
views; but if you think we should make it global, I'm happy to expand
it.
naddy suggested a while back that this makes the g key map somewhat
redundant as 1G satisfies this use case, so we could be more like
vi(1) and remove g; however, less(1) provides g so there's precedent for
both. And given this only adds nG to two views, we probably want to keep
g unless expanding nG to all views.
diff refs/heads/main refs/heads/dev/gline
commit - 2525dccb913c5e7f293cfcad103af7a761d32b34
commit + 3539279418de788b5fbc216b5fc6ba14f67cfe91
blob - 4ec26b8962b911a5c478faf7aebc3d0686931d6e
blob + 3dee27c9da2e151c418d27f26dbfec60d1f27f47
--- tog/tog.1
+++ tog/tog.1
@@ -286,8 +286,10 @@ Scroll down N half pages (default: 1).
Scroll up N half pages (default: 1).
.It Cm Home, g
Scroll to the top of the view.
-.It Cm End, G
+.It Cm End
Scroll to the bottom of the view.
+.It Cm G
+Go to line N in the diff (default: last line).
.It Cm \&[
Reduce diff context by N lines (default: 1).
.It Cm \&]
@@ -384,8 +386,10 @@ Move the selection cursor down N half pages (default:
Move the selection cursor up N half pages (default: 1).
.It Cm Home, g
Move the selection cursor to the first line of the file.
-.It Cm End, G
+.It Cm End
Move the selection cursor to the last line of the file.
+.It Cm G
+Go to line N in the file (default: last line).
.It Cm Enter
Open a
.Cm diff
blob - 41a38da73b3c99dd62ddda5ae9e19708dea2b862
blob + b04bb37cdd7c24649cca3ddc8764f73970374122
--- tog/tog.c
+++ tog/tog.c
@@ -516,6 +516,7 @@ struct tog_view {
int maxx, x; /* max column and current start column */
int lines, cols; /* copies of LINES and COLS */
int nscrolled, offset; /* lines scrolled and hsplit line offset */
+ int gline; /* navigate to this nG line */
int ch, count; /* current keymap and count prefix */
int resize; /* set when in a resize event */
int focussed; /* Only set on one parent or child view at a time. */
@@ -1224,7 +1225,6 @@ get_compound_key(struct tog_view *view, int c)
v = view->parent;
view->count = 0;
- cbreak(); /* block for input */
wmove(v->window, v->nlines - 1, 0);
wclrtoeol(v->window);
waddch(v->window, ':');
@@ -1283,15 +1283,14 @@ view_input(struct tog_view **new, int *done, struct to
return NULL;
}
- nodelay(view->window, FALSE);
+ cbreak();
+ nodelay(view->window, TRUE);
/* Allow threads to make progress while we are waiting for input. */
errcode = pthread_mutex_unlock(&tog_mutex);
if (errcode)
return got_error_set_errno(errcode, "pthread_mutex_unlock");
/* If we have an unfinished count, let C-g or backspace abort. */
if (view->count && --view->count) {
- cbreak();
- nodelay(view->window, TRUE);
ch = wgetch(view->window);
if (ch == CTRL('g') || ch == KEY_BACKSPACE)
view->count = 0;
@@ -1299,13 +1298,19 @@ view_input(struct tog_view **new, int *done, struct to
ch = view->ch;
} else {
ch = wgetch(view->window);
- if (ch >= '1' && ch <= '9')
+ if (ch >= '1' && ch <= '9') {
+ nodelay(view->window, FALSE);
view->ch = ch = get_compound_key(view, ch);
+ if (ch == 'G') {
+ view->gline = view->count;
+ view->ch = ch = 0;
+ view->count = 0;
+ }
+ }
}
errcode = pthread_mutex_lock(&tog_mutex);
if (errcode)
return got_error_set_errno(errcode, "pthread_mutex_lock");
- nodelay(view->window, TRUE);
if (tog_sigwinch_received || tog_sigcont_received) {
tog_resizeterm();
@@ -3859,13 +3864,70 @@ done:
return err;
}
+static int
+gotoline(struct tog_view *view, int *lineno, int *nprinted)
+{
+ FILE *f = NULL;
+ int *eof, *first, *selected;
+ int restart = 0;
+
+ if (view->type == TOG_VIEW_DIFF) {
+ struct tog_diff_view_state *s = &view->state.diff;
+
+ first = &s->first_displayed_line;
+ selected = &s->selected_line;
+ eof = &s->eof;
+ f = s->f;
+
+ /* Make gline the first line on the page like less(1). */
+ if (*first != 1 && *lineno > view->gline)
+ restart = 1; /* after gline */
+ else if (*lineno < view->gline) /* before gline */
+ return 0;
+ else
+ *selected = 1; /* at gline */
+ } else if (view->type == TOG_VIEW_BLAME) {
+ struct tog_blame_view_state *s = &view->state.blame;
+
+ first = &s->first_displayed_line;
+ selected = &s->selected_line;
+ eof = &s->eof;
+ f = s->blame.f;
+
+ /* Center gline in the middle of the page like vi(1). */
+ if (*first != 1 &&
+ (*lineno > view->gline - (view->nlines - 3) / 2))
+ restart = 1;
+ else if (*lineno < view->gline - (view->nlines - 3) / 2)
+ return 0;
+ else {
+ *selected = view->gline <= (view->nlines - 3) / 2 ?
+ view->gline : (view->nlines - 3) / 2 + 1;
+ }
+ } else
+ return 0;
+
+ if (restart) {
+ rewind(f);
+ *eof = 0;
+ *first = 1;
+ *lineno = 0;
+ *nprinted = 0;
+ return 0;
+ }
+
+ view->gline = 0;
+
+ return 1;
+}
+
static const struct got_error *
draw_file(struct tog_view *view, const char *header)
{
struct tog_diff_view_state *s = &view->state.diff;
regmatch_t *regmatch = &view->regmatch;
const struct got_error *err;
- int nprinted = 0;
+ int lineno, nprinted = 0;
char *line;
size_t linesize = 0;
ssize_t linelen;
@@ -3876,12 +3938,16 @@ draw_file(struct tog_view *view, const char *header)
int nlines = s->nlines;
off_t line_offset;
+ lineno = s->first_displayed_line - 1;
line_offset = s->line_offsets[s->first_displayed_line - 1];
if (fseeko(s->f, line_offset, SEEK_SET) == -1)
return got_error_from_errno("fseek");
werase(view->window);
+ if (view->gline > s->nlines - 1)
+ view->gline = s->nlines - 1;
+
if (header) {
if (asprintf(&line, "[%d/%d] %s",
s->first_displayed_line - 1 + s->selected_line, nlines,
@@ -3922,6 +3988,10 @@ draw_file(struct tog_view *view, const char *header)
return got_ferror(s->f, GOT_ERR_IO);
}
+ if (++lineno < s->first_displayed_line)
+ continue;
+ if (view->gline && !gotoline(view, &lineno, &nprinted))
+ continue;
/* Set view->maxx based on full line length. */
err = format_line(&wline, &width, NULL, line, 0, INT_MAX, 0,
view->x ? 1 : 0);
@@ -3962,7 +4032,8 @@ draw_file(struct tog_view *view, const char *header)
COLOR_PAIR(tc->colorpair), NULL);
if (width <= view->ncols - 1)
waddch(view->window, '\n');
- nprinted++;
+ if (++nprinted == 1)
+ s->first_displayed_line = lineno;
}
free(line);
if (nprinted >= 1)
@@ -5077,6 +5148,9 @@ draw_blame(struct tog_view *view)
if (width < view->ncols - 1)
waddch(view->window, '\n');
+ if (view->gline > blame->nlines)
+ view->gline = blame->nlines;
+
if (asprintf(&line, "[%d/%d] %s%s",
s->first_displayed_line - 1 + s->selected_line, blame->nlines,
s->blame_complete ? "" : "annotating... ", s->path) == -1) {
@@ -5109,6 +5183,8 @@ draw_blame(struct tog_view *view)
}
if (++lineno < s->first_displayed_line)
continue;
+ if (view->gline && !gotoline(view, &lineno, &nprinted))
+ continue;
/* Set view->maxx based on full line length. */
err = format_line(&wline, &width, NULL, line, 0, INT_MAX, 9, 1);
--
Mark Jamsek <fnc.bsdbox.org>
GPG: F2FF 13DE 6A06 C471 CA80 E6E2 2930 DC66 86EE CF68
tog: nG key map like vi(1) and less(1)