Show usage string for 'git commit-tree -h'
[gitweb.git] / grep.c
diff --git a/grep.c b/grep.c
index 9b9d2e39f8ae71e6e9a394f2b0566d7a137082e2..5d162dae6e43cdfcf6b20627df088a73cbfa98dc 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "grep.h"
+#include "userdiff.h"
 #include "xdiff-interface.h"
 
 void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)
@@ -531,10 +532,47 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        printf("%.*s\n", rest, bol);
 }
 
+static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
+{
+       xdemitconf_t *xecfg = opt->priv;
+       if (xecfg && xecfg->find_func) {
+               char buf[1];
+               return xecfg->find_func(bol, eol - bol, buf, 1,
+                                       xecfg->find_func_priv) >= 0;
+       }
+
+       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(opt, 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 +581,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(opt, 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++;
        }
@@ -571,6 +618,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
        int binary_match_only = 0;
        unsigned count = 0;
        enum grep_context ctx = GREP_CONTEXT_HEAD;
+       xdemitconf_t xecfg;
 
        opt->last_shown = 0;
 
@@ -587,6 +635,17 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                }
        }
 
+       memset(&xecfg, 0, sizeof(xecfg));
+       if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
+           !opt->name_only && !binary_match_only && !collect_hits) {
+               struct userdiff_driver *drv = userdiff_find_by_path(name);
+               if (drv && drv->funcname.pattern) {
+                       const struct userdiff_funcname *pe = &drv->funcname;
+                       xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
+                       opt->priv = &xecfg;
+               }
+       }
+
        while (left) {
                char *eol, ch;
                int hit;
@@ -635,6 +694,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;
@@ -666,6 +727,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                return 1;
        }
 
+       xdiff_clear_find_func(&xecfg);
+       opt->priv = NULL;
+
        /* NEEDSWORK:
         * The real "grep -c foo *.c" gives many "bar.c:0" lines,
         * which feels mostly useless but sometimes useful.  Maybe