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>

René Scharfe committed Aug 1, 2011 at 19:20 UTC ba8ea7496f2b95c49fccb72ed6b332afbd865e7b
5 files changed +47 -11
Documentation/git-grep.txt
+6
@@ -172,6 +172,12 @@ OPTIONS
172 patch hunk headers (see 'Defining a custom hunk-header' in
173 linkgit:gitattributes[5]).
174
175 +-W::
176 + Show the surrounding text from the previous line containing a
177 + function name up to the one before the next function name,
178 + effectively showing the whole function in which the match was
179 + found.
180 +
181 -f <file>::
182 Read patterns from <file>, one per line.
183
builtin/grep.c
+4 -1
@@ -838,6 +838,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
838 context_callback),
839 OPT_BOOLEAN('p', "show-function", &opt.funcname,
840 "show a line with the function name before matches"),
841 + OPT_BOOLEAN('W', NULL, &opt.funcbody,
842 + "show the surrounding function"),
843 OPT_GROUP(""),
844 OPT_CALLBACK('f', NULL, &opt, "file",
845 "read patterns from file", file_callback),
@@ -980,7 +982,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
982 use_threads = 0;
983
984 if (use_threads) {
983 - if (opt.pre_context || opt.post_context || opt.file_break)
985 + if (opt.pre_context || opt.post_context || opt.file_break ||
986 + opt.funcbody)
987 skip_first_line = 1;
988 start_threads(&opt);
989 }
grep.c
+22 -10
@@ -724,7 +724,7 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
724 if (opt->file_break && opt->last_shown == 0) {
725 if (opt->show_hunk_mark)
726 opt->output(opt, "\n", 1);
727 - } else if (opt->pre_context || opt->post_context) {
727 + } else if (opt->pre_context || opt->post_context || opt->funcbody) {
728 if (opt->last_shown == 0) {
729 if (opt->show_hunk_mark) {
730 output_color(opt, "--", 2, opt->color_sep);
@@ -819,10 +819,13 @@ static void show_funcname_line(struct grep_opt *opt, const char *name,
819 }
820
821 static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
822 - char *bol, unsigned lno)
822 + char *bol, char *end, unsigned lno)
823 {
824 unsigned cur = lno, from = 1, funcname_lno = 0;
825 - int funcname_needed = opt->funcname;
825 + int funcname_needed = !!opt->funcname;
826 +
827 + if (opt->funcbody && !match_funcname(opt, bol, end))
828 + funcname_needed = 2;
829
830 if (opt->pre_context < lno)
831 from = lno - opt->pre_context;
@@ -830,7 +833,8 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
833 from = opt->last_shown + 1;
834
835 /* Rewind. */
833 - while (bol > buf && cur > from) {
836 + while (bol > buf &&
837 + cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
838 char *eol = --bol;
839
840 while (bol > buf && bol[-1] != '\n')
@@ -942,13 +946,15 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
946 int binary_match_only = 0;
947 unsigned count = 0;
948 int try_lookahead = 0;
949 + int show_function = 0;
950 enum grep_context ctx = GREP_CONTEXT_HEAD;
951 xdemitconf_t xecfg;
952
953 if (!opt->output)
954 opt->output = std_output;
955
951 - if (opt->pre_context || opt->post_context || opt->file_break) {
956 + if (opt->pre_context || opt->post_context || opt->file_break ||
957 + opt->funcbody) {
958 /* Show hunk marks, except for the first file. */
959 if (opt->last_shown)
960 opt->show_hunk_mark = 1;
@@ -1004,7 +1010,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
1010 */
1011 if (try_lookahead
1012 && !(last_hit
1007 - && lno <= last_hit + opt->post_context)
1013 + && (show_function ||
1014 + lno <= last_hit + opt->post_context))
1015 && look_ahead(opt, &left, &lno, &bol))
1016 break;
1017 eol = end_of_line(bol, &left);
@@ -1051,15 +1058,20 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
1058 /* Hit at this line. If we haven't shown the
1059 * pre-context lines, we would need to show them.
1060 */
1054 - if (opt->pre_context)
1055 - show_pre_context(opt, name, buf, bol, lno);
1061 + if (opt->pre_context || opt->funcbody)
1062 + show_pre_context(opt, name, buf, bol, eol, lno);
1063 else if (opt->funcname)
1064 show_funcname_line(opt, name, buf, bol, lno);
1065 show_line(opt, bol, eol, name, lno, ':');
1066 last_hit = lno;
1067 + if (opt->funcbody)
1068 + show_function = 1;
1069 + goto next_line;
1070 }
1061 - else if (last_hit &&
1062 - lno <= last_hit + opt->post_context) {
1071 + if (show_function && match_funcname(opt, bol, eol))
1072 + show_function = 0;
1073 + if (show_function ||
1074 + (last_hit && lno <= last_hit + opt->post_context)) {
1075 /* If the last hit is within the post context,
1076 * we need to show this line.
1077 */
grep.h
+1
@@ -98,6 +98,7 @@ struct grep_opt {
98 int color;
99 int max_depth;
100 int funcname;
101 + int funcbody;
102 char color_context[COLOR_MAXLEN];
103 char color_filename[COLOR_MAXLEN];
104 char color_function[COLOR_MAXLEN];
t/t7810-grep.sh
+14
@@ -509,6 +509,20 @@ test_expect_success 'grep -p -B5' '
509 test_cmp expected actual
510 '
511
512 +cat >expected <<EOF
513 +hello.c=int main(int argc, const char **argv)
514 +hello.c-{
515 +hello.c- printf("Hello world.\n");
516 +hello.c: return 0;
517 +hello.c- /* char ?? */
518 +hello.c-}
519 +EOF
520 +
521 +test_expect_success 'grep -W' '
522 + git grep -W return >actual &&
523 + test_cmp expected actual
524 +'
525 +
526 test_expect_success 'grep from a subdirectory to search wider area (1)' '
527 mkdir -p s &&
528 (