Skip to content

Commit 128a9d8

Browse files
committed
Merge branch 'rs/grep-p'
* rs/grep-p: grep: simplify -p output grep -p: support user defined regular expressions grep: add option -p/--show-function grep: handle pre context lines on demand grep: print context hunk marks between files grep: move context hunk mark handling into show_line() userdiff: add xdiff_clear_find_func()
2 parents ce4f404 + ed24e40 commit 128a9d8

File tree

8 files changed

+228
-44
lines changed

8 files changed

+228
-44
lines changed

Documentation/git-grep.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ OPTIONS
122122
-<num>::
123123
A shortcut for specifying -C<num>.
124124

125+
-p::
126+
--show-function::
127+
Show the preceding line that contains the function name of
128+
the match, unless the matching line is a function name itself.
129+
The name is determined in the same way as 'git diff' works out
130+
patch hunk headers (see 'Defining a custom hunk-header' in
131+
linkgit:gitattributes[5]).
132+
125133
-f <file>::
126134
Read patterns from <file>, one per line.
127135

builtin-grep.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "tree-walk.h"
1212
#include "builtin.h"
1313
#include "parse-options.h"
14+
#include "userdiff.h"
1415
#include "grep.h"
1516

1617
#ifndef NO_EXTERNAL_GREP
@@ -30,6 +31,12 @@ static int grep_config(const char *var, const char *value, void *cb)
3031
{
3132
struct grep_opt *opt = cb;
3233

34+
switch (userdiff_config(var, value)) {
35+
case 0: break;
36+
case -1: return -1;
37+
default: return 0;
38+
}
39+
3340
if (!strcmp(var, "color.grep")) {
3441
opt->color = git_config_colorbool(var, value, -1);
3542
return 0;
@@ -278,6 +285,17 @@ static int flush_grep(struct grep_opt *opt,
278285
argc -= 2;
279286
}
280287

288+
if (opt->pre_context || opt->post_context) {
289+
/*
290+
* grep handles hunk marks between files, but we need to
291+
* do that ourselves between multiple calls.
292+
*/
293+
if (opt->show_hunk_mark)
294+
write_or_die(1, "--\n", 3);
295+
else
296+
opt->show_hunk_mark = 1;
297+
}
298+
281299
status = exec_grep(argc, argv);
282300

283301
if (kept_0) {
@@ -710,6 +728,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
710728
"show <n> context lines after matches"),
711729
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
712730
context_callback),
731+
OPT_BOOLEAN('p', "show-function", &opt.funcname,
732+
"show a line with the function name before matches"),
713733
OPT_GROUP(""),
714734
OPT_CALLBACK('f', NULL, &opt, "file",
715735
"read patterns from file", file_callback),
@@ -778,7 +798,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
778798
argc--;
779799
}
780800

781-
if (opt.color && !opt.color_external)
801+
if ((opt.color && !opt.color_external) || opt.funcname)
782802
external_grep_allowed = 0;
783803
if (!opt.pattern_list)
784804
die("no pattern given.");

diff.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,7 @@ static void builtin_diff(const char *name_a,
16031603
free(mf1.ptr);
16041604
if (textconv_two)
16051605
free(mf2.ptr);
1606+
xdiff_clear_find_func(&xecfg);
16061607
}
16071608

16081609
free_ab_and_return:

grep.c

Lines changed: 109 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "cache.h"
22
#include "grep.h"
3+
#include "userdiff.h"
34
#include "xdiff-interface.h"
45

56
void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)
@@ -490,6 +491,17 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
490491
{
491492
int rest = eol - bol;
492493

494+
if (opt->pre_context || opt->post_context) {
495+
if (opt->last_shown == 0) {
496+
if (opt->show_hunk_mark)
497+
fputs("--\n", stdout);
498+
else
499+
opt->show_hunk_mark = 1;
500+
} else if (lno > opt->last_shown + 1)
501+
fputs("--\n", stdout);
502+
}
503+
opt->last_shown = lno;
504+
493505
if (opt->null_following_name)
494506
sign = '\0';
495507
if (opt->pathname)
@@ -520,22 +532,95 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
520532
printf("%.*s\n", rest, bol);
521533
}
522534

535+
static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
536+
{
537+
xdemitconf_t *xecfg = opt->priv;
538+
if (xecfg && xecfg->find_func) {
539+
char buf[1];
540+
return xecfg->find_func(bol, eol - bol, buf, 1,
541+
xecfg->find_func_priv) >= 0;
542+
}
543+
544+
if (bol == eol)
545+
return 0;
546+
if (isalpha(*bol) || *bol == '_' || *bol == '$')
547+
return 1;
548+
return 0;
549+
}
550+
551+
static void show_funcname_line(struct grep_opt *opt, const char *name,
552+
char *buf, char *bol, unsigned lno)
553+
{
554+
while (bol > buf) {
555+
char *eol = --bol;
556+
557+
while (bol > buf && bol[-1] != '\n')
558+
bol--;
559+
lno--;
560+
561+
if (lno <= opt->last_shown)
562+
break;
563+
564+
if (match_funcname(opt, bol, eol)) {
565+
show_line(opt, bol, eol, name, lno, '=');
566+
break;
567+
}
568+
}
569+
}
570+
571+
static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
572+
char *bol, unsigned lno)
573+
{
574+
unsigned cur = lno, from = 1, funcname_lno = 0;
575+
int funcname_needed = opt->funcname;
576+
577+
if (opt->pre_context < lno)
578+
from = lno - opt->pre_context;
579+
if (from <= opt->last_shown)
580+
from = opt->last_shown + 1;
581+
582+
/* Rewind. */
583+
while (bol > buf && cur > from) {
584+
char *eol = --bol;
585+
586+
while (bol > buf && bol[-1] != '\n')
587+
bol--;
588+
cur--;
589+
if (funcname_needed && match_funcname(opt, bol, eol)) {
590+
funcname_lno = cur;
591+
funcname_needed = 0;
592+
}
593+
}
594+
595+
/* We need to look even further back to find a function signature. */
596+
if (opt->funcname && funcname_needed)
597+
show_funcname_line(opt, name, buf, bol, cur);
598+
599+
/* Back forward. */
600+
while (cur < lno) {
601+
char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
602+
603+
while (*eol != '\n')
604+
eol++;
605+
show_line(opt, bol, eol, name, cur, sign);
606+
bol = eol + 1;
607+
cur++;
608+
}
609+
}
610+
523611
static int grep_buffer_1(struct grep_opt *opt, const char *name,
524612
char *buf, unsigned long size, int collect_hits)
525613
{
526614
char *bol = buf;
527615
unsigned long left = size;
528616
unsigned lno = 1;
529-
struct pre_context_line {
530-
char *bol;
531-
char *eol;
532-
} *prev = NULL, *pcl;
533617
unsigned last_hit = 0;
534-
unsigned last_shown = 0;
535618
int binary_match_only = 0;
536-
const char *hunk_mark = "";
537619
unsigned count = 0;
538620
enum grep_context ctx = GREP_CONTEXT_HEAD;
621+
xdemitconf_t xecfg;
622+
623+
opt->last_shown = 0;
539624

540625
if (buffer_is_binary(buf, size)) {
541626
switch (opt->binary) {
@@ -550,10 +635,16 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
550635
}
551636
}
552637

553-
if (opt->pre_context)
554-
prev = xcalloc(opt->pre_context, sizeof(*prev));
555-
if (opt->pre_context || opt->post_context)
556-
hunk_mark = "--\n";
638+
memset(&xecfg, 0, sizeof(xecfg));
639+
if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
640+
!opt->name_only && !binary_match_only && !collect_hits) {
641+
struct userdiff_driver *drv = userdiff_find_by_path(name);
642+
if (drv && drv->funcname.pattern) {
643+
const struct userdiff_funcname *pe = &drv->funcname;
644+
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
645+
opt->priv = &xecfg;
646+
}
647+
}
557648

558649
while (left) {
559650
char *eol, ch;
@@ -601,45 +692,20 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
601692
* the context which is nonsense, but the user
602693
* deserves to get that ;-).
603694
*/
604-
if (opt->pre_context) {
605-
unsigned from;
606-
if (opt->pre_context < lno)
607-
from = lno - opt->pre_context;
608-
else
609-
from = 1;
610-
if (from <= last_shown)
611-
from = last_shown + 1;
612-
if (last_shown && from != last_shown + 1)
613-
fputs(hunk_mark, stdout);
614-
while (from < lno) {
615-
pcl = &prev[lno-from-1];
616-
show_line(opt, pcl->bol, pcl->eol,
617-
name, from, '-');
618-
from++;
619-
}
620-
last_shown = lno-1;
621-
}
622-
if (last_shown && lno != last_shown + 1)
623-
fputs(hunk_mark, stdout);
695+
if (opt->pre_context)
696+
show_pre_context(opt, name, buf, bol, lno);
697+
else if (opt->funcname)
698+
show_funcname_line(opt, name, buf, bol, lno);
624699
if (!opt->count)
625700
show_line(opt, bol, eol, name, lno, ':');
626-
last_shown = last_hit = lno;
701+
last_hit = lno;
627702
}
628703
else if (last_hit &&
629704
lno <= last_hit + opt->post_context) {
630705
/* If the last hit is within the post context,
631706
* we need to show this line.
632707
*/
633-
if (last_shown && lno != last_shown + 1)
634-
fputs(hunk_mark, stdout);
635708
show_line(opt, bol, eol, name, lno, '-');
636-
last_shown = lno;
637-
}
638-
if (opt->pre_context) {
639-
memmove(prev+1, prev,
640-
(opt->pre_context-1) * sizeof(*prev));
641-
prev->bol = bol;
642-
prev->eol = eol;
643709
}
644710

645711
next_line:
@@ -650,7 +716,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
650716
lno++;
651717
}
652718

653-
free(prev);
654719
if (collect_hits)
655720
return 0;
656721

@@ -662,6 +727,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
662727
return 1;
663728
}
664729

730+
xdiff_clear_find_func(&xecfg);
731+
opt->priv = NULL;
732+
665733
/* NEEDSWORK:
666734
* The real "grep -c foo *.c" gives many "bar.c:0" lines,
667735
* which feels mostly useless but sometimes useful. Maybe

grep.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,15 @@ struct grep_opt {
7979
int pathname;
8080
int null_following_name;
8181
int color;
82+
int funcname;
8283
char color_match[COLOR_MAXLEN];
8384
const char *color_external;
8485
int regflags;
8586
unsigned pre_context;
8687
unsigned post_context;
88+
unsigned last_shown;
89+
int show_hunk_mark;
90+
void *priv;
8791
};
8892

8993
extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);

0 commit comments

Comments
 (0)