Skip to content

Commit 2944e4e

Browse files
René Scharfegitster
authored andcommitted
grep: add option -p/--show-function
The new option -p instructs git grep to print the previous function definition as a context line, similar to diff -p. Such context lines are marked with an equal sign instead of a dash. This option complements the existing context options -A, -B, -C. Function definitions are detected using the same heuristic that diff uses. User defined regular expressions are not supported, yet. Signed-off-by: Rene Scharfe <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 49de321 commit 2944e4e

File tree

5 files changed

+98
-13
lines changed

5 files changed

+98
-13
lines changed

Documentation/git-grep.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ OPTIONS
122122
-<num>::
123123
A shortcut for specifying -C<num>.
124124

125+
-p::
126+
--show-function::
127+
Show the preceding line that contains the function name of
128+
the match, unless the matching line is a function name itself.
129+
125130
-f <file>::
126131
Read patterns from <file>, one per line.
127132

builtin-grep.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,13 @@ static int flush_grep(struct grep_opt *opt,
278278
argc -= 2;
279279
}
280280

281-
if (opt->pre_context || opt->post_context) {
281+
if (opt->pre_context || opt->post_context || opt->funcname) {
282282
/*
283283
* grep handles hunk marks between files, but we need to
284284
* do that ourselves between multiple calls.
285285
*/
286286
if (opt->show_hunk_mark)
287-
write_or_die(1, "--\n", 3);
287+
write_or_die(1, opt->funcname ? "==\n" : "--\n", 3);
288288
else
289289
opt->show_hunk_mark = 1;
290290
}
@@ -721,6 +721,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
721721
"show <n> context lines after matches"),
722722
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
723723
context_callback),
724+
OPT_BOOLEAN('p', "show-function", &opt.funcname,
725+
"show a line with the function name before matches"),
724726
OPT_GROUP(""),
725727
OPT_CALLBACK('f', NULL, &opt, "file",
726728
"read patterns from file", file_callback),
@@ -789,7 +791,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
789791
argc--;
790792
}
791793

792-
if (opt.color && !opt.color_external)
794+
if ((opt.color && !opt.color_external) || opt.funcname)
793795
external_grep_allowed = 0;
794796
if (!opt.pattern_list)
795797
die("no pattern given.");

grep.c

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -490,14 +490,18 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
490490
{
491491
int rest = eol - bol;
492492

493-
if (opt->pre_context || opt->post_context) {
493+
if (opt->pre_context || opt->post_context || opt->funcname) {
494494
if (opt->last_shown == 0) {
495495
if (opt->show_hunk_mark)
496-
fputs("--\n", stdout);
496+
fputs(opt->funcname ? "==\n" : "--\n", stdout);
497497
else
498498
opt->show_hunk_mark = 1;
499-
} else if (lno > opt->last_shown + 1)
500-
fputs("--\n", stdout);
499+
} else if (lno > opt->last_shown + 1) {
500+
if (opt->pre_context || opt->post_context)
501+
fputs((sign == '=') ? "==\n" : "--\n", stdout);
502+
else if (sign == '=')
503+
fputs("==\n", stdout);
504+
}
501505
}
502506
opt->last_shown = lno;
503507

@@ -531,10 +535,40 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
531535
printf("%.*s\n", rest, bol);
532536
}
533537

538+
static int match_funcname(char *bol, char *eol)
539+
{
540+
if (bol == eol)
541+
return 0;
542+
if (isalpha(*bol) || *bol == '_' || *bol == '$')
543+
return 1;
544+
return 0;
545+
}
546+
547+
static void show_funcname_line(struct grep_opt *opt, const char *name,
548+
char *buf, char *bol, unsigned lno)
549+
{
550+
while (bol > buf) {
551+
char *eol = --bol;
552+
553+
while (bol > buf && bol[-1] != '\n')
554+
bol--;
555+
lno--;
556+
557+
if (lno <= opt->last_shown)
558+
break;
559+
560+
if (match_funcname(bol, eol)) {
561+
show_line(opt, bol, eol, name, lno, '=');
562+
break;
563+
}
564+
}
565+
}
566+
534567
static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
535568
char *bol, unsigned lno)
536569
{
537-
unsigned cur = lno, from = 1;
570+
unsigned cur = lno, from = 1, funcname_lno = 0;
571+
int funcname_needed = opt->funcname;
538572

539573
if (opt->pre_context < lno)
540574
from = lno - opt->pre_context;
@@ -543,19 +577,28 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
543577

544578
/* Rewind. */
545579
while (bol > buf && cur > from) {
546-
bol--;
580+
char *eol = --bol;
581+
547582
while (bol > buf && bol[-1] != '\n')
548583
bol--;
549584
cur--;
585+
if (funcname_needed && match_funcname(bol, eol)) {
586+
funcname_lno = cur;
587+
funcname_needed = 0;
588+
}
550589
}
551590

591+
/* We need to look even further back to find a function signature. */
592+
if (opt->funcname && funcname_needed)
593+
show_funcname_line(opt, name, buf, bol, cur);
594+
552595
/* Back forward. */
553596
while (cur < lno) {
554-
char *eol = bol;
597+
char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
555598

556599
while (*eol != '\n')
557600
eol++;
558-
show_line(opt, bol, eol, name, cur, '-');
601+
show_line(opt, bol, eol, name, cur, sign);
559602
bol = eol + 1;
560603
cur++;
561604
}
@@ -635,6 +678,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
635678
*/
636679
if (opt->pre_context)
637680
show_pre_context(opt, name, buf, bol, lno);
681+
else if (opt->funcname)
682+
show_funcname_line(opt, name, buf, bol, lno);
638683
if (!opt->count)
639684
show_line(opt, bol, eol, name, lno, ':');
640685
last_hit = lno;

grep.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct grep_opt {
7979
int pathname;
8080
int null_following_name;
8181
int color;
82+
int funcname;
8283
char color_match[COLOR_MAXLEN];
8384
const char *color_external;
8485
int regflags;

t/t7002-grep.sh

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ test_description='git grep various.
88

99
. ./test-lib.sh
1010

11+
cat >hello.c <<EOF
12+
#include <stdio.h>
13+
int main(int argc, const char **argv)
14+
{
15+
printf("Hello world.\n");
16+
return 0;
17+
}
18+
EOF
19+
1120
test_expect_success setup '
1221
{
1322
echo foo mmap bar
@@ -22,7 +31,7 @@ test_expect_success setup '
2231
echo zzz > z &&
2332
mkdir t &&
2433
echo test >t/t &&
25-
git add file w x y z t/t &&
34+
git add file w x y z t/t hello.c &&
2635
test_tick &&
2736
git commit -m initial
2837
'
@@ -229,9 +238,32 @@ test_expect_success 'log grep (6)' '
229238
test_expect_success 'grep with CE_VALID file' '
230239
git update-index --assume-unchanged t/t &&
231240
rm t/t &&
232-
test "$(git grep --no-ext-grep t)" = "t/t:test" &&
241+
test "$(git grep --no-ext-grep test)" = "t/t:test" &&
233242
git update-index --no-assume-unchanged t/t &&
234243
git checkout t/t
235244
'
236245

246+
cat >expected <<EOF
247+
hello.c=int main(int argc, const char **argv)
248+
hello.c: return 0;
249+
EOF
250+
251+
test_expect_success 'grep -p' '
252+
git grep -p return >actual &&
253+
test_cmp expected actual
254+
'
255+
256+
cat >expected <<EOF
257+
hello.c-#include <stdio.h>
258+
hello.c=int main(int argc, const char **argv)
259+
hello.c-{
260+
hello.c- printf("Hello world.\n");
261+
hello.c: return 0;
262+
EOF
263+
264+
test_expect_success 'grep -p -B5' '
265+
git grep -p -B5 return >actual &&
266+
test_cmp expected actual
267+
'
268+
237269
test_done

0 commit comments

Comments
 (0)