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:
parent
5395cfe05a
commit
66f0d6e92e
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
pathpkg "path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -31,6 +32,8 @@ import (
|
||||||
"code.google.com/p/go.tools/godoc/vfs"
|
"code.google.com/p/go.tools/godoc/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var codewalkHTML, codewalkdirHTML *template.Template
|
||||||
|
|
||||||
// Handler for /doc/codewalk/ and below.
|
// Handler for /doc/codewalk/ and below.
|
||||||
func codewalk(w http.ResponseWriter, r *http.Request) {
|
func codewalk(w http.ResponseWriter, r *http.Request) {
|
||||||
relpath := r.URL.Path[len("/doc/codewalk/"):]
|
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.
|
// A Codewalk represents a single codewalk read from an XML file.
|
||||||
type Codewalk struct {
|
type Codewalk struct {
|
||||||
Title string `xml:"title,attr"`
|
Title string `xml:"title,attr"`
|
||||||
|
|
|
||||||
|
|
@ -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),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -30,14 +30,12 @@ package main
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
_ "expvar" // to serve /debug/vars
|
_ "expvar" // to serve /debug/vars
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
"go/build"
|
||||||
"go/printer"
|
"go/printer"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
|
@ -49,6 +47,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"code.google.com/p/go.tools/godoc"
|
"code.google.com/p/go.tools/godoc"
|
||||||
"code.google.com/p/go.tools/godoc/vfs"
|
"code.google.com/p/go.tools/godoc/vfs"
|
||||||
|
|
@ -76,8 +75,115 @@ var (
|
||||||
|
|
||||||
// command-line searches
|
// command-line searches
|
||||||
query = flag.Bool("q", false, "arguments are considered search queries")
|
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() {
|
func usage() {
|
||||||
fmt.Fprintf(os.Stderr,
|
fmt.Fprintf(os.Stderr,
|
||||||
"usage: godoc package [name ...]\n"+
|
"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?
|
// Does s look like a regular expression?
|
||||||
func isRegexp(s string) bool {
|
func isRegexp(s string) bool {
|
||||||
return strings.IndexAny(s, ".(|)*+?^$[]") >= 0
|
return strings.IndexAny(s, ".(|)*+?^$[]") >= 0
|
||||||
|
|
@ -149,84 +225,7 @@ func makeRx(names []string) (rx *regexp.Regexp) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func handleURLFlag() {
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
// Check usage: either server and no args, command line and args, or index creation mode
|
|
||||||
if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex {
|
|
||||||
usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine file system to use.
|
|
||||||
// TODO(gri) - fs and fsHttp should really be the same. Try to unify.
|
|
||||||
// - fsHttp doesn't need to be set up in command-line mode,
|
|
||||||
// same is true for the http handlers in initHandlers.
|
|
||||||
if *zipfile == "" {
|
|
||||||
// use file system of underlying OS
|
|
||||||
fs.Bind("/", vfs.OS(*goroot), "/", vfs.BindReplace)
|
|
||||||
if *templateDir != "" {
|
|
||||||
fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// use file system specified via .zip file (path separator must be '/')
|
|
||||||
rc, err := zip.OpenReader(*zipfile)
|
|
||||||
if err != nil {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind $GOPATH trees into Go root.
|
|
||||||
for _, p := range filepath.SplitList(build.Default.GOPATH) {
|
|
||||||
fs.Bind("/src/pkg", vfs.OS(p), "/src", vfs.BindAfter)
|
|
||||||
}
|
|
||||||
|
|
||||||
corpus := godoc.NewCorpus(fs)
|
|
||||||
corpus.IndexEnabled = *indexEnabled
|
|
||||||
corpus.IndexFiles = *indexFiles
|
|
||||||
|
|
||||||
pres = godoc.NewPresentation(corpus)
|
|
||||||
pres.TabWidth = *tabWidth
|
|
||||||
pres.ShowTimestamps = *showTimestamps
|
|
||||||
pres.ShowPlayground = *showPlayground
|
|
||||||
pres.ShowExamples = *showExamples
|
|
||||||
pres.DeclLinks = *declLinks
|
|
||||||
|
|
||||||
readTemplates()
|
|
||||||
|
|
||||||
godoc.InitHandlers(pres)
|
|
||||||
|
|
||||||
if *writeIndex {
|
|
||||||
// Write search index and exit.
|
|
||||||
if *indexFiles == "" {
|
|
||||||
log.Fatal("no index file specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("initialize file systems")
|
|
||||||
*verbose = true // want to see what happens
|
|
||||||
|
|
||||||
corpus.IndexThrottle = 1.0
|
|
||||||
corpus.UpdateIndex()
|
|
||||||
|
|
||||||
log.Println("writing index file", *indexFiles)
|
|
||||||
f, err := os.Create(*indexFiles)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
index, _ := corpus.CurrentIndex()
|
|
||||||
err = index.Write(f)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("done")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print content that would be served at the URL *urlFlag.
|
|
||||||
if *urlFlag != "" {
|
|
||||||
registerPublicHandlers(http.DefaultServeMux)
|
registerPublicHandlers(http.DefaultServeMux)
|
||||||
|
|
||||||
// Try up to 10 fetches, following redirects.
|
// Try up to 10 fetches, following redirects.
|
||||||
|
|
@ -264,6 +263,96 @@ func main() {
|
||||||
log.Fatalf("too many redirects")
|
log.Fatalf("too many redirects")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = usage
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Check usage: either server and no args, command line and args, or index creation mode
|
||||||
|
if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex {
|
||||||
|
usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine file system to use.
|
||||||
|
// TODO(gri) - fs and fsHttp should really be the same. Try to unify.
|
||||||
|
// - fsHttp doesn't need to be set up in command-line mode,
|
||||||
|
// same is true for the http handlers in initHandlers.
|
||||||
|
if *zipfile == "" {
|
||||||
|
// use file system of underlying OS
|
||||||
|
fs.Bind("/", vfs.OS(*goroot), "/", vfs.BindReplace)
|
||||||
|
if *templateDir != "" {
|
||||||
|
fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// use file system specified via .zip file (path separator must be '/')
|
||||||
|
rc, err := zip.OpenReader(*zipfile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("%s: %s\n", *zipfile, err)
|
||||||
|
}
|
||||||
|
defer rc.Close() // be nice (e.g., -writeIndex mode)
|
||||||
|
fs.Bind("/", zipfs.New(rc, *zipfile), *goroot, vfs.BindReplace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind $GOPATH trees into Go root.
|
||||||
|
for _, p := range filepath.SplitList(build.Default.GOPATH) {
|
||||||
|
fs.Bind("/src/pkg", vfs.OS(p), "/src", vfs.BindAfter)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
pres.ShowTimestamps = *showTimestamps
|
||||||
|
pres.ShowPlayground = *showPlayground
|
||||||
|
pres.ShowExamples = *showExamples
|
||||||
|
pres.DeclLinks = *declLinks
|
||||||
|
if *notesRx != "" {
|
||||||
|
pres.NotesRx = regexp.MustCompile(*notesRx)
|
||||||
|
}
|
||||||
|
|
||||||
|
readTemplates(pres)
|
||||||
|
|
||||||
|
godoc.InitHandlers(pres)
|
||||||
|
|
||||||
|
if *writeIndex {
|
||||||
|
// Write search index and exit.
|
||||||
|
if *indexFiles == "" {
|
||||||
|
log.Fatal("no index file specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("initialize file systems")
|
||||||
|
*verbose = true // want to see what happens
|
||||||
|
|
||||||
|
corpus.UpdateIndex()
|
||||||
|
|
||||||
|
log.Println("writing index file", *indexFiles)
|
||||||
|
f, err := os.Create(*indexFiles)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
index, _ := corpus.CurrentIndex()
|
||||||
|
err = index.Write(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("done")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print content that would be served at the URL *urlFlag.
|
||||||
|
if *urlFlag != "" {
|
||||||
|
handleURLFlag()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if *httpAddr != "" {
|
if *httpAddr != "" {
|
||||||
// HTTP server mode.
|
// HTTP server mode.
|
||||||
var handler http.Handler = http.DefaultServeMux
|
var handler http.Handler = http.DefaultServeMux
|
||||||
|
|
@ -300,22 +389,15 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
packageText := godoc.PackageText
|
packageText := pres.PackageText
|
||||||
|
|
||||||
// Command line mode.
|
// Command line mode.
|
||||||
if *html {
|
if *html {
|
||||||
packageText = godoc.PackageHTML
|
packageText = pres.PackageHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
if *query {
|
if *query {
|
||||||
// Command-line queries.
|
handleRemoteSearch()
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -375,12 +375,12 @@ func (p *Presentation) example_htmlFunc(info *PageInfo, funcName string) string
|
||||||
out = ""
|
out = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if ExampleHTML == nil {
|
if p.ExampleHTML == nil {
|
||||||
out = ""
|
out = ""
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ExampleHTML.Execute(&buf, struct {
|
err := p.ExampleHTML.Execute(&buf, struct {
|
||||||
Name, Doc, Code, Play, Output string
|
Name, Doc, Code, Play, Output string
|
||||||
}{eg.Name, eg.Doc, code, play, out})
|
}{eg.Name, eg.Doc, code, play, out})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
"text/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Page describes the contents of the top-level godoc webpage.
|
// Page describes the contents of the top-level godoc webpage.
|
||||||
|
|
@ -25,18 +24,6 @@ type Page struct {
|
||||||
Version string
|
Version string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
DirlistHTML,
|
|
||||||
ErrorHTML,
|
|
||||||
ExampleHTML,
|
|
||||||
GodocHTML,
|
|
||||||
PackageHTML,
|
|
||||||
PackageText,
|
|
||||||
SearchHTML,
|
|
||||||
SearchText,
|
|
||||||
SearchDescXML *template.Template
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Presentation) ServePage(w http.ResponseWriter, page Page) {
|
func (p *Presentation) ServePage(w http.ResponseWriter, page Page) {
|
||||||
if page.Tabtitle == "" {
|
if page.Tabtitle == "" {
|
||||||
page.Tabtitle = page.Title
|
page.Tabtitle = page.Title
|
||||||
|
|
@ -44,7 +31,7 @@ func (p *Presentation) ServePage(w http.ResponseWriter, page Page) {
|
||||||
page.SearchBox = p.Corpus.IndexEnabled
|
page.SearchBox = p.Corpus.IndexEnabled
|
||||||
page.Playground = p.ShowPlayground
|
page.Playground = p.ShowPlayground
|
||||||
page.Version = runtime.Version()
|
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.
|
// Only log if there's an error that's not about writing on HEAD requests.
|
||||||
// See Issues 5451 and 5454.
|
// See Issues 5451 and 5454.
|
||||||
log.Printf("GodocHTML.Execute: %s", err)
|
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{
|
p.ServePage(w, Page{
|
||||||
Title: "File " + relpath,
|
Title: "File " + relpath,
|
||||||
Subtitle: 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!
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,16 @@ import (
|
||||||
type Presentation struct {
|
type Presentation struct {
|
||||||
Corpus *Corpus
|
Corpus *Corpus
|
||||||
|
|
||||||
|
DirlistHTML,
|
||||||
|
ErrorHTML,
|
||||||
|
ExampleHTML,
|
||||||
|
GodocHTML,
|
||||||
|
PackageHTML,
|
||||||
|
PackageText,
|
||||||
|
SearchHTML,
|
||||||
|
SearchText,
|
||||||
|
SearchDescXML *template.Template
|
||||||
|
|
||||||
// TabWidth optionally specifies the tab width.
|
// TabWidth optionally specifies the tab width.
|
||||||
TabWidth int
|
TabWidth int
|
||||||
|
|
||||||
|
|
@ -22,6 +32,8 @@ type Presentation struct {
|
||||||
ShowExamples bool
|
ShowExamples bool
|
||||||
DeclLinks bool
|
DeclLinks bool
|
||||||
|
|
||||||
|
// NotesRx optionally specifies a regexp to match
|
||||||
|
// notes to render in the output.
|
||||||
NotesRx *regexp.Regexp
|
NotesRx *regexp.Regexp
|
||||||
|
|
||||||
initFuncMapOnce sync.Once
|
initFuncMapOnce sync.Once
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -213,7 +213,7 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode&NoHTML != 0 {
|
if mode&NoHTML != 0 {
|
||||||
h.p.ServeText(w, applyTemplate(PackageText, "packageText", info))
|
h.p.ServeText(w, applyTemplate(h.p.PackageText, "packageText", info))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,7 +253,7 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
Title: title,
|
Title: title,
|
||||||
Tabtitle: tabtitle,
|
Tabtitle: tabtitle,
|
||||||
Subtitle: subtitle,
|
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{
|
p.ServePage(w, Page{
|
||||||
Title: "Directory " + relpath,
|
Title: "Directory " + relpath,
|
||||||
Tabtitle: 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)
|
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")
|
w.Header().Set("Content-Type", "application/opensearchdescription+xml")
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"BaseURL": fmt.Sprintf("http://%s", r.Host),
|
"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.
|
// Only log if there's an error that's not about writing on HEAD requests.
|
||||||
// See Issues 5451 and 5454.
|
// See Issues 5451 and 5454.
|
||||||
log.Printf("searchDescXML.Execute: %s", err)
|
log.Printf("searchDescXML.Execute: %s", err)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
// like absolute paths w/o a leading '/'; i.e., the paths are considered
|
// like absolute paths w/o a leading '/'; i.e., the paths are considered
|
||||||
// relative to the root of the file system.
|
// relative to the root of the file system.
|
||||||
// - All path arguments to file system methods must be absolute paths.
|
// - All path arguments to file system methods must be absolute paths.
|
||||||
package zipvfs
|
package zipfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue