godoc: add a Corpus and Presentation type

Start of moving globals into those types.

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/11484043
This commit is contained in:
Brad Fitzpatrick 2013-07-18 09:52:45 +10:00
parent 3af65e49c2
commit 4fc6323385
11 changed files with 221 additions and 117 deletions

View File

@ -62,7 +62,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
cw, err := loadCodewalk(abspath + ".xml")
if err != nil {
log.Print(err)
godoc.ServeError(w, r, relpath, err)
pres.ServeError(w, r, relpath, err)
return
}
@ -71,7 +71,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
return
}
godoc.ServePage(w, godoc.Page{
pres.ServePage(w, godoc.Page{
Title: "Codewalk: " + cw.Title,
Tabtitle: cw.Title,
Body: applyTemplate(codewalkHTML, "codewalk", cw),
@ -188,7 +188,7 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string
dir, err := godoc.FS.ReadDir(abspath)
if err != nil {
log.Print(err)
godoc.ServeError(w, r, relpath, err)
pres.ServeError(w, r, relpath, err)
return
}
var v []interface{}
@ -205,7 +205,7 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string
}
}
godoc.ServePage(w, godoc.Page{
pres.ServePage(w, godoc.Page{
Title: "Codewalks",
Body: applyTemplate(codewalkdirHTML, "codewalkdir", v),
})
@ -222,7 +222,7 @@ func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) {
data, err := vfs.ReadFile(godoc.FS, abspath)
if err != nil {
log.Print(err)
godoc.ServeError(w, r, f, err)
pres.ServeError(w, r, f, err)
return
}
lo, _ := strconv.Atoi(r.FormValue("lo"))

View File

@ -9,16 +9,12 @@ import (
"flag"
"fmt"
htmlpkg "html"
"log"
"net/http"
"net/url"
pathpkg "path"
"regexp"
"runtime"
"strings"
"text/template"
@ -55,8 +51,7 @@ var (
// file system roots
// TODO(gri) consider the invariant that goroot always end in '/'
goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)")
goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
// layout control
_ = flagInt(&godoc.TabWidth, "tabwidth", 4, "tab width")
@ -67,8 +62,8 @@ var (
_ = flagBool(&godoc.DeclLinks, "links", true, "link identifiers to their declarations")
// search index
_ = flagBool(&godoc.IndexEnabled, "index", false, "enable search index")
_ = flagString(&godoc.IndexFiles, "index_files", "", "glob pattern specifying index files;"+
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")
@ -77,6 +72,8 @@ var (
_ = flagString(&godoc.NotesRx, "notes", "BUG", "regular expression matching note markers to show")
)
var pres *godoc.Presentation
func registerPublicHandlers(mux *http.ServeMux) {
godoc.CmdHandler.RegisterWithMux(mux)
godoc.PkgHandler.RegisterWithMux(mux)
@ -88,16 +85,6 @@ func registerPublicHandlers(mux *http.ServeMux) {
mux.HandleFunc("/", serveFile)
}
func initFSTree() {
dir := godoc.NewDirectory(pathpkg.Join("/", *testDir), -1)
if dir == nil {
log.Println("Warning: FSTree is nil")
return
}
godoc.FSTree.Set(dir)
godoc.InvalidateIndex()
}
// ----------------------------------------------------------------------------
// Templates
@ -176,12 +163,12 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit
src, err := vfs.ReadFile(godoc.FS, abspath)
if err != nil {
log.Printf("ReadFile: %s", err)
godoc.ServeError(w, r, relpath, err)
pres.ServeError(w, r, relpath, err)
return
}
if r.FormValue("m") == "text" {
godoc.ServeText(w, src)
pres.ServeText(w, src)
return
}
@ -191,7 +178,7 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit
buf.WriteString("</pre>")
fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath))
godoc.ServePage(w, godoc.Page{
pres.ServePage(w, godoc.Page{
Title: title + " " + relpath,
Tabtitle: relpath,
Body: buf.Bytes(),
@ -205,11 +192,11 @@ func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath str
list, err := godoc.FS.ReadDir(abspath)
if err != nil {
godoc.ServeError(w, r, relpath, err)
pres.ServeError(w, r, relpath, err)
return
}
godoc.ServePage(w, godoc.Page{
pres.ServePage(w, godoc.Page{
Title: "Directory " + relpath,
Tabtitle: relpath,
Body: applyTemplate(godoc.DirlistHTML, "dirlistHTML", list),
@ -241,7 +228,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently)
return
}
godoc.ServeHTMLDoc(w, r, abspath, relpath)
pres.ServeHTMLDoc(w, r, abspath, relpath)
return
case ".go":
@ -252,7 +239,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
dir, err := godoc.FS.Lstat(abspath)
if err != nil {
log.Print(err)
godoc.ServeError(w, r, relpath, err)
pres.ServeError(w, r, relpath, err)
return
}
@ -261,7 +248,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
return
}
if index := pathpkg.Join(abspath, "index.html"); util.IsTextFile(godoc.FS, index) {
godoc.ServeHTMLDoc(w, r, index, index)
pres.ServeHTMLDoc(w, r, index, index)
return
}
serveDirectory(w, r, abspath, relpath)
@ -362,7 +349,7 @@ func lookup(query string) (result SearchResult) {
}
// is the result accurate?
if godoc.IndexEnabled {
if pres.Corpus.IndexEnabled {
if _, ts := godoc.FSModified.Get(); 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"
@ -379,7 +366,7 @@ func search(w http.ResponseWriter, r *http.Request) {
result := lookup(query)
if godoc.GetPageInfoMode(r)&godoc.NoHTML != 0 {
godoc.ServeText(w, applyTemplate(godoc.SearchText, "searchText", result))
pres.ServeText(w, applyTemplate(godoc.SearchText, "searchText", result))
return
}
@ -390,7 +377,7 @@ func search(w http.ResponseWriter, r *http.Request) {
title = fmt.Sprintf(`No results found for query %q`, query)
}
godoc.ServePage(w, godoc.Page{
pres.ServePage(w, godoc.Page{
Title: title,
Tabtitle: query,
Query: query,

View File

@ -158,9 +158,8 @@ func main() {
usage()
}
if godoc.TabWidth < 0 {
log.Fatalf("negative tabwidth %d", godoc.TabWidth)
}
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.
@ -168,9 +167,9 @@ func main() {
// same is true for the http handlers in initHandlers.
if *zipfile == "" {
// use file system of underlying OS
godoc.FS.Bind("/", vfs.OS(*goroot), "/", vfs.BindReplace)
fs.Bind("/", vfs.OS(*goroot), "/", vfs.BindReplace)
if *templateDir != "" {
godoc.FS.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore)
fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore)
}
} else {
// use file system specified via .zip file (path separator must be '/')
@ -179,16 +178,23 @@ func main() {
log.Fatalf("%s: %s\n", *zipfile, err)
}
defer rc.Close() // be nice (e.g., -writeIndex mode)
godoc.FS.Bind("/", zipvfs.New(rc, *zipfile), *goroot, vfs.BindReplace)
fs.Bind("/", zipvfs.New(rc, *zipfile), *goroot, vfs.BindReplace)
}
// Bind $GOPATH trees into Go root.
for _, p := range filepath.SplitList(build.Default.GOPATH) {
godoc.FS.Bind("/src/pkg", vfs.OS(p), "/src", vfs.BindAfter)
fs.Bind("/src/pkg", vfs.OS(p), "/src", vfs.BindAfter)
}
readTemplates()
godoc.InitHandlers(godoc.FS)
corpus := godoc.NewCorpus(fs)
corpus.IndexEnabled = *indexEnabled
corpus.IndexFiles = *indexFiles
pres = godoc.NewPresentation(corpus)
// ...
godoc.InitHandlers(pres)
if *writeIndex {
// Write search index and exit.
@ -198,7 +204,6 @@ func main() {
log.Println("initialize file systems")
*verbose = true // want to see what happens
initFSTree()
godoc.IndexThrottle = 1.0
godoc.UpdateIndex()
@ -221,8 +226,7 @@ func main() {
// Print content that would be served at the URL *urlFlag.
if *urlFlag != "" {
registerPublicHandlers(http.DefaultServeMux)
initFSTree()
godoc.UpdateMetadata()
// Try up to 10 fetches, following redirects.
urlstr := *urlFlag
for i := 0; i < 10; i++ {
@ -268,30 +272,21 @@ func main() {
log.Printf("goroot = %s", *goroot)
log.Printf("tabwidth = %d", godoc.TabWidth)
switch {
case !godoc.IndexEnabled:
case !*indexEnabled:
log.Print("search index disabled")
case godoc.MaxResults > 0:
log.Printf("full text index enabled (maxresults = %d)", godoc.MaxResults)
default:
log.Print("identifier search index enabled")
}
godoc.FS.Fprint(os.Stderr)
fs.Fprint(os.Stderr)
handler = loggingHandler(handler)
}
registerPublicHandlers(http.DefaultServeMux)
// Initialize default directory tree with corresponding timestamp.
// (Do it in a goroutine so that launch is quick.)
go initFSTree()
// Immediately update metadata.
godoc.UpdateMetadata()
// Periodically refresh metadata.
go godoc.RefreshMetadataLoop()
// Initialize search index.
if godoc.IndexEnabled {
if *indexEnabled {
go godoc.RunIndexer()
}
@ -335,18 +330,18 @@ func main() {
var forceCmd bool
var abspath, relpath string
if filepath.IsAbs(path) {
godoc.FS.Bind(target, vfs.OS(path), "/", vfs.BindReplace)
fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace)
abspath = target
} else if build.IsLocalImport(path) {
cwd, _ := os.Getwd() // ignore errors
path = filepath.Join(cwd, path)
godoc.FS.Bind(target, vfs.OS(path), "/", vfs.BindReplace)
fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace)
abspath = target
} else if strings.HasPrefix(path, cmdPrefix) {
path = strings.TrimPrefix(path, cmdPrefix)
forceCmd = true
} else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" {
godoc.FS.Bind(target, vfs.OS(bp.Dir), "/", vfs.BindReplace)
fs.Bind(target, vfs.OS(bp.Dir), "/", vfs.BindReplace)
abspath = target
relpath = bp.ImportPath
} else {

85
godoc/corpus.go Normal file
View File

@ -0,0 +1,85 @@
// Copyright 2013 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 (
"errors"
pathpkg "path"
"code.google.com/p/go.tools/godoc/util"
"code.google.com/p/go.tools/godoc/vfs"
)
// A Corpus holds all the state related to serving and indexing a
// collection of Go code.
//
// Construct a new Corpus with NewCorpus, then modify options,
// then call its Init method.
type Corpus struct {
fs vfs.FileSystem
// IndexEnabled controls whether indexing is enabled.
IndexEnabled bool
// IndexFiles specifies a glob pattern specifying index files.
// If not empty, the index is read from these files in sorted
// order.
IndexFiles string
IndexThrottle float64
// MaxResults optionally specifies the maximum results for indexing.
// The default is 1000.
MaxResults int
testDir string // TODO(bradfitz,adg): migrate old godoc flag? looks unused.
// Send a value on this channel to trigger a metadata refresh.
// It is buffered so that if a signal is not lost if sent
// during a refresh.
refreshMetadataSignal chan bool
// 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
}
// NewCorpus returns a new Corpus from a filesystem.
// Set any options on Corpus before calling the Corpus.Init method.
func NewCorpus(fs vfs.FileSystem) *Corpus {
c := &Corpus{
fs: fs,
refreshMetadataSignal: make(chan bool, 1),
MaxResults: 1000,
IndexEnabled: true,
}
return c
}
// Init initializes Corpus, once options on Corpus are set.
// It must be called before any subsequent method calls.
func (c *Corpus) Init() error {
// TODO(bradfitz): do this in a goroutine because newDirectory might block for a long time?
// It used to be sometimes done in a goroutine before, at least in HTTP server mode.
if err := c.initFSTree(); err != nil {
return err
}
c.updateMetadata()
go c.refreshMetadataLoop()
return nil
}
func (c *Corpus) initFSTree() error {
dir := c.newDirectory(pathpkg.Join("/", c.testDir), -1)
if dir == nil {
return errors.New("godoc: corpus fstree is nil")
}
c.fsTree.Set(dir)
c.invalidateIndex()
return nil
}

View File

@ -50,6 +50,7 @@ func isPkgDir(fi os.FileInfo) bool {
}
type treeBuilder struct {
c *Corpus
maxDepth int
}
@ -69,7 +70,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
}
}
list, _ := FS.ReadDir(path)
list, _ := b.c.fs.ReadDir(path)
// determine number of subdirectories and if there are package files
ndirs := 0
@ -161,9 +162,9 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
// are assumed to contain package files even if their contents are not known
// (i.e., in this case the tree may contain directories w/o any package files).
//
func NewDirectory(root string, maxDepth int) *Directory {
func (c *Corpus) newDirectory(root string, maxDepth int) *Directory {
// The root could be a symbolic link so use Stat not Lstat.
d, err := FS.Stat(root)
d, err := c.fs.Stat(root)
// If we fail here, report detailed error messages; otherwise
// is is hard to see why a directory tree was not built.
switch {
@ -177,7 +178,7 @@ func NewDirectory(root string, maxDepth int) *Directory {
if maxDepth < 0 {
maxDepth = 1e6 // "infinity"
}
b := treeBuilder{maxDepth}
b := treeBuilder{c, maxDepth}
// the file set provided is only for local parsing, no position
// information escapes and thus we don't need to save the set
return b.newDirTree(token.NewFileSet(), root, d.Name(), 0)

View File

@ -38,6 +38,7 @@ import (
// 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
@ -54,8 +55,6 @@ var (
// TODO(bradfitz,adg): delete this flag
ShowPlayground = false
IndexEnabled = false
ShowTimestamps = false
Verbose = false

View File

@ -1038,9 +1038,9 @@ func (x *Index) LookupRegexp(r *regexp.Regexp, n int) (found int, result []FileL
// InvalidateIndex should be called whenever any of the file systems
// under godoc's observation change so that the indexer is kicked on.
func InvalidateIndex() {
FSModified.Set(nil)
refreshMetadata()
func (c *Corpus) invalidateIndex() {
c.fsModified.Set(nil)
c.refreshMetadata()
}
// indexUpToDate() returns true if the search index is not older

View File

@ -58,11 +58,11 @@ func extractMetadata(b []byte) (meta Metadata, tail []byte, err error) {
// UpdateMetadata scans $GOROOT/doc for HTML files, reads their metadata,
// and updates the DocMetadata map.
func UpdateMetadata() {
func (c *Corpus) updateMetadata() {
metadata := make(map[string]*Metadata)
var scan func(string) // scan is recursive
scan = func(dir string) {
fis, err := FS.ReadDir(dir)
fis, err := c.fs.ReadDir(dir)
if err != nil {
log.Println("updateMetadata:", err)
return
@ -77,7 +77,7 @@ func UpdateMetadata() {
continue
}
// Extract metadata from the file.
b, err := vfs.ReadFile(FS, name)
b, err := vfs.ReadFile(c.fs, name)
if err != nil {
log.Printf("updateMetadata %s: %v", name, err)
continue
@ -123,27 +123,22 @@ func MetadataFor(relpath string) *Metadata {
return nil
}
// Send a value on this channel to trigger a metadata refresh.
// It is buffered so that if a signal is not lost if sent during a refresh.
//
var refreshMetadataSignal = make(chan bool, 1)
// refreshMetadata sends a signal to update DocMetadata. If a refresh is in
// progress the metadata will be refreshed again afterward.
//
func refreshMetadata() {
func (c *Corpus) refreshMetadata() {
select {
case refreshMetadataSignal <- true:
case c.refreshMetadataSignal <- true:
default:
}
}
// RefreshMetadataLoop runs forever, updating DocMetadata when the underlying
// file system changes. It should be launched in a goroutine.
func RefreshMetadataLoop() {
func (c *Corpus) refreshMetadataLoop() {
for {
<-refreshMetadataSignal
UpdateMetadata()
<-c.refreshMetadataSignal
c.updateMetadata()
time.Sleep(10 * time.Second) // at most once every 10 seconds
}
}

View File

@ -37,11 +37,11 @@ var (
SearchDescXML *template.Template
)
func ServePage(w http.ResponseWriter, page Page) {
func (p *Presentation) ServePage(w http.ResponseWriter, page Page) {
if page.Tabtitle == "" {
page.Tabtitle = page.Title
}
page.SearchBox = IndexEnabled
page.SearchBox = p.Corpus.IndexEnabled
page.Playground = ShowPlayground
page.Version = runtime.Version()
if err := GodocHTML.Execute(w, page); err != nil && err != http.ErrBodyNotAllowed {
@ -51,9 +51,9 @@ func ServePage(w http.ResponseWriter, page Page) {
}
}
func ServeError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
func (p *Presentation) ServeError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
w.WriteHeader(http.StatusNotFound)
ServePage(w, Page{
p.ServePage(w, Page{
Title: "File " + relpath,
Subtitle: relpath,
Body: applyTemplate(ErrorHTML, "errorHTML", err), // err may contain an absolute path!

39
godoc/pres.go Normal file
View File

@ -0,0 +1,39 @@
// Copyright 2013 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 (
"text/template"
)
// Presentation generates output from a corpus.
type Presentation struct {
Corpus *Corpus
// TabWidth optionally specifies the tab width.
TabWidth int
ShowTimestamps bool
ShowPlayground bool
ShowExamples bool
DeclLinks bool
}
// NewPresentation returns a new Presentation from a corpus.
func NewPresentation(c *Corpus) *Presentation {
if c == nil {
panic("nil Corpus")
}
return &Presentation{
Corpus: c,
TabWidth: 4,
ShowExamples: true,
DeclLinks: true,
}
}
func (p *Presentation) FuncMap() template.FuncMap {
panic("")
}

View File

@ -42,16 +42,19 @@ var (
DocMetadata util.RWValue // mapping from paths to *Metadata
)
func InitHandlers(fs vfs.FileSystem) {
FileServer = http.FileServer(httpfs.New(fs))
CmdHandler = Server{"/cmd/", "/src/cmd"}
PkgHandler = Server{"/pkg/", "/src/pkg"}
func InitHandlers(p *Presentation) {
c := p.Corpus
FileServer = http.FileServer(httpfs.New(c.fs))
CmdHandler = Server{p, c, "/cmd/", "/src/cmd"}
PkgHandler = Server{p, c, "/pkg/", "/src/pkg"}
}
// Server is a godoc server.
type Server struct {
pattern string // url pattern; e.g. "/pkg/"
fsRoot string // file system root to which the pattern is mapped
p *Presentation
c *Corpus // copy of p.Corpus
pattern string // url pattern; e.g. "/pkg/"
fsRoot string // file system root to which the pattern is mapped
}
func (s *Server) FSRoot() string { return s.fsRoot }
@ -178,7 +181,7 @@ func (h *Server) GetPageInfo(abspath, relpath string, mode PageInfoMode) *PageIn
// command-line mode); compute one level for this page
// note: cannot use path filter here because in general
// it doesn't contain the FSTree path
dir = NewDirectory(abspath, 1)
dir = h.c.newDirectory(abspath, 1)
timestamp = time.Now()
}
info.Dirs = dir.listing(true)
@ -202,12 +205,12 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
info := h.GetPageInfo(abspath, relpath, mode)
if info.Err != nil {
log.Print(info.Err)
ServeError(w, r, relpath, info.Err)
h.p.ServeError(w, r, relpath, info.Err)
return
}
if mode&NoHTML != 0 {
ServeText(w, applyTemplate(PackageText, "packageText", info))
h.p.ServeText(w, applyTemplate(PackageText, "packageText", info))
return
}
@ -243,7 +246,7 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tabtitle = "Commands"
}
ServePage(w, Page{
h.p.ServePage(w, Page{
Title: title,
Tabtitle: tabtitle,
Subtitle: subtitle,
@ -431,16 +434,16 @@ func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) {
return
}
func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
src, err := vfs.ReadFile(FS, abspath)
func (p *Presentation) serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
src, err := vfs.ReadFile(p.Corpus.fs, abspath)
if err != nil {
log.Printf("ReadFile: %s", err)
ServeError(w, r, relpath, err)
p.ServeError(w, r, relpath, err)
return
}
if r.FormValue("m") == "text" {
ServeText(w, src)
p.ServeText(w, src)
return
}
@ -450,37 +453,37 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit
buf.WriteString("</pre>")
fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath))
ServePage(w, Page{
p.ServePage(w, Page{
Title: title + " " + relpath,
Tabtitle: relpath,
Body: buf.Bytes(),
})
}
func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
func (p *Presentation) serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
if redirect(w, r) {
return
}
list, err := FS.ReadDir(abspath)
if err != nil {
ServeError(w, r, relpath, err)
p.ServeError(w, r, relpath, err)
return
}
ServePage(w, Page{
p.ServePage(w, Page{
Title: "Directory " + relpath,
Tabtitle: relpath,
Body: applyTemplate(DirlistHTML, "dirlistHTML", list),
})
}
func ServeHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
func (p *Presentation) ServeHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
// get HTML body contents
src, err := vfs.ReadFile(FS, abspath)
if err != nil {
log.Printf("ReadFile: %s", err)
ServeError(w, r, relpath, err)
p.ServeError(w, r, relpath, err)
return
}
@ -502,13 +505,13 @@ func ServeHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath strin
tmpl, err := template.New("main").Funcs(TemplateFuncs).Parse(string(src))
if err != nil {
log.Printf("parsing template %s: %v", relpath, err)
ServeError(w, r, relpath, err)
p.ServeError(w, r, relpath, err)
return
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, nil); err != nil {
log.Printf("executing template %s: %v", relpath, err)
ServeError(w, r, relpath, err)
p.ServeError(w, r, relpath, err)
return
}
src = buf.Bytes()
@ -521,14 +524,14 @@ func ServeHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath strin
src = buf.Bytes()
}
ServePage(w, Page{
p.ServePage(w, Page{
Title: meta.Title,
Subtitle: meta.Subtitle,
Body: src,
})
}
func serveFile(w http.ResponseWriter, r *http.Request) {
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.
@ -553,18 +556,18 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently)
return
}
ServeHTMLDoc(w, r, abspath, relpath)
p.ServeHTMLDoc(w, r, abspath, relpath)
return
case ".go":
serveTextFile(w, r, abspath, relpath, "Source file")
p.serveTextFile(w, r, abspath, relpath, "Source file")
return
}
dir, err := FS.Lstat(abspath)
if err != nil {
log.Print(err)
ServeError(w, r, relpath, err)
p.ServeError(w, r, relpath, err)
return
}
@ -573,10 +576,10 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
return
}
if index := pathpkg.Join(abspath, "index.html"); util.IsTextFile(FS, index) {
ServeHTMLDoc(w, r, index, index)
p.ServeHTMLDoc(w, r, index, index)
return
}
serveDirectory(w, r, abspath, relpath)
p.serveDirectory(w, r, abspath, relpath)
return
}
@ -584,7 +587,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
if redirectFile(w, r) {
return
}
serveTextFile(w, r, abspath, relpath, "Text file")
p.serveTextFile(w, r, abspath, relpath, "Text file")
return
}
@ -603,7 +606,7 @@ func serveSearchDesc(w http.ResponseWriter, r *http.Request) {
}
}
func ServeText(w http.ResponseWriter, text []byte) {
func (p *Presentation) ServeText(w http.ResponseWriter, text []byte) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Write(text)
}