git-grep: support regexp

This commit is contained in:
Radosław Piliszek 2024-08-12 20:57:42 +02:00
parent 0ccefbebfc
commit 6d6116857c
4 changed files with 99 additions and 10 deletions

View file

@ -27,12 +27,20 @@ type GrepResult struct {
HighlightedRanges [][3]int HighlightedRanges [][3]int
} }
type grepMode int
const (
FixedGrepMode grepMode = iota
FixedAnyGrepMode
RegExpGrepMode
)
type GrepOptions struct { type GrepOptions struct {
RefName string RefName string
MaxResultLimit int MaxResultLimit int
MatchesPerFile int MatchesPerFile int
ContextLineNumber int ContextLineNumber int
IsFuzzy bool Mode grepMode
PathSpec []setting.Glob PathSpec []setting.Glob
} }
@ -75,11 +83,16 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
// -I skips binary files // -I skips binary files
cmd := NewCommand(ctx, "grep", cmd := NewCommand(ctx, "grep",
"-I", "--null", "--break", "--heading", "--column", "-I", "--null", "--break", "--heading", "--column",
"--fixed-strings", "--line-number", "--ignore-case", "--full-name") "--line-number", "--ignore-case", "--full-name")
if opts.Mode == RegExpGrepMode {
cmd.AddArguments("--perl-regexp")
} else {
cmd.AddArguments("--fixed-strings")
}
cmd.AddOptionValues("--context", fmt.Sprint(opts.ContextLineNumber)) cmd.AddOptionValues("--context", fmt.Sprint(opts.ContextLineNumber))
cmd.AddOptionValues("--max-count", fmt.Sprint(opts.MatchesPerFile)) cmd.AddOptionValues("--max-count", fmt.Sprint(opts.MatchesPerFile))
words := []string{search} words := []string{search}
if opts.IsFuzzy { if opts.Mode == FixedAnyGrepMode {
words = strings.Fields(search) words = strings.Fields(search)
} }
for _, word := range words { for _, word := range words {

View file

@ -201,3 +201,34 @@ func TestGrepRefs(t *testing.T) {
assert.Len(t, res, 1) assert.Len(t, res, 1)
assert.Equal(t, "A", res[0].LineCodes[0]) assert.Equal(t, "A", res[0].LineCodes[0])
} }
func TestGrepCanHazRegexOnDemand(t *testing.T) {
tmpDir := t.TempDir()
err := InitRepository(DefaultContext, tmpDir, false, Sha1ObjectFormat.Name())
require.NoError(t, err)
gitRepo, err := openRepositoryWithDefaultContext(tmpDir)
require.NoError(t, err)
defer gitRepo.Close()
require.NoError(t, os.WriteFile(path.Join(tmpDir, "matching"), []byte("It's a match!"), 0o666))
require.NoError(t, os.WriteFile(path.Join(tmpDir, "not-matching"), []byte("Orisitamatch?"), 0o666))
err = AddChanges(tmpDir, true)
require.NoError(t, err)
err = CommitChanges(tmpDir, CommitChangesOptions{Message: "Add fixtures for regexp test"})
require.NoError(t, err)
// should find nothing by default...
res, err := GrepSearch(context.Background(), gitRepo, "\\bmatch\\b", GrepOptions{})
require.NoError(t, err)
assert.Empty(t, res)
// ... unless configured explicitly
res, err = GrepSearch(context.Background(), gitRepo, "\\bmatch\\b", GrepOptions{Mode: RegExpGrepMode})
require.NoError(t, err)
assert.Len(t, res, 1)
assert.Equal(t, "matching", res[0].Filename)
}

View file

@ -17,16 +17,55 @@ import (
const tplSearch base.TplName = "repo/search" const tplSearch base.TplName = "repo/search"
type searchMode int
const (
ExactSearchMode searchMode = iota
FuzzySearchMode
RegExpSearchMode
)
func searchModeFromString(s string) searchMode {
switch s {
case "fuzzy":
return FuzzySearchMode
case "regexp":
return RegExpSearchMode
default:
return ExactSearchMode
}
}
func (m searchMode) String() string {
switch m {
case ExactSearchMode:
return "exact"
case FuzzySearchMode:
return "fuzzy"
case RegExpSearchMode:
return "regexp"
default:
panic("cannot happen")
}
}
// Search render repository search page // Search render repository search page
func Search(ctx *context.Context) { func Search(ctx *context.Context) {
language := ctx.FormTrim("l") language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q") keyword := ctx.FormTrim("q")
isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true) mode := ExactSearchMode
if modeStr := ctx.FormString("mode"); len(modeStr) > 0 {
mode = searchModeFromString(modeStr)
} else if ctx.FormOptionalBool("fuzzy").ValueOrDefault(true) { // for backward compatibility in links
mode = FuzzySearchMode
}
ctx.Data["Keyword"] = keyword ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language ctx.Data["Language"] = language
ctx.Data["IsFuzzy"] = isFuzzy ctx.Data["IsFuzzy"] = mode == FuzzySearchMode
ctx.Data["IsRegExp"] = mode == RegExpSearchMode
ctx.Data["SearchMode"] = mode.String()
ctx.Data["PageIsViewCode"] = true ctx.Data["PageIsViewCode"] = true
if keyword == "" { if keyword == "" {
@ -47,7 +86,7 @@ func Search(ctx *context.Context) {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{ total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
RepoIDs: []int64{ctx.Repo.Repository.ID}, RepoIDs: []int64{ctx.Repo.Repository.ID},
Keyword: keyword, Keyword: keyword,
IsKeywordFuzzy: isFuzzy, IsKeywordFuzzy: mode == FuzzySearchMode,
Language: language, Language: language,
Paginator: &db.ListOptions{ Paginator: &db.ListOptions{
Page: page, Page: page,
@ -64,11 +103,17 @@ func Search(ctx *context.Context) {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx) ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
} }
} else { } else {
res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{ grepOpt := git.GrepOptions{
ContextLineNumber: 1, ContextLineNumber: 1,
IsFuzzy: isFuzzy,
RefName: ctx.Repo.RefName, RefName: ctx.Repo.RefName,
}) }
switch mode {
case FuzzySearchMode:
grepOpt.Mode = git.FixedAnyGrepMode
case RegExpSearchMode:
grepOpt.Mode = git.RegExpGrepMode
}
res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, grepOpt)
if err != nil { if err != nil {
ctx.ServerError("GrepSearch", err) ctx.ServerError("GrepSearch", err)
return return

View file

@ -417,7 +417,7 @@ func SearchWikiContents(ctx context.Context, repo *repo_model.Repository, keywor
return git.GrepSearch(ctx, gitRepo, keyword, git.GrepOptions{ return git.GrepSearch(ctx, gitRepo, keyword, git.GrepOptions{
ContextLineNumber: 0, ContextLineNumber: 0,
IsFuzzy: true, Mode: git.FixedAnyGrepMode,
RefName: repo.GetWikiBranchName(), RefName: repo.GetWikiBranchName(),
MaxResultLimit: 10, MaxResultLimit: 10,
MatchesPerFile: 3, MatchesPerFile: 3,