Download raw body.
tog: nG key map like vi(1) and less(1)
On 22-07-17 12:54am, Mark Jamsek wrote: > On 22-07-14 03:24pm, Christian Weisgerber wrote: > > Mark Jamsek: > > > Below diff adds n{G,g} to all views, falling back to default G/g when no > count prefix is entered. Tree index is also added in this diff but, as > naddy suggests, will be committed separately if ok. It makes testing the > implementation easier. > > The thing I'm not too sure about, we center line n in the middle of the > page for the blame and diff views. As Stefan suggested, this makes the > most sense as it provides more context. However, the line index in the > diff header is in the form "i/N" such that i = first_displayed_line and > N = nlines in the file. Therefore, when using nG, the view is centered > on n but the index has no reference to n. Rather, i is > n - 1/2(view->nlines) [excluding header+border] except where > n < 1/2(view->nlines) when i is 1. For example, in an 80x24 terminal, > 99G will display an index of 89/N. Whether this is a problem or not, > I'm not sure. Please disregard previous. Revised diff below (no functional changes; style and tiny optimisation). diff refs/heads/main refs/heads/dev/gline commit - 86fbc0d4baa2c117d3bf5c65262d1857de548062 commit + 6748ca824b255354423f4bd36c4c60277094dfc4 blob - 4ec26b8962b911a5c478faf7aebc3d0686931d6e blob + 9f74828a77760dfa550b3531766dc6bc08694121 --- tog/tog.1 +++ tog/tog.1 @@ -93,6 +93,10 @@ N increments (default: 1). .It Cm + When in a split-screen view, increase the size of the focussed split N increments (default: 1). +.It Cm G +Go to line N in the view (default: last line). +.It Cm g +Go to line N in the view (default: first line). .El .Pp Global options must precede the command name, and are as follows: blob - 97ac0690a9232815bb10bc4a237246e2ec53e069 blob + 1e728fda4e21fedaf3ae4d8a273b023d9991393d --- 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,7 @@ get_compound_key(struct tog_view *view, int c) v = view->parent; view->count = 0; - cbreak(); /* block for input */ + nodelay(view->window, FALSE); /* block for input */ wmove(v->window, v->nlines - 1, 0); wclrtoeol(v->window); waddch(v->window, ':'); @@ -1246,6 +1247,12 @@ get_compound_key(struct tog_view *view, int c) n = n * 10 + (c - '0'); } while (((c = wgetch(view->window))) >= '0' && c <= '9' && c != ERR); + if (c == 'G' || c == 'g') { /* nG key map */ + view->gline = n; + n = 0; + c = 0; + } + /* Massage excessive or inapplicable values at the input handler. */ view->count = n; @@ -1283,15 +1290,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; @@ -1305,7 +1311,6 @@ view_input(struct tog_view **new, int *done, struct to 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(); @@ -2431,7 +2436,8 @@ log_scroll_down(struct tog_view *view, int maxscroll) /* * Ask the log thread for required amount of commits. */ - s->thread_args.commits_needed += maxscroll; + s->thread_args.commits_needed += + ncommits_needed - s->commits.ncommits; err = trigger_log_thread(view, 1); if (err) return err; @@ -3183,6 +3189,42 @@ view_init_hsplit(struct tog_view *view, int y) } static const struct got_error * +log_goto_line(struct tog_view *view, int nlines) +{ + const struct got_error *err = NULL; + struct tog_log_view_state *s = &view->state.log; + int g, idx = s->selected_entry->idx; + + g = view->gline; + view->gline = 0; + + if (g >= s->first_displayed_entry->idx + 1 && + g <= s->last_displayed_entry->idx + 1 && + g - s->first_displayed_entry->idx - 1 < nlines) { + s->selected = g - s->first_displayed_entry->idx - 1; + select_commit(s); + return NULL; + } + + if (idx + 1 < g) { + err = log_move_cursor_down(view, g - idx - 1); + if (!err && g > s->selected_entry->idx + 1) + err = log_move_cursor_down(view, + g - s->first_displayed_entry->idx - 1); + if (err) + return err; + } else if (idx + 1 > g) + log_move_cursor_up(view, idx - g + 1, 0); + + if (g < nlines && s->first_displayed_entry->idx == 0) + s->selected = g - 1; + + select_commit(s); + return NULL; + +} + +static const struct got_error * input_log_view(struct tog_view **new_view, struct tog_view *view, int ch) { const struct got_error *err = NULL; @@ -3206,6 +3248,9 @@ input_log_view(struct tog_view **new_view, struct tog_ if (view_is_hsplit_top(view)) --eos; /* border */ + if (view->gline) + return log_goto_line(view, eos); + switch (ch) { case 'q': s->quit = 1; @@ -3859,13 +3904,55 @@ done: return err; } +static int +gotoline(struct tog_view *view, int *lineno, int *nprinted) +{ + FILE *f = NULL; + int *eof, *first, *selected; + + if (view->type == TOG_VIEW_DIFF) { + struct tog_diff_view_state *s = &view->state.diff; + + first = &s->first_displayed_line; + selected = first; + eof = &s->eof; + f = s->f; + } 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; + } else + return 0; + + /* Center gline in the middle of the page like vi(1). */ + if (*lineno < view->gline - (view->nlines - 3) / 2) + return 0; + if (*first != 1 && (*lineno > view->gline - (view->nlines - 3) / 2)) { + rewind(f); + *eof = 0; + *first = 1; + *lineno = 0; + *nprinted = 0; + return 0; + } + + *selected = view->gline <= (view->nlines - 3) / 2 ? + view->gline : (view->nlines - 3) / 2 + 1; + 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 +3963,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 +4013,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 +4057,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 +5173,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 +5208,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); @@ -6164,7 +6265,7 @@ draw_tree_entries(struct tog_view *view, const char *p struct got_tree_entry *te; wchar_t *wline; struct tog_color *tc; - int width, n, i, nentries; + int width, n, nentries, i = 1; int limit = view->nlines; s->ndisplayed = 0; @@ -6194,6 +6295,15 @@ draw_tree_entries(struct tog_view *view, const char *p wstandend(view->window); free(wline); wline = NULL; + + if (s->selected_entry) { + i = got_tree_entry_get_index(s->selected_entry); + i += s->tree == s->root ? 1 : 2; /* account for ".." entry */ + } + nentries = got_object_tree_get_nentries(s->tree); + wprintw(view->window, " [%d/%d]", i, + nentries + (s->tree == s->root ? 0 : 1)); /* ".." in !root tree */ + if (width < view->ncols - 1) waddch(view->window, '\n'); if (--limit <= 0) @@ -6232,7 +6342,6 @@ draw_tree_entries(struct tog_view *view, const char *p te = s->first_displayed_entry; } - nentries = got_object_tree_get_nentries(s->tree); for (i = got_tree_entry_get_index(te); i < nentries; i++) { char *line = NULL, *id_str = NULL, *link_target = NULL; const char *modestr = ""; @@ -6352,8 +6461,10 @@ tree_scroll_down(struct tog_view *view, int maxscroll) last = s->last_displayed_entry; while (next && n++ < maxscroll) { - if (last) + if (last) { + s->last_displayed_entry = last; last = got_tree_entry_get_next(s->tree, last); + } if (last || (view->mode == TOG_VIEW_SPLIT_HRZN && next)) { s->first_displayed_entry = next; next = got_tree_entry_get_next(s->tree, next); @@ -6694,6 +6805,62 @@ show_tree_view(struct tog_view *view) } static const struct got_error * +tree_goto_line(struct tog_view *view, int nlines) +{ + const struct got_error *err = NULL; + struct tog_tree_view_state *s = &view->state.tree; + struct got_tree_entry **fte, **lte, **ste; + int g, last, first = 1, i = 1; + int root = s->tree == s->root; + int off = root ? 1 : 2; + + g = view->gline; + view->gline = 0; + + fte = &s->first_displayed_entry; + lte = &s->last_displayed_entry; + ste = &s->selected_entry; + + if (*fte != NULL) { + first = got_tree_entry_get_index(*fte); + first += off; /* account for ".." */ + } + last = got_tree_entry_get_index(*lte); + last += off; + + if (g >= first && g <= last && g - first < nlines) { + s->selected = g - first; + return NULL; /* gline is on the current page */ + } + + if (*ste != NULL) { + i = got_tree_entry_get_index(*ste); + i += off; + } + + if (i < g) { + err = tree_scroll_down(view, g - i); + if (err) + return err; + if (got_tree_entry_get_index(*lte) >= + got_object_tree_get_nentries(s->tree) - 1 && + first + s->selected < g && + s->selected < s->ndisplayed - 1) { + first = got_tree_entry_get_index(*fte); + first += off; + s->selected = g - first; + } + } else if (i > g) + tree_scroll_up(s, i - g); + + if (g < nlines && + (*fte == NULL || (root && !got_tree_entry_get_index(*fte)))) + s->selected = g - 1; + + return NULL; +} + +static const struct got_error * input_tree_view(struct tog_view **new_view, struct tog_view *view, int ch) { const struct got_error *err = NULL; @@ -6702,6 +6869,16 @@ input_tree_view(struct tog_view **new_view, struct tog struct got_tree_entry *te; int begin_y = 0, begin_x = 0, n, nscroll = view->nlines - 3; + if (view->gline) { + if (view->gline == 1) + ch = 'g'; + else if (view->gline > got_object_tree_get_nentries(s->tree)) + ch = 'G'; + else + return tree_goto_line(view, nscroll); + view->gline = 0; + } + switch (ch) { case 'i': s->show_ids = !s->show_ids; @@ -7352,8 +7529,10 @@ ref_scroll_down(struct tog_view *view, int maxscroll) last = s->last_displayed_entry; while (next && n++ < maxscroll) { - if (last) + if (last) { + s->last_displayed_entry = last; last = TAILQ_NEXT(last, entry); + } if (last || (view->mode == TOG_VIEW_SPLIT_HRZN)) { s->first_displayed_entry = next; next = TAILQ_NEXT(next, entry); @@ -7634,7 +7813,43 @@ done: free(commit_id); return err; } + static const struct got_error * +ref_goto_line(struct tog_view *view, int nlines) +{ + const struct got_error *err = NULL; + struct tog_ref_view_state *s = &view->state.ref; + int g, idx = s->selected_entry->idx; + + g = view->gline; + view->gline = 0; + + if (g >= s->first_displayed_entry->idx + 1 && + g <= s->last_displayed_entry->idx + 1 && + g - s->first_displayed_entry->idx - 1 < nlines) { + s->selected = g - s->first_displayed_entry->idx - 1; + return NULL; + } + + if (idx + 1 < g) { + err = ref_scroll_down(view, g - idx - 1); + if (err) + return err; + if (TAILQ_NEXT(s->last_displayed_entry, entry) == NULL && + s->first_displayed_entry->idx + s->selected < g && + s->selected < s->ndisplayed - 1) + s->selected = g - s->first_displayed_entry->idx - 1; + } else if (idx + 1 > g) + ref_scroll_up(s, idx - g + 1); + + if (g < nlines && s->first_displayed_entry->idx == 0) + s->selected = g - 1; + + return NULL; + +} + +static const struct got_error * input_ref_view(struct tog_view **new_view, struct tog_view *view, int ch) { const struct got_error *err = NULL; @@ -7643,6 +7858,16 @@ input_ref_view(struct tog_view **new_view, struct tog_ struct tog_reflist_entry *re; int begin_y = 0, begin_x = 0, n, nscroll = view->nlines - 1; + if (view->gline) { + if (view->gline >= s->nrefs) + ch = 'G'; + else if (view->gline == 1) + ch = 'g'; + else + return ref_goto_line(view, nscroll); + view->gline = 0; + } + switch (ch) { case 'i': s->show_ids = !s->show_ids; -- 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)