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 <rene.scharfe@lsrfire.ath.cx> Signed-off-by: Junio C Hamano <gitster@pobox.com>
René Scharfe committed
Jul 2, 2009 at 00:06 UTC
2944e4e6145bdfcb1a8730d7da671786d72c86ed
5 files changed
+98
-13
Documentation/git-grep.txt
+5
@@ -122,6 +122,11 @@ OPTIONS
122
-<num>::
123
A shortcut for specifying -C<num>.
124
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
+
130
-f <file>::
131
Read patterns from <file>, one per line.
132
builtin-grep.c
+5
-3
@@ -278,13 +278,13 @@ static int flush_grep(struct grep_opt *opt,
278
argc -= 2;
279
}
280
281
- if (opt->pre_context || opt->post_context) {
281
+ if (opt->pre_context || opt->post_context || opt->funcname) {
282
/*
283
* grep handles hunk marks between files, but we need to
284
* do that ourselves between multiple calls.
285
*/
286
if (opt->show_hunk_mark)
287
- write_or_die(1, "--\n", 3);
287
+ write_or_die(1, opt->funcname ? "==\n" : "--\n", 3);
288
else
289
opt->show_hunk_mark = 1;
290
}
@@ -721,6 +721,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
721
"show <n> context lines after matches"),
722
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
723
context_callback),
724
+ OPT_BOOLEAN('p', "show-function", &opt.funcname,
725
+ "show a line with the function name before matches"),
726
OPT_GROUP(""),
727
OPT_CALLBACK('f', NULL, &opt, "file",
728
"read patterns from file", file_callback),
@@ -789,7 +791,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
791
argc--;
792
}
793
792
- if (opt.color && !opt.color_external)
794
+ if ((opt.color && !opt.color_external) || opt.funcname)
795
external_grep_allowed = 0;
796
if (!opt.pattern_list)
797
die("no pattern given.");
grep.c
+53
-8
@@ -490,14 +490,18 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
490
{
491
int rest = eol - bol;
492
493
- if (opt->pre_context || opt->post_context) {
493
+ if (opt->pre_context || opt->post_context || opt->funcname) {
494
if (opt->last_shown == 0) {
495
if (opt->show_hunk_mark)
496
- fputs("--\n", stdout);
496
+ fputs(opt->funcname ? "==\n" : "--\n", stdout);
497
else
498
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
+ }
505
}
506
opt->last_shown = lno;
507
@@ -531,10 +535,40 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
535
printf("%.*s\n", rest, bol);
536
}
537
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
+
567
static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
568
char *bol, unsigned lno)
569
{
537
- unsigned cur = lno, from = 1;
570
+ unsigned cur = lno, from = 1, funcname_lno = 0;
571
+ int funcname_needed = opt->funcname;
572
573
if (opt->pre_context < lno)
574
from = lno - opt->pre_context;
@@ -543,19 +577,28 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
577
578
/* Rewind. */
579
while (bol > buf && cur > from) {
546
- bol--;
580
+ char *eol = --bol;
581
+
582
while (bol > buf && bol[-1] != '\n')
583
bol--;
584
cur--;
585
+ if (funcname_needed && match_funcname(bol, eol)) {
586
+ funcname_lno = cur;
587
+ funcname_needed = 0;
588
+ }
589
}
590
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
+
595
/* Back forward. */
596
while (cur < lno) {
554
- char *eol = bol;
597
+ char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
598
599
while (*eol != '\n')
600
eol++;
558
- show_line(opt, bol, eol, name, cur, '-');
601
+ show_line(opt, bol, eol, name, cur, sign);
602
bol = eol + 1;
603
cur++;
604
}
@@ -635,6 +678,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
678
*/
679
if (opt->pre_context)
680
show_pre_context(opt, name, buf, bol, lno);
681
+ else if (opt->funcname)
682
+ show_funcname_line(opt, name, buf, bol, lno);
683
if (!opt->count)
684
show_line(opt, bol, eol, name, lno, ':');
685
last_hit = lno;
grep.h
+1
@@ -79,6 +79,7 @@ struct grep_opt {
79
int pathname;
80
int null_following_name;
81
int color;
82
+ int funcname;
83
char color_match[COLOR_MAXLEN];
84
const char *color_external;
85
int regflags;
t/t7002-grep.sh
+34
-2
@@ -8,6 +8,15 @@ test_description='git grep various.
8
9
. ./test-lib.sh
10
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
+
20
test_expect_success setup '
21
{
22
echo foo mmap bar
@@ -22,7 +31,7 @@ test_expect_success setup '
31
echo zzz > z &&
32
mkdir t &&
33
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 &&
35
test_tick &&
36
git commit -m initial
37
'
@@ -229,9 +238,32 @@ test_expect_success 'log grep (6)' '
238
test_expect_success 'grep with CE_VALID file' '
239
git update-index --assume-unchanged t/t &&
240
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" &&
242
git update-index --no-assume-unchanged t/t &&
243
git checkout t/t
244
'
245
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
+
269
test_done