Download raw body.
tog: nG key map like vi(1) and less(1)
On 22-07-18 05:26pm, Mark Jamsek wrote: > On 22-07-17 10:24pm, Christian Weisgerber wrote: > > Mark Jamsek: > > > > > > 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. [...] Whether this is a problem or not, > > > > I'm not sure. > > > > I'm not sure either. :-) It feels odd. > > I think so too. But I'm also not sure what the fix should be. We could > change it back to making line n in the nG case the first displayed line, > but as stsp observed, this misses preceding context. We might have found a reasonable solution: keep centering line n in the nG case, but highlight line n till the next key press. This way, as stsp suggested, we keep the leading and following context, and provide a visual, albeit ephemeral, clue of the desired line. Rebased diff adds this solution. Highlight honours configured line colours (e.g., diff minus line highlight will be TOG_COLOR_DIFF_MINUS), which I quite like, but if it's too distracting we can make it monochromatic. I tried this, and I kept having to look at the beginning of the line to the +/-/' ' sign for a clue as to what was going on, which detracts from the benefit of running tog with TOG_COLORS. We could also make the highlight persist till the next nG selection, but I think keeping it highlighted till the next key press provides enough of a clue without cluttering the view. diff refs/heads/main refs/heads/dev/gline commit - d089c3673c062bc918fd8fedb82b992855965ec6 commit + fa2454e78de90513913ff3648efe666b9472a16d 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 - a1c09fab6d16d0c21514c895128b7972db68d6c6 blob + 167ce2965c3bd24fcca4fd197789738cb4fdba72 --- 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, hiline; /* navigate to and highlight this nG line */ int ch, count; /* current keymap and count prefix */ int resized; /* set when in a resize event */ int focussed; /* Only set on one parent or child view at a time. */ @@ -1234,6 +1235,7 @@ get_compound_key(struct tog_view *view, int c) view->count = 0; cbreak(); /* block for input */ + nodelay(view->window, FALSE); wmove(v->window, v->nlines - 1, 0); wclrtoeol(v->window); waddch(v->window, ':'); @@ -1255,6 +1257,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 = view->hiline = n; + n = 0; + c = 0; + } + /* Massage excessive or inapplicable values at the input handler. */ view->count = n; @@ -1292,7 +1300,6 @@ view_input(struct tog_view **new, int *done, struct to return NULL; } - nodelay(view->window, FALSE); /* Allow threads to make progress while we are waiting for input. */ errcode = pthread_mutex_unlock(&tog_mutex); if (errcode) @@ -1311,10 +1318,12 @@ view_input(struct tog_view **new, int *done, struct to if (ch >= '1' && ch <= '9') view->ch = ch = get_compound_key(view, ch); } + if (view->hiline && ch != ERR && ch != 0) + view->hiline = 0; /* key pressed, clear line highlight */ + nodelay(view->window, TRUE); 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(); @@ -2440,7 +2449,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; @@ -3193,6 +3203,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; @@ -3216,6 +3262,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; @@ -3869,13 +3918,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; @@ -3885,13 +3976,18 @@ draw_file(struct tog_view *view, const char *header) int max_lines = view->nlines; int nlines = s->nlines; off_t line_offset; + attr_t attr; + 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, @@ -3932,6 +4028,14 @@ draw_file(struct tog_view *view, const char *header) return got_ferror(s->f, GOT_ERR_IO); } + attr = 0; + if (++lineno < s->first_displayed_line) + continue; + if (view->gline && !gotoline(view, &lineno, &nprinted)) + continue; + if (lineno == view->hiline) + attr = A_BOLD | A_REVERSE; + /* Set view->maxx based on full line length. */ err = format_line(&wline, &width, NULL, line, 0, INT_MAX, 0, view->x ? 1 : 0); @@ -3945,8 +4049,9 @@ draw_file(struct tog_view *view, const char *header) tc = match_color(&s->colors, line); if (tc) - wattr_on(view->window, - COLOR_PAIR(tc->colorpair), NULL); + attr |= COLOR_PAIR(tc->colorpair); + if (attr) + wattron(view->window, attr); if (s->first_displayed_line + nprinted == s->matched_line && regmatch->rm_so >= 0 && regmatch->rm_so < regmatch->rm_eo) { err = add_matched_line(&width, line, view->ncols, 0, @@ -3967,12 +4072,18 @@ draw_file(struct tog_view *view, const char *header) free(wline); wline = NULL; } - if (tc) - wattr_off(view->window, - COLOR_PAIR(tc->colorpair), NULL); - if (width <= view->ncols - 1) - waddch(view->window, '\n'); - nprinted++; + if (lineno == view->hiline) { + /* highlight full gline length */ + while (width++ < view->ncols) + waddch(view->window, ' '); + } else { + if (width <= view->ncols - 1) + waddch(view->window, '\n'); + } + if (attr) + wattroff(view->window, attr); + if (++nprinted == 1) + s->first_displayed_line = lineno; } free(line); if (nprinted >= 1) @@ -5087,6 +5198,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) { @@ -5119,6 +5233,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); @@ -6174,7 +6290,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; @@ -6204,6 +6320,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) @@ -6242,7 +6367,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 = ""; @@ -6362,8 +6486,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); @@ -6704,6 +6830,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; @@ -6712,6 +6894,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; @@ -7362,8 +7554,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); @@ -7644,7 +7838,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; @@ -7653,6 +7883,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)