commit 145b6838cd6157eeb49e14f2d0a3c6518f33440f from: Stefan Sperling date: Thu Jun 16 07:00:28 2022 UTC implement horizontal scrolling for tog diff, blame, and log views Patch by Mark Jamsek commit - 1b1b91abdb4cd380995e3542580daa8700d93f6f commit + 145b6838cd6157eeb49e14f2d0a3c6518f33440f blob - 56d92245cc73778b494f7d494ab0ad7560d3fab6 blob + 1f0a260861c8ff95ea3b569c0b3bba712dc96e35 --- tog/tog.1 +++ tog/tog.1 @@ -104,6 +104,14 @@ are as follows: Move the selection cursor down. .It Cm Up-arrow, k, <, Comma, Ctrl-p Move the selection cursor up. +.It Cm Right-arrow, l +Scroll log message field to the right. Log message moves left on the screen. +.It Cm Left-arrow, h +Scroll log message field to the left. Log message moves right on the screen. +.It Cm $ +Scroll log message field to the rightmost position. +.It Cm 0 +Scroll log message field to the leftmost position. .It Cm Page-down, Ctrl+f Move the selection cursor down one page. .It Cm Page-up, Ctrl+b @@ -219,6 +227,14 @@ detected. Scroll down. .It Cm Up-arrow, k, Ctrl-p Scroll up. +.It Cm Right-arrow, l +Scroll view to the right. Diff output moves left on the screen. +.It Cm Left-arrow, h +Scroll view to the left. Diff output moves right on the screen. +.It Cm $ +Scroll view to the rightmost position. +.It Cm 0 +Scroll view left to the start of the line. .It Cm Page-down, Space, Ctrl+f Scroll down one page. .It Cm Page-up, Ctrl+b @@ -290,6 +306,14 @@ are as follows: Move the selection cursor down. .It Cm Up-arrow, k, Ctrl-p Move the selection cursor up. +.It Cm Right-arrow, l +Scroll view to the right. File output moves left on the screen. +.It Cm Left-arrow, h +Scroll view to the left. File output moves right on the screen. +.It Cm $ +Scroll view to the rightmost position. +.It Cm 0 +Scroll view left to the start of the line. .It Cm Page-down, Space, Ctrl+f Move the selection cursor down one page. .It Cm Page-up, Ctrl+b blob - 48c3db4951d9e65f17f32a087f88342694e36c1f blob + 0cd494910965981fdcb69bc69d19f094712d80f8 --- tog/tog.c +++ tog/tog.c @@ -500,6 +500,7 @@ struct tog_view { WINDOW *window; PANEL *panel; int nlines, ncols, begin_y, begin_x; + int maxx, x; /* max column and current start column */ int lines, cols; /* copies of LINES and COLS */ int focussed; /* Only set on one parent or child view at a time. */ int dying; @@ -1221,23 +1222,64 @@ done: *wlen = 0; } return err; +} + +static const struct got_error * +expand_tab(char **ptr, const char *src) +{ + char *dst; + size_t len, n, idx = 0, sz = 0; + + *ptr = NULL; + n = len = strlen(src); + dst = malloc((n + 1) * sizeof(char)); + if (dst == NULL) + return got_error_from_errno("malloc"); + + while (idx < len && src[idx]) { + const char c = src[idx]; + + if (c == '\t') { + size_t nb = TABSIZE - sz % TABSIZE; + n += nb; + dst = reallocarray(dst, n, sizeof(char)); + if (dst == NULL) + return got_error_from_errno("reallocarray"); + memcpy(dst + sz, " ", nb); + sz += nb; + } else + dst[sz++] = src[idx]; + ++idx; + } + + dst[sz] = '\0'; + *ptr = dst; + return NULL; } /* Format a line for display, ensuring that it won't overflow a width limit. */ static const struct got_error * format_line(wchar_t **wlinep, int *widthp, const char *line, int wlimit, - int col_tab_align) + int col_tab_align, int expand) { const struct got_error *err = NULL; int cols = 0; wchar_t *wline = NULL; + char *exstr = NULL; size_t wlen; int i; *wlinep = NULL; *widthp = 0; - err = mbs2ws(&wline, &wlen, line); + if (expand) { + err = expand_tab(&exstr, line); + if (err) + return err; + } + + err = mbs2ws(&wline, &wlen, expand ? exstr : line); + free(exstr); if (err) return err; @@ -1370,7 +1412,8 @@ format_author(wchar_t **wauthor, int *author_width, ch if (smallerthan && smallerthan[1] != '\0') author = smallerthan + 1; author[strcspn(author, "@>")] = '\0'; - return format_line(wauthor, author_width, author, limit, col_tab_align); + return format_line(wauthor, author_width, author, limit, col_tab_align, + 0); } static const struct got_error * @@ -1467,12 +1510,15 @@ draw_commit(struct tog_view *view, struct got_commit_o newline = strchr(logmsg, '\n'); if (newline) *newline = '\0'; - limit = avail - col; - err = format_line(&wlogmsg, &logmsg_width, logmsg, limit, col); + limit = view->x + avail - col; + err = format_line(&wlogmsg, &logmsg_width, logmsg, limit, col, 1); if (err) goto done; - waddwstr(view->window, wlogmsg); - col += logmsg_width; + if (view->x < logmsg_width - 1) + waddwstr(view->window, wlogmsg + view->x); + else + logmsg_width = 0; + col += MAX(logmsg_width - view->x, 0); while (col < avail) { waddch(view->window, ' '); col++; @@ -1709,7 +1755,7 @@ draw_commits(struct tog_view *view) header = NULL; goto done; } - err = format_line(&wline, &width, header, view->ncols, 0); + err = format_line(&wline, &width, header, view->ncols, 0, 0); if (err) goto done; @@ -1738,8 +1784,9 @@ draw_commits(struct tog_view *view) /* Grow author column size if necessary. */ entry = s->first_displayed_entry; ncommits = 0; + view->maxx = 0; while (entry) { - char *author; + char *author, *eol, *msg, *msg0; wchar_t *wauthor; int width; if (ncommits >= limit - 1) @@ -1755,6 +1802,17 @@ draw_commits(struct tog_view *view) author_cols = width; free(wauthor); free(author); + err = got_object_commit_get_logmsg(&msg0, entry->commit); + if (err) + goto done; + msg = msg0; + while (*msg == '\n') + ++msg; + if ((eol = strchr(msg, '\n'))) + view->maxx = MAX(view->maxx, eol - msg); + else + view->maxx = MAX(view->maxx, strlen(msg)); + free(msg0); ncommits++; entry = TAILQ_NEXT(entry, entry); } @@ -2475,7 +2533,22 @@ input_log_view(struct tog_view **new_view, struct tog_ switch (ch) { case 'q': s->quit = 1; + break; + case '0': + view->x = 0; + break; + case '$': + view->x = MAX(view->maxx - view->ncols / 2, 0); + break; + case KEY_RIGHT: + case 'l': + if (view->x + view->ncols / 2 < view->maxx) + view->x += 2; /* move two columns right */ break; + case KEY_LEFT: + case 'h': + view->x -= MIN(view->x, 2); /* move two columns back */ + break; case 'k': case KEY_UP: case '<': @@ -2979,62 +3052,55 @@ match_color(struct tog_colors *colors, const char *lin static const struct got_error * add_matched_line(int *wtotal, const char *line, int wlimit, int col_tab_align, - WINDOW *window, regmatch_t *regmatch) + WINDOW *window, int skip, regmatch_t *regmatch) { const struct got_error *err = NULL; wchar_t *wline; - int width; - char *s; + int rme, rms, n, width; *wtotal = 0; + rms = regmatch->rm_so; + rme = regmatch->rm_eo; - s = strndup(line, regmatch->rm_so); - if (s == NULL) - return got_error_from_errno("strndup"); - - err = format_line(&wline, &width, s, wlimit, col_tab_align); - if (err) { - free(s); + err = format_line(&wline, &width, line, wlimit + skip, + col_tab_align, 1); + if (err) return err; + + /* draw up to matched token if we haven't scrolled past it */ + n = MAX(rms - skip, 0); + if (n) { + waddnwstr(window, wline + skip, n); + wlimit -= n; + *wtotal += n; } - waddwstr(window, wline); - free(wline); - free(s); - wlimit -= width; - *wtotal += width; if (wlimit > 0) { - s = strndup(line + regmatch->rm_so, - regmatch->rm_eo - regmatch->rm_so); - if (s == NULL) { - err = got_error_from_errno("strndup"); - free(s); - return err; + int len = rme - rms; + n = 0; + if (skip > rms) { + n = skip - rms; + len = MAX(len - n, 0); } - err = format_line(&wline, &width, s, wlimit, col_tab_align); - if (err) { - free(s); - return err; + /* draw (visible part of) matched token (if scrolled into it) */ + if (len) { + wattron(window, A_STANDOUT); + waddnwstr(window, wline + rms + n, len); + wattroff(window, A_STANDOUT); + wlimit -= len; + *wtotal += len; } - wattr_on(window, A_STANDOUT, NULL); - waddwstr(window, wline); - wattr_off(window, A_STANDOUT, NULL); - free(wline); - free(s); - wlimit -= width; - *wtotal += width; } - if (wlimit > 0 && strlen(line) > regmatch->rm_eo) { - err = format_line(&wline, &width, - line + regmatch->rm_eo, wlimit, col_tab_align); - if (err) - return err; - waddwstr(window, wline); - free(wline); - *wtotal += width; + if (wlimit > 0 && skip < width) { /* draw rest of line */ + n = 0; + if (skip > rme) + n = MIN(skip - rme, width - rme); + waddnwstr(window, wline + rme + n, wlimit); } + *wtotal = width; + free(wline); return NULL; } @@ -3066,7 +3132,7 @@ draw_file(struct tog_view *view, const char *header) s->first_displayed_line - 1 + s->selected_line, nlines, header) == -1) return got_error_from_errno("asprintf"); - err = format_line(&wline, &width, line, view->ncols, 0); + err = format_line(&wline, &width, line, view->ncols, 0, 0); free(line); if (err) return err; @@ -3087,6 +3153,7 @@ draw_file(struct tog_view *view, const char *header) } s->eof = 0; + view->maxx = 0; line = NULL; while (max_lines > 0 && nprinted < max_lines) { linelen = getline(&line, &linesize, s->f); @@ -3099,6 +3166,8 @@ draw_file(struct tog_view *view, const char *header) return got_ferror(s->f, GOT_ERR_IO); } + view->maxx = MAX(view->maxx, linelen); + tc = match_color(&s->colors, line); if (tc) wattr_on(view->window, @@ -3106,25 +3175,27 @@ draw_file(struct tog_view *view, const char *header) 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, - view->window, regmatch); + view->window, view->x, regmatch); if (err) { free(line); return err; } } else { - err = format_line(&wline, &width, line, view->ncols, 0); + err = format_line(&wline, &width, line, + view->x + view->ncols, 0, view->x ? 1 : 0); if (err) { free(line); return err; } - waddwstr(view->window, wline); + if (view->x < width - 1) + waddwstr(view->window, wline + view->x); free(wline); wline = NULL; } if (tc) wattr_off(view->window, COLOR_PAIR(tc->colorpair), NULL); - if (width <= view->ncols - 1) + if (width - view->x <= view->ncols - 1) waddch(view->window, '\n'); nprinted++; } @@ -3143,7 +3214,8 @@ draw_file(struct tog_view *view, const char *header) nprinted++; } - err = format_line(&wline, &width, TOG_EOF_STRING, view->ncols, 0); + err = format_line(&wline, &width, TOG_EOF_STRING, view->ncols, + 0, 0); if (err) { return err; } @@ -3512,8 +3584,9 @@ static const struct got_error * search_next_diff_view(struct tog_view *view) { struct tog_diff_view_state *s = &view->state.diff; + const struct got_error *err = NULL; int lineno; - char *line = NULL; + char *exstr = NULL, *line = NULL; size_t linesize = 0; ssize_t linelen; @@ -3551,25 +3624,31 @@ search_next_diff_view(struct tog_view *view) return got_error_from_errno("fseeko"); } linelen = getline(&line, &linesize, s->f); + err = expand_tab(&exstr, line); + if (err) + break; if (linelen != -1 && - match_line(line, &view->regex, 1, &view->regmatch)) { + match_line(exstr, &view->regex, 1, &view->regmatch)) { view->search_next_done = TOG_SEARCH_HAVE_MORE; s->matched_line = lineno; break; } + free(exstr); + exstr = NULL; if (view->searching == TOG_SEARCH_FORWARD) lineno++; else lineno--; } free(line); + free(exstr); if (s->matched_line) { s->first_displayed_line = s->matched_line; s->selected_line = 1; } - return NULL; + return err; } static const struct got_error * @@ -3790,6 +3869,21 @@ input_diff_view(struct tog_view **new_view, struct tog int i, nscroll = view->nlines - 1; switch (ch) { + case '0': + view->x = 0; + break; + case '$': + view->x = MAX(view->maxx - view->ncols / 3, 0); + break; + case KEY_RIGHT: + case 'l': + if (view->x + view->ncols / 3 < view->maxx) + view->x += 2; /* move two columns right */ + break; + case KEY_LEFT: + case 'h': + view->x -= MIN(view->x, 2); /* move two columns back */ + break; case 'a': case 'w': if (ch == 'a') @@ -3904,6 +3998,7 @@ input_diff_view(struct tog_view **new_view, struct tog s->first_displayed_line = 1; s->last_displayed_line = view->nlines; s->matched_line = 0; + view->x = 0; diff_view_indicate_progress(view); err = create_diff(s); @@ -3929,6 +4024,7 @@ input_diff_view(struct tog_view **new_view, struct tog s->first_displayed_line = 1; s->last_displayed_line = view->nlines; s->matched_line = 0; + view->x = 0; diff_view_indicate_progress(view); err = create_diff(s); @@ -4118,7 +4214,7 @@ draw_blame(struct tog_view *view) return err; } - err = format_line(&wline, &width, line, view->ncols, 0); + err = format_line(&wline, &width, line, view->ncols, 0, 0); free(line); line = NULL; if (err) @@ -4147,7 +4243,7 @@ draw_blame(struct tog_view *view) return got_error_from_errno("asprintf"); } free(id_str); - err = format_line(&wline, &width, line, view->ncols, 0); + err = format_line(&wline, &width, line, view->ncols, 0, 0); free(line); line = NULL; if (err) @@ -4159,6 +4255,7 @@ draw_blame(struct tog_view *view) waddch(view->window, '\n'); s->eof = 0; + view->maxx = 0; while (nprinted < view->nlines - 2) { linelen = getline(&line, &linesize, blame->f); if (linelen == -1) { @@ -4172,6 +4269,8 @@ draw_blame(struct tog_view *view) if (++lineno < s->first_displayed_line) continue; + view->maxx = MAX(view->maxx, linelen); + if (view->focussed && nprinted == s->selected_line - 1) wstandout(view->window); @@ -4224,7 +4323,7 @@ draw_blame(struct tog_view *view) s->matched_line && regmatch->rm_so >= 0 && regmatch->rm_so < regmatch->rm_eo) { err = add_matched_line(&width, line, view->ncols - 9, 9, - view->window, regmatch); + view->window, view->x, regmatch); if (err) { free(line); return err; @@ -4232,11 +4331,15 @@ draw_blame(struct tog_view *view) width += 9; } else { err = format_line(&wline, &width, line, - view->ncols - 9, 9); - waddwstr(view->window, wline); + view->x + view->ncols - 9, 9, 1); + if (!err && view->x < width - 1) { + waddwstr(view->window, wline + view->x); + width += 9; + } free(wline); wline = NULL; - width += 9; + if (err) + return err; } if (width <= view->ncols - 1) @@ -4603,8 +4706,9 @@ static const struct got_error * search_next_blame_view(struct tog_view *view) { struct tog_blame_view_state *s = &view->state.blame; + const struct got_error *err = NULL; int lineno; - char *line = NULL; + char *exstr = NULL, *line = NULL; size_t linesize = 0; ssize_t linelen; @@ -4642,25 +4746,31 @@ search_next_blame_view(struct tog_view *view) return got_error_from_errno("fseeko"); } linelen = getline(&line, &linesize, s->blame.f); + err = expand_tab(&exstr, line); + if (err) + break; if (linelen != -1 && - match_line(line, &view->regex, 1, &view->regmatch)) { + match_line(exstr, &view->regex, 1, &view->regmatch)) { view->search_next_done = TOG_SEARCH_HAVE_MORE; s->matched_line = lineno; break; } + free(exstr); + exstr = NULL; if (view->searching == TOG_SEARCH_FORWARD) lineno++; else lineno--; } free(line); + free(exstr); if (s->matched_line) { s->first_displayed_line = s->matched_line; s->selected_line = 1; } - return NULL; + return err; } static const struct got_error * @@ -4697,6 +4807,21 @@ input_blame_view(struct tog_view **new_view, struct to int begin_x = 0, nscroll = view->nlines - 2; switch (ch) { + case '0': + view->x = 0; + break; + case '$': + view->x = MAX(view->maxx - view->ncols / 3, 0); + break; + case KEY_RIGHT: + case 'l': + if (view->x + view->ncols / 3 < view->maxx) + view->x += 2; /* move two columns right */ + break; + case KEY_LEFT: + case 'h': + view->x -= MIN(view->x, 2); /* move two columns back */ + break; case 'q': s->done = 1; break; @@ -5079,7 +5204,7 @@ draw_tree_entries(struct tog_view *view, const char *p if (limit == 0) return NULL; - err = format_line(&wline, &width, s->tree_label, view->ncols, 0); + err = format_line(&wline, &width, s->tree_label, view->ncols, 0, 0); if (err) return err; if (view_needs_focus_indication(view)) @@ -5100,7 +5225,7 @@ draw_tree_entries(struct tog_view *view, const char *p waddch(view->window, '\n'); if (--limit <= 0) return NULL; - err = format_line(&wline, &width, parent_path, view->ncols, 0); + err = format_line(&wline, &width, parent_path, view->ncols, 0, 0); if (err) return err; waddwstr(view->window, wline); @@ -5180,7 +5305,7 @@ draw_tree_entries(struct tog_view *view, const char *p } free(id_str); free(link_target); - err = format_line(&wline, &width, line, view->ncols, 0); + err = format_line(&wline, &width, line, view->ncols, 0, 0); if (err) { free(line); break; @@ -6291,7 +6416,7 @@ show_ref_view(struct tog_view *view) s->nrefs) == -1) return got_error_from_errno("asprintf"); - err = format_line(&wline, &width, line, view->ncols, 0); + err = format_line(&wline, &width, line, view->ncols, 0, 0); if (err) { free(line); return err; @@ -6345,7 +6470,7 @@ show_ref_view(struct tog_view *view) return got_error_from_errno("strdup"); } - err = format_line(&wline, &width, line, view->ncols, 0); + err = format_line(&wline, &width, line, view->ncols, 0, 0); if (err) { free(line); return err;