mirror of https://github.com/go-gitea/gitea
Add user/organization code search (#19977)
Fixes #19925 Screenshots: ![attels](https://user-images.githubusercontent.com/165205/173864718-fe789429-55bc-4cad-808c-9f02f335cddf.png)pull/21404/head^2
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