Code

grep: add option -p/--show-function
authorRené Scharfe <rene.scharfe@lsrfire.ath.cx>
Wed, 1 Jul 2009 22:06:34 +0000 (00:06 +0200)
committerJunio C Hamano <gitster@pobox.com>
Thu, 2 Jul 2009 02:16:49 +0000 (19:16 -0700)
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>
Documentation/git-grep.txt
builtin-grep.c
grep.c
grep.h
t/t7002-grep.sh

index fccb82deb48ec75d25f948e7caa69a44e09f5832..b3bb2836444fff2964deb47d1e8e2f6c36b1fe50 100644 (file)
@@ -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.
 
index 48998af9115379f97403e0aeb63ac46f041906eb..037452ec79475244f4cae7cfc936695e1de58a63 100644 (file)
@@ -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.");
diff --git a/grep.c b/grep.c
index 9b9d2e39f8ae71e6e9a394f2b0566d7a137082e2..3a5c138476e5bc643656ec7a6e41da9fa3168969 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -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;
diff --git a/grep.h b/grep.h
index 730ffd6f531d99787f33ca3fc9b1f49be090ab7f..3f75e3a3d4d3136d850b8c0204206f5ac9459a1d 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -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;
index 155bfdb7d75c17daae23229d87c7c3e84668f3a4..ef59ab9941ffb3bafc0bd45ebbee02e7a117c527 100755 (executable)
@@ -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