]> Cypherpunks repositories - gostls13.git/commitdiff
Daily snapshot.
authorRobert Griesemer <gri@golang.org>
Fri, 3 Apr 2009 23:19:22 +0000 (16:19 -0700)
committerRobert Griesemer <gri@golang.org>
Fri, 3 Apr 2009 23:19:22 +0000 (16:19 -0700)
- godoc now supports the following url prefixes:
  /doc/ for package documentation
  /file/ for files (directories, html, and .go files)
  /spec for the spec
  /mem for the memory model
- formatting of comments has been fixed
- tons of minor cleanups (still more to do)

Still missing:
- pretty printing of source is not as pretty as it used to be
(still a relict from the massive AST cleanup which has't quite made it's way everywhere)
- documentation entries should be sorted
- comments in code is not printed or not properly printed

TBR=r
DELTA=416  (182 added, 100 deleted, 134 changed)
OCL=27078
CL=27078

usr/gri/pretty/astprinter.go
usr/gri/pretty/docprinter.go
usr/gri/pretty/godoc.go
usr/gri/pretty/pretty.go

index 66fc3616f461b8b012376ae2df2f8de244e623f9..4e5fcba07b695ed52699d2a60beda8e90a0dc3a7 100644 (file)
@@ -5,22 +5,20 @@
 package astPrinter
 
 import (
-       "io";
-       "vector";
-       "tabwriter";
+       "ast";
        "flag";
        "fmt";
+       "io";
+       "os";
        "strings";
-       "utf8";
-       "unicode";
-
-       "utils";
+       "tabwriter";
        "token";
-       "ast";
-       "template";
-       "symboltable";
+       "unicode";
+       "utf8";
+       "vector";
 )
 
+
 var (
        debug = flag.Bool("ast_debug", false, "print debugging information");
 
@@ -75,9 +73,48 @@ func hasExportedNames(names []*ast.Ident) bool {
 }
 
 
+// ----------------------------------------------------------------------------
+// TokenPrinter
+
+// TODO This is not yet used - should fix this.
+
+// An implementation of a TokenPrinter may be provided when
+// initializing an AST Printer. It is used to print tokens.
+//
+type TokenPrinter interface {
+       PrintLit(w io.Write, tok token.Token, value []byte);
+       PrintIdent(w io.Write, value string);
+       PrintToken(w io.Write, token token.Token);
+       PrintComment(w io.Write, value []byte);
+}
+
+
+type defaultPrinter struct {}
+
+func (p defaultPrinter) PrintLit(w io.Write, tok token.Token, value []byte) {
+       w.Write(value);
+}
+
+
+func (p defaultPrinter) PrintIdent(w io.Write, value string) {
+       fmt.Fprint(w, value);
+}
+
+
+func (p defaultPrinter) PrintToken(w io.Write, token token.Token) {
+       fmt.Fprint(w, token.String());
+}
+
+
+func (p defaultPrinter) PrintComment(w io.Write, value []byte) {
+       w.Write(value);
+}
+
+
 // ----------------------------------------------------------------------------
 // ASTPrinter
 
+
 // Separators - printed in a delayed fashion, depending on context.
 const (
        none = iota;
@@ -101,14 +138,17 @@ type Printer struct {
        // output
        text io.Write;
        
+       // token printing
+       tprinter TokenPrinter;
+
        // formatting control
        html bool;
        full bool;  // if false, print interface only; print all otherwise
 
        // comments
        comments []*ast.Comment;  // the list of unassociated comments 
-       cindex int;  // the current comment group index
-       cpos token.Position;  // the position of the next comment group
+       cindex int;  // the current comment index
+       cpos token.Position;  // the position of the next comment
 
        // current state
        lastpos token.Position;  // position after last string
@@ -144,10 +184,17 @@ func (P *Printer) nextComments() {
 }
 
 
-func (P *Printer) Init(text io.Write, comments []*ast.Comment, html bool) {
+func (P *Printer) Init(text io.Write, tprinter TokenPrinter, comments []*ast.Comment, html bool) {
        // writers
        P.text = text;
-       
+
+       // token printing
+       if tprinter != nil {
+               P.tprinter = tprinter;
+       } else {
+               P.tprinter = defaultPrinter{};
+       }
+
        // formatting control
        P.html = html;
 
@@ -227,7 +274,7 @@ func (P *Printer) newline(n int) {
 func (P *Printer) TaggedString(pos token.Position, tag, s, endtag string) {
        // use estimate for pos if we don't have one
        offs := pos.Offset;
-       if offs == 0 {
+       if pos.Line == 0 {
                offs = P.lastpos.Offset;
        }
 
@@ -401,6 +448,17 @@ func (P *Printer) Error(pos token.Position, tok token.Token, msg string) {
 }
 
 
+// An astPrinter implements io.Write.
+// TODO this is not yet used.
+func (P *Printer) Write(p []byte) (n int, err *os.Error) {
+       // TODO
+       // - no string conversion every time
+       // - return proper results
+       P.String(noPos, string(p));
+       return len(p), nil;
+}
+
+
 // ----------------------------------------------------------------------------
 // HTML support
 
index 94fe50119304145f9c6abb8cc90dd1378bbb9cd5..623c51614c38cea64a30689a48104f600aea03a1 100644 (file)
@@ -136,25 +136,27 @@ func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) {
                        typ.methods[name] = fdoc;
                }
                // if the type wasn't found, it wasn't exported
+               // TODO: a non-exported type may still have exported functions
+               //       determine what to do in that case
+               return;
+       }
 
-       } else {
-               // perhaps a factory function
-               // determine result type, if any
-               if len(fun.Type.Results) >= 1 {
-                       res := fun.Type.Results[0];
-                       if len(res.Names) <= 1 {
-                               // exactly one (named or anonymous) result type
-                               typ = doc.lookupTypeDoc(res.Type);
-                               if typ != nil {
-                                       typ.factories[name] = fdoc;
-                                       return;
-                               }
+       // perhaps a factory function
+       // determine result type, if any
+       if len(fun.Type.Results) >= 1 {
+               res := fun.Type.Results[0];
+               if len(res.Names) <= 1 {
+                       // exactly one (named or anonymous) result type
+                       typ = doc.lookupTypeDoc(res.Type);
+                       if typ != nil {
+                               typ.factories[name] = fdoc;
+                               return;
                        }
                }
-
-               // ordinary function
-               doc.funcs[name] = fdoc;
        }
+
+       // ordinary function
+       doc.funcs[name] = fdoc;
 }
 
 
@@ -279,18 +281,6 @@ func untabify(s []byte) []byte {
 }
 
 
-func stripWhiteSpace(s []byte) []byte {
-       i, j := 0, len(s);
-       for i < len(s) && s[i] <= ' ' {
-               i++;
-       }
-       for j > i && s[j-1] <= ' ' {
-               j--
-       }
-       return s[i : j];
-}
-
-
 func stripCommentDelimiters(s []byte) []byte {
        switch s[1] {
        case '/': return s[2 : len(s)-1];
@@ -308,8 +298,25 @@ const /* formatting mode */ (
 )
 
 func printLine(p *astPrinter.Printer, line []byte, mode int) int {
-       indented := len(line) > 0 && line[0] == '\t';
-       line = stripWhiteSpace(line);
+       // If a line starts with " *" (as a result of a vertical /****/ comment),
+       // strip it away. For an example of such a comment, see src/lib/flag.go.
+       if len(line) >= 2 && line[0] == ' ' && line[1] == '*' {
+               line = line[2 : len(line)];
+       }
+
+       // The line is indented if it starts with a tab.
+       // In either case strip away a leading space or tab.
+       indented := false;
+       if len(line) > 0 {
+               switch line[0] {
+               case '\t':
+                       indented = true;
+                       fallthrough;
+               case ' ':
+                       line = line[1 : len(line)];
+               }
+       }
+
        if len(line) == 0 {
                // empty line
                switch mode {
@@ -426,7 +433,7 @@ func (t *typeDoc) print(p *astPrinter.Printer) {
 
 func (doc *PackageDoc) Print(writer io.Write) {
        var p astPrinter.Printer;
-       p.Init(writer, nil, true);
+       p.Init(writer, nil, nil, true);
        
        // program header
        fmt.Fprintf(writer, "<h1>package %s</h1>\n", doc.name);
index 8b428d38f05bc685eb9789a6246aace969f68331..afedd79491e19d49e494d664db485b40e4a8ecd5 100644 (file)
@@ -25,6 +25,7 @@ import (
        "regexp";
        "vector";
 
+       "astprinter";
        "compilation";  // TODO removing this causes link errors - why?
        "docprinter";
 )
@@ -33,11 +34,12 @@ import (
 // TODO
 // - uniform use of path, filename, dirname, pakname, etc.
 // - fix weirdness with double-/'s in paths
+// - cleanup uses of *root, GOROOT, etc. (quite a mess at the moment)
 
 
 const (
        docPrefix = "/doc/";
-       srcPrefix = "/src/";
+       filePrefix = "/file/";
 )
 
 
@@ -48,19 +50,16 @@ func getenv(varname string) string {
 
 
 var (
-       GOROOT string;
+       GOROOT = getenv("GOROOT");
 
        // server control
        verbose = flag.Bool("v", false, "verbose mode");
        port = flag.String("port", "6060", "server port");
-       root = flag.String("root", getenv("GOROOT"), "go root directory");
+       root = flag.String("root", GOROOT, "root directory");
 
        // layout control
        tabwidth = flag.Int("tabwidth", 4, "tab width");
        usetabs = flag.Bool("usetabs", false, "align with tabs instead of blanks");
-
-       // html template
-       godoc_template = template.NewTemplateOrDie("godoc.html");
 )
 
 
@@ -111,8 +110,13 @@ func isGoFile(dir *os.Dir) bool {
 }
 
 
+func isHTMLFile(dir *os.Dir) bool {
+       return dir.IsRegular() && hasSuffix(dir.Name, ".html");
+}
+
+
 func printLink(c *http.Conn, path, name string) {
-       fmt.Fprintf(c, "<a href=\"%s\">%s</a><br />\n", srcPrefix + path + name, name);
+       fmt.Fprintf(c, "<a href=\"%s\">%s</a><br />\n", filePrefix + path + name, name);
 }
 
 
@@ -188,6 +192,33 @@ func compile(path string, mode uint) (*ast.Program, errorList) {
 }
 
 
+// ----------------------------------------------------------------------------
+// Templates
+
+// html template
+// TODO initialize only if needed (i.e. if run as a server)
+var godoc_html = template.NewTemplateOrDie("godoc.html");
+
+func servePage(c *http.Conn, title string, contents func()) {
+       c.SetHeader("content-type", "text/html; charset=utf-8");
+       
+       // TODO handle Apply errors
+       godoc_html.Apply(c, "<!--", template.Substitution {
+               "TITLE-->" : func() { fmt.Fprint(c, title); },
+               "HEADER-->" : func() { fmt.Fprint(c, title); },
+               "TIMESTAMP-->" : func() { fmt.Fprint(c, time.UTC().String()); },
+               "CONTENTS-->" : contents
+       });
+}
+
+
+func serveError(c *http.Conn, err, arg string) {
+       servePage(c, "Error", func () {
+               fmt.Fprintf(c, "%v (%s)\n", err, arg);
+       });
+}
+
+
 // ----------------------------------------------------------------------------
 // Directories
 
@@ -214,45 +245,28 @@ func serveDir(c *http.Conn, dirname string) {
 
        sort.Sort(dirArray(list));
 
-       c.SetHeader("content-type", "text/html; charset=utf-8");
        path := dirname + "/";
 
        // Print contents in 3 sections: directories, go files, everything else
-
-       // TODO handle Apply errors
-       godoc_template.Apply(c, "<!--", template.Substitution {
-               "TITLE-->" : func() {
-                       fmt.Fprint(c, dirname);
-               },
-
-               "HEADER-->" : func() {
-                       fmt.Fprint(c, dirname);
-               },
-
-               "TIMESTAMP-->" : func() {
-                       fmt.Fprint(c, time.UTC().String());
-               },
-
-               "CONTENTS-->" : func () {
-                       fmt.Fprintln(c, "<h2>Directories</h2>");
-                       for i, entry := range list {
-                               if entry.IsDirectory() {
-                                       printLink(c, path, entry.Name);
-                               }
+       servePage(c, dirname + " - Contents", func () {
+               fmt.Fprintln(c, "<h2>Directories</h2>");
+               for i, entry := range list {
+                       if entry.IsDirectory() {
+                               printLink(c, path, entry.Name);
                        }
+               }
 
-                       fmt.Fprintln(c, "<h2>Go files</h2>");
-                       for i, entry := range list {
-                               if isGoFile(&entry) {
-                                       printLink(c, path, entry.Name);
-                               }
+               fmt.Fprintln(c, "<h2>Go files</h2>");
+               for i, entry := range list {
+                       if isGoFile(&entry) {
+                               printLink(c, path, entry.Name);
                        }
+               }
 
-                       fmt.Fprintln(c, "<h2>Other files</h2>");
-                       for i, entry := range list {
-                               if !entry.IsDirectory() && !isGoFile(&entry) {
-                                       fmt.Fprintf(c, "%s<br />\n", entry.Name);
-                               }
+               fmt.Fprintln(c, "<h2>Other files</h2>");
+               for i, entry := range list {
+                       if !entry.IsDirectory() && !isGoFile(&entry) {
+                               fmt.Fprintf(c, "%s<br />\n", entry.Name);
                        }
                }
        });
@@ -262,120 +276,96 @@ func serveDir(c *http.Conn, dirname string) {
 // ----------------------------------------------------------------------------
 // Files
 
-func printErrors(c *http.Conn, filename string, errors errorList) {
+func serveCompilationErrors(c *http.Conn, filename string, errors errorList) {
        // open file
        path := *root + filename;
        fd, err1 := os.Open(path, os.O_RDONLY, 0);
        defer fd.Close();
        if err1 != nil {
-               // TODO better error handling
-               log.Stdoutf("%s: %v", path, err1);
+               serveError(c, err1.String(), path);
+               return;
        }
 
        // read source
        var buf io.ByteBuffer;
        n, err2 := io.Copy(fd, &buf);
        if err2 != nil {
-               // TODO better error handling
-               log.Stdoutf("%s: %v", path, err2);
+               serveError(c, err2.String(), path);
+               return;
        }
        src := buf.Data();
 
        // TODO handle Apply errors
-       godoc_template.Apply(c, "<!--", template.Substitution {
-               "TITLE-->" : func() {
-                       fmt.Fprint(c, filename);
-               },
-
-               "HEADER-->" : func() {
-                       fmt.Fprint(c, filename);
-               },
-
-               "TIMESTAMP-->" : func() {
-                       fmt.Fprint(c, time.UTC().String());
-               },
-
-               "CONTENTS-->" : func () {
-                       // section title
-                       fmt.Fprintf(c, "<h1>Compilation errors in %s</h1>\n", filename);
-                       
-                       // handle read errors
-                       if err1 != nil || err2 != nil /* 6g bug139 */ {
-                               fmt.Fprintf(c, "could not read file %s\n", filename);
-                               return;
-                       }
-                       
-                       // write source with error messages interspersed
-                       fmt.Fprintln(c, "<pre>");
-                       offs := 0;
-                       for i, e := range errors {
-                               if 0 <= e.pos.Offset && e.pos.Offset <= len(src) {
-                                       // TODO handle Write errors
-                                       c.Write(src[offs : e.pos.Offset]);
-                                       // TODO this should be done using a .css file
-                                       fmt.Fprintf(c, "<b><font color=red>%s >>></font></b>", e.msg);
-                                       offs = e.pos.Offset;
-                               } else {
-                                       log.Stdoutf("error position %d out of bounds (len = %d)", e.pos.Offset, len(src));
-                               }
+       servePage(c, filename, func () {
+               // section title
+               fmt.Fprintf(c, "<h1>Compilation errors in %s</h1>\n", filename);
+               
+               // handle read errors
+               if err1 != nil || err2 != nil /* 6g bug139 */ {
+                       fmt.Fprintf(c, "could not read file %s\n", filename);
+                       return;
+               }
+               
+               // write source with error messages interspersed
+               fmt.Fprintln(c, "<pre>");
+               offs := 0;
+               for i, e := range errors {
+                       if 0 <= e.pos.Offset && e.pos.Offset <= len(src) {
+                               // TODO handle Write errors
+                               c.Write(src[offs : e.pos.Offset]);
+                               // TODO this should be done using a .css file
+                               fmt.Fprintf(c, "<b><font color=red>%s >>></font></b>", e.msg);
+                               offs = e.pos.Offset;
+                       } else {
+                               log.Stdoutf("error position %d out of bounds (len = %d)", e.pos.Offset, len(src));
                        }
-                       // TODO handle Write errors
-                       c.Write(src[offs : len(src)]);
-                       fmt.Fprintln(c, "</pre>");
                }
+               // TODO handle Write errors
+               c.Write(src[offs : len(src)]);
+               fmt.Fprintln(c, "</pre>");
        });
 }
 
 
-func serveGoFile(c *http.Conn, dirname string, filenames []string) {
-       // compute documentation
-       var doc docPrinter.PackageDoc;
-       for i, filename := range filenames {
-               path := *root + "/" + dirname + "/" + filename;
-               prog, errors := compile(path, parser.ParseComments);
-               if len(errors) > 0 {
-                       c.SetHeader("content-type", "text/html; charset=utf-8");
-                       printErrors(c, filename, errors);
-                       return;
-               }
-
-               if i == 0 {
-                       // first package - initialize docPrinter
-                       doc.Init(prog.Name.Value);
-               }
-               doc.AddProgram(prog);
+func serveGoSource(c *http.Conn, dirname string, filename string) {
+       path := dirname + "/" + filename;
+       prog, errors := compile(*root + "/" + path, parser.ParseComments);
+       if len(errors) > 0 {
+               serveCompilationErrors(c, filename, errors);
+               return;
        }
 
-       c.SetHeader("content-type", "text/html; charset=utf-8");
-       
-       godoc_template.Apply(c, "<!--", template.Substitution {
-               "TITLE-->" : func() {
-                       fmt.Fprintf(c, "%s - Go package documentation", doc.PackageName());
-               },
-
-               "HEADER-->" : func() {
-                       fmt.Fprintf(c, "%s - Go package documentation", doc.PackageName());
-               },
-
-               "TIMESTAMP-->" : func() {
-                       fmt.Fprint(c, time.UTC().String());
-               },
-
-               "CONTENTS-->" : func () {
-                       // write documentation
-                       writer := makeTabwriter(c);  // for nicely formatted output
-                       doc.Print(writer);
-                       writer.Flush();  // ignore errors
-               }
+       servePage(c, path + " - Go source", func () {
+               fmt.Fprintln(c, "<pre>");
+               var p astPrinter.Printer;
+               writer := makeTabwriter(c);  // for nicely formatted output
+               p.Init(writer, nil, nil, true);
+               p.DoProgram(prog);
+               writer.Flush();  // ignore errors
+               fmt.Fprintln(c, "</pre>");
        });
 }
 
 
-func serveSrc(c *http.Conn, path string) {
+func serveHTMLFile(c *http.Conn, filename string) {
+       src, err1 := os.Open(filename, os.O_RDONLY, 0);
+       defer src.Close();
+       if err1 != nil {
+               serveError(c, err1.String(), filename);
+               return
+       }
+       written, err2 := io.Copy(src, c);
+       if err2 != nil {
+               serveError(c, err2.String(), filename);
+               return
+       }
+}
+
+
+func serveFile(c *http.Conn, path string) {
        dir, err := os.Stat(*root + path);
        if err != nil {
-               c.WriteHeader(http.StatusNotFound);
-               fmt.Fprintf(c, "Error: %v (%s)\n", err, path);
+               serveError(c, err.String(), path);
                return;
        }
 
@@ -383,10 +373,11 @@ func serveSrc(c *http.Conn, path string) {
        case dir.IsDirectory():
                serveDir(c, path);
        case isGoFile(dir):
-               serveGoFile(c, "", []string{path});
+               serveGoSource(c, "", path);
+       case isHTMLFile(dir):
+               serveHTMLFile(c, *root + path);
        default:
-               c.WriteHeader(http.StatusNotFound);
-               fmt.Fprintf(c, "Error: Not a directory or .go file (%s)\n", path);
+               serveError(c, "Not a directory or .go file", path);
        }
 }
 
@@ -407,13 +398,12 @@ func (p pakArray) Less(i, j int) bool  { return p[i].pakname < p[j].pakname; }
 func (p pakArray) Swap(i, j int)       { p[i], p[j] = p[j], p[i]; }
 
 
-var (
-       pakMap map[string]*pakDesc;  // dirname/pakname -> package descriptor
-       pakList pakArray;  // sorted list of packages; in sync with pakMap
-)
+// The global list of packages (sorted)
+// TODO should be accessed under a lock
+var pakList pakArray;
 
 
-func addFile(dirname string, filename string) {
+func addFile(pmap map[string]*pakDesc, dirname string, filename string) {
        if hasSuffix(filename, "_test.go") {
                // ignore package tests
                return;
@@ -431,11 +421,11 @@ func addFile(dirname string, filename string) {
        pakname := dirname + "/" + prog.Name.Value;
 
        // find package descriptor
-       pakdesc, found := pakMap[pakname];
+       pakdesc, found := pmap[pakname];
        if !found {
                // add a new descriptor
                pakdesc = &pakDesc{dirname, prog.Name.Value, make(map[string]bool)};
-               pakMap[pakname] = pakdesc;
+               pmap[pakname] = pakdesc;
        }
        
        //fmt.Printf("pak = %s, file = %s\n", pakname, filename);
@@ -448,11 +438,9 @@ func addFile(dirname string, filename string) {
 }
 
 
-func addDirectory(dirname string) {
+func addDirectory(pmap map[string]*pakDesc, dirname string) {
        // TODO should properly check device and inode to see if we have
        //      traversed this directory already
-       //fmt.Printf("traversing %s\n", dirname);
-
        fd, err1 := os.Open(*root + dirname, os.O_RDONLY, 0);
        if err1 != nil {
                log.Stdoutf("%s: %v", *root + dirname, err1);
@@ -469,30 +457,33 @@ func addDirectory(dirname string) {
                switch {
                case entry.IsDirectory():
                        if entry.Name != "." && entry.Name != ".." {
-                               addDirectory(dirname + "/" + entry.Name);
+                               addDirectory(pmap, dirname + "/" + entry.Name);
                        }
                case isGoFile(&entry):  
                        //fmt.Printf("found %s/%s\n", dirname, entry.Name);
-                       addFile(dirname, entry.Name);
+                       addFile(pmap, dirname, entry.Name);
                }
        }
 }
 
 
 func makePackageMap() {
-       // TODO shold do this under a lock, eventually
+       // TODO shold do this under a lock
        // populate package map
-       pakMap = make(map[string]*pakDesc);
-       addDirectory("");
+       pmap := make(map[string]*pakDesc);
+       addDirectory(pmap, "");
        
        // build sorted package list
-       pakList = make([]*pakDesc, len(pakMap));
+       plist := make(pakArray, len(pmap));
        i := 0;
-       for tmp, pakdesc := range pakMap {
-               pakList[i] = pakdesc;
+       for tmp, pakdesc := range pmap {
+               plist[i] = pakdesc;
                i++;
        }
-       sort.Sort(pakList);
+       sort.Sort(plist);
+
+       // install package list (TODO should do this under a lock)
+       pakList = plist;
 
        if *verbose {
                log.Stdoutf("%d packages found under %s", i, *root);
@@ -500,40 +491,46 @@ func makePackageMap() {
 }
 
 
-func serveGoPackage(c *http.Conn, p *pakDesc) {
+func servePackage(c *http.Conn, p *pakDesc) {
        // make a filename list
-       list := make([]string, len(p.filenames));
+       filenames := make([]string, len(p.filenames));
        i := 0;
        for filename, tmp := range p.filenames {
-               list[i] = filename;
+               filenames[i] = filename;
                i++;
        }
        
-       serveGoFile(c, p.dirname, list);
+       // compute documentation
+       var doc docPrinter.PackageDoc;
+       for i, filename := range filenames {
+               path := *root + "/" + p.dirname + "/" + filename;
+               prog, errors := compile(path, parser.ParseComments);
+               if len(errors) > 0 {
+                       serveCompilationErrors(c, filename, errors);
+                       return;
+               }
+
+               if i == 0 {
+                       // first package - initialize docPrinter
+                       doc.Init(prog.Name.Value);
+               }
+               doc.AddProgram(prog);
+       }
+
+       servePage(c, doc.PackageName() + " - Go package documentation", func () {
+               writer := makeTabwriter(c);  // for nicely formatted output
+               doc.Print(writer);
+               writer.Flush();  // ignore errors
+       });
 }
 
 
 func servePackageList(c *http.Conn, list *vector.Vector) {
-       godoc_template.Apply(c, "<!--", template.Substitution {
-               "TITLE-->" : func() {
-                       fmt.Fprint(c, "Packages");
-               },
-
-               "HEADER-->" : func() {
-                       fmt.Fprint(c, "Packages");
-               },
-
-               "TIMESTAMP-->" : func() {
-                       fmt.Fprint(c, time.UTC().String());
-               },
-
-               "CONTENTS-->" : func () {
-                       // TODO should do this under a lock, eventually
-                       for i := 0; i < list.Len(); i++ {
-                               p := list.At(i).(*pakDesc);
-                               link := p.dirname + "/" + p.pakname;
-                               fmt.Fprintf(c, "<a href=\"%s\">%s</a> <font color=grey>(%s)</font><br />\n", docPrefix + link, p.pakname, link);
-                       }
+       servePage(c, "Packages", func () {
+               for i := 0; i < list.Len(); i++ {
+                       p := list.At(i).(*pakDesc);
+                       link := p.dirname + "/" + p.pakname;
+                       fmt.Fprintf(c, "<a href=\"%s\">%s</a> <font color=grey>(%s)</font><br />\n", docPrefix + link, p.pakname, link);
                }
        });
 }
@@ -543,8 +540,8 @@ func serveDoc(c *http.Conn, path string) {
        // make regexp for package matching
        rex, err := regexp.Compile(path);
        if err != nil {
-               // TODO report this via an error page
-               log.Stdoutf("failed to compile regexp: %s", path);
+               serveError(c, err.String(), path);
+               return;
        }
 
        // build list of matching packages
@@ -555,9 +552,12 @@ func serveDoc(c *http.Conn, path string) {
                }
        }
 
-       if list.Len() == 1 {
-               serveGoPackage(c, list.At(0).(*pakDesc));
-       } else {
+       switch list.Len() {
+       case 0:
+               serveError(c, "No packages found", path);
+       case 1:
+               servePackage(c, list.At(0).(*pakDesc));
+       default:
                servePackageList(c, list);
        }
 }
@@ -566,8 +566,17 @@ func serveDoc(c *http.Conn, path string) {
 // ----------------------------------------------------------------------------
 // Server
 
+func makeFixedFileServer(filename string) (func(c *http.Conn, path string)) {
+       return func(c *http.Conn, path string) {
+               // ignore path and always serve the same file
+               // TODO this should be serveFile but there are some issues with *root
+               serveHTMLFile(c, filename);
+       };
+}
+
+
 func installHandler(prefix string, handler func(c *http.Conn, path string)) {
-       // customized handler with prefix
+       // create a handler customized with prefix
        f := func(c *http.Conn, req *http.Request) {
                path := req.Url.Path;
                if *verbose {
@@ -604,13 +613,15 @@ func main() {
        }
 
        makePackageMap();
-
+       
+       installHandler("/mem", makeFixedFileServer(GOROOT + "/doc/go_mem.html"));
+       installHandler("/spec", makeFixedFileServer(GOROOT + "/doc/go_spec.html"));
        installHandler(docPrefix, serveDoc);
-       installHandler(srcPrefix, serveSrc);
+       installHandler(filePrefix, serveFile);
+
        {       err := http.ListenAndServe(":" + *port, nil);
                if err != nil {
                        log.Exitf("ListenAndServe: %v", err)
                }
        }
 }
-
index fe2a03ecb60a05ab0e02618a79ae51e6e51ee752..1c0f2445d4e3186646e158793600ac56b7e5db46 100644 (file)
@@ -50,7 +50,7 @@ func print(prog *ast.Program) {
 
        // initialize printer
        var printer astPrinter.Printer;
-       printer.Init(writer, prog.Comments, *html);
+       printer.Init(writer, nil, prog.Comments, *html);
 
        printer.DoProgram(prog);