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)