Download raw body.
[rfc] tog horizontal scroll (diff & blame view)
This patch introduces support for horizontal scrolling in diff and blame views. New key maps: h - scroll left (left arrow works too) l - scroll right (right arrow works too) 0 - scroll to the beginning of the line $ - scroll to the end of longest line on screen diff --git a/tog/tog.1 b/tog/tog.1 index 56d92245..d54d2135 100644 --- a/tog/tog.1 +++ b/tog/tog.1 @@ -219,6 +219,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 right to the end of the longest line on the screen. +.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 +298,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-arroe, h +Scroll view to the left. File output moves right on the screen. +.It Cm $ +Scroll view right to the end of the longest line on the screen. +.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 diff --git a/tog/tog.c b/tog/tog.c index d0d58364..6fbd65c9 100644 --- a/tog/tog.c +++ b/tog/tog.c @@ -498,6 +498,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,21 +1222,62 @@ done: 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; @@ -1368,7 +1410,8 @@ format_author(wchar_t **wauthor, int *author_width, char *author, int limit, 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 * @@ -1466,7 +1509,7 @@ draw_commit(struct tog_view *view, struct got_commit_object *commit, if (newline) *newline = '\0'; limit = avail - col; - err = format_line(&wlogmsg, &logmsg_width, logmsg, limit, col); + err = format_line(&wlogmsg, &logmsg_width, logmsg, limit, col, 0); if (err) goto done; waddwstr(view->window, wlogmsg); @@ -1707,7 +1750,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; @@ -2951,62 +2994,55 @@ match_color(struct tog_colors *colors, const char *line) 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; } @@ -3038,7 +3074,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; @@ -3059,6 +3095,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); @@ -3071,6 +3108,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, @@ -3078,25 +3117,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++; } @@ -3115,7 +3156,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; } @@ -3484,8 +3526,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; @@ -3523,25 +3566,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 * @@ -3762,6 +3811,21 @@ input_diff_view(struct tog_view **new_view, struct tog_view *view, int ch) 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') @@ -3876,6 +3940,7 @@ input_diff_view(struct tog_view **new_view, struct tog_view *view, int ch) 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); @@ -3901,6 +3966,7 @@ input_diff_view(struct tog_view **new_view, struct tog_view *view, int ch) 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); @@ -4048,6 +4114,22 @@ struct tog_blame_line { struct got_object_id *id; }; +static uint16_t +expanded_strsz(const char *str, unsigned short n) +{ + uint16_t i = 0; + + while (str && (str[i] != '\n' && str[i] != '\0')) { + if (str[i] == '\t') + n += 8 - (i % 8); /* expand tabs */ + if ((str[i] & 0xc0) == 0x80) + --n; /* utf8 continutation byte */ + ++i; + } + + return n; +} + static const struct got_error * draw_blame(struct tog_view *view) { @@ -4079,7 +4161,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) @@ -4108,7 +4190,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) @@ -4120,6 +4202,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) { @@ -4133,6 +4216,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); @@ -4185,16 +4270,16 @@ 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; } width += 9; - } else { + } else if (view->x < expanded_strsz(line, linelen) - 1) { err = format_line(&wline, &width, line, - view->ncols - 9, 9); - waddwstr(view->window, wline); + view->x + view->ncols - 9, 9, 1); + waddwstr(view->window, wline + view->x); free(wline); wline = NULL; width += 9; @@ -4553,8 +4638,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; @@ -4592,25 +4678,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 * @@ -4647,6 +4739,21 @@ input_blame_view(struct tog_view **new_view, struct tog_view *view, int ch) 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; @@ -5018,7 +5125,7 @@ draw_tree_entries(struct tog_view *view, const char *parent_path) 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)) @@ -5039,7 +5146,7 @@ draw_tree_entries(struct tog_view *view, const char *parent_path) 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); @@ -5119,7 +5226,7 @@ draw_tree_entries(struct tog_view *view, const char *parent_path) } 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; @@ -6219,7 +6326,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; @@ -6273,7 +6380,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; -- Mark Jamsek GPG: F2FF 13DE 6A06 C471 CA80 E6E2 2930 DC66 86EE CF68
[rfc] tog horizontal scroll (diff & blame view)