Improve grep search (#30843)
Reduce the context line number to 1, make "git grep" search respect the include/exclude patter, and fix #30785
This commit is contained in:
parent
9f0ef3621a
commit
0f3e717a1a
@ -29,6 +29,7 @@ type GrepOptions struct {
|
|||||||
ContextLineNumber int
|
ContextLineNumber int
|
||||||
IsFuzzy bool
|
IsFuzzy bool
|
||||||
MaxLineLength int // the maximum length of a line to parse, exceeding chars will be truncated
|
MaxLineLength int // the maximum length of a line to parse, exceeding chars will be truncated
|
||||||
|
PathspecList []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepOptions) ([]*GrepResult, error) {
|
func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepOptions) ([]*GrepResult, error) {
|
||||||
@ -62,6 +63,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
|
|||||||
cmd.AddOptionValues("-e", strings.TrimLeft(search, "-"))
|
cmd.AddOptionValues("-e", strings.TrimLeft(search, "-"))
|
||||||
}
|
}
|
||||||
cmd.AddDynamicArguments(util.IfZero(opts.RefName, "HEAD"))
|
cmd.AddDynamicArguments(util.IfZero(opts.RefName, "HEAD"))
|
||||||
|
cmd.AddDashesAndList(opts.PathspecList...)
|
||||||
opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50)
|
opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50)
|
||||||
stderr := bytes.Buffer{}
|
stderr := bytes.Buffer{}
|
||||||
err = cmd.Run(&RunOpts{
|
err = cmd.Run(&RunOpts{
|
||||||
|
@ -31,6 +31,26 @@ func TestGrepSearch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, res)
|
}, res)
|
||||||
|
|
||||||
|
res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob)java-hello/*"}})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []*GrepResult{
|
||||||
|
{
|
||||||
|
Filename: "java-hello/main.java",
|
||||||
|
LineNumbers: []int{3},
|
||||||
|
LineCodes: []string{" public static void main(String[] args)"},
|
||||||
|
},
|
||||||
|
}, res)
|
||||||
|
|
||||||
|
res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob,exclude)java-hello/*"}})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []*GrepResult{
|
||||||
|
{
|
||||||
|
Filename: "main.vendor.java",
|
||||||
|
LineNumbers: []int{3},
|
||||||
|
LineCodes: []string{" public static void main(String[] args)"},
|
||||||
|
},
|
||||||
|
}, res)
|
||||||
|
|
||||||
res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1})
|
res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, []*GrepResult{
|
assert.Equal(t, []*GrepResult{
|
||||||
|
32
modules/setting/glob.go
Normal file
32
modules/setting/glob.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import "github.com/gobwas/glob"
|
||||||
|
|
||||||
|
type GlobMatcher struct {
|
||||||
|
compiledGlob glob.Glob
|
||||||
|
patternString string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ glob.Glob = (*GlobMatcher)(nil)
|
||||||
|
|
||||||
|
func (g *GlobMatcher) Match(s string) bool {
|
||||||
|
return g.compiledGlob.Match(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GlobMatcher) PatternString() string {
|
||||||
|
return g.patternString
|
||||||
|
}
|
||||||
|
|
||||||
|
func GlobMatcherCompile(pattern string, separators ...rune) (*GlobMatcher, error) {
|
||||||
|
g, err := glob.Compile(pattern, separators...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &GlobMatcher{
|
||||||
|
compiledGlob: g,
|
||||||
|
patternString: pattern,
|
||||||
|
}, nil
|
||||||
|
}
|
@ -10,8 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
"github.com/gobwas/glob"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Indexer settings
|
// Indexer settings
|
||||||
@ -30,8 +28,8 @@ var Indexer = struct {
|
|||||||
RepoConnStr string
|
RepoConnStr string
|
||||||
RepoIndexerName string
|
RepoIndexerName string
|
||||||
MaxIndexerFileSize int64
|
MaxIndexerFileSize int64
|
||||||
IncludePatterns []glob.Glob
|
IncludePatterns []*GlobMatcher
|
||||||
ExcludePatterns []glob.Glob
|
ExcludePatterns []*GlobMatcher
|
||||||
ExcludeVendored bool
|
ExcludeVendored bool
|
||||||
}{
|
}{
|
||||||
IssueType: "bleve",
|
IssueType: "bleve",
|
||||||
@ -93,12 +91,12 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing
|
// IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing
|
||||||
func IndexerGlobFromString(globstr string) []glob.Glob {
|
func IndexerGlobFromString(globstr string) []*GlobMatcher {
|
||||||
extarr := make([]glob.Glob, 0, 10)
|
extarr := make([]*GlobMatcher, 0, 10)
|
||||||
for _, expr := range strings.Split(strings.ToLower(globstr), ",") {
|
for _, expr := range strings.Split(strings.ToLower(globstr), ",") {
|
||||||
expr = strings.TrimSpace(expr)
|
expr = strings.TrimSpace(expr)
|
||||||
if expr != "" {
|
if expr != "" {
|
||||||
if g, err := glob.Compile(expr, '.', '/'); err != nil {
|
if g, err := GlobMatcherCompile(expr, '.', '/'); err != nil {
|
||||||
log.Info("Invalid glob expression '%s' (skipped): %v", expr, err)
|
log.Info("Invalid glob expression '%s' (skipped): %v", expr, err)
|
||||||
} else {
|
} else {
|
||||||
extarr = append(extarr, g)
|
extarr = append(extarr, g)
|
||||||
|
@ -17,6 +17,16 @@ import (
|
|||||||
|
|
||||||
const tplSearch base.TplName = "repo/search"
|
const tplSearch base.TplName = "repo/search"
|
||||||
|
|
||||||
|
func indexSettingToGitGrepPathspecList() (list []string) {
|
||||||
|
for _, expr := range setting.Indexer.IncludePatterns {
|
||||||
|
list = append(list, ":(glob)"+expr.PatternString())
|
||||||
|
}
|
||||||
|
for _, expr := range setting.Indexer.ExcludePatterns {
|
||||||
|
list = append(list, ":(glob,exclude)"+expr.PatternString())
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
// 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")
|
||||||
@ -65,8 +75,14 @@ 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{ContextLineNumber: 3, IsFuzzy: isFuzzy})
|
res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{
|
||||||
|
ContextLineNumber: 1,
|
||||||
|
IsFuzzy: isFuzzy,
|
||||||
|
RefName: git.RefNameFromBranch(ctx.Repo.BranchName).String(), // BranchName should be default branch or the first existing branch
|
||||||
|
PathspecList: indexSettingToGitGrepPathspecList(),
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// TODO: if no branch exists, it reports: exit status 128, fatal: this operation must be run in a work tree.
|
||||||
ctx.ServerError("GrepSearch", err)
|
ctx.ServerError("GrepSearch", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
19
routers/web/repo/search_test.go
Normal file
19
routers/web/repo/search_test.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIndexSettingToGitGrepPathspecList(t *testing.T) {
|
||||||
|
defer test.MockVariableValue(&setting.Indexer.IncludePatterns, setting.IndexerGlobFromString("a"))()
|
||||||
|
defer test.MockVariableValue(&setting.Indexer.ExcludePatterns, setting.IndexerGlobFromString("b"))()
|
||||||
|
assert.Equal(t, []string{":(glob)a", ":(glob,exclude)b"}, indexSettingToGitGrepPathspecList())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user