grep: add option to show whole function as context

Add a new option, -W, to show the whole surrounding function of a match.

It uses the same regular expressions as -p and diff to find the beginning
of sections.

Currently it will not display comments in front of a function, but those
that are following one.  Despite this shortcoming it is already useful,
e.g. to simply see a more complete applicable context or to extract whole
functions.

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
René Scharfe 2011-08-01 19:20:53 +02:00 committed by Junio C Hamano
parent b35acb5345
commit ba8ea7496f
5 changed files with 47 additions and 11 deletions

View File

@ -172,6 +172,12 @@ OPTIONS
patch hunk headers (see 'Defining a custom hunk-header' in patch hunk headers (see 'Defining a custom hunk-header' in
linkgit:gitattributes[5]). linkgit:gitattributes[5]).
-W::
Show the surrounding text from the previous line containing a
function name up to the one before the next function name,
effectively showing the whole function in which the match was
found.
-f <file>:: -f <file>::
Read patterns from <file>, one per line. Read patterns from <file>, one per line.

View File

@ -838,6 +838,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
context_callback), context_callback),
OPT_BOOLEAN('p', "show-function", &opt.funcname, OPT_BOOLEAN('p', "show-function", &opt.funcname,
"show a line with the function name before matches"), "show a line with the function name before matches"),
OPT_BOOLEAN('W', NULL, &opt.funcbody,
"show the surrounding function"),
OPT_GROUP(""), OPT_GROUP(""),
OPT_CALLBACK('f', NULL, &opt, "file", OPT_CALLBACK('f', NULL, &opt, "file",
"read patterns from file", file_callback), "read patterns from file", file_callback),
@ -980,7 +982,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
use_threads = 0; use_threads = 0;
if (use_threads) { if (use_threads) {
if (opt.pre_context || opt.post_context || opt.file_break) if (opt.pre_context || opt.post_context || opt.file_break ||
opt.funcbody)
skip_first_line = 1; skip_first_line = 1;
start_threads(&opt); start_threads(&opt);
} }

32
grep.c
View File

@ -724,7 +724,7 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
if (opt->file_break && opt->last_shown == 0) { if (opt->file_break && opt->last_shown == 0) {
if (opt->show_hunk_mark) if (opt->show_hunk_mark)
opt->output(opt, "\n", 1); opt->output(opt, "\n", 1);
} else if (opt->pre_context || opt->post_context) { } else if (opt->pre_context || opt->post_context || opt->funcbody) {
if (opt->last_shown == 0) { if (opt->last_shown == 0) {
if (opt->show_hunk_mark) { if (opt->show_hunk_mark) {
output_color(opt, "--", 2, opt->color_sep); output_color(opt, "--", 2, opt->color_sep);
@ -819,10 +819,13 @@ static void show_funcname_line(struct grep_opt *opt, const char *name,
} }
static void show_pre_context(struct grep_opt *opt, const char *name, char *buf, static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
char *bol, unsigned lno) char *bol, char *end, unsigned lno)
{ {
unsigned cur = lno, from = 1, funcname_lno = 0; unsigned cur = lno, from = 1, funcname_lno = 0;
int funcname_needed = opt->funcname; int funcname_needed = !!opt->funcname;
if (opt->funcbody && !match_funcname(opt, bol, end))
funcname_needed = 2;
if (opt->pre_context < lno) if (opt->pre_context < lno)
from = lno - opt->pre_context; from = lno - opt->pre_context;
@ -830,7 +833,8 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
from = opt->last_shown + 1; from = opt->last_shown + 1;
/* Rewind. */ /* Rewind. */
while (bol > buf && cur > from) { while (bol > buf &&
cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
char *eol = --bol; char *eol = --bol;
while (bol > buf && bol[-1] != '\n') while (bol > buf && bol[-1] != '\n')
@ -942,13 +946,15 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
int binary_match_only = 0; int binary_match_only = 0;
unsigned count = 0; unsigned count = 0;
int try_lookahead = 0; int try_lookahead = 0;
int show_function = 0;
enum grep_context ctx = GREP_CONTEXT_HEAD; enum grep_context ctx = GREP_CONTEXT_HEAD;
xdemitconf_t xecfg; xdemitconf_t xecfg;
if (!opt->output) if (!opt->output)
opt->output = std_output; opt->output = std_output;
if (opt->pre_context || opt->post_context || opt->file_break) { if (opt->pre_context || opt->post_context || opt->file_break ||
opt->funcbody) {
/* Show hunk marks, except for the first file. */ /* Show hunk marks, except for the first file. */
if (opt->last_shown) if (opt->last_shown)
opt->show_hunk_mark = 1; opt->show_hunk_mark = 1;
@ -1004,7 +1010,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
*/ */
if (try_lookahead if (try_lookahead
&& !(last_hit && !(last_hit
&& lno <= last_hit + opt->post_context) && (show_function ||
lno <= last_hit + opt->post_context))
&& look_ahead(opt, &left, &lno, &bol)) && look_ahead(opt, &left, &lno, &bol))
break; break;
eol = end_of_line(bol, &left); eol = end_of_line(bol, &left);
@ -1051,15 +1058,20 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
/* Hit at this line. If we haven't shown the /* Hit at this line. If we haven't shown the
* pre-context lines, we would need to show them. * pre-context lines, we would need to show them.
*/ */
if (opt->pre_context) if (opt->pre_context || opt->funcbody)
show_pre_context(opt, name, buf, bol, lno); show_pre_context(opt, name, buf, bol, eol, lno);
else if (opt->funcname) else if (opt->funcname)
show_funcname_line(opt, name, buf, bol, lno); show_funcname_line(opt, name, buf, bol, lno);
show_line(opt, bol, eol, name, lno, ':'); show_line(opt, bol, eol, name, lno, ':');
last_hit = lno; last_hit = lno;
if (opt->funcbody)
show_function = 1;
goto next_line;
} }
else if (last_hit && if (show_function && match_funcname(opt, bol, eol))
lno <= last_hit + opt->post_context) { show_function = 0;
if (show_function ||
(last_hit && lno <= last_hit + opt->post_context)) {
/* If the last hit is within the post context, /* If the last hit is within the post context,
* we need to show this line. * we need to show this line.
*/ */

1
grep.h
View File

@ -98,6 +98,7 @@ struct grep_opt {
int color; int color;
int max_depth; int max_depth;
int funcname; int funcname;
int funcbody;
char color_context[COLOR_MAXLEN]; char color_context[COLOR_MAXLEN];
char color_filename[COLOR_MAXLEN]; char color_filename[COLOR_MAXLEN];
char color_function[COLOR_MAXLEN]; char color_function[COLOR_MAXLEN];

View File

@ -509,6 +509,20 @@ test_expect_success 'grep -p -B5' '
test_cmp expected actual test_cmp expected actual
' '
cat >expected <<EOF
hello.c=int main(int argc, const char **argv)
hello.c-{
hello.c- printf("Hello world.\n");
hello.c: return 0;
hello.c- /* char ?? */
hello.c-}
EOF
test_expect_success 'grep -W' '
git grep -W return >actual &&
test_cmp expected actual
'
test_expect_success 'grep from a subdirectory to search wider area (1)' ' test_expect_success 'grep from a subdirectory to search wider area (1)' '
mkdir -p s && mkdir -p s &&
( (