package state import ( "errors" "net/http" "net/url" "tangled.org/core/appview/codesearch" "tangled.org/core/appview/db" "tangled.org/core/appview/models" "tangled.org/core/appview/pages" "tangled.org/core/appview/pagination" "tangled.org/core/orm" ) func (s *State) CodeSearch(w http.ResponseWriter, r *http.Request) { q := r.URL.Query().Get("q") ctx := r.Context() page := pagination.FromContext(ctx) render := func(results []pages.CodeSearchResult, hasMore bool) { if err := s.pages.CodeSearch(w, pages.CodeSearchParams{ BaseParams: pages.BaseParamsFromContext(ctx), FilterQuery: q, Results: results, Page: page, HasMore: hasMore, }); err != nil { s.logger.Error("failed to render code search", "err", err) } } if q == "" { render(nil, false) return } // 1. perform content search res, err := s.codesearch.Search(ctx, q, page) if err != nil { // repo-name-only queries belong to the repo search page. if errors.Is(err, codesearch.ErrRepoOnlyQuery) { http.Redirect(w, r, "/search?q="+url.QueryEscape(q), http.StatusFound) return } s.logger.Error("code search failed", "err", err, "query", q) render(nil, false) return } results := res.Results // 2. resolve matched repos by their DID byDID := map[string]*models.Repo{} var dids []string for _, res := range results { did := res.RepoDID.String() if did == "" { continue } if _, ok := byDID[did]; !ok { byDID[did] = nil dids = append(dids, did) } } if len(dids) > 0 { repos, err := db.GetRepos(s.db, orm.FilterIn("repo_did", dids)) if err != nil { s.logger.Error("failed to load repos for code search", "err", err) } else { for i := range repos { byDID[repos[i].RepoDid] = &repos[i] } } } // 3. convert to page results out := make([]pages.CodeSearchResult, len(results)) for i, res := range results { csr := pages.CodeSearchResult{ RepoDID: res.RepoDID, Repo: byDID[res.RepoDID.String()], } if c := res.Chunk; c != nil { csr.Chunk = &pages.CodeSearchResult_Chunk{ FilePath: c.FilePath, Branches: c.Branches, Commit: c.Commit, Language: c.Language, Lines: pages.ChunkLines(c.Content, c.ContentStartLine, c.Ranges), } } if f := res.File; f != nil { csr.File = &pages.CodeSearchResult_File{ FilePath: f.FilePath, Branches: f.Branches, Commit: f.Commit, Language: f.Language, NameSpans: pages.FileNameSpans(f.FilePath, f.Ranges), } } out[i] = csr } render(out, res.HasMore) }