chore: minor code cleanup in search (#10549)

Minor code cleanup for code/issue search.

Mostly breaking up the common functionality into separate functions :)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10549
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
Co-committed-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
This commit is contained in:
Shiny Nematoda 2025-12-23 00:38:51 +01:00 committed by Gusted
parent 537a802125
commit b1adc7d931
6 changed files with 113 additions and 106 deletions

View file

@ -7,6 +7,7 @@ import (
"bytes"
"context"
"html/template"
"slices"
"strings"
"forgejo.org/modules/highlight"
@ -46,6 +47,19 @@ const (
SearchModeFuzzy = internal.CodeSearchModeFuzzy
)
type Results []*Result
// Get the set of repo IDs from a list of search results
func (res Results) RepoIDs() []int64 {
ids := make([]int64, len(res))
for _, r := range res {
if !slices.Contains(ids, r.RepoID) {
ids = append(ids, r.RepoID)
}
}
return ids
}
func indices(content string, selectionStartIndex, selectionEndIndex int) (int, int) {
startIndex := selectionStartIndex
numLinesBefore := 0
@ -218,7 +232,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res
}
// PerformSearch perform a search on a repository
func PerformSearch(ctx context.Context, opts *SearchOptions) (int, []*Result, []*SearchResultLanguages, error) {
func PerformSearch(ctx context.Context, opts *SearchOptions) (int, Results, []*SearchResultLanguages, error) {
if opts == nil || len(opts.Keyword) == 0 {
return 0, nil, nil, nil
}

View file

@ -233,21 +233,19 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
}
var keywords []string
if len(options.Tokens) != 0 {
for _, token := range options.Tokens {
if !token.Fuzzy {
// to make it a phrase search, we have to quote the keyword(s)
// https://www.meilisearch.com/docs/reference/api/search#phrase-search
token.Term = doubleQuoteKeyword(token.Term)
}
// internal.BoolOptShould (Default, requires no modifications)
// internal.BoolOptMust (Not supported by meilisearch)
if token.Kind == internal.BoolOptNot {
token.Term = "-" + token.Term
}
keywords = append(keywords, token.Term)
for _, token := range options.Tokens {
if !token.Fuzzy {
// to make it a phrase search, we have to quote the keyword(s)
// https://www.meilisearch.com/docs/reference/api/search#phrase-search
token.Term = doubleQuoteKeyword(token.Term)
}
// internal.BoolOptShould (Default, requires no modifications)
// internal.BoolOptMust (Not supported by meilisearch)
if token.Kind == internal.BoolOptNot {
token.Term = "-" + token.Term
}
keywords = append(keywords, token.Term)
}
searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).

55
routers/common/search.go Normal file
View file

@ -0,0 +1,55 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
code_indexer "forgejo.org/modules/indexer/code"
"forgejo.org/modules/setting"
"forgejo.org/services/context"
)
type CodeSearchOptions struct {
Language, Keyword, Path string
}
// Parses the common code search options from the context
// This functions takes care of the following ctx.Data fields
// - Keyword
// - Language
// - CodeSearchPath
func InitCodeSearchOptions(ctx *context.Context) (opts CodeSearchOptions) {
opts.Language = ctx.FormTrim("l")
opts.Keyword = ctx.FormTrim("q")
opts.Path = ctx.FormTrim("path")
ctx.Data["Keyword"] = opts.Keyword
ctx.Data["Language"] = opts.Language
ctx.Data["CodeSearchPath"] = opts.Path
return opts
}
// Returns the indexer mode to be used by the code indexer
// Also sets the ctx.Data fields "CodeSearchMode" and "CodeSearchOptions"
//
// NOTE:
// This is seperate from `InitCodeSearchOptions`
// since this is specific the indexer and only used
// where git-grep is not available.
func CodeSearchIndexerMode(ctx *context.Context) (mode code_indexer.SearchMode) {
mode = code_indexer.SearchModeExact
if m := ctx.FormTrim("mode"); m == "union" {
mode = code_indexer.SearchModeUnion
} else if m == "fuzzy" || ctx.FormBool("fuzzy") {
if setting.Indexer.RepoIndexerEnableFuzzy {
mode = code_indexer.SearchModeFuzzy
} else {
mode = code_indexer.SearchModeUnion
}
}
ctx.Data["CodeSearchOptions"] = code_indexer.CodeSearchOptions
ctx.Data["CodeSearchMode"] = mode.String()
return mode
}

View file

@ -1,4 +1,5 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package explore
@ -11,6 +12,7 @@ import (
"forgejo.org/modules/base"
code_indexer "forgejo.org/modules/indexer/code"
"forgejo.org/modules/setting"
"forgejo.org/routers/common"
"forgejo.org/services/context"
)
@ -32,29 +34,12 @@ func Code(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true
ctx.Data["PageIsExploreCode"] = true
language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q")
path := ctx.FormTrim("path")
mode := code_indexer.SearchModeExact
if m := ctx.FormTrim("mode"); m == "union" {
mode = code_indexer.SearchModeUnion
} else if m == "fuzzy" || ctx.FormBool("fuzzy") {
if setting.Indexer.RepoIndexerEnableFuzzy {
mode = code_indexer.SearchModeFuzzy
} else {
mode = code_indexer.SearchModeUnion
}
}
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["CodeSearchOptions"] = code_indexer.CodeSearchOptions
ctx.Data["CodeSearchMode"] = mode.String()
ctx.Data["PageIsViewCode"] = true
if keyword == "" {
opts := common.InitCodeSearchOptions(ctx)
mode := common.CodeSearchIndexerMode(ctx)
if opts.Keyword == "" {
ctx.HTML(http.StatusOK, tplExploreCode)
return
}
@ -84,17 +69,17 @@ func Code(ctx *context.Context) {
var (
total int
searchResults []*code_indexer.Result
searchResults code_indexer.Results
searchResultLanguages []*code_indexer.SearchResultLanguages
)
if (len(repoIDs) > 0) || isAdmin {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
RepoIDs: repoIDs,
Keyword: keyword,
Keyword: opts.Keyword,
Mode: mode,
Language: language,
Filename: path,
Language: opts.Language,
Filename: opts.Path,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
@ -110,20 +95,7 @@ func Code(ctx *context.Context) {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
}
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
var find bool
for _, id := range loadRepoIDs {
if id == result.RepoID {
find = true
break
}
}
if !find {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
loadRepoIDs := searchResults.RepoIDs()
repoMaps, err := repo_model.GetRepositoriesMapByIDs(ctx, loadRepoIDs)
if err != nil {
ctx.ServerError("GetRepositoriesMapByIDs", err)

View file

@ -1,4 +1,5 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
@ -12,6 +13,7 @@ import (
"forgejo.org/modules/git"
code_indexer "forgejo.org/modules/indexer/code"
"forgejo.org/modules/setting"
"forgejo.org/routers/common"
"forgejo.org/services/context"
)
@ -62,10 +64,7 @@ func (m searchMode) ToGitGrep() git.GrepMode {
// Search render repository search page
func Search(ctx *context.Context) {
language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q")
path := ctx.FormTrim("path")
opts := common.InitCodeSearchOptions(ctx)
mode := ExactSearchMode
if modeStr := ctx.FormString("mode"); len(modeStr) > 0 {
mode = searchModeFromString(modeStr)
@ -73,9 +72,6 @@ func Search(ctx *context.Context) {
mode = UnionSearchMode
}
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["CodeSearchPath"] = path
ctx.Data["PageIsViewCode"] = true
ctx.Data["CodeIndexerDisabled"] = !setting.Indexer.RepoIndexerEnabled
if setting.Indexer.RepoIndexerEnabled {
@ -84,7 +80,7 @@ func Search(ctx *context.Context) {
ctx.Data["CodeSearchOptions"] = git.GrepSearchOptions
}
if keyword == "" {
if opts.Keyword == "" {
ctx.HTML(http.StatusOK, tplSearch)
return
}
@ -104,10 +100,10 @@ func Search(ctx *context.Context) {
var err error
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
RepoIDs: []int64{ctx.Repo.Repository.ID},
Keyword: keyword,
Keyword: opts.Keyword,
Mode: m,
Language: language,
Filename: path,
Language: opts.Language,
Filename: opts.Path,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
@ -126,10 +122,10 @@ func Search(ctx *context.Context) {
m := mode.ToGitGrep()
ctx.Data["CodeSearchMode"] = m.String()
res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{
res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, opts.Keyword, git.GrepOptions{
ContextLineNumber: 1,
RefName: ctx.Repo.RefName,
Filename: path,
Filename: opts.Path,
Mode: m,
})
if err != nil {

View file

@ -1,4 +1,5 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
@ -11,6 +12,7 @@ import (
"forgejo.org/modules/base"
code_indexer "forgejo.org/modules/indexer/code"
"forgejo.org/modules/setting"
"forgejo.org/routers/common"
shared_user "forgejo.org/routers/web/shared/user"
"forgejo.org/services/context"
)
@ -35,30 +37,13 @@ func CodeSearch(ctx *context.Context) {
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["IsCodePage"] = true
ctx.Data["Title"] = ctx.Tr("explore.code")
language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q")
path := ctx.FormTrim("path")
opts := common.InitCodeSearchOptions(ctx)
mode := common.CodeSearchIndexerMode(ctx)
mode := code_indexer.SearchModeExact
if m := ctx.FormTrim("mode"); m == "union" {
mode = code_indexer.SearchModeUnion
} else if m == "fuzzy" || ctx.FormBool("fuzzy") {
if setting.Indexer.RepoIndexerEnableFuzzy {
mode = code_indexer.SearchModeFuzzy
} else {
mode = code_indexer.SearchModeUnion
}
}
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["CodeSearchOptions"] = code_indexer.CodeSearchOptions
ctx.Data["CodeSearchMode"] = mode.String()
ctx.Data["IsCodePage"] = true
if keyword == "" {
if opts.Keyword == "" {
ctx.HTML(http.StatusOK, tplUserCode)
return
}
@ -81,17 +66,17 @@ func CodeSearch(ctx *context.Context) {
var (
total int
searchResults []*code_indexer.Result
searchResults code_indexer.Results
searchResultLanguages []*code_indexer.SearchResultLanguages
)
if len(repoIDs) > 0 {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
RepoIDs: repoIDs,
Keyword: keyword,
Keyword: opts.Keyword,
Mode: mode,
Language: language,
Filename: path,
Language: opts.Language,
Filename: opts.Path,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
@ -107,20 +92,7 @@ func CodeSearch(ctx *context.Context) {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
}
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
var find bool
for _, id := range loadRepoIDs {
if id == result.RepoID {
find = true
break
}
}
if !find {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
loadRepoIDs := searchResults.RepoIDs()
repoMaps, err := repo_model.GetRepositoriesMapByIDs(ctx, loadRepoIDs)
if err != nil {
ctx.ServerError("GetRepositoriesMapByIDs", err)