1
0
mirror of https://github.com/golang/go synced 2024-11-18 09:04:49 -07:00

godoc: more cleanup

cmd/godoc/godoc.go is now merged into main.go, which is now
only 530 lines.

App Engine mode is still broken, but should be easy to fix up.
(just needs a global *godoc.Presentation created in init)

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/11498044
This commit is contained in:
Brad Fitzpatrick 2013-07-18 13:51:17 +10:00
parent 5395cfe05a
commit 66f0d6e92e
10 changed files with 370 additions and 353 deletions

View File

@ -20,6 +20,7 @@ import (
"log"
"net/http"
"os"
pathpkg "path"
"regexp"
"sort"
"strconv"
@ -31,6 +32,8 @@ import (
"code.google.com/p/go.tools/godoc/vfs"
)
var codewalkHTML, codewalkdirHTML *template.Template
// Handler for /doc/codewalk/ and below.
func codewalk(w http.ResponseWriter, r *http.Request) {
relpath := r.URL.Path[len("/doc/codewalk/"):]
@ -78,6 +81,20 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
})
}
func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
canonical := pathpkg.Clean(r.URL.Path)
if !strings.HasSuffix(canonical, "/") {
canonical += "/"
}
if r.URL.Path != canonical {
url := *r.URL
url.Path = canonical
http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
redirected = true
}
return
}
// A Codewalk represents a single codewalk read from an XML file.
type Codewalk struct {
Title string `xml:"title,attr"`

View File

@ -1,250 +0,0 @@
// 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.
package main
import (
"bytes"
"flag"
"fmt"
"log"
"net/http"
"net/url"
pathpkg "path"
"regexp"
"runtime"
"strings"
"text/template"
"code.google.com/p/go.tools/godoc"
"code.google.com/p/go.tools/godoc/vfs"
)
var (
verbose = flag.Bool("v", false, "verbose mode")
// file system roots
// TODO(gri) consider the invariant that goroot always end in '/'
goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
// layout control
tabWidth = flag.Int("tabwidth", 4, "tab width")
showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
templateDir = flag.String("templates", "", "directory containing alternate template files")
showPlayground = flag.Bool("play", false, "enable playground in web interface")
showExamples = flag.Bool("ex", false, "show examples in command line mode")
declLinks = flag.Bool("links", true, "link identifiers to their declarations")
// search index
indexEnabled = flag.Bool("index", false, "enable search index")
indexFiles = flag.String("index_files", "", "glob pattern specifying index files;"+
"if not empty, the index is read from these files in sorted order")
maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle")
// source code notes
notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show")
)
var (
pres *godoc.Presentation
fs = vfs.NameSpace{}
)
func registerPublicHandlers(mux *http.ServeMux) {
if pres == nil {
panic("nil Presentation")
}
godoc.CmdHandler.RegisterWithMux(mux)
godoc.PkgHandler.RegisterWithMux(mux)
mux.HandleFunc("/doc/codewalk/", codewalk)
mux.Handle("/doc/play/", godoc.FileServer)
mux.HandleFunc("/search", search)
mux.Handle("/robots.txt", godoc.FileServer)
mux.HandleFunc("/opensearch.xml", serveSearchDesc)
mux.HandleFunc("/", pres.ServeFile)
}
// ----------------------------------------------------------------------------
// Templates
func readTemplate(name string) *template.Template {
if pres == nil {
panic("no global Presentation set yet")
}
path := "lib/godoc/" + name
// use underlying file system fs to read the template file
// (cannot use template ParseFile functions directly)
data, err := vfs.ReadFile(fs, path)
if err != nil {
log.Fatal("readTemplate: ", err)
}
// be explicit with errors (for app engine use)
t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data))
if err != nil {
log.Fatal("readTemplate: ", err)
}
return t
}
var codewalkHTML, codewalkdirHTML *template.Template
func readTemplates() {
// have to delay until after flags processing since paths depend on goroot
codewalkHTML = readTemplate("codewalk.html")
codewalkdirHTML = readTemplate("codewalkdir.html")
godoc.DirlistHTML = readTemplate("dirlist.html")
godoc.ErrorHTML = readTemplate("error.html")
godoc.ExampleHTML = readTemplate("example.html")
godoc.GodocHTML = readTemplate("godoc.html")
godoc.PackageHTML = readTemplate("package.html")
godoc.PackageText = readTemplate("package.txt")
godoc.SearchHTML = readTemplate("search.html")
godoc.SearchText = readTemplate("search.txt")
godoc.SearchDescXML = readTemplate("opensearch.xml")
}
// ----------------------------------------------------------------------------
// Files
func applyTemplate(t *template.Template, name string, data interface{}) []byte {
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
log.Printf("%s.Execute: %s", name, err)
}
return buf.Bytes()
}
func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
canonical := pathpkg.Clean(r.URL.Path)
if !strings.HasSuffix(canonical, "/") {
canonical += "/"
}
if r.URL.Path != canonical {
url := *r.URL
url.Path = canonical
http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
redirected = true
}
return
}
func serveSearchDesc(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/opensearchdescription+xml")
data := map[string]interface{}{
"BaseURL": fmt.Sprintf("http://%s", r.Host),
}
if err := godoc.SearchDescXML.Execute(w, &data); err != nil && err != http.ErrBodyNotAllowed {
// Only log if there's an error that's not about writing on HEAD requests.
// See Issues 5451 and 5454.
log.Printf("searchDescXML.Execute: %s", err)
}
}
// ----------------------------------------------------------------------------
// Packages
// remoteSearchURL returns the search URL for a given query as needed by
// remoteSearch. If html is set, an html result is requested; otherwise
// the result is in textual form.
// Adjust this function as necessary if modeNames or FormValue parameters
// change.
func remoteSearchURL(query string, html bool) string {
s := "/search?m=text&q="
if html {
s = "/search?q="
}
return s + url.QueryEscape(query)
}
// ----------------------------------------------------------------------------
// Search
type SearchResult struct {
Query string
Alert string // error or warning message
// identifier matches
Pak godoc.HitList // packages matching Query
Hit *godoc.LookupResult // identifier matches of Query
Alt *godoc.AltWords // alternative identifiers to look for
// textual matches
Found int // number of textual occurrences found
Textual []godoc.FileLines // textual matches of Query
Complete bool // true if all textual occurrences of Query are reported
}
func lookup(query string) (result SearchResult) {
result.Query = query
corp := pres.Corpus
index, timestamp := corp.CurrentIndex()
if index != nil {
// identifier search
var err error
result.Pak, result.Hit, result.Alt, err = index.Lookup(query)
if err != nil && corp.MaxResults <= 0 {
// ignore the error if full text search is enabled
// since the query may be a valid regular expression
result.Alert = "Error in query string: " + err.Error()
return
}
// full text search
if corp.MaxResults > 0 && query != "" {
rx, err := regexp.Compile(query)
if err != nil {
result.Alert = "Error in query regular expression: " + err.Error()
return
}
// If we get maxResults+1 results we know that there are more than
// maxResults results and thus the result may be incomplete (to be
// precise, we should remove one result from the result set, but
// nobody is going to count the results on the result page).
result.Found, result.Textual = index.LookupRegexp(rx, corp.MaxResults+1)
result.Complete = result.Found <= corp.MaxResults
if !result.Complete {
result.Found-- // since we looked for maxResults+1
}
}
}
// is the result accurate?
if pres.Corpus.IndexEnabled {
if ts := pres.Corpus.FSModifiedTime(); timestamp.Before(ts) {
// The index is older than the latest file system change under godoc's observation.
result.Alert = "Indexing in progress: result may be inaccurate"
}
} else {
result.Alert = "Search index disabled: no results available"
}
return
}
func search(w http.ResponseWriter, r *http.Request) {
query := strings.TrimSpace(r.FormValue("q"))
result := lookup(query)
if godoc.GetPageInfoMode(r)&godoc.NoHTML != 0 {
pres.ServeText(w, applyTemplate(godoc.SearchText, "searchText", result))
return
}
var title string
if result.Hit != nil || len(result.Textual) > 0 {
title = fmt.Sprintf(`Results for query %q`, query)
} else {
title = fmt.Sprintf(`No results found for query %q`, query)
}
pres.ServePage(w, godoc.Page{
Title: title,
Tabtitle: query,
Query: query,
Body: applyTemplate(godoc.SearchHTML, "searchHTML", result),
})
}

View File

@ -30,14 +30,12 @@ package main
import (
"archive/zip"
"bytes"
"errors"
_ "expvar" // to serve /debug/vars
"flag"
"fmt"
"go/ast"
"go/build"
"go/printer"
"io"
"log"
"net/http"
"net/http/httptest"
@ -49,6 +47,7 @@ import (
"regexp"
"runtime"
"strings"
"text/template"
"code.google.com/p/go.tools/godoc"
"code.google.com/p/go.tools/godoc/vfs"
@ -76,8 +75,115 @@ var (
// command-line searches
query = flag.Bool("q", false, "arguments are considered search queries")
verbose = flag.Bool("v", false, "verbose mode")
// file system roots
// TODO(gri) consider the invariant that goroot always end in '/'
goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
// layout control
tabWidth = flag.Int("tabwidth", 4, "tab width")
showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
templateDir = flag.String("templates", "", "directory containing alternate template files")
showPlayground = flag.Bool("play", false, "enable playground in web interface")
showExamples = flag.Bool("ex", false, "show examples in command line mode")
declLinks = flag.Bool("links", true, "link identifiers to their declarations")
// search index
indexEnabled = flag.Bool("index", false, "enable search index")
indexFiles = flag.String("index_files", "", "glob pattern specifying index files;"+
"if not empty, the index is read from these files in sorted order")
maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle")
// source code notes
notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show")
)
var (
pres *godoc.Presentation
fs = vfs.NameSpace{}
)
func registerPublicHandlers(mux *http.ServeMux) {
if pres == nil {
panic("nil Presentation")
}
godoc.CmdHandler.RegisterWithMux(mux)
godoc.PkgHandler.RegisterWithMux(mux)
mux.HandleFunc("/doc/codewalk/", codewalk)
mux.Handle("/doc/play/", godoc.FileServer)
mux.HandleFunc("/search", pres.HandleSearch)
mux.Handle("/robots.txt", godoc.FileServer)
mux.HandleFunc("/opensearch.xml", serveSearchDesc)
mux.HandleFunc("/", pres.ServeFile)
}
// ----------------------------------------------------------------------------
// Templates
func readTemplate(name string) *template.Template {
if pres == nil {
panic("no global Presentation set yet")
}
path := "lib/godoc/" + name
// use underlying file system fs to read the template file
// (cannot use template ParseFile functions directly)
data, err := vfs.ReadFile(fs, path)
if err != nil {
log.Fatal("readTemplate: ", err)
}
// be explicit with errors (for app engine use)
t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data))
if err != nil {
log.Fatal("readTemplate: ", err)
}
return t
}
func readTemplates(p *godoc.Presentation) {
// have to delay until after flags processing since paths depend on goroot
codewalkHTML = readTemplate("codewalk.html")
codewalkdirHTML = readTemplate("codewalkdir.html")
p.DirlistHTML = readTemplate("dirlist.html")
p.ErrorHTML = readTemplate("error.html")
p.ExampleHTML = readTemplate("example.html")
p.GodocHTML = readTemplate("godoc.html")
p.PackageHTML = readTemplate("package.html")
p.PackageText = readTemplate("package.txt")
p.SearchHTML = readTemplate("search.html")
p.SearchText = readTemplate("search.txt")
p.SearchDescXML = readTemplate("opensearch.xml")
}
// ----------------------------------------------------------------------------
// Files
func applyTemplate(t *template.Template, name string, data interface{}) []byte {
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
log.Printf("%s.Execute: %s", name, err)
}
return buf.Bytes()
}
func serveSearchDesc(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/opensearchdescription+xml")
data := map[string]interface{}{
"BaseURL": fmt.Sprintf("http://%s", r.Host),
}
if err := pres.SearchDescXML.Execute(w, &data); err != nil && err != http.ErrBodyNotAllowed {
// Only log if there's an error that's not about writing on HEAD requests.
// See Issues 5451 and 5454.
log.Printf("searchDescXML.Execute: %s", err)
}
}
// ----------------------------------------------------------------------------
// Packages
func usage() {
fmt.Fprintf(os.Stderr,
"usage: godoc package [name ...]\n"+
@ -93,36 +199,6 @@ func loggingHandler(h http.Handler) http.Handler {
})
}
func remoteSearch(query string) (res *http.Response, err error) {
// list of addresses to try
var addrs []string
if *serverAddr != "" {
// explicit server address - only try this one
addrs = []string{*serverAddr}
} else {
addrs = []string{
defaultAddr,
"golang.org",
}
}
// remote search
search := remoteSearchURL(query, *html)
for _, addr := range addrs {
url := "http://" + addr + search
res, err = http.Get(url)
if err == nil && res.StatusCode == http.StatusOK {
break
}
}
if err == nil && res.StatusCode != http.StatusOK {
err = errors.New(res.Status)
}
return
}
// Does s look like a regular expression?
func isRegexp(s string) bool {
return strings.IndexAny(s, ".(|)*+?^$[]") >= 0
@ -149,6 +225,44 @@ func makeRx(names []string) (rx *regexp.Regexp) {
return
}
func handleURLFlag() {
registerPublicHandlers(http.DefaultServeMux)
// Try up to 10 fetches, following redirects.
urlstr := *urlFlag
for i := 0; i < 10; i++ {
// Prepare request.
u, err := url.Parse(urlstr)
if err != nil {
log.Fatal(err)
}
req := &http.Request{
URL: u,
}
// Invoke default HTTP handler to serve request
// to our buffering httpWriter.
w := httptest.NewRecorder()
http.DefaultServeMux.ServeHTTP(w, req)
// Return data, error, or follow redirect.
switch w.Code {
case 200: // ok
os.Stdout.Write(w.Body.Bytes())
return
case 301, 302, 303, 307: // redirect
redirect := w.HeaderMap.Get("Location")
if redirect == "" {
log.Fatalf("HTTP %d without Location header", w.Code)
}
urlstr = redirect
default:
log.Fatalf("HTTP error %d", w.Code)
}
}
log.Fatalf("too many redirects")
}
func main() {
flag.Usage = usage
flag.Parse()
@ -175,7 +289,7 @@ func main() {
log.Fatalf("%s: %s\n", *zipfile, err)
}
defer rc.Close() // be nice (e.g., -writeIndex mode)
fs.Bind("/", zipvfs.New(rc, *zipfile), *goroot, vfs.BindReplace)
fs.Bind("/", zipfs.New(rc, *zipfile), *goroot, vfs.BindReplace)
}
// Bind $GOPATH trees into Go root.
@ -186,6 +300,12 @@ func main() {
corpus := godoc.NewCorpus(fs)
corpus.IndexEnabled = *indexEnabled
corpus.IndexFiles = *indexFiles
if *writeIndex {
corpus.IndexThrottle = 1.0
}
if err := corpus.Init(); err != nil {
log.Fatal(err)
}
pres = godoc.NewPresentation(corpus)
pres.TabWidth = *tabWidth
@ -193,8 +313,11 @@ func main() {
pres.ShowPlayground = *showPlayground
pres.ShowExamples = *showExamples
pres.DeclLinks = *declLinks
if *notesRx != "" {
pres.NotesRx = regexp.MustCompile(*notesRx)
}
readTemplates()
readTemplates(pres)
godoc.InitHandlers(pres)
@ -207,7 +330,6 @@ func main() {
log.Println("initialize file systems")
*verbose = true // want to see what happens
corpus.IndexThrottle = 1.0
corpus.UpdateIndex()
log.Println("writing index file", *indexFiles)
@ -227,41 +349,8 @@ func main() {
// Print content that would be served at the URL *urlFlag.
if *urlFlag != "" {
registerPublicHandlers(http.DefaultServeMux)
// Try up to 10 fetches, following redirects.
urlstr := *urlFlag
for i := 0; i < 10; i++ {
// Prepare request.
u, err := url.Parse(urlstr)
if err != nil {
log.Fatal(err)
}
req := &http.Request{
URL: u,
}
// Invoke default HTTP handler to serve request
// to our buffering httpWriter.
w := httptest.NewRecorder()
http.DefaultServeMux.ServeHTTP(w, req)
// Return data, error, or follow redirect.
switch w.Code {
case 200: // ok
os.Stdout.Write(w.Body.Bytes())
return
case 301, 302, 303, 307: // redirect
redirect := w.HeaderMap.Get("Location")
if redirect == "" {
log.Fatalf("HTTP %d without Location header", w.Code)
}
urlstr = redirect
default:
log.Fatalf("HTTP error %d", w.Code)
}
}
log.Fatalf("too many redirects")
handleURLFlag()
return
}
if *httpAddr != "" {
@ -300,22 +389,15 @@ func main() {
return
}
packageText := godoc.PackageText
packageText := pres.PackageText
// Command line mode.
if *html {
packageText = godoc.PackageHTML
packageText = pres.PackageHTML
}
if *query {
// Command-line queries.
for i := 0; i < flag.NArg(); i++ {
res, err := remoteSearch(flag.Arg(i))
if err != nil {
log.Fatalf("remoteSearch: %s", err)
}
io.Copy(os.Stdout, res.Body)
}
handleRemoteSearch()
return
}

70
cmd/godoc/remotesearch.go Normal file
View File

@ -0,0 +1,70 @@
// 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.
package main
import (
"errors"
"flag"
"io"
"log"
"net/http"
"net/url"
"os"
)
func handleRemoteSearch() {
// Command-line queries.
for i := 0; i < flag.NArg(); i++ {
res, err := remoteSearch(flag.Arg(i))
if err != nil {
log.Fatalf("remoteSearch: %s", err)
}
io.Copy(os.Stdout, res.Body)
}
return
}
// remoteSearchURL returns the search URL for a given query as needed by
// remoteSearch. If html is set, an html result is requested; otherwise
// the result is in textual form.
// Adjust this function as necessary if modeNames or FormValue parameters
// change.
func remoteSearchURL(query string, html bool) string {
s := "/search?m=text&q="
if html {
s = "/search?q="
}
return s + url.QueryEscape(query)
}
func remoteSearch(query string) (res *http.Response, err error) {
// list of addresses to try
var addrs []string
if *serverAddr != "" {
// explicit server address - only try this one
addrs = []string{*serverAddr}
} else {
addrs = []string{
defaultAddr,
"golang.org",
}
}
// remote search
search := remoteSearchURL(query, *html)
for _, addr := range addrs {
url := "http://" + addr + search
res, err = http.Get(url)
if err == nil && res.StatusCode == http.StatusOK {
break
}
}
if err == nil && res.StatusCode != http.StatusOK {
err = errors.New(res.Status)
}
return
}

View File

@ -375,12 +375,12 @@ func (p *Presentation) example_htmlFunc(info *PageInfo, funcName string) string
out = ""
}
if ExampleHTML == nil {
if p.ExampleHTML == nil {
out = ""
return ""
}
err := ExampleHTML.Execute(&buf, struct {
err := p.ExampleHTML.Execute(&buf, struct {
Name, Doc, Code, Play, Output string
}{eg.Name, eg.Doc, code, play, out})
if err != nil {

View File

@ -8,7 +8,6 @@ import (
"log"
"net/http"
"runtime"
"text/template"
)
// Page describes the contents of the top-level godoc webpage.
@ -25,18 +24,6 @@ type Page struct {
Version string
}
var (
DirlistHTML,
ErrorHTML,
ExampleHTML,
GodocHTML,
PackageHTML,
PackageText,
SearchHTML,
SearchText,
SearchDescXML *template.Template
)
func (p *Presentation) ServePage(w http.ResponseWriter, page Page) {
if page.Tabtitle == "" {
page.Tabtitle = page.Title
@ -44,7 +31,7 @@ func (p *Presentation) ServePage(w http.ResponseWriter, page Page) {
page.SearchBox = p.Corpus.IndexEnabled
page.Playground = p.ShowPlayground
page.Version = runtime.Version()
if err := GodocHTML.Execute(w, page); err != nil && err != http.ErrBodyNotAllowed {
if err := p.GodocHTML.Execute(w, page); err != nil && err != http.ErrBodyNotAllowed {
// Only log if there's an error that's not about writing on HEAD requests.
// See Issues 5451 and 5454.
log.Printf("GodocHTML.Execute: %s", err)
@ -56,6 +43,6 @@ func (p *Presentation) ServeError(w http.ResponseWriter, r *http.Request, relpat
p.ServePage(w, Page{
Title: "File " + relpath,
Subtitle: relpath,
Body: applyTemplate(ErrorHTML, "errorHTML", err), // err may contain an absolute path!
Body: applyTemplate(p.ErrorHTML, "errorHTML", err), // err may contain an absolute path!
})
}

View File

@ -14,6 +14,16 @@ import (
type Presentation struct {
Corpus *Corpus
DirlistHTML,
ErrorHTML,
ExampleHTML,
GodocHTML,
PackageHTML,
PackageText,
SearchHTML,
SearchText,
SearchDescXML *template.Template
// TabWidth optionally specifies the tab width.
TabWidth int
@ -22,6 +32,8 @@ type Presentation struct {
ShowExamples bool
DeclLinks bool
// NotesRx optionally specifies a regexp to match
// notes to render in the output.
NotesRx *regexp.Regexp
initFuncMapOnce sync.Once

99
godoc/search.go Normal file
View File

@ -0,0 +1,99 @@
// 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.
package godoc
import (
"fmt"
"net/http"
"regexp"
"strings"
)
type SearchResult struct {
Query string
Alert string // error or warning message
// identifier matches
Pak HitList // packages matching Query
Hit *LookupResult // identifier matches of Query
Alt *AltWords // alternative identifiers to look for
// textual matches
Found int // number of textual occurrences found
Textual []FileLines // textual matches of Query
Complete bool // true if all textual occurrences of Query are reported
}
func (c *Corpus) Lookup(query string) SearchResult {
var result SearchResult
result.Query = query
index, timestamp := c.CurrentIndex()
if index != nil {
// identifier search
var err error
result.Pak, result.Hit, result.Alt, err = index.Lookup(query)
if err != nil && c.MaxResults <= 0 {
// ignore the error if full text search is enabled
// since the query may be a valid regular expression
result.Alert = "Error in query string: " + err.Error()
return result
}
// full text search
if c.MaxResults > 0 && query != "" {
rx, err := regexp.Compile(query)
if err != nil {
result.Alert = "Error in query regular expression: " + err.Error()
return result
}
// If we get maxResults+1 results we know that there are more than
// maxResults results and thus the result may be incomplete (to be
// precise, we should remove one result from the result set, but
// nobody is going to count the results on the result page).
result.Found, result.Textual = index.LookupRegexp(rx, c.MaxResults+1)
result.Complete = result.Found <= c.MaxResults
if !result.Complete {
result.Found-- // since we looked for maxResults+1
}
}
}
// is the result accurate?
if c.IndexEnabled {
if ts := c.FSModifiedTime(); timestamp.Before(ts) {
// The index is older than the latest file system change under godoc's observation.
result.Alert = "Indexing in progress: result may be inaccurate"
}
} else {
result.Alert = "Search index disabled: no results available"
}
return result
}
func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
query := strings.TrimSpace(r.FormValue("q"))
result := p.Corpus.Lookup(query)
if GetPageInfoMode(r)&NoHTML != 0 {
p.ServeText(w, applyTemplate(p.SearchText, "searchText", result))
return
}
var title string
if result.Hit != nil || len(result.Textual) > 0 {
title = fmt.Sprintf(`Results for query %q`, query)
} else {
title = fmt.Sprintf(`No results found for query %q`, query)
}
p.ServePage(w, Page{
Title: title,
Tabtitle: query,
Query: query,
Body: applyTemplate(p.SearchHTML, "searchHTML", result),
})
}

View File

@ -213,7 +213,7 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
if mode&NoHTML != 0 {
h.p.ServeText(w, applyTemplate(PackageText, "packageText", info))
h.p.ServeText(w, applyTemplate(h.p.PackageText, "packageText", info))
return
}
@ -253,7 +253,7 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Title: title,
Tabtitle: tabtitle,
Subtitle: subtitle,
Body: applyTemplate(PackageHTML, "packageHTML", info),
Body: applyTemplate(h.p.PackageHTML, "packageHTML", info),
})
}
@ -463,7 +463,7 @@ func (p *Presentation) serveDirectory(w http.ResponseWriter, r *http.Request, ab
p.ServePage(w, Page{
Title: "Directory " + relpath,
Tabtitle: relpath,
Body: applyTemplate(DirlistHTML, "dirlistHTML", list),
Body: applyTemplate(p.DirlistHTML, "dirlistHTML", list),
})
}
@ -587,12 +587,12 @@ func (p *Presentation) serveFile(w http.ResponseWriter, r *http.Request) {
FileServer.ServeHTTP(w, r)
}
func serveSearchDesc(w http.ResponseWriter, r *http.Request) {
func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/opensearchdescription+xml")
data := map[string]interface{}{
"BaseURL": fmt.Sprintf("http://%s", r.Host),
}
if err := SearchDescXML.Execute(w, &data); err != nil && err != http.ErrBodyNotAllowed {
if err := p.SearchDescXML.Execute(w, &data); err != nil && err != http.ErrBodyNotAllowed {
// Only log if there's an error that's not about writing on HEAD requests.
// See Issues 5451 and 5454.
log.Printf("searchDescXML.Execute: %s", err)

View File

@ -15,7 +15,7 @@
// like absolute paths w/o a leading '/'; i.e., the paths are considered
// relative to the root of the file system.
// - All path arguments to file system methods must be absolute paths.
package zipvfs
package zipfs
import (
"archive/zip"