зеркало из https://github.com/microsoft/git.git
Merge branch 'rs/color-grep'
* rs/color-grep: grep: prefer builtin over external one when coloring results grep: cast printf %.*s "precision" argument explicitly to int grep: add support for coloring with external greps grep: color patterns in output grep: add pmatch and eflags arguments to match_one_pattern() grep: remove grep_opt argument from match_expr_eval() grep: micro-optimize hit collection for AND nodes
This commit is contained in:
Коммит
b332368de8
|
@ -553,6 +553,25 @@ color.diff.<slot>::
|
|||
whitespace errors). The values of these variables may be specified as
|
||||
in color.branch.<slot>.
|
||||
|
||||
color.grep::
|
||||
When set to `always`, always highlight matches. When `false` (or
|
||||
`never`), never. When set to `true` or `auto`, use color only
|
||||
when the output is written to the terminal. Defaults to `false`.
|
||||
|
||||
color.grep.external::
|
||||
The string value of this variable is passed to an external 'grep'
|
||||
command as a command line option if match highlighting is turned
|
||||
on. If set to an empty string, no option is passed at all,
|
||||
turning off coloring for external 'grep' calls; this is the default.
|
||||
For GNU grep, set it to `--color=always` to highlight matches even
|
||||
when a pager is used.
|
||||
|
||||
color.grep.match::
|
||||
Use customized color for matches. The value of this variable
|
||||
may be specified as in color.branch.<slot>. It is passed using
|
||||
the environment variables 'GREP_COLOR' and 'GREP_COLORS' when
|
||||
calling an external 'grep'.
|
||||
|
||||
color.interactive::
|
||||
When set to `always`, always use colors for interactive prompts
|
||||
and displays (such as those used by "git-add --interactive").
|
||||
|
|
|
@ -17,6 +17,7 @@ SYNOPSIS
|
|||
[-l | --files-with-matches] [-L | --files-without-match]
|
||||
[-z | --null]
|
||||
[-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>...]
|
||||
|
@ -105,6 +106,13 @@ OPTIONS
|
|||
Instead of showing every matched line, show the number of
|
||||
lines that match.
|
||||
|
||||
--color::
|
||||
Show colored matches.
|
||||
|
||||
--no-color::
|
||||
Turn off match highlighting, 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
|
||||
|
|
|
@ -22,6 +22,28 @@
|
|||
|
||||
static int builtin_grep;
|
||||
|
||||
static int grep_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
struct grep_opt *opt = cb;
|
||||
|
||||
if (!strcmp(var, "grep.color") || !strcmp(var, "color.grep")) {
|
||||
opt->color = git_config_colorbool(var, value, -1);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "grep.color.external") ||
|
||||
!strcmp(var, "color.grep.external")) {
|
||||
return git_config_string(&(opt->color_external), var, value);
|
||||
}
|
||||
if (!strcmp(var, "grep.color.match") ||
|
||||
!strcmp(var, "color.grep.match")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
color_parse(value, var, opt->color_match);
|
||||
return 0;
|
||||
}
|
||||
return git_color_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
/*
|
||||
* git grep pathspecs are somewhat different from diff-tree pathspecs;
|
||||
* pathname wildcards are allowed.
|
||||
|
@ -269,6 +291,21 @@ static int flush_grep(struct grep_opt *opt,
|
|||
return status;
|
||||
}
|
||||
|
||||
static void grep_add_color(struct strbuf *sb, const char *escape_seq)
|
||||
{
|
||||
size_t orig_len = sb->len;
|
||||
|
||||
while (*escape_seq) {
|
||||
if (*escape_seq == 'm')
|
||||
strbuf_addch(sb, ';');
|
||||
else if (*escape_seq != '\033' && *escape_seq != '[')
|
||||
strbuf_addch(sb, *escape_seq);
|
||||
escape_seq++;
|
||||
}
|
||||
if (sb->len > orig_len && sb->buf[sb->len - 1] == ';')
|
||||
strbuf_setlen(sb, sb->len - 1);
|
||||
}
|
||||
|
||||
static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
{
|
||||
int i, nr, argc, hit, len, status;
|
||||
|
@ -339,6 +376,23 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
|||
push_arg("-e");
|
||||
push_arg(p->pattern);
|
||||
}
|
||||
if (opt->color) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
grep_add_color(&sb, opt->color_match);
|
||||
setenv("GREP_COLOR", sb.buf, 1);
|
||||
|
||||
strbuf_reset(&sb);
|
||||
strbuf_addstr(&sb, "mt=");
|
||||
grep_add_color(&sb, opt->color_match);
|
||||
strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se=");
|
||||
setenv("GREP_COLORS", sb.buf, 1);
|
||||
|
||||
strbuf_release(&sb);
|
||||
|
||||
if (opt->color_external && strlen(opt->color_external) > 0)
|
||||
push_arg(opt->color_external);
|
||||
}
|
||||
|
||||
hit = 0;
|
||||
argc = nr;
|
||||
|
@ -536,6 +590,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
opt.pattern_tail = &opt.pattern_list;
|
||||
opt.regflags = REG_NEWLINE;
|
||||
|
||||
strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
|
||||
opt.color = -1;
|
||||
git_config(grep_config, &opt);
|
||||
if (opt.color == -1)
|
||||
opt.color = git_use_color_default;
|
||||
|
||||
/*
|
||||
* If there is no -- then the paths must exist in the working
|
||||
* tree. If there is no explicit pattern specified with -e or
|
||||
|
@ -732,6 +792,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--;
|
||||
|
@ -757,6 +825,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
}
|
||||
|
||||
if (opt.color && !opt.color_external)
|
||||
builtin_grep = 1;
|
||||
if (!opt.pattern_list)
|
||||
die("no pattern given.");
|
||||
if ((opt.regflags != REG_NEWLINE) && opt.fixed)
|
||||
|
|
145
grep.c
145
grep.c
|
@ -39,6 +39,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
|
|||
{
|
||||
int err;
|
||||
|
||||
p->word_regexp = opt->word_regexp;
|
||||
|
||||
if (opt->fixed || is_fixed(p->pattern))
|
||||
p->fixed = 1;
|
||||
if (opt->regflags & REG_ICASE)
|
||||
|
@ -251,18 +253,6 @@ static int word_char(char ch)
|
|||
return isalnum(ch) || ch == '_';
|
||||
}
|
||||
|
||||
static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
|
||||
const char *name, unsigned lno, char sign)
|
||||
{
|
||||
if (opt->null_following_name)
|
||||
sign = '\0';
|
||||
if (opt->pathname)
|
||||
printf("%s%c", name, sign);
|
||||
if (opt->linenum)
|
||||
printf("%d%c", lno, sign);
|
||||
printf("%.*s\n", (int)(eol-bol), bol);
|
||||
}
|
||||
|
||||
static void show_name(struct grep_opt *opt, const char *name)
|
||||
{
|
||||
printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
|
||||
|
@ -306,11 +296,12 @@ static struct {
|
|||
{ "committer ", 10 },
|
||||
};
|
||||
|
||||
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(struct grep_pat *p, char *bol, char *eol,
|
||||
enum grep_context ctx,
|
||||
regmatch_t *pmatch, int eflags)
|
||||
{
|
||||
int hit = 0;
|
||||
int saved_ch = 0;
|
||||
regmatch_t pmatch[10];
|
||||
|
||||
if ((p->token != GREP_PATTERN) &&
|
||||
((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
|
||||
|
@ -329,16 +320,12 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
|
|||
}
|
||||
|
||||
again:
|
||||
if (!p->fixed) {
|
||||
regex_t *exp = &p->regexp;
|
||||
hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
|
||||
pmatch, 0);
|
||||
}
|
||||
else {
|
||||
if (p->fixed)
|
||||
hit = !fixmatch(p->pattern, bol, pmatch);
|
||||
}
|
||||
else
|
||||
hit = !regexec(&p->regexp, bol, 1, pmatch, eflags);
|
||||
|
||||
if (hit && opt->word_regexp) {
|
||||
if (hit && p->word_regexp) {
|
||||
if ((pmatch[0].rm_so < 0) ||
|
||||
(eol - bol) <= pmatch[0].rm_so ||
|
||||
(pmatch[0].rm_eo < 0) ||
|
||||
|
@ -378,39 +365,33 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
|
|||
return hit;
|
||||
}
|
||||
|
||||
static int match_expr_eval(struct grep_opt *o,
|
||||
struct grep_expr *x,
|
||||
char *bol, char *eol,
|
||||
enum grep_context ctx,
|
||||
int collect_hits)
|
||||
static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
|
||||
enum grep_context ctx, int collect_hits)
|
||||
{
|
||||
int h = 0;
|
||||
regmatch_t match;
|
||||
|
||||
switch (x->node) {
|
||||
case GREP_NODE_ATOM:
|
||||
h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
|
||||
h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
|
||||
break;
|
||||
case GREP_NODE_NOT:
|
||||
h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
|
||||
h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0);
|
||||
break;
|
||||
case GREP_NODE_AND:
|
||||
if (!collect_hits)
|
||||
return (match_expr_eval(o, x->u.binary.left,
|
||||
bol, eol, ctx, 0) &&
|
||||
match_expr_eval(o, x->u.binary.right,
|
||||
bol, eol, ctx, 0));
|
||||
h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
|
||||
h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
|
||||
if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0))
|
||||
return 0;
|
||||
h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0);
|
||||
break;
|
||||
case GREP_NODE_OR:
|
||||
if (!collect_hits)
|
||||
return (match_expr_eval(o, x->u.binary.left,
|
||||
return (match_expr_eval(x->u.binary.left,
|
||||
bol, eol, ctx, 0) ||
|
||||
match_expr_eval(o, x->u.binary.right,
|
||||
match_expr_eval(x->u.binary.right,
|
||||
bol, eol, ctx, 0));
|
||||
h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
|
||||
h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0);
|
||||
x->u.binary.left->hit |= h;
|
||||
h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
|
||||
h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1);
|
||||
break;
|
||||
default:
|
||||
die("Unexpected node type (internal error) %d", x->node);
|
||||
|
@ -424,24 +405,104 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol,
|
|||
enum grep_context ctx, int collect_hits)
|
||||
{
|
||||
struct grep_expr *x = opt->pattern_expression;
|
||||
return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
|
||||
return match_expr_eval(x, bol, eol, ctx, collect_hits);
|
||||
}
|
||||
|
||||
static int match_line(struct grep_opt *opt, char *bol, char *eol,
|
||||
enum grep_context ctx, int collect_hits)
|
||||
{
|
||||
struct grep_pat *p;
|
||||
regmatch_t match;
|
||||
|
||||
if (opt->extended)
|
||||
return match_expr(opt, bol, eol, ctx, collect_hits);
|
||||
|
||||
/* we do not call with collect_hits without being extended */
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
if (match_one_pattern(opt, p, bol, eol, ctx))
|
||||
if (match_one_pattern(p, bol, eol, ctx, &match, 0))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
|
||||
enum grep_context ctx,
|
||||
regmatch_t *pmatch, int eflags)
|
||||
{
|
||||
regmatch_t match;
|
||||
|
||||
if (!match_one_pattern(p, bol, eol, ctx, &match, eflags))
|
||||
return 0;
|
||||
if (match.rm_so < 0 || match.rm_eo < 0)
|
||||
return 0;
|
||||
if (pmatch->rm_so >= 0 && pmatch->rm_eo >= 0) {
|
||||
if (match.rm_so > pmatch->rm_so)
|
||||
return 1;
|
||||
if (match.rm_so == pmatch->rm_so && match.rm_eo < pmatch->rm_eo)
|
||||
return 1;
|
||||
}
|
||||
pmatch->rm_so = match.rm_so;
|
||||
pmatch->rm_eo = match.rm_eo;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int next_match(struct grep_opt *opt, char *bol, char *eol,
|
||||
enum grep_context ctx, regmatch_t *pmatch, int eflags)
|
||||
{
|
||||
struct grep_pat *p;
|
||||
int hit = 0;
|
||||
|
||||
pmatch->rm_so = pmatch->rm_eo = -1;
|
||||
if (bol < eol) {
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
switch (p->token) {
|
||||
case GREP_PATTERN: /* atom */
|
||||
case GREP_PATTERN_HEAD:
|
||||
case GREP_PATTERN_BODY:
|
||||
hit |= match_next_pattern(p, bol, eol, ctx,
|
||||
pmatch, eflags);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
static void show_line(struct grep_opt *opt, char *bol, char *eol,
|
||||
const char *name, unsigned lno, char sign)
|
||||
{
|
||||
int rest = eol - bol;
|
||||
|
||||
if (opt->null_following_name)
|
||||
sign = '\0';
|
||||
if (opt->pathname)
|
||||
printf("%s%c", name, sign);
|
||||
if (opt->linenum)
|
||||
printf("%d%c", lno, sign);
|
||||
if (opt->color) {
|
||||
regmatch_t match;
|
||||
enum grep_context ctx = GREP_CONTEXT_BODY;
|
||||
int ch = *eol;
|
||||
int eflags = 0;
|
||||
|
||||
*eol = '\0';
|
||||
while (next_match(opt, bol, eol, ctx, &match, eflags)) {
|
||||
printf("%.*s%s%.*s%s",
|
||||
(int)match.rm_so, bol,
|
||||
opt->color_match,
|
||||
(int)(match.rm_eo - match.rm_so), bol + match.rm_so,
|
||||
GIT_COLOR_RESET);
|
||||
bol += match.rm_eo;
|
||||
rest -= match.rm_eo;
|
||||
eflags = REG_NOTBOL;
|
||||
}
|
||||
*eol = ch;
|
||||
}
|
||||
printf("%.*s\n", rest, bol);
|
||||
}
|
||||
|
||||
static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||
char *buf, unsigned long size, int collect_hits)
|
||||
{
|
||||
|
|
5
grep.h
5
grep.h
|
@ -1,5 +1,6 @@
|
|||
#ifndef GREP_H
|
||||
#define GREP_H
|
||||
#include "color.h"
|
||||
|
||||
enum grep_pat_token {
|
||||
GREP_PATTERN,
|
||||
|
@ -31,6 +32,7 @@ struct grep_pat {
|
|||
enum grep_header_field field;
|
||||
regex_t regexp;
|
||||
unsigned fixed:1;
|
||||
unsigned word_regexp:1;
|
||||
};
|
||||
|
||||
enum grep_expr_node {
|
||||
|
@ -76,6 +78,9 @@ struct grep_opt {
|
|||
unsigned relative:1;
|
||||
unsigned pathname:1;
|
||||
unsigned null_following_name:1;
|
||||
int color;
|
||||
char color_match[COLOR_MAXLEN];
|
||||
const char *color_external;
|
||||
int regflags;
|
||||
unsigned pre_context;
|
||||
unsigned post_context;
|
||||
|
|
Загрузка…
Ссылка в новой задаче