Add user/organization code search (#19977)
Fixes #19925 Screenshots: ![attels](https://user-images.githubusercontent.com/165205/173864718-fe789429-55bc-4cad-808c-9f02f335cddf.png)tokarchuk/v1.18
parent
3ccebf7f40
commit
b59b0cad0a
@ -0,0 +1,114 @@ |
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package user |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/http" |
||||||
|
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo" |
||||||
|
"code.gitea.io/gitea/modules/base" |
||||||
|
"code.gitea.io/gitea/modules/context" |
||||||
|
code_indexer "code.gitea.io/gitea/modules/indexer/code" |
||||||
|
"code.gitea.io/gitea/modules/setting" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
tplUserCode base.TplName = "user/code" |
||||||
|
) |
||||||
|
|
||||||
|
// CodeSearch render user/organization code search page
|
||||||
|
func CodeSearch(ctx *context.Context) { |
||||||
|
if !setting.Indexer.RepoIndexerEnabled { |
||||||
|
ctx.Redirect(ctx.ContextUser.HomeLink()) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled |
||||||
|
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled |
||||||
|
ctx.Data["Title"] = ctx.Tr("code.title") |
||||||
|
ctx.Data["ContextUser"] = ctx.ContextUser |
||||||
|
|
||||||
|
language := ctx.FormTrim("l") |
||||||
|
keyword := ctx.FormTrim("q") |
||||||
|
|
||||||
|
queryType := ctx.FormTrim("t") |
||||||
|
isMatch := queryType == "match" |
||||||
|
|
||||||
|
ctx.Data["Keyword"] = keyword |
||||||
|
ctx.Data["Language"] = language |
||||||
|
ctx.Data["queryType"] = queryType |
||||||
|
ctx.Data["IsCodePage"] = true |
||||||
|
|
||||||
|
if keyword == "" { |
||||||
|
ctx.HTML(http.StatusOK, tplUserCode) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
repoIDs []int64 |
||||||
|
err error |
||||||
|
) |
||||||
|
|
||||||
|
page := ctx.FormInt("page") |
||||||
|
if page <= 0 { |
||||||
|
page = 1 |
||||||
|
} |
||||||
|
|
||||||
|
repoIDs, err = repo_model.FindUserCodeAccessibleOwnerRepoIDs(ctx, ctx.ContextUser.ID, ctx.Doer) |
||||||
|
if err != nil { |
||||||
|
ctx.ServerError("FindUserCodeAccessibleOwnerRepoIDs", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
total int |
||||||
|
searchResults []*code_indexer.Result |
||||||
|
searchResultLanguages []*code_indexer.SearchResultLanguages |
||||||
|
) |
||||||
|
|
||||||
|
if len(repoIDs) > 0 { |
||||||
|
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) |
||||||
|
if err != nil { |
||||||
|
if code_indexer.IsAvailable() { |
||||||
|
ctx.ServerError("SearchResults", err) |
||||||
|
return |
||||||
|
} |
||||||
|
ctx.Data["CodeIndexerUnavailable"] = true |
||||||
|
} else { |
||||||
|
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable() |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs) |
||||||
|
if err != nil { |
||||||
|
ctx.ServerError("GetRepositoriesMapByIDs", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
ctx.Data["RepoMaps"] = repoMaps |
||||||
|
} |
||||||
|
ctx.Data["SearchResults"] = searchResults |
||||||
|
ctx.Data["SearchResultLanguages"] = searchResultLanguages |
||||||
|
|
||||||
|
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) |
||||||
|
pager.SetDefaultParams(ctx) |
||||||
|
pager.AddParam(ctx, "l", "Language") |
||||||
|
ctx.Data["Page"] = pager |
||||||
|
|
||||||
|
ctx.HTML(http.StatusOK, tplUserCode) |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
<form class="ui form ignore-dirty" style="max-width: 100%"> |
||||||
|
<div class="ui fluid action input"> |
||||||
|
<input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable }} disabled{{end}} placeholder="{{.locale.Tr "explore.search"}}…" autofocus> |
||||||
|
<div class="ui dropdown selection tooltip{{if .CodeIndexerUnavailable }} disabled{{end}}" data-content="{{.locale.Tr "explore.search.type.tooltip"}}"> |
||||||
|
<input name="t" type="hidden" value="{{.queryType}}"{{if .CodeIndexerUnavailable }} disabled{{end}}>{{svg "octicon-triangle-down" 14 "dropdown icon"}} |
||||||
|
<div class="text">{{.locale.Tr (printf "explore.search.%s" (or .queryType "fuzzy"))}}</div> |
||||||
|
<div class="menu transition hidden" tabindex="-1" style="display: block !important;"> |
||||||
|
<div class="item tooltip" data-value="" data-content="{{.locale.Tr "explore.search.fuzzy.tooltip"}}">{{.locale.Tr "explore.search.fuzzy"}}</div> |
||||||
|
<div class="item tooltip" data-value="match" data-content="{{.locale.Tr "explore.search.match.tooltip"}}">{{.locale.Tr "explore.search.match"}}</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<button class="ui primary button"{{if .CodeIndexerUnavailable }} disabled{{end}}>{{.locale.Tr "explore.search"}}</button> |
||||||
|
</div> |
||||||
|
</form> |
@ -0,0 +1,43 @@ |
|||||||
|
<div class="df ac fw"> |
||||||
|
{{range $term := .SearchResultLanguages}} |
||||||
|
<a class="ui text-label df ac mr-1 my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{AppSubUrl}}{{if $.ContextUser}}/{{$.ContextUser.Name}}/-/code{{else}}/explore/code{{end}}?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}"> |
||||||
|
<i class="color-icon mr-3" style="background-color: {{$term.Color}}"></i> |
||||||
|
{{$term.Language}} |
||||||
|
<div class="detail">{{$term.Count}}</div> |
||||||
|
</a> |
||||||
|
{{end}} |
||||||
|
</div> |
||||||
|
<div class="repository search"> |
||||||
|
{{range $result := .SearchResults}} |
||||||
|
{{$repo := (index $.RepoMaps .RepoID)}} |
||||||
|
<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result"> |
||||||
|
<h4 class="ui top attached normal header"> |
||||||
|
<span class="file"> |
||||||
|
<a rel="nofollow" href="{{$repo.HTMLURL}}">{{$repo.FullName}}</a> |
||||||
|
{{if $repo.IsArchived}} |
||||||
|
<span class="ui basic label">{{$.locale.Tr "repo.desc.archived"}}</span> |
||||||
|
{{end}} |
||||||
|
- {{.Filename}} |
||||||
|
</span> |
||||||
|
<a class="ui basic tiny button" rel="nofollow" href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{.Filename | PathEscapeSegments}}">{{$.locale.Tr "repo.diff.view_file"}}</a> |
||||||
|
</h4> |
||||||
|
<div class="ui attached table segment"> |
||||||
|
<div class="file-body file-code code-view"> |
||||||
|
<table> |
||||||
|
<tbody> |
||||||
|
<tr> |
||||||
|
<td class="lines-num"> |
||||||
|
{{range .LineNumbers}} |
||||||
|
<a href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{$result.Filename | PathEscapeSegments}}#L{{.}}"><span>{{.}}</span></a> |
||||||
|
{{end}} |
||||||
|
</td> |
||||||
|
<td class="lines-code chroma"><code class="code-inner">{{.FormattedLines | Safe}}</code></td> |
||||||
|
</tr> |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{{template "shared/searchbottom" dict "root" $ "result" .}} |
||||||
|
</div> |
||||||
|
{{end}} |
||||||
|
</div> |
@ -0,0 +1,25 @@ |
|||||||
|
{{template "base/head" .}} |
||||||
|
<div class="page-content repository code-search"> |
||||||
|
{{template "user/overview/header" .}} |
||||||
|
<div class="ui container"> |
||||||
|
{{template "code/searchform" .}} |
||||||
|
<div class="ui divider"></div> |
||||||
|
<div class="ui user list"> |
||||||
|
{{if .CodeIndexerUnavailable }} |
||||||
|
<div class="ui error message"> |
||||||
|
<p>{{$.locale.Tr "explore.code_search_unavailable"}}</p> |
||||||
|
</div> |
||||||
|
{{else if .SearchResults}} |
||||||
|
<h3> |
||||||
|
{{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html }} |
||||||
|
</h3> |
||||||
|
{{template "code/searchresults" .}} |
||||||
|
{{else if .Keyword}} |
||||||
|
<div>{{$.locale.Tr "explore.code_no_results"}}</div> |
||||||
|
{{end}} |
||||||
|
</div> |
||||||
|
|
||||||
|
{{template "base/paginate" .}} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{{template "base/footer" .}} |
Loading…
Reference in new issue