All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 1/2] grep: refactor next_match() and match_one_pattern() for external use
@ 2021-09-24 15:49 Hamza Mahfooz
  2021-09-24 15:50 ` [PATCH v8 2/2] pretty: colorize pattern matches in commit messages Hamza Mahfooz
  2021-09-24 19:23 ` [PATCH v8 1/2] grep: refactor next_match() and match_one_pattern() for external use Junio C Hamano
  0 siblings, 2 replies; 4+ messages in thread
From: Hamza Mahfooz @ 2021-09-24 15:49 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Eric Sunshine, Hamza Mahfooz

These changes are made in preparation of, the colorization support for the
"git log" subcommands that, rely on regex functionality (i.e. "--author",
"--committer" and "--grep"). These changes are necessary primarily because
match_one_pattern() expects header lines to be prefixed, however, in
pretty, the prefixes are stripped from the lines because the name-email
pairs need to go through additional parsing, before they can be printed and
because next_match() doesn't handle the case of
"ctx == GREP_CONTEXT_HEAD" at all. So, teach next_match() how to handle the
new case and move match_one_pattern()'s core logic to
headless_match_one_pattern() while preserving match_one_pattern()'s uses
that depend on the additional processing.

Signed-off-by: Hamza Mahfooz <someguy@effective-light.com>
---
v5: separate grep changes from pretty changes.

v6: rescope some variables.

v7: export header_field[] and allow for subsequent matches on header lines
    in match_one_pattern().

v8: add headless_match_one_pattern() and move header_field[] back.
---
 grep.c | 83 ++++++++++++++++++++++++++++++++++++----------------------
 grep.h |  4 +++
 2 files changed, 55 insertions(+), 32 deletions(-)

diff --git a/grep.c b/grep.c
index 14fe8a0fd2..e46b046638 100644
--- a/grep.c
+++ b/grep.c
@@ -944,37 +944,19 @@ static struct {
 	{ "reflog ", 7 },
 };
 
-static int match_one_pattern(struct grep_pat *p,
-			     const char *bol, const char *eol,
-			     enum grep_context ctx,
-			     regmatch_t *pmatch, int eflags)
+static int headerless_match_one_pattern(struct grep_pat *p,
+					const char *bol, const char *eol,
+					enum grep_context ctx,
+					regmatch_t *pmatch, int eflags)
 {
 	int hit = 0;
 	const char *start = bol;
 
 	if ((p->token != GREP_PATTERN) &&
-	    ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
+	    ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)) &&
+	    ((p->token == GREP_PATTERN_BODY) != (ctx == GREP_CONTEXT_BODY)))
 		return 0;
 
-	if (p->token == GREP_PATTERN_HEAD) {
-		const char *field;
-		size_t len;
-		assert(p->field < ARRAY_SIZE(header_field));
-		field = header_field[p->field].field;
-		len = header_field[p->field].len;
-		if (strncmp(bol, field, len))
-			return 0;
-		bol += len;
-		switch (p->field) {
-		case GREP_HEADER_AUTHOR:
-		case GREP_HEADER_COMMITTER:
-			strip_timestamp(bol, &eol);
-			break;
-		default:
-			break;
-		}
-	}
-
  again:
 	hit = patmatch(p, bol, eol, pmatch, eflags);
 
@@ -1025,6 +1007,36 @@ static int match_one_pattern(struct grep_pat *p,
 	return hit;
 }
 
+static int match_one_pattern(struct grep_pat *p,
+			     const char *bol, const char *eol,
+			     enum grep_context ctx, regmatch_t *pmatch,
+			     int eflags)
+{
+	const char *field;
+	size_t len;
+
+	if (p->token == GREP_PATTERN_HEAD) {
+		assert(p->field < ARRAY_SIZE(header_field));
+		field = header_field[p->field].field;
+		len = header_field[p->field].len;
+		if (strncmp(bol, field, len))
+			return 0;
+		bol += len;
+
+		switch (p->field) {
+		case GREP_HEADER_AUTHOR:
+		case GREP_HEADER_COMMITTER:
+			strip_timestamp(bol, &eol);
+			break;
+		default:
+			break;
+		}
+	}
+
+	return headerless_match_one_pattern(p, bol, eol, ctx, pmatch, eflags);
+}
+
+
 static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x,
 			   const char *bol, const char *eol,
 			   enum grep_context ctx, ssize_t *col,
@@ -1143,7 +1155,7 @@ static int match_next_pattern(struct grep_pat *p,
 {
 	regmatch_t match;
 
-	if (!match_one_pattern(p, bol, eol, ctx, &match, eflags))
+	if (!headerless_match_one_pattern(p, bol, eol, ctx, &match, eflags))
 		return 0;
 	if (match.rm_so < 0 || match.rm_eo < 0)
 		return 0;
@@ -1158,22 +1170,28 @@ static int match_next_pattern(struct grep_pat *p,
 	return 1;
 }
 
-static int next_match(struct grep_opt *opt,
-		      const char *bol, const char *eol,
-		      enum grep_context ctx, regmatch_t *pmatch, int eflags)
+int grep_next_match(struct grep_opt *opt,
+		    const char *bol, const char *eol,
+		    enum grep_context ctx, regmatch_t *pmatch,
+		    enum grep_header_field field, 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) {
+		for (p = ((ctx == GREP_CONTEXT_HEAD)
+			   ? opt->header_list : 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);
+				if ((field == GREP_HEADER_FIELD_MAX) ||
+				    (p->field == field))
+					hit |= match_next_pattern(p, bol, eol,
+								  ctx, pmatch,
+								  eflags);
 				break;
 			default:
 				break;
@@ -1261,7 +1279,8 @@ static void show_line(struct grep_opt *opt,
 			else if (sign == '=')
 				line_color = opt->colors[GREP_COLOR_FUNCTION];
 		}
-		while (next_match(opt, bol, eol, ctx, &match, eflags)) {
+		while (grep_next_match(opt, bol, eol, ctx, &match,
+				       GREP_HEADER_FIELD_MAX, eflags)) {
 			if (match.rm_so == match.rm_eo)
 				break;
 
diff --git a/grep.h b/grep.h
index 3cb8a83ae8..521068308e 100644
--- a/grep.h
+++ b/grep.h
@@ -190,6 +190,10 @@ void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const
 void compile_grep_patterns(struct grep_opt *opt);
 void free_grep_patterns(struct grep_opt *opt);
 int grep_buffer(struct grep_opt *opt, const char *buf, unsigned long size);
+int grep_next_match(struct grep_opt *opt,
+		    const char *bol, const char *eol,
+		    enum grep_context ctx, regmatch_t *pmatch,
+		    enum grep_header_field field, int eflags);
 
 struct grep_source {
 	char *name;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v8 2/2] pretty: colorize pattern matches in commit messages
  2021-09-24 15:49 [PATCH v8 1/2] grep: refactor next_match() and match_one_pattern() for external use Hamza Mahfooz
@ 2021-09-24 15:50 ` Hamza Mahfooz
  2021-09-28  0:03   ` Junio C Hamano
  2021-09-24 19:23 ` [PATCH v8 1/2] grep: refactor next_match() and match_one_pattern() for external use Junio C Hamano
  1 sibling, 1 reply; 4+ messages in thread
From: Hamza Mahfooz @ 2021-09-24 15:50 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Eric Sunshine, Hamza Mahfooz

The "git log" command limits its output to the commits that contain strings
matched by a pattern when the "--grep=<pattern>" option is used, but unlike
output from "git grep -e <pattern>", the matches are not highlighted,
making them harder to spot.

Teach the pretty-printer code to highlight matches from the
"--grep=<pattern>", "--author=<pattern>" and "--committer=<pattern>"
options (to view the last one, you may have to ask for --pretty=fuller).

Also, it must be noted that we are effectively greping the content twice,
however it only slows down "git log --author=^H" on this repository by
around 1-2% (compared to v2.33.0), so it should be a small enough slow
down to justify the addition of the feature.

Signed-off-by: Hamza Mahfooz <someguy@effective-light.com>
---
v2: make the commit message whole (add the missing ingredients), rename
    append_matched_line() to append_line_with_color(), use
    colors[GREP_COLOR_MATCH_SELECTED] instead of
    colors[GREP_COLOR_MATCH_CONTEXT], allow the background color to be
    customized, don't copy strings to a buffer when not coloring in
    append_line_with_color(), rename next_match() to grep_next_match(),
    repurpose grep_next_match()/match_one_pattern() for use in
    append_line_with_color() (allowing us to remove duplicated matching
    code in append_line_with_color()), document how to customize the
    feature and modify some of the tests to fit the feature better.

v3: fix a formatting issue with the added documentation.

v4: add strbuf_add_with_color(), use the correct color code scheme in the
    unit tests and add more unit tests.

v5: separate grep changes from pretty changes and add some performance
    analysis in the commit message.

v6: put the documentation in the correct place, cleanup pretty.c and
    format the unit tests according to the current convention.

v7: get rid of all manual strbuf management, constify where now appropriate
    and fix the header line prefix issue properly.

v8: remove code that relies on grep_header_fields[].
---
 Documentation/config/color.txt |   7 ++-
 pretty.c                       | 101 +++++++++++++++++++++++++++++----
 t/t4202-log.sh                 |  51 +++++++++++++++++
 3 files changed, 145 insertions(+), 14 deletions(-)

diff --git a/Documentation/config/color.txt b/Documentation/config/color.txt
index e05d520a86..91d9a9da32 100644
--- a/Documentation/config/color.txt
+++ b/Documentation/config/color.txt
@@ -104,9 +104,12 @@ color.grep.<slot>::
 `matchContext`;;
 	matching text in context lines
 `matchSelected`;;
-	matching text in selected lines
+	matching text in selected lines. Also, used to customize the following
+	linkgit:git-log[1] subcommands: `--grep`, `--author` and `--committer`.
 `selected`;;
-	non-matching text in selected lines
+	non-matching text in selected lines. Also, used to customize the
+	following linkgit:git-log[1] subcommands: `--grep`, `--author` and
+	`--committer`.
 `separator`;;
 	separators between fields on a line (`:`, `-`, and `=`)
 	and between hunks (`--`)
diff --git a/pretty.c b/pretty.c
index 73b5ead509..2dd94af886 100644
--- a/pretty.c
+++ b/pretty.c
@@ -431,6 +431,52 @@ const char *show_ident_date(const struct ident_split *ident,
 	return show_date(date, tz, mode);
 }
 
+static inline void strbuf_add_with_color(struct strbuf *sb, const char *color,
+					 const char *buf, size_t buflen)
+{
+	strbuf_addstr(sb, color);
+	strbuf_add(sb, buf, buflen);
+	if (*color)
+		strbuf_addstr(sb, GIT_COLOR_RESET);
+}
+
+static void append_line_with_color(struct strbuf *sb, struct grep_opt *opt,
+				   const char *line, size_t linelen,
+				   int color, enum grep_context ctx,
+				   enum grep_header_field field)
+{
+	const char *buf, *eol, *line_color, *match_color;
+	regmatch_t match;
+	int eflags = 0;
+
+	buf = line;
+	eol = buf + linelen;
+
+	if (!opt || !want_color(color) || opt->invert)
+		goto end;
+
+	line_color = opt->colors[GREP_COLOR_SELECTED];
+	match_color = opt->colors[GREP_COLOR_MATCH_SELECTED];
+
+	while (grep_next_match(opt, buf, eol, ctx, &match, field, eflags)) {
+		if (match.rm_so == match.rm_eo)
+			break;
+
+		strbuf_add_with_color(sb, line_color, buf, match.rm_so);
+		strbuf_add_with_color(sb, match_color, buf + match.rm_so,
+				      match.rm_eo - match.rm_so);
+		buf += match.rm_eo;
+		eflags = REG_NOTBOL;
+	}
+
+	if (eflags)
+		strbuf_add_with_color(sb, line_color, buf, eol - buf);
+	else {
+end:
+		strbuf_add(sb, buf, eol - buf);
+	}
+}
+
 void pp_user_info(struct pretty_print_context *pp,
 		  const char *what, struct strbuf *sb,
 		  const char *line, const char *encoding)
@@ -496,9 +542,26 @@ void pp_user_info(struct pretty_print_context *pp,
 			strbuf_addch(sb, '\n');
 		strbuf_addf(sb, " <%.*s>\n", (int)maillen, mailbuf);
 	} else {
-		strbuf_addf(sb, "%s: %.*s%.*s <%.*s>\n", what,
-			    (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0, "    ",
-			    (int)namelen, namebuf, (int)maillen, mailbuf);
+		struct strbuf id = STRBUF_INIT;
+		enum grep_header_field field = GREP_HEADER_FIELD_MAX;
+		struct grep_opt *opt = pp->rev ? &pp->rev->grep_filter : NULL;
+
+		if (!strcmp(what, "Author"))
+			field = GREP_HEADER_AUTHOR;
+		else if (!strcmp(what, "Commit"))
+			field = GREP_HEADER_COMMITTER;
+
+		strbuf_addf(sb, "%s: ", what);
+		if (pp->fmt == CMIT_FMT_FULLER)
+			strbuf_addchars(sb, ' ', 4);
+
+		strbuf_addf(&id, "%.*s <%.*s>", (int)namelen, namebuf,
+			    (int)maillen, mailbuf);
+
+		append_line_with_color(sb, opt, id.buf, id.len, pp->color,
+				       GREP_CONTEXT_HEAD, field);
+		strbuf_addch(sb, '\n');
+		strbuf_release(&id);
 	}
 
 	switch (pp->fmt) {
@@ -1939,8 +2002,9 @@ static int pp_utf8_width(const char *start, const char *end)
 	return width;
 }
 
-static void strbuf_add_tabexpand(struct strbuf *sb, int tabwidth,
-				 const char *line, int linelen)
+static void strbuf_add_tabexpand(struct strbuf *sb, struct grep_opt *opt,
+				 int color, int tabwidth, const char *line,
+				 int linelen)
 {
 	const char *tab;
 
@@ -1957,7 +2021,9 @@ static void strbuf_add_tabexpand(struct strbuf *sb, int tabwidth,
 			break;
 
 		/* Output the data .. */
-		strbuf_add(sb, line, tab - line);
+		append_line_with_color(sb, opt, line, tab - line, color,
+				       GREP_CONTEXT_BODY,
+				       GREP_HEADER_FIELD_MAX);
 
 		/* .. and the de-tabified tab */
 		strbuf_addchars(sb, ' ', tabwidth - (width % tabwidth));
@@ -1972,7 +2038,8 @@ static void strbuf_add_tabexpand(struct strbuf *sb, int tabwidth,
 	 * worrying about width - there's nothing more to
 	 * align.
 	 */
-	strbuf_add(sb, line, linelen);
+	append_line_with_color(sb, opt, line, linelen, color, GREP_CONTEXT_BODY,
+			       GREP_HEADER_FIELD_MAX);
 }
 
 /*
@@ -1984,11 +2051,16 @@ static void pp_handle_indent(struct pretty_print_context *pp,
 			     struct strbuf *sb, int indent,
 			     const char *line, int linelen)
 {
+	struct grep_opt *opt = pp->rev ? &pp->rev->grep_filter : NULL;
+
 	strbuf_addchars(sb, ' ', indent);
 	if (pp->expand_tabs_in_log)
-		strbuf_add_tabexpand(sb, pp->expand_tabs_in_log, line, linelen);
+		strbuf_add_tabexpand(sb, opt, pp->color, pp->expand_tabs_in_log,
+				     line, linelen);
 	else
-		strbuf_add(sb, line, linelen);
+		append_line_with_color(sb, opt, line, linelen, pp->color,
+				       GREP_CONTEXT_BODY,
+				       GREP_HEADER_FIELD_MAX);
 }
 
 static int is_mboxrd_from(const char *line, int len)
@@ -2006,7 +2078,9 @@ void pp_remainder(struct pretty_print_context *pp,
 		  struct strbuf *sb,
 		  int indent)
 {
+	struct grep_opt *opt = pp->rev ? &pp->rev->grep_filter : NULL;
 	int first = 1;
+
 	for (;;) {
 		const char *line = *msg_p;
 		int linelen = get_one_line(line);
@@ -2027,14 +2101,17 @@ void pp_remainder(struct pretty_print_context *pp,
 		if (indent)
 			pp_handle_indent(pp, sb, indent, line, linelen);
 		else if (pp->expand_tabs_in_log)
-			strbuf_add_tabexpand(sb, pp->expand_tabs_in_log,
-					     line, linelen);
+			strbuf_add_tabexpand(sb, opt, pp->color,
+					     pp->expand_tabs_in_log, line,
+					     linelen);
 		else {
 			if (pp->fmt == CMIT_FMT_MBOXRD &&
 					is_mboxrd_from(line, linelen))
 				strbuf_addch(sb, '>');
 
-			strbuf_add(sb, line, linelen);
+			append_line_with_color(sb, opt, line, linelen,
+					       pp->color, GREP_CONTEXT_BODY,
+					       GREP_HEADER_FIELD_MAX);
 		}
 		strbuf_addch(sb, '\n');
 	}
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 9dfead936b..3d240bba57 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -449,6 +449,57 @@ test_expect_success !FAIL_PREREQS 'log with various grep.patternType configurati
 	)
 '
 
+test_expect_success 'log --author' '
+	cat >expect <<-\EOF &&
+	Author: <BOLD;RED>A U<RESET> Thor <author@example.com>
+	EOF
+	git log -1 --color=always --author="A U" >log &&
+	grep Author log >actual.raw &&
+	test_decode_color <actual.raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log --committer' '
+	cat >expect <<-\EOF &&
+	Commit:     C O Mitter <committer@<BOLD;RED>example<RESET>.com>
+	EOF
+	git log -1 --color=always --pretty=fuller --committer="example" >log &&
+	grep "Commit:" log >actual.raw &&
+	test_decode_color <actual.raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -i --grep with color' '
+	cat >expect <<-\EOF &&
+	    <BOLD;RED>Sec<RESET>ond
+	    <BOLD;RED>sec<RESET>ond
+	EOF
+	git log --color=always -i --grep=^sec >log &&
+	grep -i sec log >actual.raw &&
+	test_decode_color <actual.raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '-c color.grep.selected log --grep' '
+	cat >expect <<-\EOF &&
+	    <GREEN>th<RESET><BOLD;RED>ir<RESET><GREEN>d<RESET>
+	EOF
+	git -c color.grep.selected="green" log --color=always --grep=ir >log &&
+	grep ir log >actual.raw &&
+	test_decode_color <actual.raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '-c color.grep.matchSelected log --grep' '
+	cat >expect <<-\EOF &&
+	    <BLUE>i<RESET>n<BLUE>i<RESET>t<BLUE>i<RESET>al
+	EOF
+	git -c color.grep.matchSelected="blue" log --color=always --grep=i >log &&
+	grep al log >actual.raw &&
+	test_decode_color <actual.raw >actual &&
+	test_cmp expect actual
+'
+
 cat > expect <<EOF
 * Second
 * sixth
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v8 1/2] grep: refactor next_match() and match_one_pattern() for external use
  2021-09-24 15:49 [PATCH v8 1/2] grep: refactor next_match() and match_one_pattern() for external use Hamza Mahfooz
  2021-09-24 15:50 ` [PATCH v8 2/2] pretty: colorize pattern matches in commit messages Hamza Mahfooz
@ 2021-09-24 19:23 ` Junio C Hamano
  1 sibling, 0 replies; 4+ messages in thread
From: Junio C Hamano @ 2021-09-24 19:23 UTC (permalink / raw)
  To: Hamza Mahfooz; +Cc: git, Jeff King, Eric Sunshine

Hamza Mahfooz <someguy@effective-light.com> writes:

> These changes are made in preparation of, the colorization support for the
> "git log" subcommands that, rely on regex functionality (i.e. "--author",
> "--committer" and "--grep"). These changes are necessary primarily because
> match_one_pattern() expects header lines to be prefixed, however, in
> pretty, the prefixes are stripped from the lines because the name-email
> pairs need to go through additional parsing, before they can be printed and
> because next_match() doesn't handle the case of
> "ctx == GREP_CONTEXT_HEAD" at all. So, teach next_match() how to handle the
> new case and move match_one_pattern()'s core logic to
> headless_match_one_pattern() while preserving match_one_pattern()'s uses
> that depend on the additional processing.
>
> Signed-off-by: Hamza Mahfooz <someguy@effective-light.com>
> ---
> v5: separate grep changes from pretty changes.
>
> v6: rescope some variables.
>
> v7: export header_field[] and allow for subsequent matches on header lines
>     in match_one_pattern().
>
> v8: add headless_match_one_pattern() and move header_field[] back.

This looks a lot more sensible split compared to the previous round.

> ---
>  grep.c | 83 ++++++++++++++++++++++++++++++++++++----------------------
>  grep.h |  4 +++
>  2 files changed, 55 insertions(+), 32 deletions(-)
>
> diff --git a/grep.c b/grep.c
> index 14fe8a0fd2..e46b046638 100644
> --- a/grep.c
> +++ b/grep.c
> @@ -944,37 +944,19 @@ static struct {
>  	{ "reflog ", 7 },
>  };
>  
> -static int match_one_pattern(struct grep_pat *p,
> -			     const char *bol, const char *eol,
> -			     enum grep_context ctx,
> -			     regmatch_t *pmatch, int eflags)
> +static int headerless_match_one_pattern(struct grep_pat *p,
> +					const char *bol, const char *eol,
> +					enum grep_context ctx,
> +					regmatch_t *pmatch, int eflags)
>  {
>  	int hit = 0;
>  	const char *start = bol;
>  
>  	if ((p->token != GREP_PATTERN) &&
> -	    ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
> +	    ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)) &&
> +	    ((p->token == GREP_PATTERN_BODY) != (ctx == GREP_CONTEXT_BODY)))

I _think_ it makes sense to have this additional "if looking for
something in the body, do not bother if we are not in body", but I
wonder if that is a bugfix that has nothing to do with this topic.

In other words, even if for whatever reason we decide that it is not
worth painting the hits in "git log --grep=<pattern> --author=<who>"
output, would the addition to this single condition to the original
code, with or without the refactoring, fix some existing bug?

If so, perhaps that can be a separate patch with a test that
demonstrates the bug fixed.  With this change mixed in, it is
unclear if this step is pure refactoring that does not break the
existing callers of the code path.

If it is not a bugfix, we need to explain the reason why this
additional condition is necessary in the proposed log message (I am
assuming that this change is needed to get the end result working).

> +static int match_one_pattern(struct grep_pat *p,
> +			     const char *bol, const char *eol,
> +			     enum grep_context ctx, regmatch_t *pmatch,
> +			     int eflags)
> +{
> +	const char *field;
> +	size_t len;
> +
> +	if (p->token == GREP_PATTERN_HEAD) {
> +		assert(p->field < ARRAY_SIZE(header_field));
> +		field = header_field[p->field].field;
> +		len = header_field[p->field].len;
> +		if (strncmp(bol, field, len))
> +			return 0;
> +		bol += len;
> +
> +		switch (p->field) {
> +		case GREP_HEADER_AUTHOR:
> +		case GREP_HEADER_COMMITTER:
> +			strip_timestamp(bol, &eol);
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	return headerless_match_one_pattern(p, bol, eol, ctx, pmatch, eflags);
> +}

So, match_one_pattern() after all the above changes behaves almost
identically for existing callers, except that it has the "pattern
for body should only apply to the body text" condition added, which
may affect the behaviour.

> @@ -1143,7 +1155,7 @@ static int match_next_pattern(struct grep_pat *p,
>  {
>  	regmatch_t match;
>  
> -	if (!match_one_pattern(p, bol, eol, ctx, &match, eflags))
> +	if (!headerless_match_one_pattern(p, bol, eol, ctx, &match, eflags))
>  		return 0;
>  	if (match.rm_so < 0 || match.rm_eo < 0)
>  		return 0;

Since this "next" thing is designed to be repeatedly called, we use
the new helper that is meant for <bol, eol> after we preprocessed
the header and the trailing timestamp away, which makes sense, I
guess.  But the caller needs to do the preprocessing before it makes
the first call to match_next_pattern() on any given line to adjust
bol and eol.

> @@ -1158,22 +1170,28 @@ static int match_next_pattern(struct grep_pat *p,
>  	return 1;
>  }
>  
> -static int next_match(struct grep_opt *opt,
> -		      const char *bol, const char *eol,
> -		      enum grep_context ctx, regmatch_t *pmatch, int eflags)
> +int grep_next_match(struct grep_opt *opt,
> +		    const char *bol, const char *eol,
> +		    enum grep_context ctx, regmatch_t *pmatch,
> +		    enum grep_header_field field, 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) {
> +		for (p = ((ctx == GREP_CONTEXT_HEAD)
> +			   ? opt->header_list : 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);
> +				if ((field == GREP_HEADER_FIELD_MAX) ||
> +				    (p->field == field))
> +					hit |= match_next_pattern(p, bol, eol,
> +								  ctx, pmatch,
> +								  eflags);

... and this caller apparently does not.  It is made the
responsibility of the callers of grep_next_match() to adjust bol and
eol before calling it.

>  				break;
>  			default:
>  				break;
> @@ -1261,7 +1279,8 @@ static void show_line(struct grep_opt *opt,
>  			else if (sign == '=')
>  				line_color = opt->colors[GREP_COLOR_FUNCTION];
>  		}
> -		while (next_match(opt, bol, eol, ctx, &match, eflags)) {
> +		while (grep_next_match(opt, bol, eol, ctx, &match,
> +				       GREP_HEADER_FIELD_MAX, eflags)) {

... which is this caller, show_line(), which is the only caller of
grep_next_match().

But this function is not taught to strip headers and timestamp from
<bol, eol>, either, so it is the responsibility of the callers of
show_line().

I _think_ all the current callers of show_line() handle the blob
contents found by "git grep" and not "git log --grep/--author", and
there is no GREP_PATTERN_HEAD/GREP_PATTERN_BODY given, but I may be
mistaken.  With the current set of callers, do we ever call
next_match() with a pattern with p->token set to GREP_PATTERN_HEAD
or _BODY?  I am wondering why we even have those cases covered in
the switch statement in the previous hunk.

match_one_pattern() of course must be prepared to see the HEAD and
BODY requests because match_expr_eval() is evantually called via
grep_buffer() interface to implement "git log --grep/--author", but
I do not think next_match() would be called in that code path.  The
handling of GREP_PATTERN_HEAD and GREP_PATTERN_BODY in next_match(),
which I am wondering about, came from 7e8f59d5 (grep: color patterns
in output, 2009-03-07).

Anyway, let's read on.

> diff --git a/grep.h b/grep.h
> index 3cb8a83ae8..521068308e 100644
> --- a/grep.h
> +++ b/grep.h
> @@ -190,6 +190,10 @@ void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const
>  void compile_grep_patterns(struct grep_opt *opt);
>  void free_grep_patterns(struct grep_opt *opt);
>  int grep_buffer(struct grep_opt *opt, const char *buf, unsigned long size);
> +int grep_next_match(struct grep_opt *opt,
> +		    const char *bol, const char *eol,
> +		    enum grep_context ctx, regmatch_t *pmatch,
> +		    enum grep_header_field field, int eflags);

As this is made external, it at least needs a comment on the
(somewhat strange) field parameter, especially the fact that "no,
this is not grepping in the header lines in a commit object" is
signalled by a special GREP_HEADER_FIELD_MAX value.

>  
>  struct grep_source {
>  	char *name;

Other than that, looks much cleaner than the previous round.

Thanks.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v8 2/2] pretty: colorize pattern matches in commit messages
  2021-09-24 15:50 ` [PATCH v8 2/2] pretty: colorize pattern matches in commit messages Hamza Mahfooz
@ 2021-09-28  0:03   ` Junio C Hamano
  0 siblings, 0 replies; 4+ messages in thread
From: Junio C Hamano @ 2021-09-28  0:03 UTC (permalink / raw)
  To: Hamza Mahfooz; +Cc: git, Jeff King, Eric Sunshine

Hamza Mahfooz <someguy@effective-light.com> writes:

> +static void append_line_with_color(struct strbuf *sb, struct grep_opt *opt,
> +				   const char *line, size_t linelen,
> +				   int color, enum grep_context ctx,
> +				   enum grep_header_field field)
> +{
> +	const char *buf, *eol, *line_color, *match_color;
> +	regmatch_t match;
> +	int eflags = 0;
> +
> +	buf = line;
> +	eol = buf + linelen;
> +
> +	if (!opt || !want_color(color) || opt->invert)
> +		goto end;
> +
> +	line_color = opt->colors[GREP_COLOR_SELECTED];
> +	match_color = opt->colors[GREP_COLOR_MATCH_SELECTED];
> +
> +	while (grep_next_match(opt, buf, eol, ctx, &match, field, eflags)) {

So, this expects <buf, eol> to be only payload without "author " or
timestamp, ...

>  	} else {
> -		strbuf_addf(sb, "%s: %.*s%.*s <%.*s>\n", what,
> -			    (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0, "    ",
> -			    (int)namelen, namebuf, (int)maillen, mailbuf);
> +		struct strbuf id = STRBUF_INIT;
> +		enum grep_header_field field = GREP_HEADER_FIELD_MAX;
> +		struct grep_opt *opt = pp->rev ? &pp->rev->grep_filter : NULL;
> +
> +		if (!strcmp(what, "Author"))
> +			field = GREP_HEADER_AUTHOR;
> +		else if (!strcmp(what, "Commit"))
> +			field = GREP_HEADER_COMMITTER;
> +
> +		strbuf_addf(sb, "%s: ", what);
> +		if (pp->fmt == CMIT_FMT_FULLER)
> +			strbuf_addchars(sb, ' ', 4);
> +
> +		strbuf_addf(&id, "%.*s <%.*s>", (int)namelen, namebuf,
> +			    (int)maillen, mailbuf);
> +
> +		append_line_with_color(sb, opt, id.buf, id.len, pp->color,
> +				       GREP_CONTEXT_HEAD, field);

... which is exactly what this caller is doing.

Very nice.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-09-28  0:03 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-24 15:49 [PATCH v8 1/2] grep: refactor next_match() and match_one_pattern() for external use Hamza Mahfooz
2021-09-24 15:50 ` [PATCH v8 2/2] pretty: colorize pattern matches in commit messages Hamza Mahfooz
2021-09-28  0:03   ` Junio C Hamano
2021-09-24 19:23 ` [PATCH v8 1/2] grep: refactor next_match() and match_one_pattern() for external use Junio C Hamano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.