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>maint
parent
49de321698
commit
2944e4e614
|
@ -122,6 +122,11 @@ OPTIONS
|
||||||
-<num>::
|
-<num>::
|
||||||
A shortcut for specifying -C<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>::
|
-f <file>::
|
||||||
Read patterns from <file>, one per line.
|
Read patterns from <file>, one per line.
|
||||||
|
|
||||||
|
|
|
@ -278,13 +278,13 @@ static int flush_grep(struct grep_opt *opt,
|
||||||
argc -= 2;
|
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
|
* grep handles hunk marks between files, but we need to
|
||||||
* do that ourselves between multiple calls.
|
* do that ourselves between multiple calls.
|
||||||
*/
|
*/
|
||||||
if (opt->show_hunk_mark)
|
if (opt->show_hunk_mark)
|
||||||
write_or_die(1, "--\n", 3);
|
write_or_die(1, opt->funcname ? "==\n" : "--\n", 3);
|
||||||
else
|
else
|
||||||
opt->show_hunk_mark = 1;
|
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"),
|
"show <n> context lines after matches"),
|
||||||
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
|
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
|
||||||
context_callback),
|
context_callback),
|
||||||
|
OPT_BOOLEAN('p', "show-function", &opt.funcname,
|
||||||
|
"show a line with the function name before matches"),
|
||||||
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),
|
||||||
|
@ -789,7 +791,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||||
argc--;
|
argc--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt.color && !opt.color_external)
|
if ((opt.color && !opt.color_external) || opt.funcname)
|
||||||
external_grep_allowed = 0;
|
external_grep_allowed = 0;
|
||||||
if (!opt.pattern_list)
|
if (!opt.pattern_list)
|
||||||
die("no pattern given.");
|
die("no pattern given.");
|
||||||
|
|
61
grep.c
61
grep.c
|
@ -490,14 +490,18 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
|
||||||
{
|
{
|
||||||
int rest = eol - bol;
|
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->last_shown == 0) {
|
||||||
if (opt->show_hunk_mark)
|
if (opt->show_hunk_mark)
|
||||||
fputs("--\n", stdout);
|
fputs(opt->funcname ? "==\n" : "--\n", stdout);
|
||||||
else
|
else
|
||||||
opt->show_hunk_mark = 1;
|
opt->show_hunk_mark = 1;
|
||||||
} else if (lno > opt->last_shown + 1)
|
} else if (lno > opt->last_shown + 1) {
|
||||||
fputs("--\n", stdout);
|
if (opt->pre_context || opt->post_context)
|
||||||
|
fputs((sign == '=') ? "==\n" : "--\n", stdout);
|
||||||
|
else if (sign == '=')
|
||||||
|
fputs("==\n", stdout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
opt->last_shown = lno;
|
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);
|
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,
|
static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
|
||||||
char *bol, unsigned lno)
|
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)
|
if (opt->pre_context < lno)
|
||||||
from = lno - opt->pre_context;
|
from = lno - opt->pre_context;
|
||||||
|
@ -543,19 +577,28 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
|
||||||
|
|
||||||
/* Rewind. */
|
/* Rewind. */
|
||||||
while (bol > buf && cur > from) {
|
while (bol > buf && cur > from) {
|
||||||
bol--;
|
char *eol = --bol;
|
||||||
|
|
||||||
while (bol > buf && bol[-1] != '\n')
|
while (bol > buf && bol[-1] != '\n')
|
||||||
bol--;
|
bol--;
|
||||||
cur--;
|
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. */
|
/* Back forward. */
|
||||||
while (cur < lno) {
|
while (cur < lno) {
|
||||||
char *eol = bol;
|
char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
|
||||||
|
|
||||||
while (*eol != '\n')
|
while (*eol != '\n')
|
||||||
eol++;
|
eol++;
|
||||||
show_line(opt, bol, eol, name, cur, '-');
|
show_line(opt, bol, eol, name, cur, sign);
|
||||||
bol = eol + 1;
|
bol = eol + 1;
|
||||||
cur++;
|
cur++;
|
||||||
}
|
}
|
||||||
|
@ -635,6 +678,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||||
*/
|
*/
|
||||||
if (opt->pre_context)
|
if (opt->pre_context)
|
||||||
show_pre_context(opt, name, buf, bol, lno);
|
show_pre_context(opt, name, buf, bol, lno);
|
||||||
|
else if (opt->funcname)
|
||||||
|
show_funcname_line(opt, name, buf, bol, lno);
|
||||||
if (!opt->count)
|
if (!opt->count)
|
||||||
show_line(opt, bol, eol, name, lno, ':');
|
show_line(opt, bol, eol, name, lno, ':');
|
||||||
last_hit = lno;
|
last_hit = lno;
|
||||||
|
|
1
grep.h
1
grep.h
|
@ -79,6 +79,7 @@ struct grep_opt {
|
||||||
int pathname;
|
int pathname;
|
||||||
int null_following_name;
|
int null_following_name;
|
||||||
int color;
|
int color;
|
||||||
|
int funcname;
|
||||||
char color_match[COLOR_MAXLEN];
|
char color_match[COLOR_MAXLEN];
|
||||||
const char *color_external;
|
const char *color_external;
|
||||||
int regflags;
|
int regflags;
|
||||||
|
|
|
@ -8,6 +8,15 @@ test_description='git grep various.
|
||||||
|
|
||||||
. ./test-lib.sh
|
. ./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 '
|
test_expect_success setup '
|
||||||
{
|
{
|
||||||
echo foo mmap bar
|
echo foo mmap bar
|
||||||
|
@ -22,7 +31,7 @@ test_expect_success setup '
|
||||||
echo zzz > z &&
|
echo zzz > z &&
|
||||||
mkdir t &&
|
mkdir t &&
|
||||||
echo test >t/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 &&
|
test_tick &&
|
||||||
git commit -m initial
|
git commit -m initial
|
||||||
'
|
'
|
||||||
|
@ -229,9 +238,32 @@ test_expect_success 'log grep (6)' '
|
||||||
test_expect_success 'grep with CE_VALID file' '
|
test_expect_success 'grep with CE_VALID file' '
|
||||||
git update-index --assume-unchanged t/t &&
|
git update-index --assume-unchanged t/t &&
|
||||||
rm 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 update-index --no-assume-unchanged t/t &&
|
||||||
git checkout 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
|
test_done
|
||||||
|
|
Loading…
Reference in New Issue