Commit 49d295d5 authored by Robert Griesemer's avatar Robert Griesemer

- initial steps towards showing directory tree instead of

  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
parent b6735211
<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>
......@@ -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
......
......@@ -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;
......
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