Commit be56b957 authored by Robert Griesemer's avatar Robert Griesemer

- show recursive package directory structure in package pages

- removed some underbars in section headings for better looks
- various minor tweaks

R=rsc
http://go/go-review/1018026
parent 6e98b7f0
......@@ -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;
}
......
<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>
......@@ -34,14 +34,14 @@
{.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}
......@@ -53,12 +53,12 @@
<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}
......@@ -72,8 +72,10 @@
{.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}
......@@ -69,6 +69,7 @@ BUGS
{.end}
{.end}
{.section Dirs}
{.section Dirs}
SUBDIRECTORIES
......@@ -76,3 +77,4 @@ SUBDIRECTORIES
{Name}
{.end}
{.end}
{.end}
......@@ -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));
}
......
......@@ -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;
......
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