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 @@ - - -{.repeated section Subdirs} - +
{Name|html}
{@|dir}
+ +{.repeated section Dirs} + {.end}
{Name|html}
{@|dir}
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 @} -

func {Name|html}

+

func {Name|html}

{Decl|html}

{Doc|html-comment} {.end} {.end} {.section Types} {.repeated section @} -

type {Type.Name|html}

+

type {Type.Name|html}

{Doc|html-comment}

{Decl|html}

{.repeated section Consts} @@ -53,12 +53,12 @@
{Decl|html}
{.end} {.repeated section Factories} -

func {Name|html}

+

func {Name|html}

{Decl|html}

{Doc|html-comment} {.end} {.repeated section Methods} -

func ({Recv|html}) {Name|html}

+

func ({Recv|html}) {Name|html}

{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;