]> Cypherpunks repositories - gostls13.git/commitdiff
- initial steps towards showing directory tree instead of
authorRobert Griesemer <gri@golang.org>
Mon, 2 Nov 2009 17:25:39 +0000 (09:25 -0800)
committerRobert Griesemer <gri@golang.org>
Mon, 2 Nov 2009 17:25:39 +0000 (09:25 -0800)
  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

lib/godoc/dirs.html [new file with mode: 0644]
src/cmd/godoc/godoc.go
src/cmd/godoc/main.go

diff --git a/lib/godoc/dirs.html b/lib/godoc/dirs.html
new file mode 100644 (file)
index 0000000..23d66d5
--- /dev/null
@@ -0,0 +1,6 @@
+<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>
index d20775b40336bf986348b0bb2c9e85839c944210..8b28082a198d21a54dbdb652510c884f08835283 100644 (file)
@@ -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
index 76ebab07b8edd2e7b757a4c52885162b3ba472b6..76f39cba8cd6951a89d003c62394ad26bbd2e35e 100644 (file)
@@ -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;