diff --git a/cmd/godoc/codewalk.go b/cmd/godoc/codewalk.go index 8b151b46..9be894f4 100644 --- a/cmd/godoc/codewalk.go +++ b/cmd/godoc/codewalk.go @@ -43,7 +43,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) { } // If directory exists, serve list of code walks. - dir, err := godoc.FS.Lstat(abspath) + dir, err := fs.Lstat(abspath) if err == nil && dir.IsDir() { codewalkDir(w, r, relpath, abspath) return @@ -51,7 +51,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) { // If file exists, serve using standard file server. if err == nil { - serveFile(w, r) + pres.ServeFile(w, r) return } @@ -117,7 +117,7 @@ func (st *Codestep) String() string { // loadCodewalk reads a codewalk from the named XML file. func loadCodewalk(filename string) (*Codewalk, error) { - f, err := godoc.FS.Open(filename) + f, err := fs.Open(filename) if err != nil { return nil, err } @@ -138,7 +138,7 @@ func loadCodewalk(filename string) (*Codewalk, error) { i = len(st.Src) } filename := st.Src[0:i] - data, err := vfs.ReadFile(godoc.FS, filename) + data, err := vfs.ReadFile(fs, filename) if err != nil { st.Err = err continue @@ -185,7 +185,7 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string Title string } - dir, err := godoc.FS.ReadDir(abspath) + dir, err := fs.ReadDir(abspath) if err != nil { log.Print(err) pres.ServeError(w, r, relpath, err) @@ -219,7 +219,7 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string // the usual godoc HTML wrapper. func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) { abspath := f - data, err := vfs.ReadFile(godoc.FS, abspath) + data, err := vfs.ReadFile(fs, abspath) if err != nil { log.Print(err) pres.ServeError(w, r, f, err) diff --git a/cmd/godoc/godoc.go b/cmd/godoc/godoc.go index fa550553..9fc2c8c1 100644 --- a/cmd/godoc/godoc.go +++ b/cmd/godoc/godoc.go @@ -8,7 +8,6 @@ import ( "bytes" "flag" "fmt" - htmlpkg "html" "log" "net/http" "net/url" @@ -19,33 +18,9 @@ import ( "text/template" "code.google.com/p/go.tools/godoc" - "code.google.com/p/go.tools/godoc/util" "code.google.com/p/go.tools/godoc/vfs" ) -// ---------------------------------------------------------------------------- -// Globals - -func flagBool(b *bool, name string, value bool, usage string) interface{} { - flag.BoolVar(b, name, value, usage) - return nil -} - -func flagInt(v *int, name string, value int, usage string) interface{} { - flag.IntVar(v, name, value, usage) - return nil -} - -func flagString(v *string, name string, value string, usage string) interface{} { - flag.StringVar(v, name, value, usage) - return nil -} - -func flagFloat64(v *float64, name string, value float64, usage string) interface{} { - flag.Float64Var(v, name, value, usage) - return nil -} - var ( verbose = flag.Bool("v", false, "verbose mode") @@ -54,27 +29,33 @@ var ( goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") // layout control - _ = flagInt(&godoc.TabWidth, "tabwidth", 4, "tab width") - _ = flagBool(&godoc.ShowTimestamps, "timestamps", false, "show timestamps with directory listings") - templateDir = flag.String("templates", "", "directory containing alternate template files") - _ = flagBool(&godoc.ShowPlayground, "play", false, "enable playground in web interface") - _ = flagBool(&godoc.ShowExamples, "ex", false, "show examples in command line mode") - _ = flagBool(&godoc.DeclLinks, "links", true, "link identifiers to their declarations") + 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") - _ = flagInt(&godoc.MaxResults, "maxresults", 10000, "maximum number of full text search results shown") - _ = flagFloat64(&godoc.IndexThrottle, "index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle") + 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 - _ = flagString(&godoc.NotesRx, "notes", "BUG", "regular expression matching note markers to show") + notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show") ) -var pres *godoc.Presentation +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) @@ -82,23 +63,26 @@ func registerPublicHandlers(mux *http.ServeMux) { mux.HandleFunc("/search", search) mux.Handle("/robots.txt", godoc.FileServer) mux.HandleFunc("/opensearch.xml", serveSearchDesc) - mux.HandleFunc("/", serveFile) + 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(godoc.FS, path) + 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(godoc.FuncMap).Parse(string(data)) + t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data)) if err != nil { log.Fatal("readTemplate: ", err) } @@ -147,125 +131,6 @@ func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { return } -func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) { - c := pathpkg.Clean(r.URL.Path) - c = strings.TrimRight(c, "/") - if r.URL.Path != c { - url := *r.URL - url.Path = c - http.Redirect(w, r, url.String(), http.StatusMovedPermanently) - redirected = true - } - return -} - -func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) { - src, err := vfs.ReadFile(godoc.FS, abspath) - if err != nil { - log.Printf("ReadFile: %s", err) - pres.ServeError(w, r, relpath, err) - return - } - - if r.FormValue("m") == "text" { - pres.ServeText(w, src) - return - } - - var buf bytes.Buffer - buf.WriteString("
")
- godoc.FormatText(&buf, src, 1, pathpkg.Ext(abspath) == ".go", r.FormValue("h"), godoc.RangeSelection(r.FormValue("s")))
- buf.WriteString("")
- fmt.Fprintf(&buf, ``, htmlpkg.EscapeString(relpath))
-
- pres.ServePage(w, godoc.Page{
- Title: title + " " + relpath,
- Tabtitle: relpath,
- Body: buf.Bytes(),
- })
-}
-
-func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
- if redirect(w, r) {
- return
- }
-
- list, err := godoc.FS.ReadDir(abspath)
- if err != nil {
- pres.ServeError(w, r, relpath, err)
- return
- }
-
- pres.ServePage(w, godoc.Page{
- Title: "Directory " + relpath,
- Tabtitle: relpath,
- Body: applyTemplate(godoc.DirlistHTML, "dirlistHTML", list),
- })
-}
-
-func serveFile(w http.ResponseWriter, r *http.Request) {
- relpath := r.URL.Path
-
- // Check to see if we need to redirect or serve another file.
- if m := godoc.MetadataFor(relpath); m != nil {
- if m.Path != relpath {
- // Redirect to canonical path.
- http.Redirect(w, r, m.Path, http.StatusMovedPermanently)
- return
- }
- // Serve from the actual filesystem path.
- relpath = m.FilePath()
- }
-
- abspath := relpath
- relpath = relpath[1:] // strip leading slash
-
- switch pathpkg.Ext(relpath) {
- case ".html":
- if strings.HasSuffix(relpath, "/index.html") {
- // We'll show index.html for the directory.
- // Use the dir/ version as canonical instead of dir/index.html.
- http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently)
- return
- }
- pres.ServeHTMLDoc(w, r, abspath, relpath)
- return
-
- case ".go":
- serveTextFile(w, r, abspath, relpath, "Source file")
- return
- }
-
- dir, err := godoc.FS.Lstat(abspath)
- if err != nil {
- log.Print(err)
- pres.ServeError(w, r, relpath, err)
- return
- }
-
- if dir != nil && dir.IsDir() {
- if redirect(w, r) {
- return
- }
- if index := pathpkg.Join(abspath, "index.html"); util.IsTextFile(godoc.FS, index) {
- pres.ServeHTMLDoc(w, r, index, index)
- return
- }
- serveDirectory(w, r, abspath, relpath)
- return
- }
-
- if util.IsTextFile(godoc.FS, abspath) {
- if redirectFile(w, r) {
- return
- }
- serveTextFile(w, r, abspath, relpath, "Text file")
- return
- }
-
- godoc.FileServer.ServeHTTP(w, r)
-}
-
func serveSearchDesc(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/opensearchdescription+xml")
data := map[string]interface{}{
@@ -315,14 +180,13 @@ type SearchResult struct {
func lookup(query string) (result SearchResult) {
result.Query = query
- index, timestamp := godoc.SearchIndex.Get()
+ corp := pres.Corpus
+ index, timestamp := corp.CurrentIndex()
if index != nil {
- index := index.(*godoc.Index)
-
// identifier search
var err error
result.Pak, result.Hit, result.Alt, err = index.Lookup(query)
- if err != nil && godoc.MaxResults <= 0 {
+ 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()
@@ -330,7 +194,7 @@ func lookup(query string) (result SearchResult) {
}
// full text search
- if godoc.MaxResults > 0 && query != "" {
+ if corp.MaxResults > 0 && query != "" {
rx, err := regexp.Compile(query)
if err != nil {
result.Alert = "Error in query regular expression: " + err.Error()
@@ -340,8 +204,8 @@ func lookup(query string) (result SearchResult) {
// 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, godoc.MaxResults+1)
- result.Complete = result.Found <= godoc.MaxResults
+ 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
}
@@ -350,7 +214,7 @@ func lookup(query string) (result SearchResult) {
// is the result accurate?
if pres.Corpus.IndexEnabled {
- if _, ts := godoc.FSModified.Get(); timestamp.Before(ts) {
+ 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"
}
diff --git a/cmd/godoc/main.go b/cmd/godoc/main.go
index 22be2df4..4b48f273 100644
--- a/cmd/godoc/main.go
+++ b/cmd/godoc/main.go
@@ -158,9 +158,6 @@ func main() {
usage()
}
- fs := vfs.NameSpace{}
- godoc.FS = fs // temp hack
-
// 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,
@@ -186,35 +183,40 @@ func main() {
fs.Bind("/src/pkg", vfs.OS(p), "/src", vfs.BindAfter)
}
- readTemplates()
-
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 godoc.IndexFiles == "" {
+ if *indexFiles == "" {
log.Fatal("no index file specified")
}
log.Println("initialize file systems")
*verbose = true // want to see what happens
- godoc.IndexThrottle = 1.0
- godoc.UpdateIndex()
+ corpus.IndexThrottle = 1.0
+ corpus.UpdateIndex()
- log.Println("writing index file", godoc.IndexFiles)
- f, err := os.Create(godoc.IndexFiles)
+ log.Println("writing index file", *indexFiles)
+ f, err := os.Create(*indexFiles)
if err != nil {
log.Fatal(err)
}
- index, _ := godoc.SearchIndex.Get()
- err = index.(*godoc.Index).Write(f)
+ index, _ := corpus.CurrentIndex()
+ err = index.Write(f)
if err != nil {
log.Fatal(err)
}
@@ -270,12 +272,12 @@ func main() {
log.Printf("version = %s", runtime.Version())
log.Printf("address = %s", *httpAddr)
log.Printf("goroot = %s", *goroot)
- log.Printf("tabwidth = %d", godoc.TabWidth)
+ log.Printf("tabwidth = %d", *tabWidth)
switch {
case !*indexEnabled:
log.Print("search index disabled")
- case godoc.MaxResults > 0:
- log.Printf("full text index enabled (maxresults = %d)", godoc.MaxResults)
+ case *maxResults > 0:
+ log.Printf("full text index enabled (maxresults = %d)", *maxResults)
default:
log.Print("identifier search index enabled")
}
@@ -287,7 +289,7 @@ func main() {
// Initialize search index.
if *indexEnabled {
- go godoc.RunIndexer()
+ go corpus.RunIndexer()
}
// Start http server.
@@ -431,10 +433,10 @@ func main() {
}
if *html {
var buf bytes.Buffer
- godoc.WriteNode(&buf, info.FSet, cn)
+ pres.WriteNode(&buf, info.FSet, cn)
godoc.FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil)
} else {
- godoc.WriteNode(os.Stdout, info.FSet, cn)
+ pres.WriteNode(os.Stdout, info.FSet, cn)
}
fmt.Println()
}
diff --git a/godoc/corpus.go b/godoc/corpus.go
index 87d33bd9..fb889445 100644
--- a/godoc/corpus.go
+++ b/godoc/corpus.go
@@ -7,6 +7,7 @@ package godoc
import (
"errors"
pathpkg "path"
+ "time"
"code.google.com/p/go.tools/godoc/util"
"code.google.com/p/go.tools/godoc/vfs"
@@ -46,6 +47,8 @@ type Corpus struct {
fsModified util.RWValue // timestamp of last call to invalidateIndex
docMetadata util.RWValue // mapping from paths to *Metadata
+ // SearchIndex is the search index in use.
+ searchIndex util.RWValue
}
// NewCorpus returns a new Corpus from a filesystem.
@@ -61,6 +64,17 @@ func NewCorpus(fs vfs.FileSystem) *Corpus {
return c
}
+func (c *Corpus) CurrentIndex() (*Index, time.Time) {
+ v, t := c.searchIndex.Get()
+ idx, _ := v.(*Index)
+ return idx, t
+}
+
+func (c *Corpus) FSModifiedTime() time.Time {
+ _, ts := c.fsModified.Get()
+ return ts
+}
+
// Init initializes Corpus, once options on Corpus are set.
// It must be called before any subsequent method calls.
func (c *Corpus) Init() error {
diff --git a/godoc/dirtrees.go b/godoc/dirtrees.go
index edf36b82..9316c19b 100644
--- a/godoc/dirtrees.go
+++ b/godoc/dirtrees.go
@@ -86,7 +86,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
// though the directory doesn't contain any real package files - was bug)
if synopses[0] == "" {
// no "optimal" package synopsis yet; continue to collect synopses
- file, err := parseFile(fset, pathpkg.Join(path, d.Name()),
+ file, err := b.c.parseFile(fset, pathpkg.Join(path, d.Name()),
parser.ParseComments|parser.PackageClauseOnly)
if err == nil {
hasPkgFiles = true
diff --git a/godoc/godoc.go b/godoc/godoc.go
index e400f6de..093f007f 100644
--- a/godoc/godoc.go
+++ b/godoc/godoc.go
@@ -27,46 +27,10 @@ import (
"time"
"unicode"
"unicode/utf8"
-
- "code.google.com/p/go.tools/godoc/util"
- "code.google.com/p/go.tools/godoc/vfs"
)
-// FS is the file system that godoc reads from and serves.
-// It is a virtual file system that operates on slash-separated paths,
-// and its root corresponds to the Go distribution root: /src/pkg
-// holds the source tree, and so on. This means that the URLs served by
-// the godoc server are the same as the paths in the virtual file
-// system, which helps keep things simple.
-// TODO(bradfitz): delete this global
-var FS = vfs.NameSpace{}
-
-// Old flags
-var (
- // DeclLinks controls whether identifers are linked to their declaration.
- DeclLinks = true
-
- // ShowExamples controls whether to show examples in command-line mode.
- // TODO(bradfitz,adg): delete this flag
- ShowExamples = false
-
- // ShowPlayground controls whether to enable the playground in
- // the web interface.
- // TODO(bradfitz,adg): delete this flag
- ShowPlayground = false
-
- ShowTimestamps = false
-
- Verbose = false
-
- TabWidth = 4
-
- // regular expression matching note markers to show
- NotesRx = "BUG"
-)
-
-// SearchIndex is the search index in use.
-var SearchIndex util.RWValue
+// Verbose controls logging verbosity.
+var Verbose = false
// Fake relative package path for built-ins. Documentation for all globals
// (not just exported ones) will be shown for packages in this directory.
@@ -77,39 +41,57 @@ const BuiltinPkgPath = "builtin"
// Convention: template function names ending in "_html" or "_url" produce
// HTML- or URL-escaped strings; all other function results may
// require explicit escaping in the template.
-var FuncMap = template.FuncMap{
- // various helpers
- "filename": filenameFunc,
- "repeat": strings.Repeat,
+func (p *Presentation) FuncMap() template.FuncMap {
+ p.initFuncMapOnce.Do(p.initFuncMap)
+ return p.funcMap
+}
- // access to FileInfos (directory listings)
- "fileInfoName": fileInfoNameFunc,
- "fileInfoTime": fileInfoTimeFunc,
+func (p *Presentation) TemplateFuncs() template.FuncMap {
+ p.initFuncMapOnce.Do(p.initFuncMap)
+ return p.templateFuncs
+}
- // access to search result information
- "infoKind_html": infoKind_htmlFunc,
- "infoLine": infoLineFunc,
- "infoSnippet_html": infoSnippet_htmlFunc,
+func (p *Presentation) initFuncMap() {
+ if p.Corpus == nil {
+ panic("nil Presentation.Corpus")
+ }
+ p.templateFuncs = template.FuncMap{
+ "code": p.code,
+ }
+ p.funcMap = template.FuncMap{
+ // various helpers
+ "filename": filenameFunc,
+ "repeat": strings.Repeat,
- // formatting of AST nodes
- "node": nodeFunc,
- "node_html": node_htmlFunc,
- "comment_html": comment_htmlFunc,
- "comment_text": comment_textFunc,
+ // access to FileInfos (directory listings)
+ "fileInfoName": fileInfoNameFunc,
+ "fileInfoTime": fileInfoTimeFunc,
- // support for URL attributes
- "pkgLink": pkgLinkFunc,
- "srcLink": srcLinkFunc,
- "posLink_url": posLink_urlFunc,
+ // access to search result information
+ "infoKind_html": infoKind_htmlFunc,
+ "infoLine": p.infoLineFunc,
+ "infoSnippet_html": p.infoSnippet_htmlFunc,
- // formatting of Examples
- "example_html": example_htmlFunc,
- "example_text": example_textFunc,
- "example_name": example_nameFunc,
- "example_suffix": example_suffixFunc,
+ // formatting of AST nodes
+ "node": p.nodeFunc,
+ "node_html": p.node_htmlFunc,
+ "comment_html": comment_htmlFunc,
+ "comment_text": comment_textFunc,
- // formatting of Notes
- "noteTitle": noteTitle,
+ // support for URL attributes
+ "pkgLink": pkgLinkFunc,
+ "srcLink": srcLinkFunc,
+ "posLink_url": posLink_urlFunc,
+
+ // formatting of Examples
+ "example_html": p.example_htmlFunc,
+ "example_text": p.example_textFunc,
+ "example_name": p.example_nameFunc,
+ "example_suffix": p.example_suffixFunc,
+
+ // formatting of Notes
+ "noteTitle": noteTitle,
+ }
}
func filenameFunc(path string) string {
@@ -148,10 +130,10 @@ func infoKind_htmlFunc(info SpotInfo) string {
return infoKinds[info.Kind()] // infoKind entries are html-escaped
}
-func infoLineFunc(info SpotInfo) int {
+func (p *Presentation) infoLineFunc(info SpotInfo) int {
line := info.Lori()
if info.IsIndex() {
- index, _ := SearchIndex.Get()
+ index, _ := p.Corpus.searchIndex.Get()
if index != nil {
line = index.(*Index).Snippet(line).Line
} else {
@@ -165,27 +147,27 @@ func infoLineFunc(info SpotInfo) int {
return line
}
-func infoSnippet_htmlFunc(info SpotInfo) string {
+func (p *Presentation) infoSnippet_htmlFunc(info SpotInfo) string {
if info.IsIndex() {
- index, _ := SearchIndex.Get()
+ index, _ := p.Corpus.searchIndex.Get()
// Snippet.Text was HTML-escaped when it was generated
return index.(*Index).Snippet(info.Lori()).Text
}
return `no snippet text available`
}
-func nodeFunc(info *PageInfo, node interface{}) string {
+func (p *Presentation) nodeFunc(info *PageInfo, node interface{}) string {
var buf bytes.Buffer
- writeNode(&buf, info.FSet, node)
+ p.writeNode(&buf, info.FSet, node)
return buf.String()
}
-func node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string {
+func (p *Presentation) node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string {
var buf1 bytes.Buffer
- writeNode(&buf1, info.FSet, node)
+ p.writeNode(&buf1, info.FSet, node)
var buf2 bytes.Buffer
- if n, _ := node.(ast.Node); n != nil && linkify && DeclLinks {
+ if n, _ := node.(ast.Node); n != nil && linkify && p.DeclLinks {
LinkifyText(&buf2, buf1.Bytes(), n)
} else {
FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
@@ -307,8 +289,8 @@ func srcLinkFunc(s string) string {
return pathpkg.Clean("/" + s)
}
-func example_textFunc(info *PageInfo, funcName, indent string) string {
- if !ShowExamples {
+func (p *Presentation) example_textFunc(info *PageInfo, funcName, indent string) string {
+ if !p.ShowExamples {
return ""
}
@@ -328,7 +310,7 @@ func example_textFunc(info *PageInfo, funcName, indent string) string {
// print code
cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
var buf1 bytes.Buffer
- writeNode(&buf1, info.FSet, cnode)
+ p.writeNode(&buf1, info.FSet, cnode)
code := buf1.String()
// Additional formatting if this is a function body.
if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
@@ -348,7 +330,7 @@ func example_textFunc(info *PageInfo, funcName, indent string) string {
return buf.String()
}
-func example_htmlFunc(info *PageInfo, funcName string) string {
+func (p *Presentation) example_htmlFunc(info *PageInfo, funcName string) string {
var buf bytes.Buffer
for _, eg := range info.Examples {
name := stripExampleSuffix(eg.Name)
@@ -359,7 +341,7 @@ func example_htmlFunc(info *PageInfo, funcName string) string {
// print code
cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
- code := node_htmlFunc(info, cnode, true)
+ code := p.node_htmlFunc(info, cnode, true)
out := eg.Output
wholeFile := true
@@ -379,7 +361,7 @@ func example_htmlFunc(info *PageInfo, funcName string) string {
// Write out the playground code in standard Go style
// (use tabs, no comment highlight, etc).
play := ""
- if eg.Play != nil && ShowPlayground {
+ if eg.Play != nil && p.ShowPlayground {
var buf bytes.Buffer
if err := format.Node(&buf, info.FSet, eg.Play); err != nil {
log.Print(err)
@@ -410,7 +392,7 @@ func example_htmlFunc(info *PageInfo, funcName string) string {
// example_nameFunc takes an example function name and returns its display
// name. For example, "Foo_Bar_quux" becomes "Foo.Bar (Quux)".
-func example_nameFunc(s string) string {
+func (p *Presentation) example_nameFunc(s string) string {
name, suffix := splitExampleName(s)
// replace _ with . for method names
name = strings.Replace(name, "_", ".", 1)
@@ -423,7 +405,7 @@ func example_nameFunc(s string) string {
// example_suffixFunc takes an example function name and returns its suffix in
// parenthesized form. For example, "Foo_Bar_quux" becomes " (Quux)".
-func example_suffixFunc(name string) string {
+func (p *Presentation) example_suffixFunc(name string) string {
_, suffix := splitExampleName(name)
return suffix
}
@@ -462,7 +444,7 @@ func splitExampleName(s string) (name, suffix string) {
}
// Write an AST node to w.
-func writeNode(w io.Writer, fset *token.FileSet, x interface{}) {
+func (p *Presentation) writeNode(w io.Writer, fset *token.FileSet, x interface{}) {
// convert trailing tabs into spaces using a tconv filter
// to ensure a good outcome in most browsers (there may still
// be tabs in comments and strings, but converting those into
@@ -472,10 +454,13 @@ func writeNode(w io.Writer, fset *token.FileSet, x interface{}) {
// with an another printer mode (which is more efficiently
// implemented in the printer than here with another layer)
mode := printer.TabIndent | printer.UseSpaces
- err := (&printer.Config{Mode: mode, Tabwidth: TabWidth}).Fprint(&tconv{output: w}, fset, x)
+ err := (&printer.Config{Mode: mode, Tabwidth: p.TabWidth}).Fprint(&tconv{p: p, output: w}, fset, x)
if err != nil {
log.Print(err)
}
}
-var WriteNode = writeNode
+// WriteNote writes x to w.
+func (p *Presentation) WriteNode(w io.Writer, fset *token.FileSet, x interface{}) {
+ p.writeNode(w, fset, x)
+}
diff --git a/godoc/index.go b/godoc/index.go
index d7c79203..3040a358 100644
--- a/godoc/index.go
+++ b/godoc/index.go
@@ -62,19 +62,6 @@ import (
"code.google.com/p/go.tools/godoc/util"
)
-// TODO(bradfitz,adg): legacy flag vars. clean up.
-var (
- MaxResults = 1000
-
- // index throttle value; 0.0 = no time allocated, 1.0 = full throttle
- IndexThrottle float64 = 0.75
-
- // IndexFiles is a glob pattern specifying index files; if
- // not empty, the index is read from these files in sorted
- // order")
- IndexFiles string
-)
-
// ----------------------------------------------------------------------------
// InterfaceSlice is a helper type for sorting interface
// slices according to some slice-specific sort criteria.
@@ -384,6 +371,7 @@ type Statistics struct {
// interface for walking file trees, and the ast.Visitor interface for
// walking Go ASTs.
type Indexer struct {
+ c *Corpus
fset *token.FileSet // file set for all indexed files
sources bytes.Buffer // concatenated sources
packages map[string]*Pak // map of canonicalized *Paks
@@ -549,7 +537,7 @@ func pkgName(filename string) string {
// failed (that is, if the file was not added), it returns file == nil.
func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *ast.File) {
// open file
- f, err := FS.Open(filename)
+ f, err := x.c.fs.Open(filename)
if err != nil {
return
}
@@ -716,19 +704,20 @@ func canonical(w string) string { return strings.ToLower(w) }
// NewIndex creates a new index for the .go files
// in the directories given by dirnames.
//
-func NewIndex(dirnames <-chan string, fulltextIndex bool, throttle float64) *Index {
+func NewIndex(c *Corpus, dirnames <-chan string, fulltextIndex bool, throttle float64) *Index {
var x Indexer
th := util.NewThrottle(throttle, 100*time.Millisecond) // run at least 0.1s at a time
// initialize Indexer
// (use some reasonably sized maps to start)
+ x.c = c
x.fset = token.NewFileSet()
x.packages = make(map[string]*Pak, 256)
x.words = make(map[string]*IndexResult, 8192)
// index all files in the directories given by dirnames
for dirname := range dirnames {
- list, err := FS.ReadDir(dirname)
+ list, err := c.fs.ReadDir(dirname)
if err != nil {
continue // ignore this directory
}
@@ -1046,19 +1035,19 @@ func (c *Corpus) invalidateIndex() {
// indexUpToDate() returns true if the search index is not older
// than any of the file systems under godoc's observation.
//
-func indexUpToDate() bool {
- _, fsTime := FSModified.Get()
- _, siTime := SearchIndex.Get()
+func (c *Corpus) indexUpToDate() bool {
+ _, fsTime := c.fsModified.Get()
+ _, siTime := c.searchIndex.Get()
return !fsTime.After(siTime)
}
// feedDirnames feeds the directory names of all directories
// under the file system given by root to channel c.
//
-func feedDirnames(root *util.RWValue, c chan<- string) {
- if dir, _ := root.Get(); dir != nil {
+func (c *Corpus) feedDirnames(ch chan<- string) {
+ if dir, _ := c.fsTree.Get(); dir != nil {
for d := range dir.(*Directory).iter(false) {
- c <- d.Path
+ ch <- d.Path
}
}
}
@@ -1066,16 +1055,16 @@ func feedDirnames(root *util.RWValue, c chan<- string) {
// fsDirnames() returns a channel sending all directory names
// of all the file systems under godoc's observation.
//
-func fsDirnames() <-chan string {
- c := make(chan string, 256) // buffered for fewer context switches
+func (c *Corpus) fsDirnames() <-chan string {
+ ch := make(chan string, 256) // buffered for fewer context switches
go func() {
- feedDirnames(&FSTree, c)
- close(c)
+ c.feedDirnames(ch)
+ close(ch)
}()
- return c
+ return ch
}
-func readIndex(filenames string) error {
+func (c *Corpus) readIndex(filenames string) error {
matches, err := filepath.Glob(filenames)
if err != nil {
return err
@@ -1096,18 +1085,18 @@ func readIndex(filenames string) error {
if err := x.Read(io.MultiReader(files...)); err != nil {
return err
}
- SearchIndex.Set(x)
+ c.searchIndex.Set(x)
return nil
}
-func UpdateIndex() {
+func (c *Corpus) UpdateIndex() {
if Verbose {
log.Printf("updating index...")
}
start := time.Now()
- index := NewIndex(fsDirnames(), MaxResults > 0, IndexThrottle)
+ index := NewIndex(c, c.fsDirnames(), c.MaxResults > 0, c.IndexThrottle)
stop := time.Now()
- SearchIndex.Set(index)
+ c.searchIndex.Set(index)
if Verbose {
secs := stop.Sub(start).Seconds()
stats := index.Stats()
@@ -1123,19 +1112,19 @@ func UpdateIndex() {
}
// RunIndexer runs forever, indexing.
-func RunIndexer() {
+func (c *Corpus) RunIndexer() {
// initialize the index from disk if possible
- if IndexFiles != "" {
- if err := readIndex(IndexFiles); err != nil {
+ if c.IndexFiles != "" {
+ if err := c.readIndex(c.IndexFiles); err != nil {
log.Printf("error reading index: %s", err)
}
}
// repeatedly update the index when it goes out of date
for {
- if !indexUpToDate() {
+ if !c.indexUpToDate() {
// index possibly out of date - make a new one
- UpdateIndex()
+ c.UpdateIndex()
}
delay := 60 * time.Second // by default, try every 60s
if false { // TODO(bradfitz): was: *testDir != "" {
diff --git a/godoc/meta.go b/godoc/meta.go
index 86f25e62..ae8ee5a9 100644
--- a/godoc/meta.go
+++ b/godoc/meta.go
@@ -99,14 +99,14 @@ func (c *Corpus) updateMetadata() {
}
}
scan("/doc")
- DocMetadata.Set(metadata)
+ c.docMetadata.Set(metadata)
}
// MetadataFor returns the *Metadata for a given relative path or nil if none
// exists.
//
-func MetadataFor(relpath string) *Metadata {
- if m, _ := DocMetadata.Get(); m != nil {
+func (c *Corpus) MetadataFor(relpath string) *Metadata {
+ if m, _ := c.docMetadata.Get(); m != nil {
meta := m.(map[string]*Metadata)
// If metadata for this relpath exists, return it.
if p := meta[relpath]; p != nil {
diff --git a/godoc/page.go b/godoc/page.go
index f8ee60ef..18934113 100644
--- a/godoc/page.go
+++ b/godoc/page.go
@@ -42,7 +42,7 @@ func (p *Presentation) ServePage(w http.ResponseWriter, page Page) {
page.Tabtitle = page.Title
}
page.SearchBox = p.Corpus.IndexEnabled
- page.Playground = ShowPlayground
+ page.Playground = p.ShowPlayground
page.Version = runtime.Version()
if err := GodocHTML.Execute(w, page); err != nil && err != http.ErrBodyNotAllowed {
// Only log if there's an error that's not about writing on HEAD requests.
diff --git a/godoc/parser.go b/godoc/parser.go
index d8b5779a..a27d4fdc 100644
--- a/godoc/parser.go
+++ b/godoc/parser.go
@@ -16,19 +16,19 @@ import (
"code.google.com/p/go.tools/godoc/vfs"
)
-func parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) {
- src, err := vfs.ReadFile(FS, filename)
+func (c *Corpus) parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) {
+ src, err := vfs.ReadFile(c.fs, filename)
if err != nil {
return nil, err
}
return parser.ParseFile(fset, filename, src, mode)
}
-func parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) {
+func (c *Corpus) parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) {
files := make(map[string]*ast.File)
for _, f := range localnames {
absname := pathpkg.Join(abspath, f)
- file, err := parseFile(fset, absname, parser.ParseComments)
+ file, err := c.parseFile(fset, absname, parser.ParseComments)
if err != nil {
return nil, err
}
diff --git a/godoc/pres.go b/godoc/pres.go
index a002b3a0..3191ab80 100644
--- a/godoc/pres.go
+++ b/godoc/pres.go
@@ -5,6 +5,8 @@
package godoc
import (
+ "regexp"
+ "sync"
"text/template"
)
@@ -19,6 +21,12 @@ type Presentation struct {
ShowPlayground bool
ShowExamples bool
DeclLinks bool
+
+ NotesRx *regexp.Regexp
+
+ initFuncMapOnce sync.Once
+ funcMap template.FuncMap
+ templateFuncs template.FuncMap
}
// NewPresentation returns a new Presentation from a corpus.
@@ -33,7 +41,3 @@ func NewPresentation(c *Corpus) *Presentation {
DeclLinks: true,
}
}
-
-func (p *Presentation) FuncMap() template.FuncMap {
- panic("")
-}
diff --git a/godoc/server.go b/godoc/server.go
index c4793ed7..9b2149b0 100644
--- a/godoc/server.go
+++ b/godoc/server.go
@@ -19,7 +19,6 @@ import (
"os"
pathpkg "path"
"path/filepath"
- "regexp"
"strings"
"text/template"
"time"
@@ -35,11 +34,6 @@ var (
FileServer http.Handler // default file server
CmdHandler Server
PkgHandler Server
-
- // file system information
- FSTree util.RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now)
- FSModified util.RWValue // timestamp of last call to invalidateIndex
- DocMetadata util.RWValue // mapping from paths to *Metadata
)
func InitHandlers(p *Presentation) {
@@ -83,8 +77,17 @@ func (h *Server) GetPageInfo(abspath, relpath string, mode PageInfoMode) *PageIn
// set ctxt.GOOS and ctxt.GOARCH before calling ctxt.ImportDir.
ctxt := build.Default
ctxt.IsAbsPath = pathpkg.IsAbs
- ctxt.ReadDir = fsReadDir
- ctxt.OpenFile = fsOpenFile
+ ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
+ return h.c.fs.ReadDir(filepath.ToSlash(dir))
+ }
+ ctxt.OpenFile = func(name string) (r io.ReadCloser, err error) {
+ data, err := vfs.ReadFile(h.c.fs, filepath.ToSlash(name))
+ if err != nil {
+ return nil, err
+ }
+ return ioutil.NopCloser(bytes.NewReader(data)), nil
+ }
+
pkginfo, err := ctxt.ImportDir(abspath, 0)
// continue if there are no Go source files; we still want the directory info
if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
@@ -109,7 +112,7 @@ func (h *Server) GetPageInfo(abspath, relpath string, mode PageInfoMode) *PageIn
if len(pkgfiles) > 0 {
// build package AST
fset := token.NewFileSet()
- files, err := parseFiles(fset, abspath, pkgfiles)
+ files, err := h.c.parseFiles(fset, abspath, pkgfiles)
if err != nil {
info.Err = err
return info
@@ -133,7 +136,7 @@ func (h *Server) GetPageInfo(abspath, relpath string, mode PageInfoMode) *PageIn
// collect examples
testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...)
- files, err = parseFiles(fset, abspath, testfiles)
+ files, err = h.c.parseFiles(fset, abspath, testfiles)
if err != nil {
log.Println("parsing examples:", err)
}
@@ -142,7 +145,7 @@ func (h *Server) GetPageInfo(abspath, relpath string, mode PageInfoMode) *PageIn
// collect any notes that we want to show
if info.PDoc.Notes != nil {
// could regexp.Compile only once per godoc, but probably not worth it
- if rx, err := regexp.Compile(NotesRx); err == nil {
+ if rx := h.p.NotesRx; rx != nil {
for m, n := range info.PDoc.Notes {
if rx.MatchString(m) {
if info.Notes == nil {
@@ -169,7 +172,7 @@ func (h *Server) GetPageInfo(abspath, relpath string, mode PageInfoMode) *PageIn
// get directory information, if any
var dir *Directory
var timestamp time.Time
- if tree, ts := FSTree.Get(); tree != nil && tree.(*Directory) != nil {
+ if tree, ts := h.c.fsTree.Get(); tree != nil && tree.(*Directory) != nil {
// directory tree is present; lookup respective directory
// (may still fail if the file system was updated and the
// new directory tree has not yet been computed)
@@ -223,7 +226,7 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
default:
tabtitle = info.Dirname
title = "Directory "
- if ShowTimestamps {
+ if h.p.ShowTimestamps {
subtitle = "Last update: " + info.DirTime.String()
}
}
@@ -292,20 +295,6 @@ var AdjustPageInfoMode = func(_ *http.Request, mode PageInfoMode) PageInfoMode {
return mode
}
-// fsReadDir implements ReadDir for the go/build package.
-func fsReadDir(dir string) ([]os.FileInfo, error) {
- return FS.ReadDir(filepath.ToSlash(dir))
-}
-
-// fsOpenFile implements OpenFile for the go/build package.
-func fsOpenFile(name string) (r io.ReadCloser, err error) {
- data, err := vfs.ReadFile(FS, filepath.ToSlash(name))
- if err != nil {
- return nil, err
- }
- return ioutil.NopCloser(bytes.NewReader(data)), nil
-}
-
// poorMansImporter returns a (dummy) package object named
// by the last path component of the provided package path
// (as is the convention for packages). This is sufficient
@@ -465,7 +454,7 @@ func (p *Presentation) serveDirectory(w http.ResponseWriter, r *http.Request, ab
return
}
- list, err := FS.ReadDir(abspath)
+ list, err := p.Corpus.fs.ReadDir(abspath)
if err != nil {
p.ServeError(w, r, relpath, err)
return
@@ -480,7 +469,7 @@ func (p *Presentation) serveDirectory(w http.ResponseWriter, r *http.Request, ab
func (p *Presentation) ServeHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
// get HTML body contents
- src, err := vfs.ReadFile(FS, abspath)
+ src, err := vfs.ReadFile(p.Corpus.fs, abspath)
if err != nil {
log.Printf("ReadFile: %s", err)
p.ServeError(w, r, relpath, err)
@@ -502,7 +491,7 @@ func (p *Presentation) ServeHTMLDoc(w http.ResponseWriter, r *http.Request, absp
// evaluate as template if indicated
if meta.Template {
- tmpl, err := template.New("main").Funcs(TemplateFuncs).Parse(string(src))
+ tmpl, err := template.New("main").Funcs(p.TemplateFuncs()).Parse(string(src))
if err != nil {
log.Printf("parsing template %s: %v", relpath, err)
p.ServeError(w, r, relpath, err)
@@ -531,11 +520,15 @@ func (p *Presentation) ServeHTMLDoc(w http.ResponseWriter, r *http.Request, absp
})
}
+func (p *Presentation) ServeFile(w http.ResponseWriter, r *http.Request) {
+ p.serveFile(w, r)
+}
+
func (p *Presentation) serveFile(w http.ResponseWriter, r *http.Request) {
relpath := r.URL.Path
// Check to see if we need to redirect or serve another file.
- if m := MetadataFor(relpath); m != nil {
+ if m := p.Corpus.MetadataFor(relpath); m != nil {
if m.Path != relpath {
// Redirect to canonical path.
http.Redirect(w, r, m.Path, http.StatusMovedPermanently)
@@ -564,7 +557,7 @@ func (p *Presentation) serveFile(w http.ResponseWriter, r *http.Request) {
return
}
- dir, err := FS.Lstat(abspath)
+ dir, err := p.Corpus.fs.Lstat(abspath)
if err != nil {
log.Print(err)
p.ServeError(w, r, relpath, err)
@@ -575,7 +568,7 @@ func (p *Presentation) serveFile(w http.ResponseWriter, r *http.Request) {
if redirect(w, r) {
return
}
- if index := pathpkg.Join(abspath, "index.html"); util.IsTextFile(FS, index) {
+ if index := pathpkg.Join(abspath, "index.html"); util.IsTextFile(p.Corpus.fs, index) {
p.ServeHTMLDoc(w, r, index, index)
return
}
@@ -583,7 +576,7 @@ func (p *Presentation) serveFile(w http.ResponseWriter, r *http.Request) {
return
}
- if util.IsTextFile(FS, abspath) {
+ if util.IsTextFile(p.Corpus.fs, abspath) {
if redirectFile(w, r) {
return
}
diff --git a/godoc/snippet.go b/godoc/snippet.go
index 1466d3a4..dd9c8225 100644
--- a/godoc/snippet.go
+++ b/godoc/snippet.go
@@ -21,10 +21,10 @@ type Snippet struct {
Text string // HTML-escaped
}
-func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
+func (p *Presentation) newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
// TODO instead of pretty-printing the node, should use the original source instead
var buf1 bytes.Buffer
- writeNode(&buf1, fset, decl)
+ p.writeNode(&buf1, fset, decl)
// wrap text with tag
var buf2 bytes.Buffer
buf2.WriteString("")
@@ -55,7 +55,7 @@ func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec {
return nil
}
-func genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet {
+func (p *Presentation) genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet {
s := findSpec(d.Specs, id)
if s == nil {
return nil // declaration doesn't contain id - exit gracefully
@@ -71,10 +71,10 @@ func genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet {
Rparen: d.Rparen,
}
- return newSnippet(fset, dd, id)
+ return p.newSnippet(fset, dd, id)
}
-func funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet {
+func (p *Presentation) funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet {
if d.Name != id {
return nil // declaration doesn't contain id - exit gracefully
}
@@ -87,19 +87,30 @@ func funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet {
Type: d.Type,
}
- return newSnippet(fset, dd, id)
+ return p.newSnippet(fset, dd, id)
}
// NewSnippet creates a text snippet from a declaration decl containing an
// identifier id. Parts of the declaration not containing the identifier
// may be removed for a more compact snippet.
-//
-func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet) {
+func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
+ // TODO(bradfitz, adg): remove this function. But it's used by indexer, which
+ // doesn't have a *Presentation, and NewSnippet needs a TabWidth.
+ var p Presentation
+ p.TabWidth = 4
+ return p.NewSnippet(fset, decl, id)
+}
+
+// NewSnippet creates a text snippet from a declaration decl containing an
+// identifier id. Parts of the declaration not containing the identifier
+// may be removed for a more compact snippet.
+func (p *Presentation) NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
+ var s *Snippet
switch d := decl.(type) {
case *ast.GenDecl:
- s = genSnippet(fset, d, id)
+ s = p.genSnippet(fset, d, id)
case *ast.FuncDecl:
- s = funcSnippet(fset, d, id)
+ s = p.funcSnippet(fset, d, id)
}
// handle failure gracefully
@@ -108,5 +119,5 @@ func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet)
fmt.Fprintf(&buf, `could not generate a snippet for %s`, id.Name)
s = &Snippet{fset.Position(id.Pos()).Line, buf.String()}
}
- return
+ return s
}
diff --git a/godoc/tab.go b/godoc/tab.go
index 012fab61..7973b740 100644
--- a/godoc/tab.go
+++ b/godoc/tab.go
@@ -16,6 +16,7 @@ type tconv struct {
output io.Writer
state int // indenting or collecting
indent int // valid if state == indenting
+ p *Presentation
}
func (p *tconv) writeIndent() (err error) {
@@ -44,7 +45,7 @@ func (p *tconv) Write(data []byte) (n int, err error) {
case indenting:
switch b {
case '\t':
- p.indent += TabWidth
+ p.indent += p.p.TabWidth
case '\n':
p.indent = 0
if _, err = p.output.Write(data[n : n+1]); err != nil {
diff --git a/godoc/template.go b/godoc/template.go
index e8c4ba4a..325bc8ca 100644
--- a/godoc/template.go
+++ b/godoc/template.go
@@ -37,7 +37,6 @@ import (
"log"
"regexp"
"strings"
- "text/template"
"code.google.com/p/go.tools/godoc/vfs"
)
@@ -45,14 +44,10 @@ import (
// Functions in this file panic on error, but the panic is recovered
// to an error by 'code'.
-var TemplateFuncs = template.FuncMap{
- "code": code,
-}
-
// contents reads and returns the content of the named file
// (from the virtual file system, so for example /doc refers to $GOROOT/doc).
-func contents(name string) string {
- file, err := vfs.ReadFile(FS, name)
+func (c *Corpus) contents(name string) string {
+ file, err := vfs.ReadFile(c.fs, name)
if err != nil {
log.Panic(err)
}
@@ -75,14 +70,14 @@ func stringFor(arg interface{}) string {
return ""
}
-func code(file string, arg ...interface{}) (s string, err error) {
+func (p *Presentation) code(file string, arg ...interface{}) (s string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
}()
- text := contents(file)
+ text := p.Corpus.contents(file)
var command string
switch len(arg) {
case 0:
@@ -90,10 +85,10 @@ func code(file string, arg ...interface{}) (s string, err error) {
command = fmt.Sprintf("code %q", file)
case 1:
command = fmt.Sprintf("code %q %s", file, stringFor(arg[0]))
- text = oneLine(file, text, arg[0])
+ text = p.Corpus.oneLine(file, text, arg[0])
case 2:
command = fmt.Sprintf("code %q %s %s", file, stringFor(arg[0]), stringFor(arg[1]))
- text = multipleLines(file, text, arg[0], arg[1])
+ text = p.Corpus.multipleLines(file, text, arg[0], arg[1])
default:
return "", fmt.Errorf("incorrect code invocation: code %q %q", file, arg)
}
@@ -125,8 +120,8 @@ func parseArg(arg interface{}, file string, max int) (ival int, sval string, isI
}
// oneLine returns the single line generated by a two-argument code invocation.
-func oneLine(file, text string, arg interface{}) string {
- lines := strings.SplitAfter(contents(file), "\n")
+func (c *Corpus) oneLine(file, text string, arg interface{}) string {
+ lines := strings.SplitAfter(c.contents(file), "\n")
line, pattern, isInt := parseArg(arg, file, len(lines))
if isInt {
return lines[line-1]
@@ -135,8 +130,8 @@ func oneLine(file, text string, arg interface{}) string {
}
// multipleLines returns the text generated by a three-argument code invocation.
-func multipleLines(file, text string, arg1, arg2 interface{}) string {
- lines := strings.SplitAfter(contents(file), "\n")
+func (c *Corpus) multipleLines(file, text string, arg1, arg2 interface{}) string {
+ lines := strings.SplitAfter(c.contents(file), "\n")
line1, pattern1, isInt1 := parseArg(arg1, file, len(lines))
line2, pattern2, isInt2 := parseArg(arg2, file, len(lines))
if !isInt1 {