1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package godoc
6
7import (
8	"net/http"
9	"regexp"
10	"sync"
11	"text/template"
12
13	"golang.org/x/tools/godoc/vfs/httpfs"
14)
15
16// SearchResultFunc functions return an HTML body for displaying search results.
17type SearchResultFunc func(p *Presentation, result SearchResult) []byte
18
19// Presentation generates output from a corpus.
20type Presentation struct {
21	Corpus *Corpus
22
23	mux        *http.ServeMux
24	fileServer http.Handler
25	cmdHandler handlerServer
26	pkgHandler handlerServer
27
28	CallGraphHTML,
29	DirlistHTML,
30	ErrorHTML,
31	ExampleHTML,
32	GodocHTML,
33	ImplementsHTML,
34	MethodSetHTML,
35	PackageHTML,
36	PackageRootHTML,
37	SearchHTML,
38	SearchDocHTML,
39	SearchCodeHTML,
40	SearchTxtHTML,
41	SearchDescXML *template.Template
42
43	// TabWidth optionally specifies the tab width.
44	TabWidth int
45
46	ShowTimestamps bool
47	ShowPlayground bool
48	DeclLinks      bool
49
50	// NotesRx optionally specifies a regexp to match
51	// notes to render in the output.
52	NotesRx *regexp.Regexp
53
54	// AdjustPageInfoMode optionally specifies a function to
55	// modify the PageInfoMode of a request. The default chosen
56	// value is provided.
57	AdjustPageInfoMode func(req *http.Request, mode PageInfoMode) PageInfoMode
58
59	// URLForSrc optionally specifies a function that takes a source file and
60	// returns a URL for it.
61	// The source file argument has the form /src/<path>/<filename>.
62	URLForSrc func(src string) string
63
64	// URLForSrcPos optionally specifies a function to create a URL given a
65	// source file, a line from the source file (1-based), and low & high offset
66	// positions (0-based, bytes from beginning of file). Ideally, the returned
67	// URL will be for the specified line of the file, while the high & low
68	// positions will be used to highlight a section of the file.
69	// The source file argument has the form /src/<path>/<filename>.
70	URLForSrcPos func(src string, line, low, high int) string
71
72	// URLForSrcQuery optionally specifies a function to create a URL given a
73	// source file, a query string, and a line from the source file (1-based).
74	// The source file argument has the form /src/<path>/<filename>.
75	// The query argument will be escaped for the purposes of embedding in a URL
76	// query parameter.
77	// Ideally, the returned URL will be for the specified line of the file with
78	// the query string highlighted.
79	URLForSrcQuery func(src, query string, line int) string
80
81	// SearchResults optionally specifies a list of functions returning an HTML
82	// body for displaying search results.
83	SearchResults []SearchResultFunc
84
85	// GoogleAnalytics optionally adds Google Analytics via the provided
86	// tracking ID to each page.
87	GoogleAnalytics string
88
89	initFuncMapOnce sync.Once
90	funcMap         template.FuncMap
91	templateFuncs   template.FuncMap
92}
93
94// NewPresentation returns a new Presentation from a corpus.
95// It sets SearchResults to:
96// [SearchResultDoc SearchResultCode SearchResultTxt].
97func NewPresentation(c *Corpus) *Presentation {
98	if c == nil {
99		panic("nil Corpus")
100	}
101	p := &Presentation{
102		Corpus:     c,
103		mux:        http.NewServeMux(),
104		fileServer: http.FileServer(httpfs.New(c.fs)),
105
106		TabWidth:  4,
107		DeclLinks: true,
108		SearchResults: []SearchResultFunc{
109			(*Presentation).SearchResultDoc,
110			(*Presentation).SearchResultCode,
111			(*Presentation).SearchResultTxt,
112		},
113	}
114	p.cmdHandler = handlerServer{
115		p:       p,
116		c:       c,
117		pattern: "/cmd/",
118		fsRoot:  "/src",
119	}
120	p.pkgHandler = handlerServer{
121		p:           p,
122		c:           c,
123		pattern:     "/pkg/",
124		stripPrefix: "pkg/",
125		fsRoot:      "/src",
126		exclude:     []string{"/src/cmd"},
127	}
128	p.cmdHandler.registerWithMux(p.mux)
129	p.pkgHandler.registerWithMux(p.mux)
130	p.mux.HandleFunc("/", p.ServeFile)
131	p.mux.HandleFunc("/search", p.HandleSearch)
132	p.mux.HandleFunc("/opensearch.xml", p.serveSearchDesc)
133	return p
134}
135
136func (p *Presentation) FileServer() http.Handler {
137	return p.fileServer
138}
139
140func (p *Presentation) ServeHTTP(w http.ResponseWriter, r *http.Request) {
141	p.mux.ServeHTTP(w, r)
142}
143
144func (p *Presentation) PkgFSRoot() string {
145	return p.pkgHandler.fsRoot
146}
147
148func (p *Presentation) CmdFSRoot() string {
149	return p.cmdHandler.fsRoot
150}
151
152// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
153// but this doesn't feel right.
154func (p *Presentation) GetPkgPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
155	return p.pkgHandler.GetPageInfo(abspath, relpath, mode, "", "")
156}
157
158// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
159// but this doesn't feel right.
160func (p *Presentation) GetCmdPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
161	return p.cmdHandler.GetPageInfo(abspath, relpath, mode, "", "")
162}
163