grep.[ch]: extend grep_opt to allow showing matched column

To support showing the matched column when calling 'git-grep(1)', teach 'grep_opt' the normal set of options to configure the default behavior and colorization of this feature. Now that we have opt->columnnum, use it to disable short-circuiting over ORs and ANDs so that col and icol are always filled with the earliest matches on each line. In addition, don't return the first match from match_line(), for the same reason. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed Jun 22, 2018 at 10:49 UTC 017c0fcfdb21dd44e2c83f533e9a6d78513e7d8c
2 files changed +39 -10
grep.c
+37 -10
@@ -46,6 +46,7 @@ void init_grep_defaults(void)
46 color_set(opt->color_filename, "");
47 color_set(opt->color_function, "");
48 color_set(opt->color_lineno, "");
49 + color_set(opt->color_columnno, "");
50 color_set(opt->color_match_context, GIT_COLOR_BOLD_RED);
51 color_set(opt->color_match_selected, GIT_COLOR_BOLD_RED);
52 color_set(opt->color_selected, "");
@@ -155,6 +156,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
156 opt->extended_regexp_option = def->extended_regexp_option;
157 opt->pattern_type_option = def->pattern_type_option;
158 opt->linenum = def->linenum;
159 + opt->columnnum = def->columnnum;
160 opt->max_depth = def->max_depth;
161 opt->pathname = def->pathname;
162 opt->relative = def->relative;
@@ -164,6 +166,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
166 color_set(opt->color_filename, def->color_filename);
167 color_set(opt->color_function, def->color_function);
168 color_set(opt->color_lineno, def->color_lineno);
169 + color_set(opt->color_columnno, def->color_columnno);
170 color_set(opt->color_match_context, def->color_match_context);
171 color_set(opt->color_match_selected, def->color_match_selected);
172 color_set(opt->color_selected, def->color_selected);
@@ -1277,23 +1280,36 @@ static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, char *bol,
1280 0);
1281 break;
1282 case GREP_NODE_AND:
1280 - if (!match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col,
1281 - icol, 0))
1282 - return 0;
1283 - h = match_expr_eval(opt, x->u.binary.right, bol, eol, ctx, col,
1283 + h = match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col,
1284 icol, 0);
1285 + if (h || opt->columnnum) {
1286 + /*
1287 + * Don't short-circuit AND when given --column, since a
1288 + * NOT earlier in the tree may turn this into an OR. In
1289 + * this case, see the below comment.
1290 + */
1291 + h &= match_expr_eval(opt, x->u.binary.right, bol, eol,
1292 + ctx, col, icol, 0);
1293 + }
1294 break;
1295 case GREP_NODE_OR:
1287 - if (!collect_hits)
1296 + if (!(collect_hits || opt->columnnum)) {
1297 + /*
1298 + * Don't short-circuit OR when given --column (or
1299 + * collecting hits) to ensure we don't skip a later
1300 + * child that would produce an earlier match.
1301 + */
1302 return (match_expr_eval(opt, x->u.binary.left, bol, eol,
1303 ctx, col, icol, 0) ||
1304 match_expr_eval(opt, x->u.binary.right, bol,
1305 eol, ctx, col, icol, 0));
1306 + }
1307 h = match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col,
1308 icol, 0);
1294 - x->u.binary.left->hit |= h;
1309 + if (collect_hits)
1310 + x->u.binary.left->hit |= h;
1311 h |= match_expr_eval(opt, x->u.binary.right, bol, eol, ctx, col,
1296 - icol, 1);
1312 + icol, collect_hits);
1313 break;
1314 default:
1315 die("Unexpected node type (internal error) %d", x->node);
@@ -1316,6 +1332,7 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,
1332 enum grep_context ctx, int collect_hits)
1333 {
1334 struct grep_pat *p;
1335 + int hit = 0;
1336
1337 if (opt->extended)
1338 return match_expr(opt, bol, eol, ctx, col, icol,
@@ -1325,11 +1342,21 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,
1342 for (p = opt->pattern_list; p; p = p->next) {
1343 regmatch_t tmp;
1344 if (match_one_pattern(p, bol, eol, ctx, &tmp, 0)) {
1328 - *col = tmp.rm_so;
1329 - return 1;
1345 + hit |= 1;
1346 + if (!opt->columnnum) {
1347 + /*
1348 + * Without --column, any single match on a line
1349 + * is enough to know that it needs to be
1350 + * printed. With --column, scan _all_ patterns
1351 + * to find the earliest.
1352 + */
1353 + break;
1354 + }
1355 + if (*col < 0 || tmp.rm_so < *col)
1356 + *col = tmp.rm_so;
1357 }
1358 }
1332 - return 0;
1359 + return hit;
1360 }
1361
1362 static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
grep.h
+2
@@ -127,6 +127,7 @@ struct grep_opt {
127 int prefix_length;
128 regex_t regexp;
129 int linenum;
130 + int columnnum;
131 int invert;
132 int ignore_case;
133 int status_only;
@@ -159,6 +160,7 @@ struct grep_opt {
160 char color_filename[COLOR_MAXLEN];
161 char color_function[COLOR_MAXLEN];
162 char color_lineno[COLOR_MAXLEN];
163 + char color_columnno[COLOR_MAXLEN];
164 char color_match_context[COLOR_MAXLEN];
165 char color_match_selected[COLOR_MAXLEN];
166 char color_selected[COLOR_MAXLEN];