godoc: remove more global variables

More moves into Corpus and Presentation.

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/11492043
diff --git a/godoc/godoc.go b/godoc/godoc.go
index e400f6d..093f007 100644
--- a/godoc/godoc.go
+++ b/godoc/godoc.go
@@ -27,46 +27,10 @@
 	"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 @@
 // 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 @@
 	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 @@
 	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 `<span class="alert">no snippet text available</span>`
 }
 
-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 @@
 	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 @@
 		// 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 @@
 	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 @@
 
 		// 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 @@
 		// 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 @@
 
 // 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 @@
 
 // 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 @@
 }
 
 // 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 @@
 	//           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)
+}