]> Cypherpunks repositories - gostls13.git/commitdiff
- show recursive package directory structure in package pages
authorRobert Griesemer <gri@golang.org>
Tue, 3 Nov 2009 06:44:01 +0000 (22:44 -0800)
committerRobert Griesemer <gri@golang.org>
Tue, 3 Nov 2009 06:44:01 +0000 (22:44 -0800)
- removed some underbars in section headings for better looks
- various minor tweaks

R=rsc
http://go/go-review/1018026

doc/style.css
lib/godoc/dirs.html
lib/godoc/package.html
lib/godoc/package.txt
src/cmd/godoc/godoc.go
src/cmd/godoc/main.go

index d2dd7c9022f8ca744f9ce348094c7dffcd4a4690..c89e406d002832b3b37039c2396ebfabf56a882f 100644 (file)
@@ -10,7 +10,7 @@
 code, .code {
   font-size: 100%;
   font-family: monospace;
-  color:#007000;
+  color: #007000;
 }
 
 kbd {
@@ -149,12 +149,19 @@ div#linkList li.navhead {
 
 
 /* ------------------------------------------------------------------------- */
-/* Styles used by go/printer Styler implementations. */
+/* Styles used by godoc */
 
 a.noline {
   text-decoration: none;
 }
 
+table.layout {
+  border-width: 0px;
+  border-spacing: 0px;
+  border-width: 0px;
+  padding: 0px;
+}
+
 span.comment {
   color: #0000a0;
 }
index 23d66d5bcbddce883873abe1a33a67deda5b86b0..eef96b69530fed15d2f7a1b40f3ab8cda190ae9a 100644 (file)
@@ -1,6 +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>
+<table class="layout">
+<tr><td colspan="2"><a href="/pkg/{Path|html}">{Name|html}</a></td></tr>
+{.repeated section Dirs}
+       <tr><td width="25em"></td><td>{@|dir}</td></tr>
 {.end}
 </table>
index f2980f2068c861b06d6dd6b85e072825da94485b..b3caa0d294b8c51a1fa30736a86021d631e32f9b 100644 (file)
        {.end}
        {.section Funcs}
                {.repeated section @}
-                       <h2>func <a href="{Decl|link}">{Name|html}</a></h2>
+                       <h2>func <a href="{Decl|link}" class="noline">{Name|html}</a></h2>
                        <p><code>{Decl|html}</code></p>
                        {Doc|html-comment}
                {.end}
        {.end}
        {.section Types}
                {.repeated section @}
-                       <h2>type <a href="{Decl|link}">{Type.Name|html}</a></h2>
+                       <h2>type <a href="{Decl|link}" class="noline">{Type.Name|html}</a></h2>
                        {Doc|html-comment}
                        <p><pre>{Decl|html}</pre></p>
                        {.repeated section Consts}
                                <pre>{Decl|html}</pre>
                        {.end}
                        {.repeated section Factories}
-                               <h3>func <a href="{Decl|link}">{Name|html}</a></h3>
+                               <h3>func <a href="{Decl|link}" class="noline">{Name|html}</a></h3>
                                <p><code>{Decl|html}</code></p>
                                {Doc|html-comment}
                        {.end}
                        {.repeated section Methods}
-                               <h3>func ({Recv|html}) <a href="{Decl|link}">{Name|html}</a></h3>
+                               <h3>func ({Recv|html}) <a href="{Decl|link}" class="noline">{Name|html}</a></h3>
                                <p><code>{Decl|html}</code></p>
                                {Doc|html-comment}
                        {.end}
        {.end}
 {.end}
 {.section Dirs}
-       <h2>Subdirectories</h2>
-       {.repeated section @}
-               <a href="{Name|html}">{Name|html}</a><br />
+       {.section Dirs}
+               <h2>Subdirectories</h2>
+               {.repeated section @}
+                       {@|dir}
+               {.end}
        {.end}
 {.end}
index d8c3c3121190ca46b82ccddc7c881d1528a0e231..dfb0791ef9df381605e5ec3d39f3c814da24e4f3 100644 (file)
@@ -69,6 +69,7 @@ BUGS
 {.end}
 {.end}
 {.section Dirs}
+{.section Dirs}
 
 SUBDIRECTORIES
 
@@ -76,3 +77,4 @@ SUBDIRECTORIES
        {Name}
 {.end}
 {.end}
+{.end}
index cd438a9b2c0bf5e263a63fcc45e3fe0029b6e886..2996866962806cb7aee68531f7ac7da764a04550 100644 (file)
@@ -6,7 +6,6 @@ package main
 
 import (
        "bytes";
-       "container/vector";
        "flag";
        "fmt";
        "go/ast";
@@ -20,7 +19,6 @@ import (
        "log";
        "os";
        pathutil "path";
-       "sort";
        "strings";
        "sync";
        "template";
@@ -128,54 +126,100 @@ func htmlEscape(s string) string {
 
 
 // ----------------------------------------------------------------------------
-// Directory trees
+// Package directories
 
 type Directory struct {
-       Path string;  // including Name
+       Path string;  // relative to *pkgroot, includes Name
        Name string;
-       Subdirs []*Directory
+       Dirs []*Directory;
 }
 
 
-func newDirTree0(path, name string) *Directory {
-       list, _ := io.ReadDir(path);  // ignore errors
-       // determine number of subdirectories n
-       n := 0;
+func newDirTree(path, name string, depth int) *Directory {
+       if depth <= 0 {
+               // return a dummy directory so that the parent directory
+               // doesn't get discarded just because we reached the max
+               // directory depth
+               return &Directory{path, name, nil};
+       }
+
+       fullpath := pathutil.Join(*pkgroot, path);
+       list, _ := io.ReadDir(fullpath);  // ignore errors
+
+       // determine number of subdirectories and package files
+       ndirs := 0;
+       nfiles := 0;
        for _, d := range list {
-               if isPkgDir(d) {
-                       n++;
+               switch {
+               case isPkgDir(d):
+                       ndirs++;
+               case isPkgFile(d):
+                       nfiles++;
                }
        }
-       // create Directory node
-       var subdirs []*Directory;
-       if n > 0 {
-               subdirs = make([]*Directory, n);
+
+       // create subdirectory tree
+       var dirs []*Directory;
+       if ndirs > 0 {
+               dirs = make([]*Directory, ndirs);
                i := 0;
                for _, d := range list {
                        if isPkgDir(d) {
-                               subdirs[i] = newDirTree0(pathutil.Join(path, d.Name), d.Name);
-                               i++;
+                               dd := newDirTree(pathutil.Join(path, d.Name), d.Name, depth-1);
+                               if dd != nil {
+                                       dirs[i] = dd;
+                                       i++;
+                               }
                        }
                }
+               dirs = dirs[0:i];
        }
-       if strings.HasPrefix(path, "src/") {
-               path = path[len("src/") : len(path)];
+
+       // if there are no package files and no subdirectories
+       // (with package files), ignore the directory
+       if nfiles == 0 && len(dirs) == 0 {
+               return nil;
        }
-       return &Directory{path, name, subdirs};
+
+       return &Directory{path, name, dirs};
 }
 
 
-func newDirTree(root string) *Directory {
-       d, err := os.Lstat(root);
-       if err != nil {
-               log.Stderrf("%v", err);
+// newDirectory creates a new package directory tree with at most depth
+// levels, anchored at root which is relative to Pkg. The result tree
+// only contains directories that contain package files or that contain
+// subdirectories containing package files (transitively).
+//
+func newDirectory(root string, depth int) *Directory {
+       fullpath := pathutil.Join(*pkgroot, root);
+       d, err := os.Lstat(fullpath);
+       if err != nil || !isPkgDir(d) {
                return nil;
        }
-       if !isPkgDir(d) {
-               log.Stderrf("not a package directory: %s", d.Name);
-               return nil;
+       return newDirTree(root, d.Name, depth);
+}
+
+
+// lookup looks for the *Directory for a given path, relative to dir.
+func (dir *Directory) lookup(path string) *Directory {
+       path = pathutil.Clean(path);  // no trailing '/'
+
+       if dir == nil || path == "" || path == "." {
+               return dir;
        }
-       return newDirTree0(root, d.Name);
+
+       dpath, dname := pathutil.Split(path);
+       if dpath == "" {
+               // directory-local name
+               for _, d := range dir.Dirs {
+                       if dname == d.Name {
+                               return d;
+                       }
+               }
+               return nil
+       }
+
+       return dir.lookup(dpath).lookup(dname);
 }
 
 
@@ -610,20 +654,6 @@ func serveFile(c *http.Conn, r *http.Request) {
 // ----------------------------------------------------------------------------
 // Packages
 
-// TODO if we don't plan to use the directory information, simplify to []string
-type dirList []*os.Dir
-
-func (d dirList) Len() int {
-       return len(d);
-}
-func (d dirList) Less(i, j int) bool {
-       return d[i].Name < d[j].Name;
-}
-func (d dirList) Swap(i, j int) {
-       d[i], d[j] = d[j], d[i];
-}
-
-
 func pkgName(filename string) string {
        file, err := parse(filename, parser.PackageClauseOnly);
        if err != nil || file == nil {
@@ -635,7 +665,7 @@ func pkgName(filename string) string {
 
 type PageInfo struct {
        PDoc    *doc.PackageDoc;        // nil if no package found
-       Dirs    dirList;                // nil if no subdirectories found
+       Dirs    *Directory;             // nil if no directory information found
 }
 
 
@@ -651,10 +681,7 @@ func getPageInfo(path string) PageInfo {
        // the package name is the directory name within its parent
        _, pkgname := pathutil.Split(dirname);
 
-       // filter function to select the desired .go files and
-       // collect subdirectories
-       var subdirlist vector.Vector;
-       subdirlist.Init(0);
+       // filter function to select the desired .go files
        filter := func(d *os.Dir) bool {
                if isPkgFile(d) {
                        // Some directories contain main packages: Only accept
@@ -663,9 +690,6 @@ func getPageInfo(path string) PageInfo {
                        // found" errors.
                        return pkgName(dirname + "/" + d.Name) == pkgname;
                }
-               if isPkgDir(d) {
-                       subdirlist.Push(d);
-               }
                return false;
        };
 
@@ -673,17 +697,7 @@ func getPageInfo(path string) PageInfo {
        pkg, err := parser.ParsePackage(dirname, filter, parser.ParseComments);
        if err != nil {
                // TODO: parse errors should be shown instead of an empty directory
-               log.Stderr(err);
-       }
-
-       // convert and sort subdirectory list, if any
-       var subdirs dirList;
-       if subdirlist.Len() > 0 {
-               subdirs = make(dirList, subdirlist.Len());
-               for i := 0; i < subdirlist.Len(); i++ {
-                       subdirs[i] = subdirlist.At(i).(*os.Dir);
-               }
-               sort.Sort(subdirs);
+               log.Stderrf("parser.parsePackage: %s", err);
        }
 
        // compute package documentation
@@ -693,7 +707,20 @@ func getPageInfo(path string) PageInfo {
                pdoc = doc.NewPackageDoc(pkg, pathutil.Clean(path));    // no trailing '/' in importpath
        }
 
-       return PageInfo{pdoc, subdirs};
+       // get directory information
+       var dir *Directory;
+       if tree, _ := pkgTree.get(); tree != nil {
+               // directory tree is present; lookup respective directory
+               // (may still fail if the file system was updated and the
+               // new directory tree has not yet beet computed)
+               dir = tree.(*Directory).lookup(pathutil.Clean(path));
+       } else {
+               // no directory tree present (either early after startup
+               // or command-line mode); compute one level for this page
+               dir = newDirectory(path, 1);
+       }
+       
+       return PageInfo{pdoc, dir};
 }
 
 
@@ -734,21 +761,6 @@ 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
 
@@ -795,7 +807,6 @@ 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));
 }
index 76f39cba8cd6951a89d003c62394ad26bbd2e35e..7515b724cfb3909fb79d1af113e3f48ef95e35b5 100644 (file)
@@ -97,13 +97,19 @@ func exec(c *http.Conn, args []string) (status int) {
 }
 
 
+// Maximum package directory depth, adjust as needed.
+const maxPkgDirDepth = 16;
+
 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;
-               // update package tree
-               pkgTree.set(newDirTree(*pkgroot));
+               // update package tree.
+               // TODO(gri): The directory tree may be temporarily out-of-sync.
+               //            Consider keeping separate time stamps so the web-
+               //            page can indicate this discrepancy.
+               pkgTree.set(newDirectory(".", maxPkgDirDepth));
                fallthrough;
        case 1:
                // sync failed because no files changed;
@@ -156,6 +162,7 @@ func main() {
        readTemplates();
 
        if *httpaddr != "" {
+               // Http server mode.
                var handler http.Handler = http.DefaultServeMux;
                if *verbose {
                        log.Stderrf("Go Documentation Server\n");
@@ -171,8 +178,14 @@ func main() {
                        http.Handle("/debug/sync", http.HandlerFunc(dosync));
                }
 
-               // Compute package tree with corresponding timestamp.
-               pkgTree.set(newDirTree(*pkgroot));
+               // Initialize package tree with corresponding timestamp.
+               // Do it in two steps:
+               // 1) set timestamp right away so that the indexer is kicked on
+               pkgTree.set(nil);
+               // 2) compute initial package tree in a goroutine so that launch is quick
+               go func() {
+                       pkgTree.set(newDirectory(".", maxPkgDirDepth));
+               }();
 
                // Start sync goroutine, if enabled.
                if *syncCmd != "" && *syncMin > 0 {
@@ -206,9 +219,6 @@ func main() {
        }
 
        // 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;