Commit 2a9f1ee2 authored by Robert Griesemer's avatar Robert Griesemer

Daily snapshot.

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