Files
Rafail Giavrimisandsilverwind 38389f00be feat: unify code search, add search API (#39403)
Adds GitHub-style code search endpoints and makes code search behave the
same in the web UI and the API, whether it runs on git grep, bleve or
Elasticsearch.

- `GET /repos/{owner}/{repo}/code/search` searches any branch, tag or
commit with git grep, so it works without the code indexer.
- `GET /search/code` searches the default branches of all accessible
repositories with the code indexer.
- One query syntax everywhere: every term must match a file's content or
path, case-insensitively. It supports `"phrases"`, `/regexp/` (git grep
only) and the `path:`, `language:`, `repo:`, `user:` and `org:`
qualifiers. Invalid queries return an error.
- All engines apply the same include, exclude, vendored and size rules,
and return every match. The web UI shows them as multiple snippets per
file with the matched text highlighted.

<img width="359" height="295" alt="image"
src="https://github.com/user-attachments/assets/59b7aa66-b1dc-4938-9ef9-66668ae08d7c"
/>

Fixes: https://github.com/go-gitea/gitea/issues/10706
Fixed: https://github.com/go-gitea/gitea/issues/15841
Fixes: https://github.com/go-gitea/gitea/issues/31375
Related: https://gitea.com/gitea/gitea-mcp/issues/280
Docs: https://gitea.com/gitea/docs/pulls/615

AI usage disclosure: Yes, I used AI to write code, review changes and
verify the factuality of the overview I provided. I take full
responsibility for the code in this PR.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-13 07:24:37 +00:00

45 lines
1.5 KiB
Go

// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package code
import (
"testing"
"gitea.dev/models/db"
"gitea.dev/models/unittest"
"gitea.dev/modules/indexer/code/bleve"
"gitea.dev/modules/indexer/code/internal"
_ "gitea.dev/models"
_ "gitea.dev/models/actions"
_ "gitea.dev/models/activities"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) { unittest.MainTest(m) }
func TestIndexRepository(t *testing.T) {
unittest.PrepareTestEnv(t)
idx := bleve.NewIndexer(t.TempDir())
_, err := idx.Init(t.Context())
require.NoError(t, err)
defer idx.Close()
require.NoError(t, index(t.Context(), idx, 1))
var indexer internal.Indexer = idx
previousIndexer := globalIndexer.Load()
defer globalIndexer.Store(previousIndexer)
globalIndexer.Store(&indexer)
total, results, _, err := PerformSearch(t.Context(), &SearchOptions{Terms: []string{"Description"}, Paginator: &db.ListOptions{Page: 1, PageSize: 1}})
require.NoError(t, err)
assert.Positive(t, total)
require.Len(t, results, 1)
assert.Equal(t, []*ResultFragment{{StartLine: 2, Content: "\nDescription for repo1", Matches: []MatchRange{{Start: 1, End: 12}}}}, BuildResultFragments(results[0].Content, results[0].ContentMatches))
require.NoError(t, idx.Delete(t.Context(), 1))
total, _, _, err = idx.Search(t.Context(), &internal.SearchOptions{Terms: []string{"Description"}, Paginator: &db.ListOptions{Page: 1, PageSize: 1}})
require.NoError(t, err)
assert.Zero(t, total)
}