Monorepo for Tangled
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / state / codesearch.go
3.1 kB 117 lines
1package state 2 3import ( 4 "errors" 5 "net/http" 6 "net/url" 7 "slices" 8 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 "tangled.org/core/appview/codesearch" 11 "tangled.org/core/appview/db" 12 "tangled.org/core/appview/models" 13 "tangled.org/core/appview/pages" 14 "tangled.org/core/appview/pagination" 15 "tangled.org/core/orm" 16) 17 18func (s *State) handleCodeSearch(w http.ResponseWriter, r *http.Request) { 19 l := s.logger.With("handler", "CodeSearch") 20 ctx := r.Context() 21 page := pagination.FromContext(ctx) 22 q := r.URL.Query().Get("q") 23 24 var redirected bool 25 var params pages.CodeSearchParams 26 params.BaseParams = pages.BaseParamsFromContext(ctx) 27 params.FilterQuery = q 28 params.Page = page 29 defer func() { 30 if redirected { 31 return 32 } 33 if err := s.pages.CodeSearch(w, params); err != nil { 34 l.Error("failed to render code search", "err", err) 35 } 36 }() 37 38 if q == "" { 39 return 40 } 41 42 if params.BaseParams.LoggedInUser == nil { 43 return 44 } 45 46 res, err := s.codesearch.Search(ctx, q, page) 47 if err != nil { 48 // repo-name-only queries belong to the repo search page; redirect with 49 // the rewritten query (repo: prefix dropped, lang: kept). 50 var repoErr *codesearch.RepoOnlyError 51 if errors.As(err, &repoErr) { 52 redirected = true 53 http.Redirect(w, r, "/search?q="+url.QueryEscape(repoErr.Query), http.StatusFound) 54 return 55 } 56 l.Error("code search failed", "err", err, "query", q) 57 params.ErrorMsg = "Failed to perform search. Please try again later." 58 return 59 } 60 results := res.Results 61 62 repoMap := map[syntax.DID]*models.Repo{} 63 var repoDids []string 64 for _, res := range results { 65 if res.RepoDID == "" { 66 continue 67 } 68 if _, ok := repoMap[res.RepoDID]; !ok { 69 repoMap[res.RepoDID] = nil 70 repoDids = append(repoDids, res.RepoDID.String()) 71 } 72 } 73 if len(repoDids) > 0 { 74 repos, err := db.GetRepos(s.db, orm.FilterIn("repo_did", repoDids)) 75 if err != nil { 76 l.Error("failed to load repos for code search", "err", err) 77 params.ErrorMsg = "Failed to load repos for code search. Please try again later." 78 return 79 } 80 for i := range repos { 81 repoMap[syntax.DID(repos[i].RepoDid)] = &repos[i] 82 } 83 } 84 85 out := make([]pages.SearchResult, 0, len(results)) 86 for _, res := range results { 87 csr := pages.SearchResult{ 88 RepoDID: res.RepoDID, 89 Repo: repoMap[res.RepoDID], 90 FilePath: res.FilePath, 91 Branches: res.Branches, 92 Commit: res.Commit, 93 Language: res.Language, 94 } 95 if f := res.File; f != nil { 96 csr.File = &pages.CodeSearchResult_File{ 97 NameSpans: pages.FileNameSpans(res.FilePath, f.Ranges), 98 } 99 } 100 slices.SortStableFunc(res.Chunks, func(a, b models.Result_ChunkMatch) int { 101 return a.ContentStartLine - b.ContentStartLine 102 }) 103 for _, c := range res.Chunks { 104 csr.Chunks = append(csr.Chunks, pages.CodeSearchResult_Chunk{ 105 Lines: pages.ChunkLines(c.Content, c.ContentStartLine, c.Ranges), 106 MatchCount: len(c.Ranges), 107 }) 108 } 109 out = append(out, csr) 110 } 111 112 params.Results = out 113 params.HasMore = res.HasMore 114 params.MatchCount = res.Stats.MatchCount 115 params.FileCount = res.Stats.FileCount 116 params.TimeTaken = res.Stats.Duration 117}