{"thread":{"id":"18190","subject":"[PATCH 0/5] grep: color search patterns","startedAt":"2009-03-07T12:24:59Z","lastAt":"2009-03-16T17:31:17Z","messageCount":10,"participants":["René Scharfe","Nguyen Thai Ngoc Duy","pclouds@gmail.com"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"107290","messageId":"1236428699.6486.41.camel@ubuntu.ubuntu-domain","threadId":"18190","inReplyTo":null,"subject":"[PATCH 0/5] grep: color search patterns","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-03-07T12:24:59Z","receivedAt":"2009-03-07T12:24:59Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Match coloring is a major missing feature of git grep.  It makes reading\nthe output much easier and brings grep onto the same visual level as the\nother colorized git commands.\n\nTwo earlier attempts to implement this feature didn't go in.  They\ndidn't support support pattern expressions (--and, --or etc.) or\nexternal greps, unlike this series.\n\n[PATCH 1/5] grep: micro-optimize hit collection for AND nodes\n[PATCH 2/5] grep: remove grep_opt argument from match_expr_eval()\n\n   These are cleanup patches and unrelated to coloring.  They touch the\n   same code, though, so it's a good idea to take this opportunity to\n   beautify the code a bit before adding new features.\n\n[PATCH 3/5] grep: add pmatch and eflags arguments to match_one_pattern()\n\n   The two arguments added by this patch are used by the next one.\n\n[PATCH 4/5] grep: color patterns in output\n\n   Add color support to internal git grep.\n\n[PATCH 5/5] grep: add support for coloring with external greps\n\n   Extends color support to external greps.\n"},{"id":"107291","messageId":"1236428835.6486.43.camel@ubuntu.ubuntu-domain","threadId":"18190","inReplyTo":"1236428699.6486.41.camel@ubuntu.ubuntu-domain","subject":"[PATCH 1/5] grep: micro-optimize hit collection for AND nodes","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-03-07T12:27:15Z","receivedAt":"2009-03-07T12:27:15Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"In addition to returning if an expression matches a line,\nmatch_expr_eval() updates the expression's hit flag if the parameter\ncollect_hits is set.  It never sets collect_hits for children of AND\nnodes, though, so their hit flag will never be updated.  Because of\nthat we can return early if the first child didn't match, no matter\nif collect_hits is set or not.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n grep.c |   10 +++-------\n 1 files changed, 3 insertions(+), 7 deletions(-)\n\ndiff --git a/grep.c b/grep.c\nindex 062b2b6..db341b6 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -394,13 +394,9 @@ static int match_expr_eval(struct grep_opt *o,\n \t\th = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);\n \t\tbreak;\n \tcase GREP_NODE_AND:\n-\t\tif (!collect_hits)\n-\t\t\treturn (match_expr_eval(o, x->u.binary.left,\n-\t\t\t\t\t\tbol, eol, ctx, 0) &&\n-\t\t\t\tmatch_expr_eval(o, x->u.binary.right,\n-\t\t\t\t\t\tbol, eol, ctx, 0));\n-\t\th = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);\n-\t\th &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);\n+\t\tif (!match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0))\n+\t\t\treturn 0;\n+\t\th = match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);\n \t\tbreak;\n \tcase GREP_NODE_OR:\n \t\tif (!collect_hits)\n-- \n1.6.2\n"},{"id":"107292","messageId":"1236428920.6486.45.camel@ubuntu.ubuntu-domain","threadId":"18190","inReplyTo":"1236428699.6486.41.camel@ubuntu.ubuntu-domain","subject":"[PATCH 2/5] grep: remove grep_opt argument from match_expr_eval()","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-03-07T12:28:40Z","receivedAt":"2009-03-07T12:28:40Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"The only use of the struct grep_opt argument of match_expr_eval()\nis to pass the option word_regexp to match_one_pattern().  By adding\na pattern flag for it we can reduce the number of function arguments\nof these two functions, as a cleanup and preparation for adding more\nin the next patch.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n grep.c |   34 +++++++++++++++++-----------------\n grep.h |    1 +\n 2 files changed, 18 insertions(+), 17 deletions(-)\n\ndiff --git a/grep.c b/grep.c\nindex db341b6..f455182 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -39,6 +39,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)\n {\n \tint err;\n \n+\tp->word_regexp = opt->word_regexp;\n+\n \tif (opt->fixed || is_fixed(p->pattern))\n \t\tp->fixed = 1;\n \tif (opt->regflags & REG_ICASE)\n@@ -306,7 +308,8 @@ static struct {\n \t{ \"committer \", 10 },\n };\n \n-static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)\n+static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,\n+\t\t\t     enum grep_context ctx)\n {\n \tint hit = 0;\n \tint saved_ch = 0;\n@@ -338,7 +341,7 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol\n \t\thit = !fixmatch(p->pattern, bol, pmatch);\n \t}\n \n-\tif (hit && opt->word_regexp) {\n+\tif (hit && p->word_regexp) {\n \t\tif ((pmatch[0].rm_so < 0) ||\n \t\t    (eol - bol) <= pmatch[0].rm_so ||\n \t\t    (pmatch[0].rm_eo < 0) ||\n@@ -378,35 +381,32 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol\n \treturn hit;\n }\n \n-static int match_expr_eval(struct grep_opt *o,\n-\t\t\t   struct grep_expr *x,\n-\t\t\t   char *bol, char *eol,\n-\t\t\t   enum grep_context ctx,\n-\t\t\t   int collect_hits)\n+static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,\n+\t\t\t   enum grep_context ctx, int collect_hits)\n {\n \tint h = 0;\n \n \tswitch (x->node) {\n \tcase GREP_NODE_ATOM:\n-\t\th = match_one_pattern(o, x->u.atom, bol, eol, ctx);\n+\t\th = match_one_pattern(x->u.atom, bol, eol, ctx);\n \t\tbreak;\n \tcase GREP_NODE_NOT:\n-\t\th = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);\n+\t\th = !match_expr_eval(x->u.unary, bol, eol, ctx, 0);\n \t\tbreak;\n \tcase GREP_NODE_AND:\n-\t\tif (!match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0))\n+\t\tif (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0))\n \t\t\treturn 0;\n-\t\th = match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);\n+\t\th = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0);\n \t\tbreak;\n \tcase GREP_NODE_OR:\n \t\tif (!collect_hits)\n-\t\t\treturn (match_expr_eval(o, x->u.binary.left,\n+\t\t\treturn (match_expr_eval(x->u.binary.left,\n \t\t\t\t\t\tbol, eol, ctx, 0) ||\n-\t\t\t\tmatch_expr_eval(o, x->u.binary.right,\n+\t\t\t\tmatch_expr_eval(x->u.binary.right,\n \t\t\t\t\t\tbol, eol, ctx, 0));\n-\t\th = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);\n+\t\th = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0);\n \t\tx->u.binary.left->hit |= h;\n-\t\th |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);\n+\t\th |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1);\n \t\tbreak;\n \tdefault:\n \t\tdie(\"Unexpected node type (internal error) %d\", x->node);\n@@ -420,7 +420,7 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol,\n \t\t      enum grep_context ctx, int collect_hits)\n {\n \tstruct grep_expr *x = opt->pattern_expression;\n-\treturn match_expr_eval(opt, x, bol, eol, ctx, collect_hits);\n+\treturn match_expr_eval(x, bol, eol, ctx, collect_hits);\n }\n \n static int match_line(struct grep_opt *opt, char *bol, char *eol,\n@@ -432,7 +432,7 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,\n \n \t/* we do not call with collect_hits without being extended */\n \tfor (p = opt->pattern_list; p; p = p->next) {\n-\t\tif (match_one_pattern(opt, p, bol, eol, ctx))\n+\t\tif (match_one_pattern(p, bol, eol, ctx))\n \t\t\treturn 1;\n \t}\n \treturn 0;\ndiff --git a/grep.h b/grep.h\nindex 5102ce3..d2a8674 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -31,6 +31,7 @@ struct grep_pat {\n \tenum grep_header_field field;\n \tregex_t regexp;\n \tunsigned fixed:1;\n+\tunsigned word_regexp:1;\n };\n \n enum grep_expr_node {\n-- \n1.6.2\n"},{"id":"107293","messageId":"1236429027.6486.47.camel@ubuntu.ubuntu-domain","threadId":"18190","inReplyTo":"1236428699.6486.41.camel@ubuntu.ubuntu-domain","subject":"[PATCH 3/5] grep: add pmatch and eflags arguments to match_one_pattern()","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-03-07T12:30:27Z","receivedAt":"2009-03-07T12:30:27Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Push pmatch and eflags to the callers of match_one_pattern(), which\nallows them to specify regex execution flags and to get the location\nof a match.\n\nSince we only use the first element of the matches array and aren't\ninterested in submatches, no provision is made for callers to\nprovide a larger array.\n\neflags are ignored for fixed patterns, but that's OK, since they\nonly have a meaning in connection with regular expressions\ncontaining ^ or $.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n grep.c |   21 ++++++++++-----------\n 1 files changed, 10 insertions(+), 11 deletions(-)\n\ndiff --git a/grep.c b/grep.c\nindex f455182..bdcff7b 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -309,11 +309,11 @@ static struct {\n };\n \n static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,\n-\t\t\t     enum grep_context ctx)\n+\t\t\t     enum grep_context ctx,\n+\t\t\t     regmatch_t *pmatch, int eflags)\n {\n \tint hit = 0;\n \tint saved_ch = 0;\n-\tregmatch_t pmatch[10];\n \n \tif ((p->token != GREP_PATTERN) &&\n \t    ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))\n@@ -332,14 +332,10 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,\n \t}\n \n  again:\n-\tif (!p->fixed) {\n-\t\tregex_t *exp = &p->regexp;\n-\t\thit = !regexec(exp, bol, ARRAY_SIZE(pmatch),\n-\t\t\t       pmatch, 0);\n-\t}\n-\telse {\n+\tif (p->fixed)\n \t\thit = !fixmatch(p->pattern, bol, pmatch);\n-\t}\n+\telse\n+\t\thit = !regexec(&p->regexp, bol, 1, pmatch, eflags);\n \n \tif (hit && p->word_regexp) {\n \t\tif ((pmatch[0].rm_so < 0) ||\n@@ -385,10 +381,11 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,\n \t\t\t   enum grep_context ctx, int collect_hits)\n {\n \tint h = 0;\n+\tregmatch_t match;\n \n \tswitch (x->node) {\n \tcase GREP_NODE_ATOM:\n-\t\th = match_one_pattern(x->u.atom, bol, eol, ctx);\n+\t\th = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);\n \t\tbreak;\n \tcase GREP_NODE_NOT:\n \t\th = !match_expr_eval(x->u.unary, bol, eol, ctx, 0);\n@@ -427,12 +424,14 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,\n \t\t      enum grep_context ctx, int collect_hits)\n {\n \tstruct grep_pat *p;\n+\tregmatch_t match;\n+\n \tif (opt->extended)\n \t\treturn match_expr(opt, bol, eol, ctx, collect_hits);\n \n \t/* we do not call with collect_hits without being extended */\n \tfor (p = opt->pattern_list; p; p = p->next) {\n-\t\tif (match_one_pattern(p, bol, eol, ctx))\n+\t\tif (match_one_pattern(p, bol, eol, ctx, &match, 0))\n \t\t\treturn 1;\n \t}\n \treturn 0;\n-- \n1.6.2\n"},{"id":"107296","messageId":"1236429152.6486.50.camel@ubuntu.ubuntu-domain","threadId":"18190","inReplyTo":"1236428699.6486.41.camel@ubuntu.ubuntu-domain","subject":"[PATCH 4/5] grep: color patterns in output","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-03-07T12:32:32Z","receivedAt":"2009-03-07T12:32:32Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Coloring matches makes them easier to spot in the output.\n\nAdd two options and two parameters: color.grep (to turn coloring on\nor off), color.grep.match (to set the color of matches), --color\nand --no-color (to turn coloring on or off, respectively).\n\nThe output of external greps is not changed.\n\nThis patch is based on earlier ones by Nguyễn Thái Ngọc Duy and\nThiago Alves.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n Documentation/config.txt   |    9 ++++\n Documentation/git-grep.txt |    8 ++++\n builtin-grep.c             |   32 +++++++++++++++\n grep.c                     |   90 ++++++++++++++++++++++++++++++++++++++------\n grep.h                     |    3 +\n 5 files changed, 130 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex f5152c5..b75dada 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -548,6 +548,15 @@ color.diff.<slot>::\n \twhitespace errors). The values of these variables may be specified as\n \tin color.branch.<slot>.\n \n+color.grep::\n+\tWhen set to `always`, always highlight matches.  When `false` (or\n+\t`never`), never.  When set to `true` or `auto`, use color only\n+\twhen the output is written to the terminal.  Defaults to `false`.\n+\n+color.grep.match::\n+\tUse customized color for matches.  The value of this variable\n+\tmay be specified as in color.branch.<slot>.\n+\n color.interactive::\n \tWhen set to `always`, always use colors for interactive prompts\n \tand displays (such as those used by \"git-add --interactive\").\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex 553da6c..fccb82d 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -17,6 +17,7 @@ SYNOPSIS\n \t   [-l | --files-with-matches] [-L | --files-without-match]\n \t   [-z | --null]\n \t   [-c | --count] [--all-match]\n+\t   [--color | --no-color]\n \t   [-A <post-context>] [-B <pre-context>] [-C <context>]\n \t   [-f <file>] [-e] <pattern>\n \t   [--and|--or|--not|(|)|-e <pattern>...] [<tree>...]\n@@ -105,6 +106,13 @@ OPTIONS\n \tInstead of showing every matched line, show the number of\n \tlines that match.\n \n+--color::\n+\tShow colored matches.\n+\n+--no-color::\n+\tTurn off match highlighting, even when the configuration file\n+\tgives the default to color output.\n+\n -[ABC] <context>::\n \tShow `context` trailing (`A` -- after), or leading (`B`\n \t-- before), or both (`C` -- context) lines, and place a\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 3f12ba3..e2c0f01 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -22,6 +22,24 @@\n \n static int builtin_grep;\n \n+static int grep_config(const char *var, const char *value, void *cb)\n+{\n+\tstruct grep_opt *opt = cb;\n+\n+\tif (!strcmp(var, \"grep.color\") || !strcmp(var, \"color.grep\")) {\n+\t\topt->color = git_config_colorbool(var, value, -1);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(var, \"grep.color.match\") ||\n+\t    !strcmp(var, \"color.grep.match\")) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\tcolor_parse(value, var, opt->color_match);\n+\t\treturn 0;\n+\t}\n+\treturn git_color_default_config(var, value, cb);\n+}\n+\n /*\n  * git grep pathspecs are somewhat different from diff-tree pathspecs;\n  * pathname wildcards are allowed.\n@@ -536,6 +554,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \topt.pattern_tail = &opt.pattern_list;\n \topt.regflags = REG_NEWLINE;\n \n+\tstrcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);\n+\topt.color = -1;\n+\tgit_config(grep_config, &opt);\n+\tif (opt.color == -1)\n+\t\topt.color = git_use_color_default;\n+\n \t/*\n \t * If there is no -- then the paths must exist in the working\n \t * tree.  If there is no explicit pattern specified with -e or\n@@ -732,6 +756,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t\topt.relative = 0;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(\"--color\", arg)) {\n+\t\t\topt.color = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"--no-color\", arg)) {\n+\t\t\topt.color = 0;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(\"--\", arg)) {\n \t\t\t/* later processing wants to have this at argv[1] */\n \t\t\targv--;\ndiff --git a/grep.c b/grep.c\nindex bdcff7b..cace1c8 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -253,18 +253,6 @@ static int word_char(char ch)\n \treturn isalnum(ch) || ch == '_';\n }\n \n-static void show_line(struct grep_opt *opt, const char *bol, const char *eol,\n-\t\t      const char *name, unsigned lno, char sign)\n-{\n-\tif (opt->null_following_name)\n-\t\tsign = '\\0';\n-\tif (opt->pathname)\n-\t\tprintf(\"%s%c\", name, sign);\n-\tif (opt->linenum)\n-\t\tprintf(\"%d%c\", lno, sign);\n-\tprintf(\"%.*s\\n\", (int)(eol-bol), bol);\n-}\n-\n static void show_name(struct grep_opt *opt, const char *name)\n {\n \tprintf(\"%s%c\", name, opt->null_following_name ? '\\0' : '\\n');\n@@ -437,6 +425,84 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,\n \treturn 0;\n }\n \n+static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,\n+\t\t\t      enum grep_context ctx,\n+\t\t\t      regmatch_t *pmatch, int eflags)\n+{\n+\tregmatch_t match;\n+\n+\tif (!match_one_pattern(p, bol, eol, ctx, &match, eflags))\n+\t\treturn 0;\n+\tif (match.rm_so < 0 || match.rm_eo < 0)\n+\t\treturn 0;\n+\tif (pmatch->rm_so >= 0 && pmatch->rm_eo >= 0) {\n+\t\tif (match.rm_so > pmatch->rm_so)\n+\t\t\treturn 1;\n+\t\tif (match.rm_so == pmatch->rm_so && match.rm_eo < pmatch->rm_eo)\n+\t\t\treturn 1;\n+\t}\n+\tpmatch->rm_so = match.rm_so;\n+\tpmatch->rm_eo = match.rm_eo;\n+\treturn 1;\n+}\n+\n+static int next_match(struct grep_opt *opt, char *bol, char *eol,\n+\t\t      enum grep_context ctx, regmatch_t *pmatch, int eflags)\n+{\n+\tstruct grep_pat *p;\n+\tint hit = 0;\n+\n+\tpmatch->rm_so = pmatch->rm_eo = -1;\n+\tif (bol < eol) {\n+\t\tfor (p = opt->pattern_list; p; p = p->next) {\n+\t\t\tswitch (p->token) {\n+\t\t\tcase GREP_PATTERN: /* atom */\n+\t\t\tcase GREP_PATTERN_HEAD:\n+\t\t\tcase GREP_PATTERN_BODY:\n+\t\t\t\thit |= match_next_pattern(p, bol, eol, ctx,\n+\t\t\t\t\t\t\t  pmatch, eflags);\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn hit;\n+}\n+\n+static void show_line(struct grep_opt *opt, char *bol, char *eol,\n+\t\t      const char *name, unsigned lno, char sign)\n+{\n+\tint rest = eol - bol;\n+\n+\tif (opt->null_following_name)\n+\t\tsign = '\\0';\n+\tif (opt->pathname)\n+\t\tprintf(\"%s%c\", name, sign);\n+\tif (opt->linenum)\n+\t\tprintf(\"%d%c\", lno, sign);\n+\tif (opt->color) {\n+\t\tregmatch_t match;\n+\t\tenum grep_context ctx = GREP_CONTEXT_BODY;\n+\t\tint ch = *eol;\n+\t\tint eflags = 0;\n+\n+\t\t*eol = '\\0';\n+\t\twhile (next_match(opt, bol, eol, ctx, &match, eflags)) {\n+\t\t\tprintf(\"%.*s%s%.*s%s\",\n+\t\t\t       match.rm_so, bol,\n+\t\t\t       opt->color_match,\n+\t\t\t       match.rm_eo - match.rm_so, bol + match.rm_so,\n+\t\t\t       GIT_COLOR_RESET);\n+\t\t\tbol += match.rm_eo;\n+\t\t\trest -= match.rm_eo;\n+\t\t\teflags = REG_NOTBOL;\n+\t\t}\n+\t\t*eol = ch;\n+\t}\n+\tprintf(\"%.*s\\n\", rest, bol);\n+}\n+\n static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t\t char *buf, unsigned long size, int collect_hits)\n {\ndiff --git a/grep.h b/grep.h\nindex d2a8674..73b33ab 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -1,5 +1,6 @@\n #ifndef GREP_H\n #define GREP_H\n+#include \"color.h\"\n \n enum grep_pat_token {\n \tGREP_PATTERN,\n@@ -77,6 +78,8 @@ struct grep_opt {\n \tunsigned relative:1;\n \tunsigned pathname:1;\n \tunsigned null_following_name:1;\n+\tint color;\n+\tchar color_match[COLOR_MAXLEN];\n \tint regflags;\n \tunsigned pre_context;\n \tunsigned post_context;\n-- \n1.6.2\n"},{"id":"107297","messageId":"1236429286.6486.52.camel@ubuntu.ubuntu-domain","threadId":"18190","inReplyTo":"1236428699.6486.41.camel@ubuntu.ubuntu-domain","subject":"[PATCH 5/5] grep: add support for coloring with external greps","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-03-07T12:34:46Z","receivedAt":"2009-03-07T12:34:46Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Add the config variable color.grep.external, which can be used to\nswitch on coloring of external greps.  To enable auto coloring with\nGNU grep, one needs to set color.grep.external to --color=always to\ndefeat the pager started by git grep.  The value of the config\nvariable will be passed to the external grep only if it would\ncolorize internal grep's output, so automatic terminal detected\nworks.  The default is to not pass any option, because the external\ngrep command could be a program without color support.\n\nAlso set the environment variables GREP_COLOR and GREP_COLORS to\npass the configured color for matches to the external grep.  This\nworks with GNU grep; other variables could be added as needed.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n Documentation/config.txt |   12 +++++++++++-\n builtin-grep.c           |   36 ++++++++++++++++++++++++++++++++++++\n grep.h                   |    1 +\n 3 files changed, 48 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex b75dada..4d42bff 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -553,9 +553,19 @@ color.grep::\n \t`never`), never.  When set to `true` or `auto`, use color only\n \twhen the output is written to the terminal.  Defaults to `false`.\n \n+color.grep.external::\n+\tThe string value of this variable is passed to an external 'grep'\n+\tcommand as a command line option if match highlighting is turned\n+\ton.  If set to an empty string, no option is passed at all,\n+\tturning off coloring for external 'grep' calls; this is the default.\n+\tFor GNU grep, set it to `--color=always` to highlight matches even\n+\twhen a pager is used.\n+\n color.grep.match::\n \tUse customized color for matches.  The value of this variable\n-\tmay be specified as in color.branch.<slot>.\n+\tmay be specified as in color.branch.<slot>.  It is passed using\n+\tthe environment variables 'GREP_COLOR' and 'GREP_COLORS' when\n+\tcalling an external 'grep'.\n \n color.interactive::\n \tWhen set to `always`, always use colors for interactive prompts\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex e2c0f01..9e7e766 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -30,6 +30,10 @@ static int grep_config(const char *var, const char *value, void *cb)\n \t\topt->color = git_config_colorbool(var, value, -1);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"grep.color.external\") ||\n+\t    !strcmp(var, \"color.grep.external\")) {\n+\t\treturn git_config_string(&(opt->color_external), var, value);\n+\t}\n \tif (!strcmp(var, \"grep.color.match\") ||\n \t    !strcmp(var, \"color.grep.match\")) {\n \t\tif (!value)\n@@ -287,6 +291,21 @@ static int flush_grep(struct grep_opt *opt,\n \treturn status;\n }\n \n+static void grep_add_color(struct strbuf *sb, const char *escape_seq)\n+{\n+\tsize_t orig_len = sb->len;\n+\n+\twhile (*escape_seq) {\n+\t\tif (*escape_seq == 'm')\n+\t\t\tstrbuf_addch(sb, ';');\n+\t\telse if (*escape_seq != '\\033' && *escape_seq  != '[')\n+\t\t\tstrbuf_addch(sb, *escape_seq);\n+\t\tescape_seq++;\n+\t}\n+\tif (sb->len > orig_len && sb->buf[sb->len - 1] == ';')\n+\t\tstrbuf_setlen(sb, sb->len - 1);\n+}\n+\n static int external_grep(struct grep_opt *opt, const char **paths, int cached)\n {\n \tint i, nr, argc, hit, len, status;\n@@ -357,6 +376,23 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)\n \t\tpush_arg(\"-e\");\n \t\tpush_arg(p->pattern);\n \t}\n+\tif (opt->color) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tgrep_add_color(&sb, opt->color_match);\n+\t\tsetenv(\"GREP_COLOR\", sb.buf, 1);\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_addstr(&sb, \"mt=\");\n+\t\tgrep_add_color(&sb, opt->color_match);\n+\t\tstrbuf_addstr(&sb, \":sl=:cx=:fn=:ln=:bn=:se=\");\n+\t\tsetenv(\"GREP_COLORS\", sb.buf, 1);\n+\n+\t\tstrbuf_release(&sb);\n+\n+\t\tif (opt->color_external && strlen(opt->color_external) > 0)\n+\t\t\tpush_arg(opt->color_external);\n+\t}\n \n \thit = 0;\n \targc = nr;\ndiff --git a/grep.h b/grep.h\nindex 73b33ab..a67005d 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -80,6 +80,7 @@ struct grep_opt {\n \tunsigned null_following_name:1;\n \tint color;\n \tchar color_match[COLOR_MAXLEN];\n+\tconst char *color_external;\n \tint regflags;\n \tunsigned pre_context;\n \tunsigned post_context;\n-- \n1.6.2\n"},{"id":"107541","messageId":"fcaeb9bf0903092301i7bc6322dtbd37f662fe4b224b@mail.gmail.com","threadId":"18190","inReplyTo":"1236428699.6486.41.camel@ubuntu.ubuntu-domain","subject":"Re: [PATCH 0/5] grep: color search patterns","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-03-10T06:01:24Z","receivedAt":"2009-03-10T06:01:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On 3/7/09, René Scharfe <rene.scharfe@lsrfire.ath.cx> wrote:\n> Match coloring is a major missing feature of git grep.  It makes reading\n>  the output much easier and brings grep onto the same visual level as the\n>  other colorized git commands.\n\n\"git --color test\" did not colorize the result for me. \"git --color\n--no-ext-grep test\" did. Maybe you should ignore external grep unless\nit is explicitly requested, like in the last patch. I have very\nlimited net access these days. Let's see if I can work out something\nfor tomorrow, unless you beat me to it.\n-- \nDuy\n"},{"id":"107593","messageId":"49B6979B.8080900@lsrfire.ath.cx","threadId":"18190","inReplyTo":"fcaeb9bf0903092301i7bc6322dtbd37f662fe4b224b@mail.gmail.com","subject":"Re: [PATCH 0/5] grep: color search patterns","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-03-10T16:38:51Z","receivedAt":"2009-03-10T16:38:51Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Nguyen Thai Ngoc Duy schrieb:\n> On 3/7/09, René Scharfe <rene.scharfe@lsrfire.ath.cx> wrote:\n>> Match coloring is a major missing feature of git grep.  It makes reading\n>>  the output much easier and brings grep onto the same visual level as the\n>>  other colorized git commands.\n> \n> \"git --color test\" did not colorize the result for me. \"git --color\n> --no-ext-grep test\" did. Maybe you should ignore external grep unless\n> it is explicitly requested, like in the last patch. I have very\n> limited net access these days. Let's see if I can work out something\n> for tomorrow, unless you beat me to it.\n\nI assume with \"last patch\" you mean your last patch, right?  It\nautomatically switched to internal grep if coloring was turned on.\n\nPatch 5 of my series adds support for coloring external greps, but you\nhave to explicitly specify the config option grep.color.external.  It\ncan be set by distributors in the system config file -- they should know\nwhich grep option is needed.\n\nBut I can see that a half-colored git grep can be confusing.\n\nRené\n"},{"id":"108064","messageId":"1237170004-27768-1-git-send-email-pclouds@gmail.com","threadId":"18190","inReplyTo":"49B6979B.8080900@lsrfire.ath.cx","subject":"[PATCH] grep: prefer builtin over external one when coloring results","fromName":"","fromEmail":"pclouds@gmail.com","sentAt":"2009-03-16T02:20:04Z","receivedAt":"2009-03-16T02:20:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"As far as I know, not all grep programs support coloring, so we should\nrely on builtin grep. If you want external grep, set\ncolor.grep.external to empty string.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n How about this?\n\n builtin-grep.c |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 9e7e766..89489dd 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -825,6 +825,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tif (opt.color && !opt.color_external)\n+\t\tbuiltin_grep = 1;\n \tif (!opt.pattern_list)\n \t\tdie(\"no pattern given.\");\n \tif ((opt.regflags != REG_NEWLINE) && opt.fixed)\n-- \n1.6.1.446.gc7851\n"},{"id":"108104","messageId":"49BE8CE5.2020603@lsrfire.ath.cx","threadId":"18190","inReplyTo":"1237170004-27768-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH] grep: prefer builtin over external one when coloring results","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-03-16T17:31:17Z","receivedAt":"2009-03-16T17:31:17Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"pclouds@gmail.com schrieb:\n> As far as I know, not all grep programs support coloring, so we should\n> rely on builtin grep. If you want external grep, set\n> color.grep.external to empty string.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  How about this?\n> \n>  builtin-grep.c |    2 ++\n>  1 files changed, 2 insertions(+), 0 deletions(-)\n> \n> diff --git a/builtin-grep.c b/builtin-grep.c\n> index 9e7e766..89489dd 100644\n> --- a/builtin-grep.c\n> +++ b/builtin-grep.c\n> @@ -825,6 +825,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>  \t\t}\n>  \t}\n>  \n> +\tif (opt.color && !opt.color_external)\n> +\t\tbuiltin_grep = 1;\n>  \tif (!opt.pattern_list)\n>  \t\tdie(\"no pattern given.\");\n>  \tif ((opt.regflags != REG_NEWLINE) && opt.fixed)\n\nMakes sense, thanks!\n\nRené\n"}]}