2009-03-30 18:13:11 -06: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";
|
2009-04-01 16:00:22 -06:00
|
|
|
"token";
|
2009-03-30 18:13:11 -06:00
|
|
|
"astprinter";
|
|
|
|
"template";
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Elementary support
|
|
|
|
|
|
|
|
// TODO this should be an AST method
|
|
|
|
func isExported(name *ast.Ident) bool {
|
2009-04-02 16:58:58 -06:00
|
|
|
ch, len := utf8.DecodeRuneInString(name.Value, 0);
|
2009-03-30 18:13:11 -06:00
|
|
|
return unicode.IsUpper(ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func hasExportedNames(names []*ast.Ident) bool {
|
|
|
|
for i, name := range names {
|
|
|
|
if isExported(name) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-02 11:16:17 -06:00
|
|
|
func hasExportedSpecs(specs []ast.Spec) bool {
|
|
|
|
for i, s := range specs {
|
|
|
|
// only called for []astSpec lists of *ast.ValueSpec
|
|
|
|
return hasExportedNames(s.(*ast.ValueSpec).Names);
|
2009-04-01 16:00:22 -06:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-30 18:13:11 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2009-04-02 11:16:17 -06:00
|
|
|
type valueDoc struct {
|
|
|
|
decl *ast.GenDecl; // len(decl.Specs) >= 1, and the element type is *ast.ValueSpec
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type funcDoc struct {
|
2009-03-31 17:53:58 -06:00
|
|
|
decl *ast.FuncDecl;
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type typeDoc struct {
|
2009-04-02 11:16:17 -06:00
|
|
|
decl *ast.GenDecl; // len(decl.Specs) == 1, and the element type is *ast.TypeSpec
|
2009-03-31 19:46:21 -06:00
|
|
|
factories map[string] *funcDoc;
|
2009-03-30 18:13:11 -06:00
|
|
|
methods map[string] *funcDoc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type PackageDoc struct {
|
|
|
|
name string; // package name
|
2009-04-01 16:00:22 -06:00
|
|
|
doc ast.Comments; // package documentation, if any
|
2009-04-02 11:16:17 -06:00
|
|
|
consts *vector.Vector; // list of *valueDoc
|
2009-03-30 18:13:11 -06:00
|
|
|
types map[string] *typeDoc;
|
2009-04-02 11:16:17 -06:00
|
|
|
vars *vector.Vector; // list of *valueDoc
|
2009-03-30 18:13:11 -06:00
|
|
|
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 17:53:58 -06:00
|
|
|
func (doc *PackageDoc) Init(name string) {
|
|
|
|
doc.name = name;
|
2009-04-01 16:00:22 -06:00
|
|
|
doc.consts = vector.New(0);
|
2009-03-31 17:53:58 -06:00
|
|
|
doc.types = make(map[string] *typeDoc);
|
2009-04-01 16:00:22 -06:00
|
|
|
doc.vars = vector.New(0);
|
2009-03-31 17:53:58 -06:00
|
|
|
doc.funcs = make(map[string] *funcDoc);
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-31 19:46:21 -06:00
|
|
|
func baseTypeName(typ ast.Expr) string {
|
|
|
|
switch t := typ.(type) {
|
|
|
|
case *ast.Ident:
|
2009-04-02 16:58:58 -06:00
|
|
|
return string(t.Value);
|
2009-03-31 19:46:21 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-02 11:16:17 -06:00
|
|
|
func (doc *PackageDoc) addType(decl *ast.GenDecl) {
|
|
|
|
typ := decl.Specs[0].(*ast.TypeSpec);
|
2009-04-02 16:58:58 -06:00
|
|
|
name := typ.Name.Value;
|
2009-04-02 11:16:17 -06:00
|
|
|
tdoc := &typeDoc{decl, make(map[string] *funcDoc), make(map[string] *funcDoc)};
|
2009-04-01 16:00:22 -06:00
|
|
|
doc.types[name] = tdoc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-31 19:46:21 -06:00
|
|
|
func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) {
|
2009-04-02 16:58:58 -06:00
|
|
|
name := fun.Name.Value;
|
2009-03-31 19:46:21 -06:00
|
|
|
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 17:53:58 -06:00
|
|
|
func (doc *PackageDoc) addDecl(decl ast.Decl) {
|
2009-03-30 18:13:11 -06:00
|
|
|
switch d := decl.(type) {
|
2009-04-02 11:16:17 -06:00
|
|
|
case *ast.GenDecl:
|
|
|
|
if len(d.Specs) > 0 {
|
|
|
|
switch d.Tok {
|
|
|
|
case token.IMPORT:
|
|
|
|
// ignore
|
|
|
|
case token.CONST:
|
|
|
|
// constants are always handled as a group
|
|
|
|
if hasExportedSpecs(d.Specs) {
|
|
|
|
doc.consts.Push(&valueDoc{d});
|
|
|
|
}
|
|
|
|
case token.TYPE:
|
|
|
|
// types are handled individually
|
|
|
|
for i, spec := range d.Specs {
|
|
|
|
s := spec.(*ast.TypeSpec);
|
|
|
|
if isExported(s.Name) {
|
|
|
|
// make a (fake) GenDecl node for this TypeSpec
|
|
|
|
// (we need to do this here - as opposed to just
|
|
|
|
// for printing - so we don't loose the GenDecl
|
|
|
|
// documentation)
|
|
|
|
var noPos token.Position;
|
|
|
|
doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{s}, noPos});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case token.VAR:
|
|
|
|
// variables are always handled as a group
|
|
|
|
if hasExportedSpecs(d.Specs) {
|
|
|
|
doc.vars.Push(&valueDoc{d});
|
|
|
|
}
|
|
|
|
}
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
case *ast.FuncDecl:
|
|
|
|
if isExported(d.Name) {
|
2009-03-31 19:46:21 -06:00
|
|
|
doc.addFunc(d);
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-31 17:53:58 -06:00
|
|
|
// AddProgram adds the AST of a source file belonging to the same
|
2009-04-01 16:00:22 -06:00
|
|
|
// package. The package names must match. If the source was added
|
|
|
|
// before, AddProgram is a no-op.
|
2009-03-30 18:13:11 -06:00
|
|
|
//
|
2009-04-01 16:00:22 -06:00
|
|
|
func (doc *PackageDoc) AddProgram(prog *ast.Program) {
|
2009-04-02 16:58:58 -06:00
|
|
|
if doc.name != prog.Name.Value {
|
2009-03-30 18:13:11 -06:00
|
|
|
panic("package names don't match");
|
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
|
|
|
|
// add package documentation
|
|
|
|
// TODO what to do if there are multiple files?
|
|
|
|
if prog.Doc != nil {
|
|
|
|
doc.doc = prog.Doc
|
|
|
|
}
|
|
|
|
|
2009-04-02 11:16:17 -06:00
|
|
|
// add all exported declarations
|
2009-04-01 16:00:22 -06:00
|
|
|
for i, decl := range prog.Decls {
|
2009-03-31 17:53:58 -06:00
|
|
|
doc.addDecl(decl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Printing
|
|
|
|
|
2009-04-01 16:00:22 -06:00
|
|
|
func htmlEscape(s []byte) []byte {
|
|
|
|
var buf io.ByteBuffer;
|
|
|
|
|
|
|
|
i0 := 0;
|
2009-03-31 17:53:58 -06:00
|
|
|
for i := 0; i < len(s); i++ {
|
2009-04-01 16:00:22 -06:00
|
|
|
var esc string;
|
2009-03-31 17:53:58 -06:00
|
|
|
switch s[i] {
|
|
|
|
case '<': esc = "<";
|
|
|
|
case '&': esc = "&";
|
|
|
|
default: continue;
|
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
fmt.Fprintf(&buf, "%s%s", s[i0 : i], esc);
|
|
|
|
i0 := i+1; // skip escaped char
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the rest
|
|
|
|
if i0 > 0 {
|
|
|
|
buf.Write(s[i0 : len(s)]);
|
|
|
|
s = buf.Data();
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Reduce contiguous sequences of '\t' in a string to a single '\t'.
|
2009-04-01 16:00:22 -06:00
|
|
|
// This will produce better results when the string is printed via
|
|
|
|
// a tabwriter.
|
|
|
|
// TODO make this functionality optional.
|
|
|
|
//
|
|
|
|
func untabify(s []byte) []byte {
|
|
|
|
var buf io.ByteBuffer;
|
|
|
|
|
|
|
|
i0 := 0;
|
2009-03-31 17:53:58 -06:00
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
if s[i] == '\t' {
|
2009-04-01 16:00:22 -06:00
|
|
|
i++; // include '\t'
|
|
|
|
buf.Write(s[i0 : i]);
|
|
|
|
// skip additional tabs
|
|
|
|
for i < len(s) && s[i] == '\t' {
|
|
|
|
i++;
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
i0 := i;
|
|
|
|
} else {
|
|
|
|
i++;
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
|
|
|
|
// write the rest
|
|
|
|
if i0 > 0 {
|
|
|
|
buf.Write(s[i0 : len(s)]);
|
|
|
|
s = buf.Data();
|
|
|
|
}
|
2009-03-31 17:53:58 -06:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-01 16:00:22 -06:00
|
|
|
func stripCommentDelimiters(s []byte) []byte {
|
2009-03-31 17:53:58 -06:00
|
|
|
switch s[1] {
|
2009-04-01 16:00:22 -06:00
|
|
|
case '/': return s[2 : len(s)-1];
|
|
|
|
case '*': return s[2 : len(s)-2];
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
panic();
|
|
|
|
return nil;
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-01 16:00:22 -06:00
|
|
|
const /* formatting mode */ (
|
|
|
|
in_gap = iota;
|
|
|
|
in_paragraph;
|
|
|
|
in_preformatted;
|
|
|
|
)
|
|
|
|
|
|
|
|
func printLine(p *astPrinter.Printer, line []byte, mode int) int {
|
|
|
|
indented := len(line) > 0 && line[0] == '\t';
|
|
|
|
line = stripWhiteSpace(line);
|
|
|
|
if len(line) == 0 {
|
|
|
|
// empty line
|
|
|
|
switch mode {
|
|
|
|
case in_paragraph:
|
|
|
|
p.Printf("</p>\n");
|
|
|
|
mode = in_gap;
|
|
|
|
case in_preformatted:
|
|
|
|
p.Printf("\n");
|
|
|
|
// remain in preformatted
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// non-empty line
|
|
|
|
if indented {
|
|
|
|
switch mode {
|
|
|
|
case in_gap:
|
|
|
|
p.Printf("<pre>\n");
|
|
|
|
case in_paragraph:
|
|
|
|
p.Printf("</p>\n");
|
|
|
|
p.Printf("<pre>\n");
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
mode = in_preformatted;
|
2009-03-31 17:53:58 -06:00
|
|
|
} else {
|
2009-04-01 16:00:22 -06:00
|
|
|
switch mode {
|
|
|
|
case in_gap:
|
|
|
|
p.Printf("<p>\n");
|
|
|
|
case in_preformatted:
|
|
|
|
p.Printf("</pre>\n");
|
|
|
|
p.Printf("<p>\n");
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
mode = in_paragraph;
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
// print line
|
|
|
|
p.Printf("%s\n", untabify(htmlEscape(line)));
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func closeMode(p *astPrinter.Printer, mode int) {
|
|
|
|
switch mode {
|
|
|
|
case in_paragraph:
|
2009-03-31 17:53:58 -06:00
|
|
|
p.Printf("</p>\n");
|
2009-04-01 16:00:22 -06:00
|
|
|
case in_preformatted:
|
|
|
|
p.Printf("</pre>\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func printComments(p *astPrinter.Printer, comment ast.Comments) {
|
|
|
|
mode := in_gap;
|
|
|
|
for i, c := range comment {
|
|
|
|
s := stripCommentDelimiters(c.Text);
|
|
|
|
|
|
|
|
// split comment into lines and print the lines
|
|
|
|
i0 := 0; // beginning of current line
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
if s[i] == '\n' {
|
|
|
|
// reached line end - print current line
|
|
|
|
mode = printLine(p, s[i0 : i], mode);
|
|
|
|
i0 = i + 1; // beginning of next line; skip '\n'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// print last line
|
|
|
|
mode = printLine(p, s[i0 : len(s)], mode);
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
2009-04-01 16:00:22 -06:00
|
|
|
closeMode(p, mode);
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-02 11:16:17 -06:00
|
|
|
func (c *valueDoc) print(p *astPrinter.Printer) {
|
2009-04-01 16:00:22 -06:00
|
|
|
printComments(p, c.decl.Doc);
|
|
|
|
p.Printf("<pre>");
|
2009-04-02 11:16:17 -06:00
|
|
|
p.DoGenDecl(c.decl);
|
2009-04-01 16:00:22 -06:00
|
|
|
p.Printf("</pre>\n");
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-31 19:46:21 -06:00
|
|
|
func (f *funcDoc) print(p *astPrinter.Printer, hsize int) {
|
2009-03-31 17:53:58 -06:00
|
|
|
d := f.decl;
|
|
|
|
if d.Recv != nil {
|
2009-03-31 19:46:21 -06:00
|
|
|
p.Printf("<h%d>func (", hsize);
|
2009-03-31 17:53:58 -06:00
|
|
|
p.Expr(d.Recv.Type);
|
2009-04-02 16:58:58 -06:00
|
|
|
p.Printf(") %s</h%d>\n", d.Name.Value, hsize);
|
2009-03-31 17:53:58 -06:00
|
|
|
} else {
|
2009-04-02 16:58:58 -06:00
|
|
|
p.Printf("<h%d>func %s</h%d>\n", hsize, d.Name.Value, hsize);
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
|
|
|
p.Printf("<p><code>");
|
|
|
|
p.DoFuncDecl(d);
|
|
|
|
p.Printf("</code></p>\n");
|
2009-04-01 16:00:22 -06:00
|
|
|
printComments(p, d.Doc);
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-31 17:53:58 -06:00
|
|
|
func (t *typeDoc) print(p *astPrinter.Printer) {
|
|
|
|
d := t.decl;
|
2009-04-02 11:16:17 -06:00
|
|
|
s := d.Specs[0].(*ast.TypeSpec);
|
2009-04-02 16:58:58 -06:00
|
|
|
p.Printf("<h2>type %s</h2>\n", s.Name.Value);
|
2009-03-31 17:53:58 -06:00
|
|
|
p.Printf("<p><pre>");
|
2009-04-02 11:16:17 -06:00
|
|
|
p.DoGenDecl(d);
|
2009-03-31 17:53:58 -06:00
|
|
|
p.Printf("</pre></p>\n");
|
2009-04-02 11:16:17 -06:00
|
|
|
printComments(p, s.Doc);
|
2009-03-31 17:53:58 -06:00
|
|
|
|
|
|
|
// print associated methods, if any
|
2009-03-31 19:46:21 -06:00
|
|
|
for name, m := range t.factories {
|
|
|
|
m.print(p, 3);
|
|
|
|
}
|
|
|
|
|
2009-03-31 17:53:58 -06:00
|
|
|
for name, m := range t.methods {
|
2009-03-31 19:46:21 -06:00
|
|
|
m.print(p, 3);
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
2009-03-30 18:13:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO make this a parameter for Init or Print?
|
|
|
|
var templ = template.NewTemplateOrDie("template.html");
|
|
|
|
|
2009-03-31 17:53:58 -06:00
|
|
|
func (doc *PackageDoc) Print(writer io.Write) {
|
|
|
|
var p astPrinter.Printer;
|
|
|
|
p.Init(writer, nil, true);
|
2009-03-30 18:13:11 -06:00
|
|
|
|
2009-03-31 17:53:58 -06:00
|
|
|
// TODO propagate Apply errors
|
|
|
|
templ.Apply(writer, "<!--", template.Substitution {
|
|
|
|
"PACKAGE_NAME-->" :
|
|
|
|
func() {
|
|
|
|
fmt.Fprint(writer, doc.name);
|
|
|
|
},
|
|
|
|
|
|
|
|
"PROGRAM_HEADER-->":
|
|
|
|
func() {
|
2009-04-01 16:00:22 -06:00
|
|
|
fmt.Fprintf(writer, "<p><code>import \"%s\"</code></p>\n", doc.name);
|
|
|
|
printComments(&p, doc.doc);
|
2009-03-31 17:53:58 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
"CONSTANTS-->" :
|
|
|
|
func() {
|
2009-04-01 16:00:22 -06:00
|
|
|
if doc.consts.Len() > 0 {
|
|
|
|
fmt.Fprintln(writer, "<hr />");
|
|
|
|
fmt.Fprintln(writer, "<h2>Constants</h2>");
|
|
|
|
for i := 0; i < doc.consts.Len(); i++ {
|
2009-04-02 11:16:17 -06:00
|
|
|
doc.consts.At(i).(*valueDoc).print(&p);
|
2009-04-01 16:00:22 -06:00
|
|
|
}
|
|
|
|
}
|
2009-03-31 17:53:58 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
"TYPES-->" :
|
|
|
|
func() {
|
|
|
|
for name, t := range doc.types {
|
2009-04-01 16:00:22 -06:00
|
|
|
fmt.Fprintln(writer, "<hr />");
|
2009-03-31 17:53:58 -06:00
|
|
|
t.print(&p);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
"VARIABLES-->" :
|
|
|
|
func() {
|
2009-04-01 16:00:22 -06:00
|
|
|
if doc.vars.Len() > 0 {
|
|
|
|
fmt.Fprintln(writer, "<hr />");
|
|
|
|
fmt.Fprintln(writer, "<h2>Variables</h2>");
|
|
|
|
for i := 0; i < doc.vars.Len(); i++ {
|
2009-04-02 11:16:17 -06:00
|
|
|
doc.vars.At(i).(*valueDoc).print(&p);
|
2009-04-01 16:00:22 -06:00
|
|
|
}
|
|
|
|
}
|
2009-03-31 17:53:58 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
"FUNCTIONS-->" :
|
|
|
|
func() {
|
2009-04-01 16:00:22 -06:00
|
|
|
if len(doc.funcs) > 0 {
|
|
|
|
fmt.Fprintln(writer, "<hr />");
|
|
|
|
for name, f := range doc.funcs {
|
|
|
|
f.print(&p, 2);
|
|
|
|
}
|
2009-03-31 17:53:58 -06:00
|
|
|
}
|
|
|
|
},
|
2009-03-30 18:13:11 -06:00
|
|
|
});
|
|
|
|
}
|