Skip to content

Commit

Permalink
support *grep context options
Browse files Browse the repository at this point in the history
Support parsing the output of *grep when displaying context lines such
as with `-A`, `-B` or `-C`.

Fixes: #118
Signed-off-by: Valentin Rothberg <[email protected]>
  • Loading branch information
vrothberg committed Oct 31, 2020
1 parent c6a6614 commit c434d99
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 20 deletions.
8 changes: 4 additions & 4 deletions test/simple.bats
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ load helpers

# Other checks

@test "Fail gracefully with error message when unable to parse output" {
@test "Search with -C5 for context lines" {
run_vgrep -d -C5 peanut
[ "$status" -eq 1 ]
[[ ${lines[@]} =~ "failed to parse results, did you use an option that modifies the output?" ]]
[[ ${lines[@]} =~ "level=debug msg=\"failed to parse:" ]]
[ "$status" -eq 0 ]
echo "${lines[@]}"
[[ ${lines[@]} =~ "level=debug msg=\"skipping line \\\"--\\\" (parse error: expected 2 but split into 1 items ([0]))\"" ]]
}

@test "Exit with 1 when a search has no matches" {
Expand Down
39 changes: 23 additions & 16 deletions vgrep.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,23 +273,27 @@ func (v *vgrep) grep(args []string) {
os.Exit(1)
}
v.matches = make([][]string, len(output))
for i, m := range output {
file, line, content := v.splitMatch(m, greptype)
i := 0
for _, m := range output {
file, line, content, err := v.splitMatch(m, greptype)
if err != nil {
logrus.Debugf("skipping line %q (parse error: %v)", m, err)
continue
}
v.matches[i] = make([]string, 4)
v.matches[i][0] = strconv.Itoa(i)
v.matches[i][1] = file
v.matches[i][2] = line
v.matches[i][3] = content
i++
}

logrus.Debugf("found %d matches", len(v.matches))
}

// splitMatch splits match into its file, line and content. The format of
// match varies depending if it has been produced by grep or git-grep.
func (v *vgrep) splitMatch(match string, greptype string) (file, line, content string) {
parseError := fmt.Errorf("failed to parse results, did you use an option that modifies the output?")

func (v *vgrep) splitMatch(match string, greptype string) (file, line, content string, err error) {
if greptype == RIPGrep {
// remove default color ansi escape codes from ripgrep's output
match = strings.Replace(match, "\x1b[0m", "", 4)
Expand All @@ -305,25 +309,28 @@ func (v *vgrep) splitMatch(match string, greptype string) (file, line, content s
case BSDGrep, GITGrep:
spl := bytes.SplitN([]byte(match), separator, 3)
if len(spl) < 3 {
fmt.Fprintln(os.Stderr, parseError)
logrus.Debugf("failed to parse: %s", match)
os.Exit(1)
err = fmt.Errorf("expected %d but split into %d items (%v)", 3, len(spl), separator)
return
}
file, line, content = string(spl[0]), string(spl[1]), string(spl[2])
case GNUGrep, RIPGrep:
spl := bytes.SplitN([]byte(match), separator, 2)
if len(spl) < 2 {
fmt.Fprintln(os.Stderr, parseError)
logrus.Debugf("failed to parse: %s", match)
os.Exit(1)
err = fmt.Errorf("expected %d but split into %d items (%v)", 2, len(spl), separator)
return
}
splline := bytes.SplitN(spl[1], []byte(":"), 2)
if len(splline) < 2 {
fmt.Fprintln(os.Stderr, parseError)
logrus.Debugf("failed to parse: %s", match)
os.Exit(1)
if len(splline) != 2 {
// Fall back to "-" which is used when displaying
// context lines.
splline = bytes.SplitN(spl[1], []byte("-"), 2)
}
file, line, content = string(spl[0]), string(splline[0]), string(splline[1])
if len(splline) == 2 {
file, line, content = string(spl[0]), string(splline[0]), string(splline[1])
return
}
err = fmt.Errorf("unexpected input")
return
}
return
}
Expand Down

0 comments on commit c434d99

Please sign in to comment.