Hi guys,
recently I try to put some colors on my git-grep and found a patch from Nguyễn Thái Ngọc Duy <pclouds@gmail.com> to do it! The problem with this patch is that it doesn't support the config file to persist the color option on grep.
I took the liberty to change his patch and add support to config files and bash completion.
Attached is the resultant patch!
I hope this could be useful to someone.
Thiago dos Santos Alves Computer Scientist
thiago.salves@gmail.com ----------------------------------------------------- "Man's mind, once stretched by a new idea, never regains it's original dimensions."
Show changes to 5 files +177 −8
Documentation/git-grep.txt, builtin-grep.c, contrib/completion/git-completion.bash, grep.c, grep.h
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index fa4d133..8554068 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -15,7 +15,7 @@ SYNOPSIS [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings] [-n] [-l | --files-with-matches] [-L | --files-without-match] - [-c | --count] [--all-match] + [-c | --count] [--all-match] [--color | --no-color] [-A <post-context>] [-B <pre-context>] [-C <context>] [-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...] [<tree>...] @@ -99,6 +99,13 @@ OPTIONS Instead of showing every matched line, show the number of lines that match. +--color:: + Colorize grep output highlighting matches, file names and line numbers. + +--no-color:: + Turn off grep colors, even when the configuration file gives the + default to color output. + -[ABC] <context>:: Show `context` trailing (`A` -- after), or leading (`B` -- before), or both (`C` -- context) lines, and place a diff --git a/builtin-grep.c b/builtin-grep.c index 631129d..29e7d5a 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -11,6 +11,7 @@ #include "tree-walk.h" #include "builtin.h" #include "grep.h" +#include "color.h" #ifndef NO_EXTERNAL_GREP #ifdef __unix__ @@ -20,6 +21,7 @@ #endif #endif +static int grep_use_color = -1; /* * git grep pathspecs are somewhat different from diff-tree pathspecs; * pathname wildcards are allowed. @@ -386,7 +388,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) * we grep through the checked-out files. It tends to * be a lot more optimized */ - if (!cached) { + if (!cached && !opt->color) { hit = external_grep(opt, paths, cached); if (hit >= 0) return hit; @@ -507,6 +509,22 @@ static const char emsg_missing_context_len[] = static const char emsg_missing_argument[] = "option requires an argument -%s"; +static int git_grep_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "color.grep")) { + grep_use_color = git_config_colorbool(var, value, -1); + return 0; + } + if (!prefixcmp(var, "color.grep.")) { + int slot = parse_grep_color_slot(var, 11); + if (!value) + return config_error_nonbool(var); + color_parse(value, var, grep_get_color(slot)); + return 0; + } + return git_color_default_config(var, value, cb); +} + int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; @@ -524,6 +542,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; + git_config(git_grep_config, NULL); + if (grep_use_color == -1) + grep_use_color = git_use_color_default; + opt.color = grep_use_color; + /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or @@ -711,6 +734,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.relative = 0; continue; } + if (!strcmp("--color", arg)) { + opt.color = 1; + continue; + } + if (!strcmp("--no-color", arg)) { + opt.color = 0; + continue; + } if (!strcmp("--", arg)) { /* later processing wants to have this at argv[1] */ argv--; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3396e35..ee4d509 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1157,7 +1157,7 @@ _git_config () __gitcomp "$(__git_merge_strategies)" return ;; - color.branch|color.diff|color.status) + color.branch|color.diff|color.status|color.grep) __gitcomp "always never auto" return ;; @@ -1240,6 +1240,11 @@ _git_config () color.diff.new color.diff.commit color.diff.whitespace + color.grep + color.grep.filepath + color.grep.lineno + color.grep.separator + color.grep.match color.pager color.status color.status.header @@ -1390,6 +1395,22 @@ _git_shortlog () __git_complete_revlist } +_git_grep () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --cached --text --ignore-case --word-regexp --invert-match + --full-name --extend-regexp --fixed-strings --files-with-matches + --files-without-match --count --and --or --not --all-match + --color + " + return + ;; + esac +} + _git_show () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1642,7 +1663,7 @@ _git () fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; - grep) _git_grep ;; + grep) _git_grep ;; help) _git_help ;; init) _git_init ;; log) _git_log ;; diff --git a/grep.c b/grep.c index f67d671..5c3e6cd 100644 --- a/grep.c +++ b/grep.c @@ -1,7 +1,39 @@ #include "cache.h" #include "grep.h" +#include "color.h" #include "xdiff-interface.h" +static char grep_colors[][COLOR_MAXLEN] = { + "\033[m", /* reset */ + "", /* PLAIN (normal) */ + "\033[35m", /* FILEPATH (magenta) */ + "\033[32m", /* LINENO (green) */ + "\033[36m", /* SEPARATOR (cyan) */ + "\033[1m\033[31m", /* MATCH (red)*/ +}; + +const char *grep_get_color(enum color_grep ix) +{ + return grep_colors[ix]; +} + +int parse_grep_color_slot(const char *var, int ofs) +{ + if (!strcasecmp(var+ofs, "plain")) + return COLOR_GREP_PLAIN; + if (!strcasecmp(var+ofs, "reset")) + return COLOR_GREP_RESET; + if (!strcasecmp(var+ofs, "filepath")) + return COLOR_GREP_FILEPATH; + if (!strcasecmp(var+ofs, "lineno")) + return COLOR_GREP_LINENO; + if (!strcasecmp(var+ofs, "separator")) + return COLOR_GREP_SEPARATOR; + if (!strcasecmp(var+ofs, "match")) + return COLOR_GREP_MATCH; + die("bad config variable '%s'", var); +} + void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t) { @@ -247,7 +279,10 @@ static int fixmatch(const char *pattern, char *line, regmatch_t *match) } } -static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx) +static int match_one_pattern_1(struct grep_opt *opt, struct grep_pat *p, + char *bol, char *eol, + enum grep_context ctx, int eflags, + int *rm_so, int *rm_eo) { int hit = 0; int at_true_bol = 1; @@ -261,7 +296,7 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol if (!opt->fixed) { regex_t *exp = &p->regexp; hit = !regexec(exp, bol, ARRAY_SIZE(pmatch), - pmatch, 0); + pmatch, eflags); } else { hit = !fixmatch(p->pattern, bol, pmatch); @@ -298,9 +333,20 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol goto again; } } + if (hit) { + if (rm_so) + *rm_so = pmatch[0].rm_so; + if (rm_eo) + *rm_eo = pmatch[0].rm_eo; + } return hit; } +static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx) +{ + return match_one_pattern_1(opt, p, bol, eol, ctx, 0, NULL, NULL); +} + static int match_expr_eval(struct grep_opt *o, struct grep_expr *x, char *bol, char *eol, @@ -365,6 +411,54 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol, return 0; } +static void show_line_colored(struct grep_opt *opt, char *bol, char *eol, + const char *name, unsigned lno, char sign) +{ + struct grep_pat *p; + int rm_so, rm_eo, eflags = 0; + int ch; + + if (opt->pathname) + printf("%s%s%s%c%s", + grep_get_color(COLOR_GREP_FILEPATH), + name, + grep_get_color(COLOR_GREP_SEPARATOR), + sign, + grep_get_color(COLOR_GREP_RESET)); + if (opt->linenum) + printf("%s%d%s%c%s", + grep_get_color(COLOR_GREP_LINENO), + lno, + grep_get_color(COLOR_GREP_SEPARATOR), + sign, + grep_get_color(COLOR_GREP_RESET)); + + ch = *eol; + *eol = 0; + while (bol < eol) { + for (p = opt->pattern_list; p; p = p->next) { + if (match_one_pattern_1(opt, p, bol, eol, GREP_CONTEXT_BODY, eflags, &rm_so, &rm_eo)) + break; + } + + /* No match, break the loop */ + if (!p || + (rm_so < 0) || (eol - bol) <= rm_so || + (rm_eo < 0) || (eol - bol) < rm_eo) + break; + + printf("%.*s%s%.*s%s", + rm_so, bol, + grep_get_color(COLOR_GREP_MATCH), + rm_eo-rm_so, bol+rm_so, + grep_get_color(COLOR_GREP_RESET)); + bol += rm_eo; + eflags = REG_NOTBOL; + } + printf("%s\n", bol); + *eol = ch; +} + static int grep_buffer_1(struct grep_opt *opt, const char *name, char *buf, unsigned long size, int collect_hits) { @@ -466,8 +560,12 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name, } if (last_shown && lno != last_shown + 1) printf(hunk_mark); - if (!opt->count) - show_line(opt, bol, eol, name, lno, ':'); + if (!opt->count) { + if (opt->color) + show_line_colored(opt, bol, eol, name, lno, ':'); + else + show_line(opt, bol, eol, name, lno, ':'); + } last_shown = last_hit = lno; } else if (last_hit && diff --git a/grep.h b/grep.h index d252dd2..9073114 100644 --- a/grep.h +++ b/grep.h @@ -33,6 +33,15 @@ enum grep_expr_node { GREP_NODE_OR, }; +enum color_grep { + COLOR_GREP_RESET = 0, + COLOR_GREP_PLAIN = 1, + COLOR_GREP_FILEPATH = 2, + COLOR_GREP_LINENO = 3, + COLOR_GREP_SEPARATOR = 4, + COLOR_GREP_MATCH = 5, +}; + struct grep_expr { enum grep_expr_node node; unsigned hit; @@ -68,11 +77,14 @@ struct grep_opt { unsigned extended:1; unsigned relative:1; unsigned pathname:1; + unsigned color:1; int regflags; unsigned pre_context; unsigned post_context; }; +extern const char *grep_get_color(enum color_grep ix); +extern int parse_grep_color_slot(const char *var, int ofs); extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t); extern void compile_grep_patterns(struct grep_opt *opt); extern void free_grep_patterns(struct grep_opt *opt);