2009-03-30 17:13:11 -07:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package docPrinter
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"vector";
|
|
|
|
|
"utf8";
|
|
|
|
|
"unicode";
|
|
|
|
|
"io";
|
|
|
|
|
"fmt";
|
|
|
|
|
|
|
|
|
|
"ast";
|
|
|
|
|
"astprinter";
|
|
|
|
|
"template";
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Elementary support
|
|
|
|
|
|
|
|
|
|
// TODO this should be an AST method
|
|
|
|
|
func isExported(name *ast.Ident) bool {
|
|
|
|
|
ch, len := utf8.DecodeRune(name.Lit);
|
|
|
|
|
return unicode.IsUpper(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func hasExportedNames(names []*ast.Ident) bool {
|
|
|
|
|
for i, name := range names {
|
|
|
|
|
if isExported(name) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
type constDoc struct {
|
2009-03-31 16:53:58 -07:00
|
|
|
decl *ast.ConstDecl;
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type varDoc struct {
|
2009-03-31 16:53:58 -07:00
|
|
|
decl *ast.VarDecl;
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type funcDoc struct {
|
2009-03-31 16:53:58 -07:00
|
|
|
decl *ast.FuncDecl;
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type typeDoc struct {
|
2009-03-31 16:53:58 -07:00
|
|
|
decl *ast.TypeDecl;
|
2009-03-31 18:46:21 -07:00
|
|
|
factories map[string] *funcDoc;
|
2009-03-30 17:13:11 -07:00
|
|
|
methods map[string] *funcDoc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type PackageDoc struct {
|
|
|
|
|
name string; // package name
|
|
|
|
|
imports map[string] string;
|
|
|
|
|
consts map[string] *constDoc;
|
|
|
|
|
types map[string] *typeDoc;
|
|
|
|
|
vars map[string] *varDoc;
|
|
|
|
|
funcs map[string] *funcDoc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// PackageDoc initializes a document to collect package documentation.
|
|
|
|
|
// The package name is provided as initial argument. Use AddPackage to
|
|
|
|
|
// add the AST for each source file belonging to the same package.
|
|
|
|
|
//
|
2009-03-31 16:53:58 -07:00
|
|
|
func (doc *PackageDoc) Init(name string) {
|
|
|
|
|
doc.name = name;
|
|
|
|
|
doc.imports = make(map[string] string);
|
|
|
|
|
doc.consts = make(map[string] *constDoc);
|
|
|
|
|
doc.types = make(map[string] *typeDoc);
|
|
|
|
|
doc.vars = make(map[string] *varDoc);
|
|
|
|
|
doc.funcs = make(map[string] *funcDoc);
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 18:46:21 -07:00
|
|
|
func baseTypeName(typ ast.Expr) string {
|
|
|
|
|
switch t := typ.(type) {
|
|
|
|
|
case *ast.Ident:
|
|
|
|
|
return string(t.Lit);
|
|
|
|
|
case *ast.StarExpr:
|
|
|
|
|
return baseTypeName(t.X);
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (doc *PackageDoc) lookupTypeDoc(typ ast.Expr) *typeDoc {
|
|
|
|
|
tdoc, found := doc.types[baseTypeName(typ)];
|
|
|
|
|
if found {
|
|
|
|
|
return tdoc;
|
|
|
|
|
}
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) {
|
|
|
|
|
name := string(fun.Name.Lit);
|
|
|
|
|
fdoc := &funcDoc{fun};
|
|
|
|
|
|
|
|
|
|
// determine if it should be associated with a type
|
|
|
|
|
var typ *typeDoc;
|
|
|
|
|
if fun.Recv != nil {
|
|
|
|
|
// method
|
|
|
|
|
typ = doc.lookupTypeDoc(fun.Recv.Type);
|
|
|
|
|
if typ != nil {
|
|
|
|
|
typ.methods[name] = fdoc;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// TODO other heuristics (e.g. name is "NewTypename"?)
|
|
|
|
|
|
|
|
|
|
// ordinary function
|
|
|
|
|
doc.funcs[name] = fdoc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
func (doc *PackageDoc) addDecl(decl ast.Decl) {
|
2009-03-30 17:13:11 -07:00
|
|
|
switch d := decl.(type) {
|
|
|
|
|
case *ast.ImportDecl:
|
|
|
|
|
case *ast.ConstDecl:
|
|
|
|
|
if hasExportedNames(d.Names) {
|
|
|
|
|
}
|
2009-03-31 16:53:58 -07:00
|
|
|
|
2009-03-30 17:13:11 -07:00
|
|
|
case *ast.TypeDecl:
|
|
|
|
|
if isExported(d.Name) {
|
2009-03-31 16:53:58 -07:00
|
|
|
// TODO only add if not there already - or ignore?
|
|
|
|
|
name := string(d.Name.Lit);
|
2009-03-31 18:46:21 -07:00
|
|
|
tdoc := &typeDoc{d, make(map[string] *funcDoc), make(map[string] *funcDoc)};
|
2009-03-31 16:53:58 -07:00
|
|
|
doc.types[name] = tdoc;
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
2009-03-31 16:53:58 -07:00
|
|
|
|
2009-03-30 17:13:11 -07:00
|
|
|
case *ast.VarDecl:
|
|
|
|
|
if hasExportedNames(d.Names) {
|
|
|
|
|
}
|
2009-03-31 16:53:58 -07:00
|
|
|
|
2009-03-30 17:13:11 -07:00
|
|
|
case *ast.FuncDecl:
|
|
|
|
|
if isExported(d.Name) {
|
2009-03-31 18:46:21 -07:00
|
|
|
doc.addFunc(d);
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
2009-03-31 16:53:58 -07:00
|
|
|
|
2009-03-30 17:13:11 -07:00
|
|
|
case *ast.DeclList:
|
|
|
|
|
for i, decl := range d.List {
|
2009-03-31 16:53:58 -07:00
|
|
|
doc.addDecl(decl);
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
// AddProgram adds the AST of a source file belonging to the same
|
2009-03-30 17:13:11 -07:00
|
|
|
// package. The package names must match. If the package was added
|
|
|
|
|
// before, AddPackage is a no-op.
|
|
|
|
|
//
|
2009-03-31 16:53:58 -07:00
|
|
|
func (doc *PackageDoc) AddProgram(pak *ast.Program) {
|
|
|
|
|
if doc.name != string(pak.Name.Lit) {
|
2009-03-30 17:13:11 -07:00
|
|
|
panic("package names don't match");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add all declarations
|
|
|
|
|
for i, decl := range pak.Decls {
|
2009-03-31 16:53:58 -07:00
|
|
|
doc.addDecl(decl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Printing
|
|
|
|
|
|
|
|
|
|
func htmlEscape(s string) string {
|
|
|
|
|
var esc string;
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
switch s[i] {
|
|
|
|
|
case '<': esc = "<";
|
|
|
|
|
case '&': esc = "&";
|
|
|
|
|
default: continue;
|
|
|
|
|
}
|
|
|
|
|
return s[0 : i] + esc + htmlEscape(s[i+1 : len(s)]);
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reduce contiguous sequences of '\t' in a string to a single '\t'.
|
|
|
|
|
func untabify(s string) string {
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
if s[i] == '\t' {
|
|
|
|
|
j := i;
|
|
|
|
|
for j < len(s) && s[j] == '\t' {
|
|
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
if j-i > 1 { // more then one tab
|
|
|
|
|
return s[0 : i+1] + untabify(s[j : len(s)]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 cleanComment(s []byte) []byte {
|
|
|
|
|
switch s[1] {
|
|
|
|
|
case '/': s = s[2 : len(s)-1];
|
|
|
|
|
case '*': s = s[2 : len(s)-2];
|
|
|
|
|
default : panic("illegal comment");
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
2009-03-31 16:53:58 -07:00
|
|
|
return stripWhiteSpace(s);
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
func printComment(p *astPrinter.Printer, comment ast.Comments) {
|
|
|
|
|
in_paragraph := false;
|
|
|
|
|
for i, c := range comment {
|
|
|
|
|
s := cleanComment(c.Text);
|
|
|
|
|
if len(s) > 0 {
|
|
|
|
|
if !in_paragraph {
|
|
|
|
|
p.Printf("<p>\n");
|
|
|
|
|
in_paragraph = true;
|
|
|
|
|
}
|
|
|
|
|
p.Printf("%s\n", htmlEscape(untabify(string(s))));
|
|
|
|
|
} else {
|
|
|
|
|
if in_paragraph {
|
|
|
|
|
p.Printf("</p>\n");
|
|
|
|
|
in_paragraph = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if in_paragraph {
|
|
|
|
|
p.Printf("</p>\n");
|
|
|
|
|
}
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
func (c *constDoc) printConsts(p *astPrinter.Printer) {
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 18:46:21 -07:00
|
|
|
func (f *funcDoc) print(p *astPrinter.Printer, hsize int) {
|
2009-03-31 16:53:58 -07:00
|
|
|
d := f.decl;
|
|
|
|
|
if d.Recv != nil {
|
2009-03-31 18:46:21 -07:00
|
|
|
p.Printf("<h%d>func (", hsize);
|
2009-03-31 16:53:58 -07:00
|
|
|
p.Expr(d.Recv.Type);
|
2009-03-31 18:46:21 -07:00
|
|
|
p.Printf(") %s</h%d>\n", d.Name.Lit, hsize);
|
2009-03-31 16:53:58 -07:00
|
|
|
} else {
|
2009-03-31 18:46:21 -07:00
|
|
|
p.Printf("<h%d>func %s</h%d>\n", hsize, d.Name.Lit, hsize);
|
2009-03-31 16:53:58 -07:00
|
|
|
}
|
|
|
|
|
p.Printf("<p><code>");
|
|
|
|
|
p.DoFuncDecl(d);
|
|
|
|
|
p.Printf("</code></p>\n");
|
|
|
|
|
if d.Doc != nil {
|
|
|
|
|
printComment(p, d.Doc);
|
|
|
|
|
}
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
func (t *typeDoc) print(p *astPrinter.Printer) {
|
|
|
|
|
d := t.decl;
|
|
|
|
|
p.Printf("<h2>type %s</h2>\n", string(d.Name.Lit));
|
|
|
|
|
p.Printf("<p><pre>");
|
|
|
|
|
p.DoTypeDecl(d);
|
|
|
|
|
p.Printf("</pre></p>\n");
|
|
|
|
|
if d.Doc != nil {
|
|
|
|
|
printComment(p, d.Doc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// print associated methods, if any
|
2009-03-31 18:46:21 -07:00
|
|
|
for name, m := range t.factories {
|
|
|
|
|
m.print(p, 3);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
for name, m := range t.methods {
|
2009-03-31 18:46:21 -07:00
|
|
|
m.print(p, 3);
|
2009-03-31 16:53:58 -07:00
|
|
|
}
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
func (v *varDoc) print(p *astPrinter.Printer) {
|
2009-03-30 17:13:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
/*
|
|
|
|
|
func (P *Printer) Interface(p *ast.Program) {
|
|
|
|
|
P.full = false;
|
|
|
|
|
for i := 0; i < len(p.Decls); i++ {
|
|
|
|
|
switch d := p.Decls[i].(type) {
|
|
|
|
|
case *ast.ConstDecl:
|
|
|
|
|
if hasExportedNames(d.Names) {
|
|
|
|
|
P.Printf("<h2>Constants</h2>\n");
|
|
|
|
|
P.Printf("<p><pre>");
|
|
|
|
|
P.DoConstDecl(d);
|
|
|
|
|
P.String(nopos, "");
|
|
|
|
|
P.Printf("</pre></p>\n");
|
|
|
|
|
if d.Doc != nil {
|
|
|
|
|
P.printComment(d.Doc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case *ast.VarDecl:
|
|
|
|
|
if hasExportedNames(d.Names) {
|
|
|
|
|
P.Printf("<h2>Variables</h2>\n");
|
|
|
|
|
P.Printf("<p><pre>");
|
|
|
|
|
P.DoVarDecl(d);
|
|
|
|
|
P.String(nopos, "");
|
|
|
|
|
P.Printf("</pre></p>\n");
|
|
|
|
|
if d.Doc != nil {
|
|
|
|
|
P.printComment(d.Doc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case *ast.DeclList:
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2009-03-30 17:13:11 -07:00
|
|
|
// TODO make this a parameter for Init or Print?
|
|
|
|
|
var templ = template.NewTemplateOrDie("template.html");
|
|
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
func (doc *PackageDoc) Print(writer io.Write) {
|
|
|
|
|
var p astPrinter.Printer;
|
|
|
|
|
p.Init(writer, nil, true);
|
2009-03-30 17:13:11 -07:00
|
|
|
|
2009-03-31 16:53:58 -07:00
|
|
|
// TODO propagate Apply errors
|
|
|
|
|
templ.Apply(writer, "<!--", template.Substitution {
|
|
|
|
|
"PACKAGE_NAME-->" :
|
|
|
|
|
func() {
|
|
|
|
|
fmt.Fprint(writer, doc.name);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"PROGRAM_HEADER-->":
|
|
|
|
|
func() {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"CONSTANTS-->" :
|
|
|
|
|
func() {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"TYPES-->" :
|
|
|
|
|
func() {
|
|
|
|
|
for name, t := range doc.types {
|
|
|
|
|
p.Printf("<hr />\n");
|
|
|
|
|
t.print(&p);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"VARIABLES-->" :
|
|
|
|
|
func() {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"FUNCTIONS-->" :
|
|
|
|
|
func() {
|
2009-03-31 18:46:21 -07:00
|
|
|
p.Printf("<hr />\n");
|
2009-03-31 16:53:58 -07:00
|
|
|
for name, f := range doc.funcs {
|
2009-03-31 18:46:21 -07:00
|
|
|
f.print(&p, 2);
|
2009-03-31 16:53:58 -07:00
|
|
|
}
|
|
|
|
|
},
|
2009-03-30 17:13:11 -07:00
|
|
|
});
|
|
|
|
|
}
|