diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 460378ce0..404d6f93f 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -11,13 +11,17 @@ import ( code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/context" - "code.gitea.io/gitea/services/repository/files" ) const tplSearch base.TplName = "repo/search" // Search render repository search page func Search(ctx *context.Context) { + if !setting.Indexer.RepoIndexerEnabled { + ctx.Redirect(ctx.Repo.RepoLink) + return + } + language := ctx.FormTrim("l") keyword := ctx.FormTrim("q") @@ -33,57 +37,32 @@ func Search(ctx *context.Context) { return } - ctx.Data["Repo"] = ctx.Repo.Repository - page := ctx.FormInt("page") if page <= 0 { page = 1 } - - if setting.Indexer.RepoIndexerEnabled { - ctx.Data["CodeIndexerDisabled"] = false - - total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{ - RepoIDs: []int64{ctx.Repo.Repository.ID}, - Keyword: keyword, - IsKeywordFuzzy: isFuzzy, - Language: language, - Paginator: &db.ListOptions{ - Page: page, - PageSize: setting.UI.RepoSearchPagingNum, - }, - }) - if err != nil { - if code_indexer.IsAvailable(ctx) { - ctx.ServerError("SearchResults", err) - return - } - ctx.Data["CodeIndexerUnavailable"] = true - } else { - ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx) - } - - ctx.Data["SearchResults"] = searchResults - ctx.Data["SearchResultLanguages"] = searchResultLanguages - - pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) - pager.SetDefaultParams(ctx) - pager.AddParam(ctx, "l", "Language") - ctx.Data["Page"] = pager - } else { - data, err := files.NewRepoGrep(ctx, ctx.Repo.Repository, keyword) - if err != nil { - ctx.ServerError("NewRepoGrep", err) + + total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, []int64{ctx.Repo.Repository.ID}, + language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + if err != nil { + if code_indexer.IsAvailable(ctx) { + ctx.ServerError("SearchResults", err) return } - - ctx.Data["CodeIndexerDisabled"] = true - ctx.Data["SearchResults"] = data - - pager := context.NewPagination(len(data), setting.UI.RepoSearchPagingNum, page, 5) - pager.SetDefaultParams(ctx) - ctx.Data["Page"] = pager + ctx.Data["CodeIndexerUnavailable"] = true + } else { + ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx) } + ctx.Data["Repo"] = ctx.Repo.Repository + ctx.Data["SourcePath"] = ctx.Repo.Repository.Link() + ctx.Data["SearchResults"] = searchResults + ctx.Data["SearchResultLanguages"] = searchResultLanguages + + pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) + pager.SetDefaultParams(ctx) + pager.AddParam(ctx, "l", "Language") + ctx.Data["Page"] = pager + ctx.HTML(http.StatusOK, tplSearch) } diff --git a/services/repository/files/search.go b/services/repository/files/search.go deleted file mode 100644 index 09c3ab5bf..000000000 --- a/services/repository/files/search.go +++ /dev/null @@ -1,111 +0,0 @@ -package files - -import ( - "context" - "html/template" - "strconv" - "strings" - - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/gitrepo" - "code.gitea.io/gitea/modules/highlight" - "code.gitea.io/gitea/modules/timeutil" - - "github.com/go-enry/go-enry/v2" -) - -type Result struct { - RepoID int64 // ignored - Filename string - CommitID string // branch - UpdatedUnix timeutil.TimeStamp // ignored - Language string - Color string - Lines []ResultLine -} - -type ResultLine struct { - Num int64 - FormattedContent template.HTML -} - -const pHEAD = "HEAD:" - -func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword string) ([]*Result, error) { - t, _, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) - if err != nil { - return nil, err - } - - data := []*Result{} - - stdout, _, err := git.NewCommand(ctx, - "grep", - "-1", // n before and after lines - "-z", - "--heading", - "--break", // easier parsing - "--fixed-strings", // disallow regex for now - "-n", // line nums - "-i", // ignore case - "--full-name", // full file path, rel to repo - //"--column", // for adding better highlighting support - "-e", // for queries starting with "-" - ). - AddDynamicArguments(keyword). - AddArguments("HEAD"). - RunStdString(&git.RunOpts{Dir: t.Path}) - if err != nil { - return data, nil // non zero exit code when there are no results - } - - for _, block := range strings.Split(stdout, "\n\n") { - res := Result{CommitID: repo.DefaultBranch} - - linenum := []int64{} - code := []string{} - - for _, line := range strings.Split(block, "\n") { - if strings.HasPrefix(line, pHEAD) { - res.Filename = strings.TrimPrefix(line, pHEAD) - continue - } - - if ln, after, ok := strings.Cut(line, "\x00"); ok { - i, err := strconv.ParseInt(ln, 10, 64) - if err != nil { - continue - } - - linenum = append(linenum, i) - code = append(code, after) - } - } - - if res.Filename == "" || len(code) == 0 || len(linenum) == 0 { - continue - } - - var hl template.HTML - - hl, res.Language = highlight.Code(res.Filename, "", strings.Join(code, "\n")) - res.Color = enry.GetColor(res.Language) - - hlCode := strings.Split(string(hl), "\n") - n := min(len(hlCode), len(linenum)) - - res.Lines = make([]ResultLine, n) - - for i := 0; i < n; i++ { - res.Lines[i] = ResultLine{ - Num: linenum[i], - FormattedContent: template.HTML(hlCode[i]), - } - } - - data = append(data, &res) - } - - return data, nil -} diff --git a/services/repository/files/search_test.go b/services/repository/files/search_test.go deleted file mode 100644 index 2f2f87368..000000000 --- a/services/repository/files/search_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package files - -import ( - "testing" - - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/services/contexttest" - - "github.com/stretchr/testify/assert" -) - -func TestNewRepoGrep(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx, _ := contexttest.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - contexttest.LoadRepo(t, ctx, 1) - contexttest.LoadRepoCommit(t, ctx) - contexttest.LoadUser(t, ctx, 2) - contexttest.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - t.Run("with result", func(t *testing.T) { - res, err := NewRepoGrep(ctx, ctx.Repo.Repository, "Description") - assert.NoError(t, err) - - expected := []*Result{ - { - RepoID: 0, - Filename: "README.md", - CommitID: "master", - UpdatedUnix: 0, - Language: "Markdown", - Color: "#083fa1", - Lines: []ResultLine{ - {Num: 2, FormattedContent: ""}, - {Num: 3, FormattedContent: "Description for repo1"}, - }, - }, - } - - assert.EqualValues(t, res, expected) - }) - - t.Run("empty result", func(t *testing.T) { - res, err := NewRepoGrep(ctx, ctx.Repo.Repository, "keyword that does not match in the repo") - assert.NoError(t, err) - - assert.EqualValues(t, res, []*Result{}) - }) -} diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index bfb5ab523..fcaacfc01 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -11,21 +11,23 @@ {{if $description}}{{$description | RenderCodeBlock}}{{else if .IsRepositoryAdmin}}{{ctx.Locale.Tr "repo.no_desc"}}{{end}} {{.Repository.Website}} -
{{range .Topics}}{{.Name}}{{end}} diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go index aecf75025..56cc45d90 100644 --- a/tests/integration/repo_search_test.go +++ b/tests/integration/repo_search_test.go @@ -11,15 +11,14 @@ import ( repo_model "code.gitea.io/gitea/models/repo" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" ) -func resultFilenames(t testing.TB, doc *goquery.Selection) []string { - filenameSelections := doc.Find(".header").Find("span.file") +func resultFilenames(t testing.TB, doc *HTMLDoc) []string { + filenameSelections := doc.doc.Find(".repository.search").Find(".repo-search-result").Find(".header").Find("span.file") result := make([]string, filenameSelections.Length()) filenameSelections.Each(func(i int, selection *goquery.Selection) { result[i] = selection.Text() @@ -27,66 +26,36 @@ func resultFilenames(t testing.TB, doc *goquery.Selection) []string { return result } -func checkResultLinks(t *testing.T, substr string, doc *goquery.Selection) { - t.Helper() - linkSelections := doc.Find("a[href]") - linkSelections.Each(func(i int, selection *goquery.Selection) { - assert.Contains(t, selection.AttrOr("href", ""), substr) - }) -} - -func testSearchRepo(t *testing.T, useExternalIndexer bool) { +func TestSearchRepo(t *testing.T) { defer tests.PrepareTestEnv(t)() - defer test.MockVariableValue(&setting.Indexer.RepoIndexerEnabled, useExternalIndexer)() repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "repo1") assert.NoError(t, err) - gitReference := "/branch/" + repo.DefaultBranch + code_indexer.UpdateRepoIndexer(repo) - if useExternalIndexer { - gitReference = "/commit/" - code_indexer.UpdateRepoIndexer(repo) - } + testSearch(t, "/user2/repo1/search?q=Description&page=1", []string{"README.md"}) - testSearch(t, "/user2/repo1/search?q=Description&page=1", gitReference, []string{"README.md"}) + setting.Indexer.IncludePatterns = setting.IndexerGlobFromString("**.txt") + setting.Indexer.ExcludePatterns = setting.IndexerGlobFromString("**/y/**") - if useExternalIndexer { - setting.Indexer.IncludePatterns = setting.IndexerGlobFromString("**.txt") - setting.Indexer.ExcludePatterns = setting.IndexerGlobFromString("**/y/**") + repo, err = repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "glob") + assert.NoError(t, err) - repo, err = repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "glob") - assert.NoError(t, err) + code_indexer.UpdateRepoIndexer(repo) - code_indexer.UpdateRepoIndexer(repo) - - testSearch(t, "/user2/glob/search?q=loren&page=1", gitReference, []string{"a.txt"}) - testSearch(t, "/user2/glob/search?q=loren&page=1&t=match", gitReference, []string{"a.txt"}) - testSearch(t, "/user2/glob/search?q=file3&page=1", gitReference, []string{"x/b.txt", "a.txt"}) - testSearch(t, "/user2/glob/search?q=file3&page=1&t=match", gitReference, []string{"x/b.txt", "a.txt"}) - testSearch(t, "/user2/glob/search?q=file4&page=1&t=match", gitReference, []string{"x/b.txt", "a.txt"}) - testSearch(t, "/user2/glob/search?q=file5&page=1&t=match", gitReference, []string{"x/b.txt", "a.txt"}) - } + testSearch(t, "/user2/glob/search?q=loren&page=1", []string{"a.txt"}) + testSearch(t, "/user2/glob/search?q=loren&page=1&t=match", []string{"a.txt"}) + testSearch(t, "/user2/glob/search?q=file3&page=1", []string{"x/b.txt", "a.txt"}) + testSearch(t, "/user2/glob/search?q=file3&page=1&t=match", []string{"x/b.txt", "a.txt"}) + testSearch(t, "/user2/glob/search?q=file4&page=1&t=match", []string{"x/b.txt", "a.txt"}) + testSearch(t, "/user2/glob/search?q=file5&page=1&t=match", []string{"x/b.txt", "a.txt"}) } -func TestIndexerSearchRepo(t *testing.T) { - testSearchRepo(t, true) -} - -func TestNoIndexerSearchRepo(t *testing.T) { - testSearchRepo(t, false) -} - -func testSearch(t *testing.T, url, gitRef string, expected []string) { +func testSearch(t *testing.T, url string, expected []string) { req := NewRequest(t, "GET", url) resp := MakeRequest(t, req, http.StatusOK) - doc := NewHTMLParser(t, resp.Body).doc. - Find(".repository.search"). - Find(".repo-search-result") - - filenames := resultFilenames(t, doc) + filenames := resultFilenames(t, NewHTMLParser(t, resp.Body)) assert.EqualValues(t, expected, filenames) - - checkResultLinks(t, gitRef, doc) }