From 6d5bba5148f57a28bc19d86f21ccb114e9aaf614 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Wed, 1 Apr 2009 15:00:22 -0700
Subject: [PATCH] More gds functionality: - package headers - constants -
variables - formatted comments
Next steps:
- sorted output
- collection of all files belonging to a package
- fine-tuning of output
R=r
OCL=26997
CL=26997
---
usr/gri/pretty/docprinter.go | 301 +++++++++++++++++++++++------------
1 file changed, 200 insertions(+), 101 deletions(-)
diff --git a/usr/gri/pretty/docprinter.go b/usr/gri/pretty/docprinter.go
index f724f7d5f6..aa72687044 100644
--- a/usr/gri/pretty/docprinter.go
+++ b/usr/gri/pretty/docprinter.go
@@ -12,6 +12,7 @@ import (
"fmt";
"ast";
+ "token";
"astprinter";
"template";
)
@@ -37,15 +38,26 @@ func hasExportedNames(names []*ast.Ident) bool {
}
+func hasExportedDecls(decl []ast.Decl) bool {
+ for i, d := range decl {
+ switch t := d.(type) {
+ case *ast.ConstDecl:
+ return hasExportedNames(t.Names);
+ }
+ }
+ return false;
+}
+
+
// ----------------------------------------------------------------------------
type constDoc struct {
- decl *ast.ConstDecl;
+ decl *ast.DeclList;
}
type varDoc struct {
- decl *ast.VarDecl;
+ decl *ast.DeclList;
}
@@ -63,10 +75,10 @@ type typeDoc struct {
type PackageDoc struct {
name string; // package name
- imports map[string] string;
- consts map[string] *constDoc;
+ doc ast.Comments; // package documentation, if any
+ consts *vector.Vector; // list of *ast.DeclList with Tok == token.CONST
+ vars *vector.Vector; // list of *ast.DeclList with Tok == token.CONST
types map[string] *typeDoc;
- vars map[string] *varDoc;
funcs map[string] *funcDoc;
}
@@ -77,10 +89,9 @@ type PackageDoc struct {
//
func (doc *PackageDoc) Init(name string) {
doc.name = name;
- doc.imports = make(map[string] string);
- doc.consts = make(map[string] *constDoc);
+ doc.consts = vector.New(0);
doc.types = make(map[string] *typeDoc);
- doc.vars = make(map[string] *varDoc);
+ doc.vars = vector.New(0);
doc.funcs = make(map[string] *funcDoc);
}
@@ -105,6 +116,13 @@ func (doc *PackageDoc) lookupTypeDoc(typ ast.Expr) *typeDoc {
}
+func (doc *PackageDoc) addType(typ *ast.TypeDecl) {
+ name := string(typ.Name.Lit);
+ tdoc := &typeDoc{typ, make(map[string] *funcDoc), make(map[string] *funcDoc)};
+ doc.types[name] = tdoc;
+}
+
+
func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) {
name := string(fun.Name.Lit);
fdoc := &funcDoc{fun};
@@ -142,21 +160,19 @@ func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) {
func (doc *PackageDoc) addDecl(decl ast.Decl) {
switch d := decl.(type) {
- case *ast.ImportDecl:
case *ast.ConstDecl:
if hasExportedNames(d.Names) {
+ // TODO
}
case *ast.TypeDecl:
if isExported(d.Name) {
- // TODO only add if not there already - or ignore?
- name := string(d.Name.Lit);
- tdoc := &typeDoc{d, make(map[string] *funcDoc), make(map[string] *funcDoc)};
- doc.types[name] = tdoc;
+ doc.addType(d);
}
case *ast.VarDecl:
if hasExportedNames(d.Names) {
+ // TODO
}
case *ast.FuncDecl:
@@ -165,24 +181,41 @@ func (doc *PackageDoc) addDecl(decl ast.Decl) {
}
case *ast.DeclList:
- for i, decl := range d.List {
- doc.addDecl(decl);
+ switch d.Tok {
+ case token.IMPORT, token.TYPE:
+ for i, decl := range d.List {
+ doc.addDecl(decl);
+ }
+ case token.CONST:
+ if hasExportedDecls(d.List) {
+ doc.consts.Push(&constDoc{d});
+ }
+ case token.VAR:
+ if hasExportedDecls(d.List) {
+ doc.consts.Push(&varDoc{d});
+ }
}
}
}
// AddProgram adds the AST of a source file belonging to the same
-// package. The package names must match. If the package was added
-// before, AddPackage is a no-op.
+// package. The package names must match. If the source was added
+// before, AddProgram is a no-op.
//
-func (doc *PackageDoc) AddProgram(pak *ast.Program) {
- if doc.name != string(pak.Name.Lit) {
+func (doc *PackageDoc) AddProgram(prog *ast.Program) {
+ if doc.name != string(prog.Name.Lit) {
panic("package names don't match");
}
-
+
+ // add package documentation
+ // TODO what to do if there are multiple files?
+ if prog.Doc != nil {
+ doc.doc = prog.Doc
+ }
+
// add all declarations
- for i, decl := range pak.Decls {
+ for i, decl := range prog.Decls {
doc.addDecl(decl);
}
}
@@ -191,33 +224,58 @@ func (doc *PackageDoc) AddProgram(pak *ast.Program) {
// ----------------------------------------------------------------------------
// Printing
-func htmlEscape(s string) string {
- var esc string;
+func htmlEscape(s []byte) []byte {
+ var buf io.ByteBuffer;
+
+ i0 := 0;
for i := 0; i < len(s); i++ {
+ var esc string;
switch s[i] {
case '<': esc = "<";
case '&': esc = "&";
default: continue;
}
- return s[0 : i] + esc + htmlEscape(s[i+1 : len(s)]);
+ 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();
}
return s;
}
// Reduce contiguous sequences of '\t' in a string to a single '\t'.
-func untabify(s string) string {
+// 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;
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)]);
+ i++; // include '\t'
+ buf.Write(s[i0 : i]);
+ // skip additional tabs
+ for i < len(s) && s[i] == '\t' {
+ i++;
}
+ i0 := i;
+ } else {
+ i++;
}
}
+
+ // write the rest
+ if i0 > 0 {
+ buf.Write(s[i0 : len(s)]);
+ s = buf.Data();
+ }
return s;
}
@@ -234,40 +292,108 @@ func stripWhiteSpace(s []byte) []byte {
}
-func cleanComment(s []byte) []byte {
+func stripCommentDelimiters(s []byte) []byte {
switch s[1] {
- case '/': s = s[2 : len(s)-1];
- case '*': s = s[2 : len(s)-2];
- default : panic("illegal comment");
+ case '/': return s[2 : len(s)-1];
+ case '*': return s[2 : len(s)-2];
}
- return stripWhiteSpace(s);
+ panic();
+ return nil;
}
-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("\n");
- in_paragraph = true;
+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("
\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("\n");
+ case in_paragraph:
+ p.Printf("
\n");
+ p.Printf("\n");
}
- p.Printf("%s\n", htmlEscape(untabify(string(s))));
+ mode = in_preformatted;
} else {
- if in_paragraph {
- p.Printf("\n");
- in_paragraph = false;
+ switch mode {
+ case in_gap:
+ p.Printf("\n");
+ case in_preformatted:
+ p.Printf("
\n");
+ p.Printf("\n");
}
+ mode = in_paragraph;
}
+ // print line
+ p.Printf("%s\n", untabify(htmlEscape(line)));
}
- if in_paragraph {
+ return mode;
+}
+
+
+func closeMode(p *astPrinter.Printer, mode int) {
+ switch mode {
+ case in_paragraph:
p.Printf("
\n");
+ case in_preformatted:
+ p.Printf("\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);
}
+ closeMode(p, mode);
}
-func (c *constDoc) printConsts(p *astPrinter.Printer) {
+func (c *constDoc) print(p *astPrinter.Printer) {
+ printComments(p, c.decl.Doc);
+ p.Printf("");
+ p.DoDeclList(c.decl);
+ p.Printf("
\n");
+}
+
+
+func (c *varDoc) print(p *astPrinter.Printer) {
+ printComments(p, c.decl.Doc);
+ p.Printf("");
+ p.DoDeclList(c.decl);
+ p.Printf("
\n");
}
@@ -283,9 +409,7 @@ func (f *funcDoc) print(p *astPrinter.Printer, hsize int) {
p.Printf("");
p.DoFuncDecl(d);
p.Printf("
\n");
- if d.Doc != nil {
- printComment(p, d.Doc);
- }
+ printComments(p, d.Doc);
}
@@ -295,9 +419,7 @@ func (t *typeDoc) print(p *astPrinter.Printer) {
p.Printf("");
p.DoTypeDecl(d);
p.Printf("
\n");
- if d.Doc != nil {
- printComment(p, d.Doc);
- }
+ printComments(p, d.Doc);
// print associated methods, if any
for name, m := range t.factories {
@@ -310,47 +432,6 @@ func (t *typeDoc) print(p *astPrinter.Printer) {
}
-func (v *varDoc) print(p *astPrinter.Printer) {
-}
-
-
-/*
-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("Constants
\n");
- P.Printf("");
- P.DoConstDecl(d);
- P.String(nopos, "");
- P.Printf("
\n");
- if d.Doc != nil {
- P.printComment(d.Doc);
- }
- }
-
- case *ast.VarDecl:
- if hasExportedNames(d.Names) {
- P.Printf("Variables
\n");
- P.Printf("");
- P.DoVarDecl(d);
- P.String(nopos, "");
- P.Printf("
\n");
- if d.Doc != nil {
- P.printComment(d.Doc);
- }
- }
-
- case *ast.DeclList:
-
- }
- }
-}
-*/
-
-
// TODO make this a parameter for Init or Print?
var templ = template.NewTemplateOrDie("template.html");
@@ -367,29 +448,47 @@ func (doc *PackageDoc) Print(writer io.Write) {
"PROGRAM_HEADER-->":
func() {
+ fmt.Fprintf(writer, "import \"%s\"
\n", doc.name);
+ printComments(&p, doc.doc);
},
"CONSTANTS-->" :
func() {
+ if doc.consts.Len() > 0 {
+ fmt.Fprintln(writer, "
");
+ fmt.Fprintln(writer, "Constants
");
+ for i := 0; i < doc.consts.Len(); i++ {
+ doc.consts.At(i).(*constDoc).print(&p);
+ }
+ }
},
"TYPES-->" :
func() {
for name, t := range doc.types {
- p.Printf("
\n");
+ fmt.Fprintln(writer, "
");
t.print(&p);
}
},
"VARIABLES-->" :
func() {
+ if doc.vars.Len() > 0 {
+ fmt.Fprintln(writer, "
");
+ fmt.Fprintln(writer, "Variables
");
+ for i := 0; i < doc.vars.Len(); i++ {
+ doc.vars.At(i).(*varDoc).print(&p);
+ }
+ }
},
"FUNCTIONS-->" :
func() {
- p.Printf("
\n");
- for name, f := range doc.funcs {
- f.print(&p, 2);
+ if len(doc.funcs) > 0 {
+ fmt.Fprintln(writer, "
");
+ for name, f := range doc.funcs {
+ f.print(&p, 2);
+ }
}
},
});
--
2.48.1