grep: add option to show whole function as context
authorRené Scharfe <rene.scharfe@lsrfire.ath.cx>
Mon, 1 Aug 2011 17:20:53 +0000 (19:20 +0200)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 Aug 2011 23:09:15 +0000 (16:09 -0700)
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>
Documentation/git-grep.txt
builtin/grep.c
grep.c
grep.h
t/t7810-grep.sh

index 07b3c6a0866c5b4ff0b4653b462b332ea0d348c9..6cd0c503f63c2e2ffe49ef5ffc7e64677ae721cd 100644 (file)
@@ -172,6 +172,12 @@ OPTIONS
        patch hunk headers (see 'Defining a custom hunk-header' in
        linkgit:gitattributes[5]).
 
+-W::
+       Show the surrounding text from the previous line containing a
+       function name up to the one before the next function name,
+       effectively showing the whole function in which the match was
+       found.
+
 -f <file>::
        Read patterns from <file>, one per line.
 
index cccf8da6d2a600154536ea642250699d9356f148..1fae66262f25fb0d3837ceffad8db44555c5b88b 100644 (file)
@@ -838,6 +838,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        context_callback),
                OPT_BOOLEAN('p', "show-function", &opt.funcname,
                        "show a line with the function name before matches"),
+               OPT_BOOLEAN('W', NULL, &opt.funcbody,
+                       "show the surrounding function"),
                OPT_GROUP(""),
                OPT_CALLBACK('f', NULL, &opt, "file",
                        "read patterns from file", file_callback),
@@ -980,7 +982,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                use_threads = 0;
 
        if (use_threads) {
-               if (opt.pre_context || opt.post_context || opt.file_break)
+               if (opt.pre_context || opt.post_context || opt.file_break ||
+                   opt.funcbody)
                        skip_first_line = 1;
                start_threads(&opt);
        }
diff --git a/grep.c b/grep.c
index 04e9ba4ec46b9f2002135293ede1bc5570fa73dc..26e8d8ec4cbec60cd5f4e2bef0d99607c7118fb7 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -724,7 +724,7 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        if (opt->file_break && opt->last_shown == 0) {
                if (opt->show_hunk_mark)
                        opt->output(opt, "\n", 1);
-       } else if (opt->pre_context || opt->post_context) {
+       } else if (opt->pre_context || opt->post_context || opt->funcbody) {
                if (opt->last_shown == 0) {
                        if (opt->show_hunk_mark) {
                                output_color(opt, "--", 2, opt->color_sep);
@@ -819,10 +819,13 @@ static void show_funcname_line(struct grep_opt *opt, const char *name,
 }
 
 static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
-                            char *bol, unsigned lno)
+                            char *bol, char *end, unsigned lno)
 {
        unsigned cur = lno, from = 1, funcname_lno = 0;
-       int funcname_needed = opt->funcname;
+       int funcname_needed = !!opt->funcname;
+
+       if (opt->funcbody && !match_funcname(opt, bol, end))
+               funcname_needed = 2;
 
        if (opt->pre_context < lno)
                from = lno - opt->pre_context;
@@ -830,7 +833,8 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
                from = opt->last_shown + 1;
 
        /* Rewind. */
-       while (bol > buf && cur > from) {
+       while (bol > buf &&
+              cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
                char *eol = --bol;
 
                while (bol > buf && bol[-1] != '\n')
@@ -942,13 +946,15 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
        int binary_match_only = 0;
        unsigned count = 0;
        int try_lookahead = 0;
+       int show_function = 0;
        enum grep_context ctx = GREP_CONTEXT_HEAD;
        xdemitconf_t xecfg;
 
        if (!opt->output)
                opt->output = std_output;
 
-       if (opt->pre_context || opt->post_context || opt->file_break) {
+       if (opt->pre_context || opt->post_context || opt->file_break ||
+           opt->funcbody) {
                /* Show hunk marks, except for the first file. */
                if (opt->last_shown)
                        opt->show_hunk_mark = 1;
@@ -1004,7 +1010,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                 */
                if (try_lookahead
                    && !(last_hit
-                        && lno <= last_hit + opt->post_context)
+                        && (show_function ||
+                            lno <= last_hit + opt->post_context))
                    && look_ahead(opt, &left, &lno, &bol))
                        break;
                eol = end_of_line(bol, &left);
@@ -1051,15 +1058,20 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                        /* Hit at this line.  If we haven't shown the
                         * pre-context lines, we would need to show them.
                         */
-                       if (opt->pre_context)
-                               show_pre_context(opt, name, buf, bol, lno);
+                       if (opt->pre_context || opt->funcbody)
+                               show_pre_context(opt, name, buf, bol, eol, lno);
                        else if (opt->funcname)
                                show_funcname_line(opt, name, buf, bol, lno);
                        show_line(opt, bol, eol, name, lno, ':');
                        last_hit = lno;
+                       if (opt->funcbody)
+                               show_function = 1;
+                       goto next_line;
                }
-               else if (last_hit &&
-                        lno <= last_hit + opt->post_context) {
+               if (show_function && match_funcname(opt, bol, eol))
+                       show_function = 0;
+               if (show_function ||
+                   (last_hit && lno <= last_hit + opt->post_context)) {
                        /* If the last hit is within the post context,
                         * we need to show this line.
                         */
diff --git a/grep.h b/grep.h
index c5682973eaf696099b0d0367e21df6c0a4624836..ae50c45a4d408c1931f39b3d393ea0188bdde950 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -98,6 +98,7 @@ struct grep_opt {
        int color;
        int max_depth;
        int funcname;
+       int funcbody;
        char color_context[COLOR_MAXLEN];
        char color_filename[COLOR_MAXLEN];
        char color_function[COLOR_MAXLEN];
index a29ae45b399f89123f886f5b35580bcdc0e803f1..0d600163c8284a318fbd21f3a00dd7853b2f8956 100755 (executable)
@@ -509,6 +509,20 @@ test_expect_success 'grep -p -B5' '
        test_cmp expected actual
 '
 
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c-       printf("Hello world.\n");
+hello.c:       return 0;
+hello.c-       /* char ?? */
+hello.c-}
+EOF
+
+test_expect_success 'grep -W' '
+       git grep -W return >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'grep from a subdirectory to search wider area (1)' '
        mkdir -p s &&
        (