diff --git a/doc/style.css b/doc/style.css
index d2dd7c9022f..c89e406d002 100644
--- a/doc/style.css
+++ b/doc/style.css
@@ -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;
}
diff --git a/lib/godoc/dirs.html b/lib/godoc/dirs.html
index 23d66d5bcbd..eef96b69530 100644
--- a/lib/godoc/dirs.html
+++ b/lib/godoc/dirs.html
@@ -1,6 +1,6 @@
-
-{Name|html} |
-{.repeated section Subdirs}
- | {@|dir} |
+
+{Name|html} |
+{.repeated section Dirs}
+ | {@|dir} |
{.end}
diff --git a/lib/godoc/package.html b/lib/godoc/package.html
index f2980f2068c..b3caa0d294b 100644
--- a/lib/godoc/package.html
+++ b/lib/godoc/package.html
@@ -34,14 +34,14 @@
{.end}
{.section Funcs}
{.repeated section @}
-
+
{Decl|html}
{Doc|html-comment}
{.end}
{.end}
{.section Types}
{.repeated section @}
-
+
{Doc|html-comment}
{Decl|html}
{.repeated section Consts}
@@ -53,12 +53,12 @@
{Decl|html}
{.end}
{.repeated section Factories}
-
+
{Decl|html}
{Doc|html-comment}
{.end}
{.repeated section Methods}
-
+
{Decl|html}
{Doc|html-comment}
{.end}
@@ -72,8 +72,10 @@
{.end}
{.end}
{.section Dirs}
- Subdirectories
- {.repeated section @}
- {Name|html}
+ {.section Dirs}
+ Subdirectories
+ {.repeated section @}
+ {@|dir}
+ {.end}
{.end}
{.end}
diff --git a/lib/godoc/package.txt b/lib/godoc/package.txt
index d8c3c312119..dfb0791ef9d 100644
--- a/lib/godoc/package.txt
+++ b/lib/godoc/package.txt
@@ -69,6 +69,7 @@ BUGS
{.end}
{.end}
{.section Dirs}
+{.section Dirs}
SUBDIRECTORIES
@@ -76,3 +77,4 @@ SUBDIRECTORIES
{Name}
{.end}
{.end}
+{.end}
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index cd438a9b2c0..29968669628 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -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));
}
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 76f39cba8cd..7515b724cfb 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -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;