commit 6d17833f854902ee602bfbaf73cc67df9b25e1e8 from: Stefan Sperling date: Fri Nov 08 13:10:36 2019 UTC add color support to 'tog diff' commit - 6570a66d534155e8cfc35bc2023b9f655a6f30eb commit + 6d17833f854902ee602bfbaf73cc67df9b25e1e8 blob - f1c654737061914c1e6789930e5d84be5117070c blob + 4e252ffbac5584645f2d0ce7bd4ba2688825f65d --- include/got_error.h +++ include/got_error.h @@ -125,6 +125,7 @@ #define GOT_ERR_TAG_EXISTS 109 #define GOT_ERR_GIT_REPO_FORMAT 110 #define GOT_ERR_REBASE_REQUIRED 111 +#define GOT_ERR_REGEX 112 static const struct got_error { int code; @@ -256,6 +257,7 @@ static const struct got_error { { GOT_ERR_TAG_EXISTS,"specified tag already exists" }, { GOT_ERR_GIT_REPO_FORMAT,"unknown git repository format version" }, { GOT_ERR_REBASE_REQUIRED,"specified branch must be rebased first" }, + { GOT_ERR_REGEX, "regular expression error" }, }; /* blob - 4af5ca7c1f56bf6f604dc53635ac15ce4a13edb1 blob + 59ba7c9f63a0734b9e9e30f34ef9a5d707f40b4a --- tog/tog.1 +++ tog/tog.1 @@ -296,6 +296,43 @@ An abbreviated hash argument will be expanded to a ful automatically, provided the abbreviation is unique. .El .El +.Sh ENVIRONMENT +.Bl -tag -width TOG_COLORS +.It Ev TOG_COLORS +.Nm +shows colorized output if this variable is set to a non-empty value. +The default color scheme can be modified by setting the environment +variables documented below. +The colors available in color schemes are +.Dq black , +.Dq red , +.Dq green , +.Dq yellow , +.Dq blue , +.Dq megenta , +and +.Dq cyan . +.It Ev TOG_COLOR_DIFF_MINUS +The color used to mark up removed lines in diffs. +If not set, the default value +.Dq magenta +is used. +.It Ev TOG_COLOR_DIFF_PLUS +The color used to mark up added lines in diffs. +If not set, the default value +.Dq cyan +is used. +.It Ev TOG_COLOR_DIFF_CHUNK_HEADER +The color used to mark up chunk header lines in diffs. +If not set, the default value +.Dq yellow +is used. +.It Ev TOG_COLOR_DIFF_META +The color used to mark up meta data in diffs. +If not set, the default value +.Dq green +is used. +.El .Sh EXIT STATUS .Ex -std tog .Sh SEE ALSO blob - 0ff3b6287ab653b866bd0d00bd1eb725becd81e6 blob + cc9b6c1d8c40fe9084dbd96074e8889eec6a6e97 --- tog/tog.c +++ tog/tog.c @@ -110,7 +110,14 @@ TAILQ_HEAD(commit_queue_head, commit_queue_entry); struct commit_queue { int ncommits; struct commit_queue_head head; +}; + +struct tog_line_color { + SIMPLEQ_ENTRY(tog_line_color) entry; + regex_t regex; + short colorpair; }; +SIMPLEQ_HEAD(tog_line_colors, tog_line_color); struct tog_diff_view_state { struct got_object_id *id1, *id2; @@ -121,6 +128,7 @@ struct tog_diff_view_state { int diff_context; struct got_repository *repo; struct got_reflist_head *refs; + struct tog_line_colors line_colors; /* passed from log view; may be NULL */ struct tog_view *log_view; @@ -2257,6 +2265,10 @@ init_curses(void) intrflush(stdscr, FALSE); keypad(stdscr, TRUE); curs_set(0); + if (getenv("TOG_COLORS") != NULL) { + start_color(); + use_default_colors(); + } signal(SIGWINCH, tog_sigwinch); signal(SIGPIPE, tog_sigpipe); } @@ -2437,14 +2449,36 @@ parse_next_line(FILE *f, size_t *len) return line; } +static int +match_line(const char *line, regex_t *regex) +{ + regmatch_t regmatch; + + return regexec(regex, line, 1, ®match, 0) == 0; +} + +struct tog_line_color * +match_line_color(struct tog_line_colors *colors, const char *line) +{ + struct tog_line_color *tc = NULL; + + SIMPLEQ_FOREACH(tc, colors, entry) { + if (match_line(line, &tc->regex)) + return tc; + } + + return NULL; +} + static const struct got_error * draw_file(struct tog_view *view, FILE *f, int *first_displayed_line, - int *last_displayed_line, int *eof, int max_lines, - char *header) + int *last_displayed_line, int *eof, int max_lines, char *header, + struct tog_line_colors *colors) { const struct got_error *err; int nlines = 0, nprinted = 0; char *line; + struct tog_line_color *lc; size_t len; wchar_t *wline; int width; @@ -2488,7 +2522,15 @@ draw_file(struct tog_view *view, FILE *f, int *first_d free(line); return err; } + + lc = match_line_color(colors, line); + if (lc) + wattr_on(view->window, + COLOR_PAIR(lc->colorpair), NULL); waddwstr(view->window, wline); + if (lc) + wattr_off(view->window, + COLOR_PAIR(lc->colorpair), NULL); if (width <= view->ncols - 1) waddch(view->window, '\n'); if (++nprinted == 1) @@ -2681,6 +2723,93 @@ diff_view_indicate_progress(struct tog_view *view) mvwaddstr(view->window, 0, 0, "diffing..."); update_panels(); doupdate(); +} + +static const struct got_error * +add_line_color(struct tog_line_colors *colors, const char *pattern, + int idx, short color) +{ + const struct got_error *err = NULL; + struct tog_line_color *lc; + int regerr = 0; + + init_pair(idx, color, -1); + + lc = calloc(1, sizeof(*lc)); + if (lc == NULL) + return got_error_from_errno("calloc"); + regerr = regcomp(&lc->regex, pattern, + REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + if (regerr) { + static char regerr_msg[512]; + static char err_msg[512]; + regerror(regerr, &lc->regex, regerr_msg, + sizeof(regerr_msg)); + snprintf(err_msg, sizeof(err_msg), "regcomp: %s", + regerr_msg); + err = got_error_msg(GOT_ERR_REGEX, err_msg); + free(lc); + return err; + } + lc->colorpair = idx; + SIMPLEQ_INSERT_HEAD(colors, lc, entry); + return NULL; +} + +void +free_line_colors(struct tog_line_colors *colors) +{ + struct tog_line_color *lc; + + while (!SIMPLEQ_EMPTY(colors)) { + lc = SIMPLEQ_FIRST(colors); + SIMPLEQ_REMOVE_HEAD(colors, entry); + regfree(&lc->regex); + free(lc); + } +} + +static int +default_color_value(const char *envvar) +{ + if (strcmp(envvar, "TOG_COLOR_DIFF_MINUS") == 0) + return COLOR_MAGENTA; + if (strcmp(envvar, "TOG_COLOR_DIFF_PLUS") == 0) + return COLOR_CYAN; + if (strcmp(envvar, "TOG_COLOR_DIFF_CHUNK_HEADER") == 0) + return COLOR_YELLOW; + if (strcmp(envvar, "TOG_COLOR_DIFF_META") == 0) + return COLOR_GREEN; + + return -1; +} + +static int +get_color_value(const char *envvar) +{ + const char *val = getenv(envvar); + + if (val == NULL) + return default_color_value(envvar); + + if (strcasecmp(val, "black") == 0) + return COLOR_BLACK; + if (strcasecmp(val, "red") == 0) + return COLOR_RED; + if (strcasecmp(val, "green") == 0) + return COLOR_GREEN; + if (strcasecmp(val, "yellow") == 0) + return COLOR_YELLOW; + if (strcasecmp(val, "blue") == 0) + return COLOR_BLUE; + if (strcasecmp(val, "magenta") == 0) + return COLOR_MAGENTA; + if (strcasecmp(val, "cyan") == 0) + return COLOR_CYAN; + if (strcasecmp(val, "white") == 0) + return COLOR_WHITE; + + return default_color_value(envvar); } static const struct got_error * @@ -2723,6 +2852,35 @@ open_diff_view(struct tog_view *view, struct got_objec view->state.diff.log_view = log_view; view->state.diff.repo = repo; view->state.diff.refs = refs; + SIMPLEQ_INIT(&view->state.diff.line_colors); + + if (has_colors() && getenv("TOG_COLORS") != NULL) { + err = add_line_color(&view->state.diff.line_colors, + "^-", 1, get_color_value("TOG_COLOR_DIFF_MINUS")); + if (err) + return err; + err = add_line_color(&view->state.diff.line_colors, + "^\\+", 2, get_color_value("TOG_COLOR_DIFF_PLUS")); + if (err) { + free_line_colors(&view->state.diff.line_colors); + return err; + } + err = add_line_color(&view->state.diff.line_colors, + "^@@", 3, + get_color_value("TOG_COLOR_DIFF_CHUNK_HEADER")); + if (err) { + free_line_colors(&view->state.diff.line_colors); + return err; + } + + err = add_line_color(&view->state.diff.line_colors, + "^(commit|(blob|file) [-+] )", 4, + get_color_value("TOG_COLOR_DIFF_META")); + if (err) { + free_line_colors(&view->state.diff.line_colors); + return err; + } + } if (log_view && view_is_splitscreen(view)) show_log_view(log_view); /* draw vborder */ @@ -2755,6 +2913,7 @@ close_diff_view(struct tog_view *view) view->state.diff.id2 = NULL; if (view->state.diff.f && fclose(view->state.diff.f) == EOF) err = got_error_from_errno("fclose"); + free_line_colors(&view->state.diff.line_colors); return err; } @@ -2786,7 +2945,7 @@ show_diff_view(struct tog_view *view) return draw_file(view, s->f, &s->first_displayed_line, &s->last_displayed_line, &s->eof, view->nlines, - header); + header, &s->line_colors); } static const struct got_error * @@ -3492,15 +3651,6 @@ search_start_blame_view(struct tog_view *view) return NULL; } -static int -match_line(const char *line, regex_t *regex) -{ - regmatch_t regmatch; - - return regexec(regex, line, 1, ®match, 0) == 0; -} - - static const struct got_error * search_next_blame_view(struct tog_view *view) {