From: Robert Griesemer Date: Mon, 2 Nov 2009 17:25:39 +0000 (-0800) Subject: - initial steps towards showing directory tree instead of X-Git-Tag: weekly.2009-11-06~140 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=49d295d5924ea083d3a21fd97682958342258398;p=gostls13.git - initial steps towards showing directory tree instead of just a single directory - all pieces present but not well integrated - directory tree served at the moment under /tree R=rsc http://go/go-review/1018016 --- diff --git a/lib/godoc/dirs.html b/lib/godoc/dirs.html new file mode 100644 index 0000000000..23d66d5bcb --- /dev/null +++ b/lib/godoc/dirs.html @@ -0,0 +1,6 @@ + + +{.repeated section Subdirs} + +{.end} +
{Name|html}
{@|dir}
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index d20775b403..8b28082a19 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -85,9 +85,6 @@ var ( pkgroot = flag.String("pkgroot", "src/pkg", "root package source directory (if unrooted, relative to goroot)"); tmplroot = flag.String("tmplroot", "lib/godoc", "root template directory (if unrooted, relative to goroot)"); - // periodic sync - syncTime RWValue; // time of last sync - // layout control tabwidth = flag.Int("tabwidth", 4, "tab width"); ) @@ -99,7 +96,6 @@ func init() { goroot = "/home/r/go-release/go"; } flag.StringVar(&goroot, "goroot", goroot, "Go root directory"); - syncTime.set(nil); // have a reasonable initial value (time is shown on web page) } @@ -131,6 +127,58 @@ func htmlEscape(s string) string { } +// ---------------------------------------------------------------------------- +// Directory trees + +type Directory struct { + Path string; // including Name + Name string; + Subdirs []*Directory +} + + +func newDirTree0(path, name string) *Directory { + list, _ := io.ReadDir(path); // ignore errors + // determine number of subdirectories n + n := 0; + for _, d := range list { + if isPkgDir(d) { + n++; + } + } + // create Directory node + var subdirs []*Directory; + if n > 0 { + subdirs = make([]*Directory, n); + i := 0; + for _, d := range list { + if isPkgDir(d) { + subdirs[i] = newDirTree0(pathutil.Join(path, d.Name), d.Name); + i++; + } + } + } + if strings.HasPrefix(path, "src/") { + path = path[len("src/") : len(path)]; + } + return &Directory{path, name, subdirs}; +} + + +func newDirTree(root string) *Directory { + d, err := os.Lstat(root); + if err != nil { + log.Stderrf("%v", err); + return nil; + } + if !isPkgDir(d) { + log.Stderrf("not a package directory: %s", d.Name); + return nil; + } + return newDirTree0(root, d.Name); +} + + // ---------------------------------------------------------------------------- // Parsing @@ -310,6 +358,15 @@ func textFmt(w io.Writer, x interface{}, format string) { } +// Template formatter for "dir" format. +func dirFmt(w io.Writer, x interface{}, format string) { + _ = x.(*Directory); // die quickly if x has the wrong type + if err := dirsHtml.Execute(x, w); err != nil { + log.Stderrf("dirsHtml.Execute: %s", err); + } +} + + // Template formatter for "link" format. func linkFmt(w io.Writer, x interface{}, format string) { type Positioner interface { @@ -375,6 +432,7 @@ var fmap = template.FormatterMap{ "": textFmt, "html": htmlFmt, "html-comment": htmlCommentFmt, + "dir": dirFmt, "link": linkFmt, "infoClass": infoClassFmt, "infoLine": infoLineFmt, @@ -397,6 +455,7 @@ func readTemplate(name string) *template.Template { var ( + dirsHtml, godocHtml, packageHtml, packageText, @@ -408,6 +467,7 @@ var ( func readTemplates() { // have to delay until after flags processing, // so that main has chdir'ed to goroot. + dirsHtml = readTemplate("dirs.html"); godocHtml = readTemplate("godoc.html"); packageHtml = readTemplate("package.html"); packageText = readTemplate("package.txt"); @@ -420,6 +480,8 @@ func readTemplates() { // ---------------------------------------------------------------------------- // Generic HTML wrapper +var pkgTree RWValue; // *Directory tree of packages, updated with each sync + func servePage(c *http.Conn, title, query string, content []byte) { type Data struct { Title string; @@ -428,7 +490,7 @@ func servePage(c *http.Conn, title, query string, content []byte) { Content []byte; } - _, ts := syncTime.get(); + _, ts := pkgTree.get(); d := Data{ Title: title, Timestamp: time.SecondsToLocalTime(ts).String(), @@ -672,6 +734,21 @@ func servePkg(c *http.Conn, r *http.Request) { } +// ---------------------------------------------------------------------------- +// Directory tree + +// TODO(gri): Temporary - integrate with package serving. + +func serveTree(c *http.Conn, r *http.Request) { + dir, _ := pkgTree.get(); + + var buf bytes.Buffer; + dirFmt(&buf, dir, ""); + + servePage(c, "Package tree", "", buf.Bytes()); +} + + // ---------------------------------------------------------------------------- // Search @@ -692,7 +769,7 @@ func search(c *http.Conn, r *http.Request) { if index, timestamp := searchIndex.get(); index != nil { result.Query = query; result.Hit, result.Alt = index.(*Index).Lookup(query); - _, ts := syncTime.get(); + _, ts := pkgTree.get(); result.Accurate = timestamp >= ts; result.Legend = &infoClasses; } @@ -718,6 +795,7 @@ func search(c *http.Conn, r *http.Request) { func registerPublicHandlers(mux *http.ServeMux) { mux.Handle(Pkg, http.HandlerFunc(servePkg)); + mux.Handle("/tree", http.HandlerFunc(serveTree)); // TODO(gri): integrate with package serving mux.Handle("/search", http.HandlerFunc(search)); mux.Handle("/", http.HandlerFunc(serveFile)); } @@ -726,7 +804,7 @@ func registerPublicHandlers(mux *http.ServeMux) { // Indexing goroutine. func indexer() { for { - _, ts := syncTime.get(); + _, ts := pkgTree.get(); if _, timestamp := searchIndex.get(); timestamp < ts { // index possibly out of date - make a new one // (could use a channel to send an explicit signal diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 76ebab07b8..76f39cba8c 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -101,12 +101,13 @@ func dosync(c *http.Conn, r *http.Request) { args := []string{"/bin/sh", "-c", *syncCmd}; switch exec(c, args) { case 0: - // sync succeeded and some files have changed - syncTime.set(nil); + // sync succeeded and some files have changed; + // update package tree + pkgTree.set(newDirTree(*pkgroot)); fallthrough; case 1: - // sync failed because no files changed - // don't change the sync time + // sync failed because no files changed; + // don't change the package tree syncDelay.set(*syncMin); // revert to regular sync schedule default: // sync failed because of an error - back off exponentially, but try at least once a day @@ -170,10 +171,8 @@ func main() { http.Handle("/debug/sync", http.HandlerFunc(dosync)); } - // The server may have been restarted; always wait 1sec to - // give the forking server a chance to shut down and release - // the http port. - time.Sleep(1e9); + // Compute package tree with corresponding timestamp. + pkgTree.set(newDirTree(*pkgroot)); // Start sync goroutine, if enabled. if *syncCmd != "" && *syncMin > 0 { @@ -193,6 +192,12 @@ func main() { // Start indexing goroutine. go indexer(); + // The server may have been restarted; always wait 1sec to + // give the forking server a chance to shut down and release + // the http port. + // TODO(gri): Do we still need this? + time.Sleep(1e9); + // Start http server. if err := http.ListenAndServe(*httpaddr, handler); err != nil { log.Exitf("ListenAndServe %s: %v", *httpaddr, err); @@ -200,6 +205,10 @@ func main() { return; } + // Command line mode. + // No package tree; set it to nil so we have a reasonable time stamp. + pkgTree.set(nil); + if *html { packageText = packageHtml; parseerrorText = parseerrorHtml;