Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sidebar with GUI filters to the search results page #103

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions web/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ type Repository struct {

// PrintInput is provided to the server.Print template.
type PrintInput struct {
Repo, Name string
Lines []string
Last LastInput
Repo, Name string
Lines []string
Last LastInput
FileMatches []*FileMatch
}
28 changes: 28 additions & 0 deletions web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,28 @@ var Funcmap = template.FuncMap{
"More": func(orig int) int {
return orig * 3
},
"IsLangFilterChecked": func(query, lang string) bool {
return strings.Contains(query, "lang:"+lang)
},
"IsRepoFilterChecked": func(query, repo string) bool {
return strings.Contains(query, "r:"+repo)
},
"Repos": func(fileMatches []*FileMatch) map[string]int {
repos := make(map[string]int)
for _, fileMatch := range fileMatches {
repos[fileMatch.Repo]++
}

return repos
},
"Languages": func(fileMatches []*FileMatch) map[string]int {
languages := make(map[string]int)
for _, fileMatch := range fileMatches {
languages[fileMatch.Language]++
}

return languages
},
"HumanUnit": func(orig int64) string {
b := orig
suffix := ""
Expand Down Expand Up @@ -542,6 +564,11 @@ func (s *Server) servePrintErr(w http.ResponseWriter, r *http.Request) error {
strLines = append(strLines, string(l))
}

fileMatches, err := s.formatResults(result, queryStr, s.Print)
if err != nil {
return err
}

d := PrintInput{
Name: f.FileName,
Repo: f.Repository,
Expand All @@ -551,6 +578,7 @@ func (s *Server) servePrintErr(w http.ResponseWriter, r *http.Request) error {
Num: num,
AutoFocus: false,
},
FileMatches: fileMatches,
}

var buf bytes.Buffer
Expand Down
164 changes: 118 additions & 46 deletions web/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ var TemplateText = map[string]string{
padding: unset;
overflow: unset;
}

.info-count {
margin-bottom: 0.5rem;
display: flex;
justify-content: space-between;
}

:target { background-color: #ccf; }
table tbody tr td { border: none !important; padding: 2px !important; }
</style>
Expand Down Expand Up @@ -122,15 +129,61 @@ var TemplateText = map[string]string{
<input class="form-control"
placeholder="Search for some code..." role="search"
id="navsearchbox" type="text" name="q" autofocus
{{if .Query}}
value={{.Query}}
{{if .Last.Query}}
value={{.Last.Query}}
{{end}}>
<div class="input-group">
<div class="input-group-addon">Max Results</div>
<input class="form-control" type="number" id="maxhits" name="num" value="{{.Num}}">
<input class="form-control" type="number" id="maxhits" name="num" value="{{.Last.Num}}">
</div>
<button class="btn btn-primary">Search</button>
</div>
<div class="input-group dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Language
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
{{range $key, $value := Languages .FileMatches}}
<li><a>
<div class="info-count">
<div>
{{if IsLangFilterChecked $.Last.Query $key}}
<input type="checkbox" checked onclick="addFilter('lang:{{$key}}')"/>
{{else}}
<input type="checkbox" onclick="addFilter('lang:{{$key}}')"/>
{{end}}
{{$key}}
</div>
<div class="badge badge-pill">{{$value}}</div>
</div>
</a></li>
{{end}}
</ul>
</div>
<div class="input-group dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Repo
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
{{range $key, $value := Repos .FileMatches}}
<li><a>
<div class="info-count">
<div>
{{if IsRepoFilterChecked $.Last.Query $key}}
<input type="checkbox" checked onclick="addFilter('r:{{$key}}')"/>
{{else}}
<input type="checkbox" onclick="addFilter('r:{{$key}}')"/>
{{end}}
{{$key}}
</div>
<div class="badge badge-pill">{{$value}}</div>
</div>
</a></li>
{{end}}
</ul>
</div>
<button class="btn btn-primary">Search</button>
</form>
</div>
</div>
Expand Down Expand Up @@ -205,49 +258,70 @@ var TemplateText = map[string]string{
window.location.href = "/search?q=" + escape("{{.QueryStr}}" + " " + atom) +
"&" + "num=" + {{.Last.Num}};
}

function addFilter(filter) {
var searchBox = document.getElementById("navsearchbox");
var search = document.querySelector(".btn.btn-primary");

var oldQuery = searchBox.value.trim();

if (oldQuery.includes(filter)) {
searchBox.value = oldQuery.replace(filter, '');
search.click();
return;
}

searchBox.value = (oldQuery + ' ' + filter).trim();
search.click();
}

</script>
</style>
<body id="results">
{{template "navbar" .Last}}
<div class="container-fluid container-results">
<h5>
{{if .Stats.Crashes}}<br><b>{{.Stats.Crashes}} shards crashed</b><br>{{end}}
{{ $fileCount := len .FileMatches }}
Found {{.Stats.MatchCount}} results in {{.Stats.FileCount}} files{{if or (lt $fileCount .Stats.FileCount) (or (gt .Stats.ShardsSkipped 0) (gt .Stats.FilesSkipped 0)) }},
showing top {{ $fileCount }} files (<a rel="nofollow"
href="search?q={{.Last.Query}}&num={{More .Last.Num}}">show more</a>).
{{else}}.{{end}}
</h5>
{{range .FileMatches}}
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>
{{if .URL}}<a name="{{.ResultID}}" class="result"></a><a href="{{.URL}}" >{{else}}<a name="{{.ResultID}}">{{end}}
<small>
{{.Repo}}:{{.FileName}}</a>:
<span style="font-weight: normal">[ {{if .Branches}}{{range .Branches}}<span class="label label-default">{{.}}</span>,{{end}}{{end}} ]</span>
{{if .Language}}<button
title="restrict search to files written in {{.Language}}"
onclick="zoektAddQ('lang:{{.Language}}')" class="label label-primary">language {{.Language}}</button></span>{{end}}
{{if .DuplicateID}}<a class="label label-dup" href="#{{.DuplicateID}}">Duplicate result</a>{{end}}
</small>
</th>
</tr>
</thead>
{{if not .DuplicateID}}
<tbody>
{{range .Matches}}
<tr>
<td style="background-color: rgba(238, 238, 255, 0.6);">
<pre class="inline-pre"><span class="noselect">{{if .URL}}<a href="{{.URL}}">{{end}}<u>{{.LineNum}}</u>{{if .URL}}</a>{{end}}: </span>{{range .Fragments}}{{LimitPre 100 .Pre}}<b>{{.Match}}</b>{{LimitPost 100 .Post}}{{end}}</pre>
</td>
</tr>
{{template "navbar" .}}
<div class="search-main">
<div class="container-fluid container-results">
<h5>
{{if .Stats.Crashes}}<br><b>{{.Stats.Crashes}} shards crashed</b><br>{{end}}
{{ $fileCount := len .FileMatches }}
Found {{.Stats.MatchCount}} results in {{.Stats.FileCount}} files{{if or (lt $fileCount .Stats.FileCount) (or (gt .Stats.ShardsSkipped 0) (gt .Stats.FilesSkipped 0)) }},
showing top {{ $fileCount }} files (<a rel="nofollow"
href="search?q={{.Last.Query}}&num={{More .Last.Num}}">show more</a>).
{{else}}.{{end}}
</h5>
{{range .FileMatches}}
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>
{{if .URL}}<a name="{{.ResultID}}" class="result"></a><a href="{{.URL}}" >{{else}}<a name="{{.ResultID}}">{{end}}
<small>
{{.Repo}}:{{.FileName}}</a>:
<span style="font-weight: normal">[ {{if .Branches}}{{range .Branches}}<span class="label label-default">{{.}}</span>,{{end}}{{end}} ]</span>
{{if .Language}}<button
title="restrict search to files written in {{.Language}}"
onclick="zoektAddQ('lang:{{.Language}}')" class="label label-primary">language {{.Language}}</button></span>{{end}}
{{if .DuplicateID}}<a class="label label-dup" href="#{{.DuplicateID}}">Duplicate result</a>{{end}}
</small>
</th>
</tr>
</thead>
{{if not .DuplicateID}}
<tbody>
{{range .Matches}}
<tr>
<td style="background-color: rgba(238, 238, 255, 0.6);">
<pre class="inline-pre"><span class="noselect">{{if .URL}}<a href="{{.URL}}">{{end}}<u>{{.LineNum}}</u>{{if .URL}}</a>{{end}}: </span>{{range .Fragments}}{{LimitPre 100 .Pre}}<b>{{.Match}}</b>{{LimitPost 100 .Post}}{{end}}</pre>
</td>
</tr>
{{end}}
</tbody>
{{end}}
</tbody>
</table>
{{end}}
</table>
{{end}}

</div>
</div>
<nav class="navbar navbar-default navbar-bottom">
<div class="container">
{{template "footerBoilerplate"}}
Expand All @@ -262,18 +336,16 @@ var TemplateText = map[string]string{
</p>
</div>
</nav>
</div>
{{ template "jsdep"}}
</body>
</html>
`,

"repolist": `
<html>
{{template "head"}}
<body id="results">
<div class="container">
{{template "navbar" .Last}}
{{template "navbar" .}}
<div><b>
Found {{.Stats.Repos}} repositories ({{.Stats.Documents}} files, {{HumanUnit .Stats.ContentBytes}}b content)
</b></div>
Expand Down Expand Up @@ -321,7 +393,7 @@ var TemplateText = map[string]string{
{{template "head"}}
<title>{{.Repo}}:{{.Name}}</title>
<body id="results">
{{template "navbar" .Last}}
{{template "navbar" .}}
<div class="container-fluid container-results" >
<div class="table table-hover table-condensed" style="overflow:auto; background: #eef;">
{{ range $index, $ln := .Lines}}
Expand Down