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 <m...@ttaylorr.com>
---
 grep.c | 47 +++++++++++++++++++++++++++++++++++++----------
 grep.h |  2 ++
 2 files changed, 39 insertions(+), 10 deletions(-)

diff --git a/grep.c b/grep.c
index dedfe17f93..c885101017 100644
--- a/grep.c
+++ b/grep.c
@@ -46,6 +46,7 @@ void init_grep_defaults(void)
        color_set(opt->color_filename, "");
        color_set(opt->color_function, "");
        color_set(opt->color_lineno, "");
+       color_set(opt->color_columnno, "");
        color_set(opt->color_match_context, GIT_COLOR_BOLD_RED);
        color_set(opt->color_match_selected, GIT_COLOR_BOLD_RED);
        color_set(opt->color_selected, "");
@@ -155,6 +156,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
        opt->extended_regexp_option = def->extended_regexp_option;
        opt->pattern_type_option = def->pattern_type_option;
        opt->linenum = def->linenum;
+       opt->columnnum = def->columnnum;
        opt->max_depth = def->max_depth;
        opt->pathname = def->pathname;
        opt->relative = def->relative;
@@ -164,6 +166,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
        color_set(opt->color_filename, def->color_filename);
        color_set(opt->color_function, def->color_function);
        color_set(opt->color_lineno, def->color_lineno);
+       color_set(opt->color_columnno, def->color_columnno);
        color_set(opt->color_match_context, def->color_match_context);
        color_set(opt->color_match_selected, def->color_match_selected);
        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,
                                     0);
                break;
        case GREP_NODE_AND:
-               if (!match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col,
-                                    icol, 0))
-                       return 0;
-               h = match_expr_eval(opt, x->u.binary.right, bol, eol, ctx, col,
+               h = match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col,
                                    icol, 0);
+               if (h || opt->columnnum) {
+                       /*
+                        * Don't short-circuit AND when given --column, since a
+                        * NOT earlier in the tree may turn this into an OR. In
+                        * this case, see the below comment.
+                        */
+                       h &= match_expr_eval(opt, x->u.binary.right, bol, eol,
+                                            ctx, col, icol, 0);
+               }
                break;
        case GREP_NODE_OR:
-               if (!collect_hits)
+               if (!(collect_hits || opt->columnnum)) {
+                       /*
+                        * Don't short-circuit OR when given --column (or
+                        * collecting hits) to ensure we don't skip a later
+                        * child that would produce an earlier match.
+                        */
                        return (match_expr_eval(opt, x->u.binary.left, bol, eol,
                                                ctx, col, icol, 0) ||
                                match_expr_eval(opt, x->u.binary.right, bol,
                                                eol, ctx, col, icol, 0));
+               }
                h = match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col,
                                    icol, 0);
-               x->u.binary.left->hit |= h;
+               if (collect_hits)
+                       x->u.binary.left->hit |= h;
                h |= match_expr_eval(opt, x->u.binary.right, bol, eol, ctx, col,
-                                    icol, 1);
+                                    icol, collect_hits);
                break;
        default:
                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,
                      enum grep_context ctx, int collect_hits)
 {
        struct grep_pat *p;
+       int hit = 0;
 
        if (opt->extended)
                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,
        for (p = opt->pattern_list; p; p = p->next) {
                regmatch_t tmp;
                if (match_one_pattern(p, bol, eol, ctx, &tmp, 0)) {
-                       *col = tmp.rm_so;
-                       return 1;
+                       hit |= 1;
+                       if (!opt->columnnum) {
+                               /*
+                                * Without --column, any single match on a line
+                                * is enough to know that it needs to be
+                                * printed. With --column, scan _all_ patterns
+                                * to find the earliest.
+                                */
+                               break;
+                       }
+                       if (*col < 0 || tmp.rm_so < *col)
+                               *col = tmp.rm_so;
                }
        }
-       return 0;
+       return hit;
 }
 
 static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
diff --git a/grep.h b/grep.h
index 399381c908..08a0b391c5 100644
--- a/grep.h
+++ b/grep.h
@@ -127,6 +127,7 @@ struct grep_opt {
        int prefix_length;
        regex_t regexp;
        int linenum;
+       int columnnum;
        int invert;
        int ignore_case;
        int status_only;
@@ -159,6 +160,7 @@ struct grep_opt {
        char color_filename[COLOR_MAXLEN];
        char color_function[COLOR_MAXLEN];
        char color_lineno[COLOR_MAXLEN];
+       char color_columnno[COLOR_MAXLEN];
        char color_match_context[COLOR_MAXLEN];
        char color_match_selected[COLOR_MAXLEN];
        char color_selected[COLOR_MAXLEN];
-- 
2.18.0

Reply via email to