Monorepo for Tangled
tangled.org
1package repo
2
3import (
4 "fmt"
5 "net/http"
6 "slices"
7
8 "github.com/bluesky-social/indigo/atproto/syntax"
9 "tangled.org/core/appview/models"
10 "tangled.org/core/appview/pages"
11 "tangled.org/core/appview/pagination"
12)
13
14func (rp *Repo) Search(w http.ResponseWriter, r *http.Request) {
15 if r.Header.Get("Hx-Request") == "true" {
16 rp.searchResultsFragment(w, r)
17 return
18 }
19 if err := rp.pages.RepoSearchPage(w, pages.RepoSearchParams{
20 BaseParams: pages.BaseParamsFromContext(r.Context()),
21 RepoInfo: rp.repoResolver.GetRepoInfo(r, rp.oauth.GetMultiAccountUser(r)),
22 FilterQuery: "",
23 }); err != nil {
24 rp.logger.Error("failed to render", "err", err)
25 }
26}
27
28func (rp *Repo) searchResultsFragment(w http.ResponseWriter, r *http.Request) {
29 l := rp.logger.With("handler", "Search")
30 repo, err := rp.repoResolver.Resolve(r)
31 if err != nil {
32 l.Error("failed to get repo", "err", err)
33 return
34 }
35 q := r.URL.Query().Get("q")
36
37 var params pages.RepoSearchResultsFragmentParams
38 defer func() {
39 if err := rp.pages.RepoSearchResultsFragment(w, params); err != nil {
40 l.Error("failed to render", "err", err)
41 }
42 }()
43
44 if q == "" {
45 return
46 }
47
48 q = fmt.Sprintf(`meta.did:%s %q`, repo.RepoDid, q)
49
50 ctx := r.Context()
51
52 res, err := rp.codesearch.Search(ctx, q, pagination.Page{Limit: 50})
53 if err != nil {
54 l.Error("failed to search files", "err", err)
55 params.ErrorMsg = "Failed to perform search. Please try again later."
56 return
57 }
58
59 if len(res.Results) == 0 {
60 return
61 }
62
63 out := make([]pages.SearchResult, 0, len(res.Results))
64 for _, res := range res.Results {
65 if res.RepoDID != syntax.DID(repo.RepoDid) {
66 continue
67 }
68 csr := pages.SearchResult{
69 RepoDID: res.RepoDID,
70 Repo: repo,
71 FilePath: res.FilePath,
72 Branches: res.Branches,
73 Commit: res.Commit,
74 Language: res.Language,
75 }
76 if f := res.File; f != nil {
77 csr.File = &pages.CodeSearchResult_File{
78 NameSpans: pages.FileNameSpans(res.FilePath, f.Ranges),
79 }
80 }
81 slices.SortStableFunc(res.Chunks, func(a, b models.Result_ChunkMatch) int {
82 return a.ContentStartLine - b.ContentStartLine
83 })
84 for _, c := range res.Chunks {
85 csr.Chunks = append(csr.Chunks, pages.CodeSearchResult_Chunk{
86 Lines: pages.ChunkLines(c.Content, c.ContentStartLine, c.Ranges),
87 MatchCount: len(c.Ranges),
88 })
89 }
90 out = append(out, csr)
91 }
92
93 l.Debug("repo file search result", "len", len(out), "duration", res.Stats.Duration)
94 params.Results = out
95}