mirror of
https://github.com/golang/go
synced 2024-11-22 02:24:41 -07:00
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
This commit is contained in:
parent
461fb39367
commit
2a9f1ee215
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
// first package - initialize docPrinter
|
||||
doc.Init(prog.Name.Value);
|
||||
}
|
||||
doc.AddProgram(prog);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// 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>");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
func serveSrc(c *http.Conn, path string) {
|
||||
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;
|
||||
}
|
||||
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user