Commit 49d295d5 authored by Robert Griesemer's avatar Robert Griesemer

- 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
parent b6735211
<table border="0" cellpadding="0" cellspacing="0">
<tr><td><a href="{Path|html}">{Name|html}</a></td></tr>
{.repeated section Subdirs}
<tr><td></td><td>{@|dir}</td></tr>
{.end}
</table>
...@@ -85,9 +85,6 @@ var ( ...@@ -85,9 +85,6 @@ var (
pkgroot = flag.String("pkgroot", "src/pkg", "root package source directory (if unrooted, relative to goroot)"); 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)"); tmplroot = flag.String("tmplroot", "lib/godoc", "root template directory (if unrooted, relative to goroot)");
// periodic sync
syncTime RWValue; // time of last sync
// layout control // layout control
tabwidth = flag.Int("tabwidth", 4, "tab width"); tabwidth = flag.Int("tabwidth", 4, "tab width");
) )
...@@ -99,7 +96,6 @@ func init() { ...@@ -99,7 +96,6 @@ func init() {
goroot = "/home/r/go-release/go"; goroot = "/home/r/go-release/go";
} }
flag.StringVar(&goroot, "goroot", goroot, "Go root directory"); 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 { ...@@ -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 // Parsing
...@@ -310,6 +358,15 @@ func textFmt(w io.Writer, x interface{}, format string) { ...@@ -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. // Template formatter for "link" format.
func linkFmt(w io.Writer, x interface{}, format string) { func linkFmt(w io.Writer, x interface{}, format string) {
type Positioner interface { type Positioner interface {
...@@ -375,6 +432,7 @@ var fmap = template.FormatterMap{ ...@@ -375,6 +432,7 @@ var fmap = template.FormatterMap{
"": textFmt, "": textFmt,
"html": htmlFmt, "html": htmlFmt,
"html-comment": htmlCommentFmt, "html-comment": htmlCommentFmt,
"dir": dirFmt,
"link": linkFmt, "link": linkFmt,
"infoClass": infoClassFmt, "infoClass": infoClassFmt,
"infoLine": infoLineFmt, "infoLine": infoLineFmt,
...@@ -397,6 +455,7 @@ func readTemplate(name string) *template.Template { ...@@ -397,6 +455,7 @@ func readTemplate(name string) *template.Template {
var ( var (
dirsHtml,
godocHtml, godocHtml,
packageHtml, packageHtml,
packageText, packageText,
...@@ -408,6 +467,7 @@ var ( ...@@ -408,6 +467,7 @@ var (
func readTemplates() { func readTemplates() {
// have to delay until after flags processing, // have to delay until after flags processing,
// so that main has chdir'ed to goroot. // so that main has chdir'ed to goroot.
dirsHtml = readTemplate("dirs.html");
godocHtml = readTemplate("godoc.html"); godocHtml = readTemplate("godoc.html");
packageHtml = readTemplate("package.html"); packageHtml = readTemplate("package.html");
packageText = readTemplate("package.txt"); packageText = readTemplate("package.txt");
...@@ -420,6 +480,8 @@ func readTemplates() { ...@@ -420,6 +480,8 @@ func readTemplates() {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Generic HTML wrapper // Generic HTML wrapper
var pkgTree RWValue; // *Directory tree of packages, updated with each sync
func servePage(c *http.Conn, title, query string, content []byte) { func servePage(c *http.Conn, title, query string, content []byte) {
type Data struct { type Data struct {
Title string; Title string;
...@@ -428,7 +490,7 @@ func servePage(c *http.Conn, title, query string, content []byte) { ...@@ -428,7 +490,7 @@ func servePage(c *http.Conn, title, query string, content []byte) {
Content []byte; Content []byte;
} }
_, ts := syncTime.get(); _, ts := pkgTree.get();
d := Data{ d := Data{
Title: title, Title: title,
Timestamp: time.SecondsToLocalTime(ts).String(), Timestamp: time.SecondsToLocalTime(ts).String(),
...@@ -672,6 +734,21 @@ func servePkg(c *http.Conn, r *http.Request) { ...@@ -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 // Search
...@@ -692,7 +769,7 @@ func search(c *http.Conn, r *http.Request) { ...@@ -692,7 +769,7 @@ func search(c *http.Conn, r *http.Request) {
if index, timestamp := searchIndex.get(); index != nil { if index, timestamp := searchIndex.get(); index != nil {
result.Query = query; result.Query = query;
result.Hit, result.Alt = index.(*Index).Lookup(query); result.Hit, result.Alt = index.(*Index).Lookup(query);
_, ts := syncTime.get(); _, ts := pkgTree.get();
result.Accurate = timestamp >= ts; result.Accurate = timestamp >= ts;
result.Legend = &infoClasses; result.Legend = &infoClasses;
} }
...@@ -718,6 +795,7 @@ func search(c *http.Conn, r *http.Request) { ...@@ -718,6 +795,7 @@ func search(c *http.Conn, r *http.Request) {
func registerPublicHandlers(mux *http.ServeMux) { func registerPublicHandlers(mux *http.ServeMux) {
mux.Handle(Pkg, http.HandlerFunc(servePkg)); 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("/search", http.HandlerFunc(search));
mux.Handle("/", http.HandlerFunc(serveFile)); mux.Handle("/", http.HandlerFunc(serveFile));
} }
...@@ -726,7 +804,7 @@ func registerPublicHandlers(mux *http.ServeMux) { ...@@ -726,7 +804,7 @@ func registerPublicHandlers(mux *http.ServeMux) {
// Indexing goroutine. // Indexing goroutine.
func indexer() { func indexer() {
for { for {
_, ts := syncTime.get(); _, ts := pkgTree.get();
if _, timestamp := searchIndex.get(); timestamp < ts { if _, timestamp := searchIndex.get(); timestamp < ts {
// index possibly out of date - make a new one // index possibly out of date - make a new one
// (could use a channel to send an explicit signal // (could use a channel to send an explicit signal
......
...@@ -101,12 +101,13 @@ func dosync(c *http.Conn, r *http.Request) { ...@@ -101,12 +101,13 @@ func dosync(c *http.Conn, r *http.Request) {
args := []string{"/bin/sh", "-c", *syncCmd}; args := []string{"/bin/sh", "-c", *syncCmd};
switch exec(c, args) { switch exec(c, args) {
case 0: case 0:
// sync succeeded and some files have changed // sync succeeded and some files have changed;
syncTime.set(nil); // update package tree
pkgTree.set(newDirTree(*pkgroot));
fallthrough; fallthrough;
case 1: case 1:
// sync failed because no files changed // sync failed because no files changed;
// don't change the sync time // don't change the package tree
syncDelay.set(*syncMin); // revert to regular sync schedule syncDelay.set(*syncMin); // revert to regular sync schedule
default: default:
// sync failed because of an error - back off exponentially, but try at least once a day // sync failed because of an error - back off exponentially, but try at least once a day
...@@ -170,10 +171,8 @@ func main() { ...@@ -170,10 +171,8 @@ func main() {
http.Handle("/debug/sync", http.HandlerFunc(dosync)); http.Handle("/debug/sync", http.HandlerFunc(dosync));
} }
// The server may have been restarted; always wait 1sec to // Compute package tree with corresponding timestamp.
// give the forking server a chance to shut down and release pkgTree.set(newDirTree(*pkgroot));
// the http port.
time.Sleep(1e9);
// Start sync goroutine, if enabled. // Start sync goroutine, if enabled.
if *syncCmd != "" && *syncMin > 0 { if *syncCmd != "" && *syncMin > 0 {
...@@ -193,6 +192,12 @@ func main() { ...@@ -193,6 +192,12 @@ func main() {
// Start indexing goroutine. // Start indexing goroutine.
go indexer(); 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. // Start http server.
if err := http.ListenAndServe(*httpaddr, handler); err != nil { if err := http.ListenAndServe(*httpaddr, handler); err != nil {
log.Exitf("ListenAndServe %s: %v", *httpaddr, err); log.Exitf("ListenAndServe %s: %v", *httpaddr, err);
...@@ -200,6 +205,10 @@ func main() { ...@@ -200,6 +205,10 @@ func main() {
return; return;
} }
// Command line mode.
// No package tree; set it to nil so we have a reasonable time stamp.
pkgTree.set(nil);
if *html { if *html {
packageText = packageHtml; packageText = packageHtml;
parseerrorText = parseerrorHtml; parseerrorText = parseerrorHtml;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment