diff options
-rw-r--r-- | Documentation/git-grep.txt | 5 | ||||
-rw-r--r-- | builtin-grep.c | 8 | ||||
-rw-r--r-- | grep.c | 61 | ||||
-rw-r--r-- | grep.h | 1 | ||||
-rwxr-xr-x | t/t7002-grep.sh | 36 |
5 files changed, 98 insertions, 13 deletions
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index fccb82deb4..b3bb283644 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -122,6 +122,11 @@ OPTIONS -<num>:: A shortcut for specifying -C<num>. +-p:: +--show-function:: + Show the preceding line that contains the function name of + the match, unless the matching line is a function name itself. + -f <file>:: Read patterns from <file>, one per line. diff --git a/builtin-grep.c b/builtin-grep.c index 48998af911..037452ec79 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -278,13 +278,13 @@ static int flush_grep(struct grep_opt *opt, argc -= 2; } - if (opt->pre_context || opt->post_context) { + if (opt->pre_context || opt->post_context || opt->funcname) { /* * grep handles hunk marks between files, but we need to * do that ourselves between multiple calls. */ if (opt->show_hunk_mark) - write_or_die(1, "--\n", 3); + write_or_die(1, opt->funcname ? "==\n" : "--\n", 3); else opt->show_hunk_mark = 1; } @@ -721,6 +721,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) "show <n> context lines after matches"), OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM", context_callback), + OPT_BOOLEAN('p', "show-function", &opt.funcname, + "show a line with the function name before matches"), OPT_GROUP(""), OPT_CALLBACK('f', NULL, &opt, "file", "read patterns from file", file_callback), @@ -789,7 +791,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) argc--; } - if (opt.color && !opt.color_external) + if ((opt.color && !opt.color_external) || opt.funcname) external_grep_allowed = 0; if (!opt.pattern_list) die("no pattern given."); @@ -490,14 +490,18 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, { int rest = eol - bol; - if (opt->pre_context || opt->post_context) { + if (opt->pre_context || opt->post_context || opt->funcname) { if (opt->last_shown == 0) { if (opt->show_hunk_mark) - fputs("--\n", stdout); + fputs(opt->funcname ? "==\n" : "--\n", stdout); else opt->show_hunk_mark = 1; - } else if (lno > opt->last_shown + 1) - fputs("--\n", stdout); + } else if (lno > opt->last_shown + 1) { + if (opt->pre_context || opt->post_context) + fputs((sign == '=') ? "==\n" : "--\n", stdout); + else if (sign == '=') + fputs("==\n", stdout); + } } opt->last_shown = lno; @@ -531,10 +535,40 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, printf("%.*s\n", rest, bol); } +static int match_funcname(char *bol, char *eol) +{ + if (bol == eol) + return 0; + if (isalpha(*bol) || *bol == '_' || *bol == '$') + return 1; + return 0; +} + +static void show_funcname_line(struct grep_opt *opt, const char *name, + char *buf, char *bol, unsigned lno) +{ + while (bol > buf) { + char *eol = --bol; + + while (bol > buf && bol[-1] != '\n') + bol--; + lno--; + + if (lno <= opt->last_shown) + break; + + if (match_funcname(bol, eol)) { + show_line(opt, bol, eol, name, lno, '='); + break; + } + } +} + static void show_pre_context(struct grep_opt *opt, const char *name, char *buf, char *bol, unsigned lno) { - unsigned cur = lno, from = 1; + unsigned cur = lno, from = 1, funcname_lno = 0; + int funcname_needed = opt->funcname; if (opt->pre_context < lno) from = lno - opt->pre_context; @@ -543,19 +577,28 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf, /* Rewind. */ while (bol > buf && cur > from) { - bol--; + char *eol = --bol; + while (bol > buf && bol[-1] != '\n') bol--; cur--; + if (funcname_needed && match_funcname(bol, eol)) { + funcname_lno = cur; + funcname_needed = 0; + } } + /* We need to look even further back to find a function signature. */ + if (opt->funcname && funcname_needed) + show_funcname_line(opt, name, buf, bol, cur); + /* Back forward. */ while (cur < lno) { - char *eol = bol; + char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-'; while (*eol != '\n') eol++; - show_line(opt, bol, eol, name, cur, '-'); + show_line(opt, bol, eol, name, cur, sign); bol = eol + 1; cur++; } @@ -635,6 +678,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name, */ if (opt->pre_context) show_pre_context(opt, name, buf, bol, lno); + else if (opt->funcname) + show_funcname_line(opt, name, buf, bol, lno); if (!opt->count) show_line(opt, bol, eol, name, lno, ':'); last_hit = lno; @@ -79,6 +79,7 @@ struct grep_opt { int pathname; int null_following_name; int color; + int funcname; char color_match[COLOR_MAXLEN]; const char *color_external; int regflags; diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh index 155bfdb7d7..ef59ab9941 100755 --- a/t/t7002-grep.sh +++ b/t/t7002-grep.sh @@ -8,6 +8,15 @@ test_description='git grep various. . ./test-lib.sh +cat >hello.c <<EOF +#include <stdio.h> +int main(int argc, const char **argv) +{ + printf("Hello world.\n"); + return 0; +} +EOF + test_expect_success setup ' { echo foo mmap bar @@ -22,7 +31,7 @@ test_expect_success setup ' echo zzz > z && mkdir t && echo test >t/t && - git add file w x y z t/t && + git add file w x y z t/t hello.c && test_tick && git commit -m initial ' @@ -229,9 +238,32 @@ test_expect_success 'log grep (6)' ' test_expect_success 'grep with CE_VALID file' ' git update-index --assume-unchanged t/t && rm t/t && - test "$(git grep --no-ext-grep t)" = "t/t:test" && + test "$(git grep --no-ext-grep test)" = "t/t:test" && git update-index --no-assume-unchanged t/t && git checkout t/t ' +cat >expected <<EOF +hello.c=int main(int argc, const char **argv) +hello.c: return 0; +EOF + +test_expect_success 'grep -p' ' + git grep -p return >actual && + test_cmp expected actual +' + +cat >expected <<EOF +hello.c-#include <stdio.h> +hello.c=int main(int argc, const char **argv) +hello.c-{ +hello.c- printf("Hello world.\n"); +hello.c: return 0; +EOF + +test_expect_success 'grep -p -B5' ' + git grep -p -B5 return >actual && + test_cmp expected actual +' + test_done |