godoc: add ability to change or disable the display of search results.

The display of search results can now be changed. A slice of functions for displaying the results can now be provided.  By default, three functions are provided to display documentation, source code, and textual results.
This makes it possible to replace them with equivalents that, say,
obtain search results from alternative source code search engines.

R=bradfitz, adg
CC=golang-codereviews
https://golang.org/cl/43470043
This commit is contained in:
Brad Garcia 2014-01-06 09:51:01 -05:00
parent 5f5d42a466
commit a28efa5d8c
9 changed files with 307 additions and 227 deletions

View File

@ -71,6 +71,9 @@ func readTemplates(p *godoc.Presentation, html bool) {
p.GodocHTML = readTemplate("godoc.html")
p.PackageHTML = readTemplate("package.html")
p.SearchHTML = readTemplate("search.html")
p.SearchDocHTML = readTemplate("searchdoc.html")
p.SearchCodeHTML = readTemplate("searchcode.html")
p.SearchTxtHTML = readTemplate("searchtxt.html")
p.SearchDescXML = readTemplate("opensearch.xml")
}
}

View File

@ -13,6 +13,9 @@ import (
"code.google.com/p/go.tools/godoc/vfs/httpfs"
)
// SearchResultFunc functions return an HTML body for displaying search results.
type SearchResultFunc func(p *Presentation, result SearchResult) []byte
// Presentation generates output from a corpus.
type Presentation struct {
Corpus *Corpus
@ -29,6 +32,9 @@ type Presentation struct {
PackageHTML,
PackageText,
SearchHTML,
SearchDocHTML,
SearchCodeHTML,
SearchTxtHTML,
SearchText,
SearchDescXML *template.Template
@ -76,12 +82,18 @@ type Presentation struct {
// the query string highlighted.
URLForSrcQuery func(src, query string, line int) string
// SearchResults optionally specifies a list of functions returning an HTML
// body for displaying search results.
SearchResults []SearchResultFunc
initFuncMapOnce sync.Once
funcMap template.FuncMap
templateFuncs template.FuncMap
}
// NewPresentation returns a new Presentation from a corpus.
// It sets SearchResults to:
// [SearchResultDoc SearchResultCode SearchResultTxt].
func NewPresentation(c *Corpus) *Presentation {
if c == nil {
panic("nil Corpus")
@ -94,6 +106,11 @@ func NewPresentation(c *Corpus) *Presentation {
TabWidth: 4,
ShowExamples: true,
DeclLinks: true,
SearchResults: []SearchResultFunc{
(*Presentation).SearchResultDoc,
(*Presentation).SearchResultCode,
(*Presentation).SearchResultTxt,
},
}
p.cmdHandler = handlerServer{p, c, "/cmd/", "/src/cmd"}
p.pkgHandler = handlerServer{p, c, "/pkg/", "/src/pkg"}

View File

@ -5,6 +5,7 @@
package godoc
import (
"bytes"
"fmt"
"log"
"net/http"
@ -75,6 +76,26 @@ func (c *Corpus) Lookup(query string) SearchResult {
return *result
}
// SearchResultDoc optionally specifies a function returning an HTML body
// displaying search results matching godoc documentation.
func (p *Presentation) SearchResultDoc(result SearchResult) []byte {
return applyTemplate(p.SearchDocHTML, "searchDocHTML", result)
}
// SearchResultCode optionally specifies a function returning an HTML body
// displaying search results matching source code.
func (p *Presentation) SearchResultCode(result SearchResult) []byte {
return applyTemplate(p.SearchCodeHTML, "searchCodeHTML", result)
}
// SearchResultTxt optionally specifies a function returning an HTML body
// displaying search results of textual matches.
func (p *Presentation) SearchResultTxt(result SearchResult) []byte {
return applyTemplate(p.SearchTxtHTML, "searchTxtHTML", result)
}
// HandleSearch obtains results for the requested search and returns a page
// to display them.
func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
query := strings.TrimSpace(r.FormValue("q"))
result := p.Corpus.Lookup(query)
@ -83,28 +104,29 @@ func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
p.ServeText(w, applyTemplate(p.SearchText, "searchText", result))
return
}
haveResults := result.Hit != nil || len(result.Textual) > 0
if !haveResults {
for _, ir := range result.Idents {
if ir != nil {
haveResults = true
break
}
}
contents := bytes.Buffer{}
for _, f := range p.SearchResults {
contents.Write(f(p, result))
}
var title string
if haveResults {
if haveResults := contents.Len() > 0; haveResults {
title = fmt.Sprintf(`Results for query %q`, query)
if !p.Corpus.IndexEnabled {
result.Alert = ""
}
} else {
title = fmt.Sprintf(`No results found for query %q`, query)
}
body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result))
body.Write(contents.Bytes())
p.ServePage(w, Page{
Title: title,
Tabtitle: query,
Query: query,
Body: applyTemplate(p.SearchHTML, "searchHTML", result),
Body: body.Bytes(),
})
}

View File

@ -21,6 +21,9 @@ STATIC="
playground.js
search.html
search.txt
searchcode.html
searchdoc.html
searchtxt.html
style.css
"

View File

@ -3,7 +3,6 @@
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{$query_url := urlquery .Query}}
{{with .Alert}}
<p>
<span class="alert" style="font-size:120%">{{html .}}</span>
@ -17,110 +16,3 @@
{{end}}
</p>
{{end}}
{{with .Pak}}
<h2 id="Packages">Package {{html $.Query}}</h2>
<p>
<table class="layout">
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<tr><td><a href="/{{$pkg_html}}">{{$pkg_html}}</a></td></tr>
{{end}}
</table>
</p>
{{end}}
{{range $key, $val := .Idents}}
{{if $val}}
<h2 id="Global">{{$key.Name}}</h2>
{{range $val}}
{{$pkg_html := pkgLink .Path | html}}
{{$doc_html := docLink .Path .Name| html}}
<a href="/{{$pkg_html}}">{{html .Package}}</a>.<a href="{{$doc_html}}">{{.Name}}</a>
{{if .Doc}}
<p>{{comment_html .Doc}}</p>
{{else}}
<p><em>No documentation available</em></p>
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Hit}}
{{with .Decls}}
<h2 id="Global">Package-level declarations</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Global_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
{{range .Groups}}
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$file}}:{{$line}}</a>
{{infoSnippet_html .}}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Others}}
<h2 id="Local">Local declarations and uses</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Local_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
<a href="{{queryLink $file $query_url 0 | html}}">{{$file}}</a>
<table class="layout">
{{range .Groups}}
<tr>
<td width="25"></td>
<th align="left" valign="top">{{index . 0 | infoKind_html}}</th>
<td align="left" width="4"></td>
<td>
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$line}}</a>
{{end}}
</td>
</tr>
{{end}}
</table>
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Textual}}
{{if $.Complete}}
<h2 id="Textual">{{html $.Found}} textual occurrences</h2>
{{else}}
<h2 id="Textual">More than {{html $.Found}} textual occurrences</h2>
<p>
<span class="alert" style="font-size:120%">Not all files or lines containing "{{html $.Query}}" are shown.</span>
</p>
{{end}}
<p>
<table class="layout">
{{range .}}
{{$file := .Filename}}
<tr>
<td align="left" valign="top">
<a href="{{queryLink $file $query_url 0}}">{{$file}}</a>:
</td>
<td align="left" width="4"></td>
<th align="left" valign="top">{{len .Lines}}</th>
<td align="left" width="4"></td>
<td align="left">
{{range .Lines}}
<a href="{{queryLink $file $query_url .}}">{{html .}}</a>
{{end}}
{{if not $.Complete}}
...
{{end}}
</td>
</tr>
{{end}}
{{if not $.Complete}}
<tr><td align="left">...</td></tr>
{{end}}
</table>
</p>
{{end}}

View File

@ -0,0 +1,51 @@
<!--
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{$query_url := urlquery .Query}}
{{with .Hit}}
{{with .Decls}}
<h2 id="Global">Package-level declarations</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Global_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
{{range .Groups}}
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$file}}:{{$line}}</a>
{{infoSnippet_html .}}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Others}}
<h2 id="Local">Local declarations and uses</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Local_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
<a href="{{queryLink $file $query_url 0 | html}}">{{$file}}</a>
<table class="layout">
{{range .Groups}}
<tr>
<td width="25"></td>
<th align="left" valign="top">{{index . 0 | infoKind_html}}</th>
<td align="left" width="4"></td>
<td>
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$line}}</a>
{{end}}
</td>
</tr>
{{end}}
</table>
{{end}}
{{end}}
{{end}}
{{end}}

View File

@ -0,0 +1,31 @@
<!--
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{with .Pak}}
<h2 id="Packages">Package {{html $.Query}}</h2>
<p>
<table class="layout">
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<tr><td><a href="/{{$pkg_html}}">{{$pkg_html}}</a></td></tr>
{{end}}
</table>
</p>
{{end}}
{{range $key, $val := .Idents}}
{{if $val}}
<h2 id="Global">{{$key.Name}}</h2>
{{range $val}}
{{$pkg_html := pkgLink .Path | html}}
{{$doc_html := docLink .Path .Name| html}}
<a href="/{{$pkg_html}}">{{html .Package}}</a>.<a href="{{$doc_html}}">{{.Name}}</a>
{{if .Doc}}
<p>{{comment_html .Doc}}</p>
{{else}}
<p><em>No documentation available</em></p>
{{end}}
{{end}}
{{end}}
{{end}}

View File

@ -0,0 +1,42 @@
<!--
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{$query_url := urlquery .Query}}
{{with .Textual}}
{{if $.Complete}}
<h2 id="Textual">{{html $.Found}} textual occurrences</h2>
{{else}}
<h2 id="Textual">More than {{html $.Found}} textual occurrences</h2>
<p>
<span class="alert" style="font-size:120%">Not all files or lines containing "{{html $.Query}}" are shown.</span>
</p>
{{end}}
<p>
<table class="layout">
{{range .}}
{{$file := .Filename}}
<tr>
<td align="left" valign="top">
<a href="{{queryLink $file $query_url 0}}">{{$file}}</a>:
</td>
<td align="left" width="4"></td>
<th align="left" valign="top">{{len .Lines}}</th>
<td align="left" width="4"></td>
<td align="left">
{{range .Lines}}
<a href="{{queryLink $file $query_url .}}">{{html .}}</a>
{{end}}
{{if not $.Complete}}
...
{{end}}
</td>
</tr>
{{end}}
{{if not $.Complete}}
<tr><td align="left">...</td></tr>
{{end}}
</table>
</p>
{{end}}

View File

@ -1372,7 +1372,6 @@ function PlaygroundOutput(el) {
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{$query_url := urlquery .Query}}
{{with .Alert}}
<p>
<span class="alert" style="font-size:120%">{{html .}}</span>
@ -1386,113 +1385,6 @@ function PlaygroundOutput(el) {
{{end}}
</p>
{{end}}
{{with .Pak}}
<h2 id="Packages">Package {{html $.Query}}</h2>
<p>
<table class="layout">
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<tr><td><a href="/{{$pkg_html}}">{{$pkg_html}}</a></td></tr>
{{end}}
</table>
</p>
{{end}}
{{range $key, $val := .Idents}}
{{if $val}}
<h2 id="Global">{{$key.Name}}</h2>
{{range $val}}
{{$pkg_html := pkgLink .Path | html}}
{{$doc_html := docLink .Path .Name| html}}
<a href="/{{$pkg_html}}">{{html .Package}}</a>.<a href="{{$doc_html}}">{{.Name}}</a>
{{if .Doc}}
<p>{{comment_html .Doc}}</p>
{{else}}
<p><em>No documentation available</em></p>
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Hit}}
{{with .Decls}}
<h2 id="Global">Package-level declarations</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Global_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
{{range .Groups}}
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$file}}:{{$line}}</a>
{{infoSnippet_html .}}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Others}}
<h2 id="Local">Local declarations and uses</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Local_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
<a href="{{queryLink $file $query_url 0 | html}}">{{$file}}</a>
<table class="layout">
{{range .Groups}}
<tr>
<td width="25"></td>
<th align="left" valign="top">{{index . 0 | infoKind_html}}</th>
<td align="left" width="4"></td>
<td>
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$line}}</a>
{{end}}
</td>
</tr>
{{end}}
</table>
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Textual}}
{{if $.Complete}}
<h2 id="Textual">{{html $.Found}} textual occurrences</h2>
{{else}}
<h2 id="Textual">More than {{html $.Found}} textual occurrences</h2>
<p>
<span class="alert" style="font-size:120%">Not all files or lines containing "{{html $.Query}}" are shown.</span>
</p>
{{end}}
<p>
<table class="layout">
{{range .}}
{{$file := .Filename}}
<tr>
<td align="left" valign="top">
<a href="{{queryLink $file $query_url 0}}">{{$file}}</a>:
</td>
<td align="left" width="4"></td>
<th align="left" valign="top">{{len .Lines}}</th>
<td align="left" width="4"></td>
<td align="left">
{{range .Lines}}
<a href="{{queryLink $file $query_url .}}">{{html .}}</a>
{{end}}
{{if not $.Complete}}
...
{{end}}
</td>
</tr>
{{end}}
{{if not $.Complete}}
<tr><td align="left">...</td></tr>
{{end}}
</table>
</p>
{{end}}
`,
"search.txt": `QUERY
{{.Query}}
@ -1548,6 +1440,133 @@ function PlaygroundOutput(el) {
{{range .Textual}}{{len .Lines}} {{srcLink .Filename}}
{{end}}{{if not .Complete}}... ...
{{end}}{{end}}
`,
"searchcode.html": `<!--
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{$query_url := urlquery .Query}}
{{with .Hit}}
{{with .Decls}}
<h2 id="Global">Package-level declarations</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Global_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
{{range .Groups}}
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$file}}:{{$line}}</a>
{{infoSnippet_html .}}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Others}}
<h2 id="Local">Local declarations and uses</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Local_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
<a href="{{queryLink $file $query_url 0 | html}}">{{$file}}</a>
<table class="layout">
{{range .Groups}}
<tr>
<td width="25"></td>
<th align="left" valign="top">{{index . 0 | infoKind_html}}</th>
<td align="left" width="4"></td>
<td>
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$line}}</a>
{{end}}
</td>
</tr>
{{end}}
</table>
{{end}}
{{end}}
{{end}}
{{end}}
`,
"searchdoc.html": `<!--
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{with .Pak}}
<h2 id="Packages">Package {{html $.Query}}</h2>
<p>
<table class="layout">
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<tr><td><a href="/{{$pkg_html}}">{{$pkg_html}}</a></td></tr>
{{end}}
</table>
</p>
{{end}}
{{range $key, $val := .Idents}}
{{if $val}}
<h2 id="Global">{{$key.Name}}</h2>
{{range $val}}
{{$pkg_html := pkgLink .Path | html}}
{{$doc_html := docLink .Path .Name| html}}
<a href="/{{$pkg_html}}">{{html .Package}}</a>.<a href="{{$doc_html}}">{{.Name}}</a>
{{if .Doc}}
<p>{{comment_html .Doc}}</p>
{{else}}
<p><em>No documentation available</em></p>
{{end}}
{{end}}
{{end}}
{{end}}
`,
"searchtxt.html": `<!--
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{$query_url := urlquery .Query}}
{{with .Textual}}
{{if $.Complete}}
<h2 id="Textual">{{html $.Found}} textual occurrences</h2>
{{else}}
<h2 id="Textual">More than {{html $.Found}} textual occurrences</h2>
<p>
<span class="alert" style="font-size:120%">Not all files or lines containing "{{html $.Query}}" are shown.</span>
</p>
{{end}}
<p>
<table class="layout">
{{range .}}
{{$file := .Filename}}
<tr>
<td align="left" valign="top">
<a href="{{queryLink $file $query_url 0}}">{{$file}}</a>:
</td>
<td align="left" width="4"></td>
<th align="left" valign="top">{{len .Lines}}</th>
<td align="left" width="4"></td>
<td align="left">
{{range .Lines}}
<a href="{{queryLink $file $query_url .}}">{{html .}}</a>
{{end}}
{{if not $.Complete}}
...
{{end}}
</td>
</tr>
{{end}}
{{if not $.Complete}}
<tr><td align="left">...</td></tr>
{{end}}
</table>
</p>
{{end}}
`,
"style.css": `body {
margin: 0;