Commit 90e6656c authored by Robert Griesemer's avatar Robert Griesemer

go/ast/filter.go:

- more orthogonal functionality of filter functions for better re-use

go/doc/doc.go:
- simplified interface
- collect filenames of packages so that they can be shown

godoc:
- removed TODO, show list of package (linked) files used to create documentation

R=rsc
DELTA=130  (68 added, 24 deleted, 38 changed)
OCL=32549
CL=32552
parent 9717a794
{.section Dirs} {.section Dirs}
<h2>Subdirectories</h2> <h2>Subdirectories</h2>
{.repeated section @} {.repeated section @}
<a href="{Name|html}/">{Name|html}</a><br /> <a href="{Name|html}">{Name|html}</a><br />
{.end} {.end}
<hr /> <hr />
{.end} {.end}
{.section PDoc} {.section PDoc}
<h1>package {PackageName|html}</h1> <h1>package {PackageName|html}</h1>
<p><code>import "{ImportPath|html}"</code></p> <p><code>import "{ImportPath|html}"</code></p>
{Doc|html-comment} {Doc|html-comment}
{.section Filenames}
<p>
<h4>Package files</h4>
<font size=-1>
{.repeated section @}
<a href="/{FilePath|html}/{@|html}">{@|html}</a>
{.end}
</font>
</p>
{.end}
{.section Consts} {.section Consts}
<h2>Constants</h2> <h2>Constants</h2>
{.repeated section @} {.repeated section @}
......
...@@ -444,12 +444,8 @@ func getPageInfo(path string) PageInfo { ...@@ -444,12 +444,8 @@ func getPageInfo(path string) PageInfo {
// compute package documentation // compute package documentation
var pdoc *doc.PackageDoc; var pdoc *doc.PackageDoc;
if pkg != nil { if pkg != nil {
// TODO(gri) Simplify DocReader interface: no need anymore to add ast.PackageExports(pkg);
// more than one file because of ast.PackageInterface. pdoc = doc.NewPackageDoc(pkg, pathutil.Clean(path)); // no trailing '/' in importpath
var r doc.DocReader;
r.Init(pkg.Name, pathutil.Clean(path)); // no trailing '/' in importpath
r.AddFile(ast.PackageExports(pkg));
pdoc = r.Doc();
} }
return PageInfo{pdoc, subdirs}; return PageInfo{pdoc, subdirs};
......
...@@ -146,8 +146,8 @@ func main() { ...@@ -146,8 +146,8 @@ func main() {
if !*silent { if !*silent {
w := makeTabwriter(os.Stdout); w := makeTabwriter(os.Stdout);
if *exports { if *exports {
src := ast.PackageExports(pkg); ast.PackageExports(pkg);
printer.Fprint(w, src, printerMode()); // ignore errors printer.Fprint(w, ast.MergePackageFiles(pkg), printerMode()); // ignore errors
} else { } else {
for _, src := range pkg.Files { for _, src := range pkg.Files {
printer.Fprint(w, src, printerMode()); // ignore errors printer.Fprint(w, src, printerMode()); // ignore errors
......
...@@ -168,16 +168,17 @@ func filterDecl(decl Decl) bool { ...@@ -168,16 +168,17 @@ func filterDecl(decl Decl) bool {
} }
// FilterExports trims an AST in place such that only exported nodes remain: // FileExports trims the AST for a Go source file in place such that only
// all top-level identifiers which are not exported and their associated // exported nodes remain: all top-level identifiers which are not exported
// information (such as type, initial value, or function body) are removed. // and their associated information (such as type, initial value, or function
// Non-exported fields and methods of exported types are stripped, and the // body) are removed. Non-exported fields and methods of exported types are
// function bodies of exported functions are set to nil. // stripped, and the function bodies of exported functions are set to nil.
// The File.comments list is not changed.
// //
// FilterExports returns true if there is an exported declaration; it returns // FileExports returns true if there is an exported declaration; it returns
// false otherwise. // false otherwise.
// //
func FilterExports(src *File) bool { func FileExports(src *File) bool {
j := 0; j := 0;
for _, d := range src.Decls { for _, d := range src.Decls {
if filterDecl(d) { if filterDecl(d) {
...@@ -190,33 +191,44 @@ func FilterExports(src *File) bool { ...@@ -190,33 +191,44 @@ func FilterExports(src *File) bool {
} }
// PackageExports trims the AST for a Go package in place such that only
// exported nodes remain. The pkg.Files list is not changed, so that file
// names and top-level package comments don't get lost.
//
// PackageExports returns true if there is an exported declaration; it
// returns false otherwise.
//
func PackageExports(pkg *Package) bool {
hasExports := false;
for _, f := range pkg.Files {
if FileExports(f) {
hasExports = true;
}
}
return hasExports;
}
// separator is an empty //-style comment that is interspersed between // separator is an empty //-style comment that is interspersed between
// different comment groups when they are concatenated into a single group // different comment groups when they are concatenated into a single group
// //
var separator = &Comment{noPos, []byte{'/', '/'}}; var separator = &Comment{noPos, []byte{'/', '/'}};
// PackageExports returns an AST containing only the exported declarations // MergePackageFiles creates a file AST by merging the ASTs of the
// of the package pkg. PackageExports modifies the pkg AST. // files belonging to a package.
// //
func PackageExports(pkg *Package) *File { func MergePackageFiles(pkg *Package) *File {
// Collect all source files with exported declarations and count // Count the number of package comments and declarations across
// the number of package comments and declarations in all files. // all package files.
files := make([]*File, len(pkg.Files));
ncomments := 0; ncomments := 0;
ndecls := 0; ndecls := 0;
i := 0;
for _, f := range pkg.Files { for _, f := range pkg.Files {
if f.Doc != nil { if f.Doc != nil {
ncomments += len(f.Doc.List) + 1; // +1 for separator ncomments += len(f.Doc.List) + 1; // +1 for separator
} }
if FilterExports(f) { ndecls += len(f.Decls);
ndecls += len(f.Decls);
files[i] = f;
i++;
}
} }
files = files[0 : i];
// Collect package comments from all package files into a single // Collect package comments from all package files into a single
// CommentGroup - the collected package documentation. The order // CommentGroup - the collected package documentation. The order
...@@ -243,12 +255,12 @@ func PackageExports(pkg *Package) *File { ...@@ -243,12 +255,12 @@ func PackageExports(pkg *Package) *File {
doc = &CommentGroup{list, nil}; doc = &CommentGroup{list, nil};
} }
// Collect exported declarations from all package files. // Collect declarations from all package files.
var decls []Decl; var decls []Decl;
if ndecls > 0 { if ndecls > 0 {
decls = make([]Decl, ndecls); decls = make([]Decl, ndecls);
i := 0; i := 0;
for _, f := range files { for _, f := range pkg.Files {
for _, d := range f.Decls { for _, d := range f.Decls {
decls[i] = d; decls[i] = d;
i++; i++;
...@@ -256,5 +268,8 @@ func PackageExports(pkg *Package) *File { ...@@ -256,5 +268,8 @@ func PackageExports(pkg *Package) *File {
} }
} }
// TODO(gri) Should collect comments as well. For that the comment
// list should be changed back into a []*CommentGroup,
// otherwise need to modify the existing linked list.
return &File{doc, noPos, &Ident{noPos, pkg.Name}, decls, nil}; return &File{doc, noPos, &Ident{noPos, pkg.Name}, decls, nil};
} }
...@@ -26,16 +26,14 @@ type typeDoc struct { ...@@ -26,16 +26,14 @@ type typeDoc struct {
} }
// DocReader accumulates documentation for a single package. // docReader accumulates documentation for a single package.
// It modifies the AST: Comments (declaration documentation) // It modifies the AST: Comments (declaration documentation)
// that have been collected by the DocReader are set to nil // that have been collected by the DocReader are set to nil
// in the respective AST nodes so that they are not printed // in the respective AST nodes so that they are not printed
// twice (once when printing the documentation and once when // twice (once when printing the documentation and once when
// printing the corresponding AST node). // printing the corresponding AST node).
// //
type DocReader struct { type docReader struct {
name string; // package name
path string; // import path
doc *ast.CommentGroup; // package documentation, if any doc *ast.CommentGroup; // package documentation, if any
consts *vector.Vector; // list of *ast.GenDecl consts *vector.Vector; // list of *ast.GenDecl
types map[string] *typeDoc; types map[string] *typeDoc;
...@@ -45,12 +43,7 @@ type DocReader struct { ...@@ -45,12 +43,7 @@ type DocReader struct {
} }
// Init initializes a DocReader to collect package documentation func (doc *docReader) init() {
// for the package with the given package name and import path.
//
func (doc *DocReader) Init(pkg, imp string) {
doc.name = pkg;
doc.path = imp;
doc.consts = vector.New(0); doc.consts = vector.New(0);
doc.types = make(map[string] *typeDoc); doc.types = make(map[string] *typeDoc);
doc.vars = vector.New(0); doc.vars = vector.New(0);
...@@ -70,7 +63,7 @@ func baseTypeName(typ ast.Expr) string { ...@@ -70,7 +63,7 @@ func baseTypeName(typ ast.Expr) string {
} }
func (doc *DocReader) lookupTypeDoc(typ ast.Expr) *typeDoc { func (doc *docReader) lookupTypeDoc(typ ast.Expr) *typeDoc {
tdoc, found := doc.types[baseTypeName(typ)]; tdoc, found := doc.types[baseTypeName(typ)];
if found { if found {
return tdoc; return tdoc;
...@@ -79,7 +72,7 @@ func (doc *DocReader) lookupTypeDoc(typ ast.Expr) *typeDoc { ...@@ -79,7 +72,7 @@ func (doc *DocReader) lookupTypeDoc(typ ast.Expr) *typeDoc {
} }
func (doc *DocReader) addType(decl *ast.GenDecl) { func (doc *docReader) addType(decl *ast.GenDecl) {
typ := decl.Specs[0].(*ast.TypeSpec); typ := decl.Specs[0].(*ast.TypeSpec);
name := typ.Name.Value; name := typ.Name.Value;
if _, found := doc.types[name]; !found { if _, found := doc.types[name]; !found {
...@@ -91,7 +84,7 @@ func (doc *DocReader) addType(decl *ast.GenDecl) { ...@@ -91,7 +84,7 @@ func (doc *DocReader) addType(decl *ast.GenDecl) {
} }
func (doc *DocReader) addFunc(fun *ast.FuncDecl) { func (doc *docReader) addFunc(fun *ast.FuncDecl) {
name := fun.Name.Value; name := fun.Name.Value;
// determine if it should be associated with a type // determine if it should be associated with a type
...@@ -131,7 +124,7 @@ func (doc *DocReader) addFunc(fun *ast.FuncDecl) { ...@@ -131,7 +124,7 @@ func (doc *DocReader) addFunc(fun *ast.FuncDecl) {
} }
func (doc *DocReader) addDecl(decl ast.Decl) { func (doc *docReader) addDecl(decl ast.Decl) {
switch d := decl.(type) { switch d := decl.(type) {
case *ast.GenDecl: case *ast.GenDecl:
if len(d.Specs) > 0 { if len(d.Specs) > 0 {
...@@ -186,22 +179,22 @@ var ( ...@@ -186,22 +179,22 @@ var (
) )
// AddFile adds the AST for a source file to the DocReader. // addFile adds the AST for a source file to the docReader.
// Adding the same AST multiple times is a no-op. // Adding the same AST multiple times is a no-op.
// //
func (doc *DocReader) AddFile(src *ast.File) { func (doc *docReader) addFile(src *ast.File) {
if bug_markers == nil { if bug_markers == nil {
bug_markers = makeRex("^/[/*][ \t]*BUG\\(.*\\):[ \t]*"); // BUG(uid): bug_markers = makeRex("^/[/*][ \t]*BUG\\(.*\\):[ \t]*"); // BUG(uid):
bug_content = makeRex("[^ \n\r\t]+"); // at least one non-whitespace char bug_content = makeRex("[^ \n\r\t]+"); // at least one non-whitespace char
} }
if doc.name != src.Name.Value {
panic("package names don't match");
}
// add package documentation // add package documentation
// TODO(gri) what to do if there are multiple files?
if src.Doc != nil { if src.Doc != nil {
// TODO(gri) This won't do the right thing if there is more
// than one file with package comments. Consider
// using ast.MergePackageFiles which handles these
// comments correctly (but currently looses BUG(...)
// comments).
doc.doc = src.Doc; doc.doc = src.Doc;
src.Doc = nil; // doc consumed - remove from ast.File node src.Doc = nil; // doc consumed - remove from ast.File node
} }
...@@ -228,6 +221,32 @@ func (doc *DocReader) AddFile(src *ast.File) { ...@@ -228,6 +221,32 @@ func (doc *DocReader) AddFile(src *ast.File) {
src.Comments = nil; // consumed unassociated comments - remove from ast.File node src.Comments = nil; // consumed unassociated comments - remove from ast.File node
} }
type PackageDoc struct
func (doc *docReader) newDoc(pkgname, importpath, filepath string, filenames []string) *PackageDoc
func NewFileDoc(file *ast.File) *PackageDoc {
var r docReader;
r.init();
r.addFile(file);
return r.newDoc(file.Name.Value, "", "", nil);
}
func NewPackageDoc(pkg *ast.Package, importpath string) *PackageDoc {
var r docReader;
r.init();
filenames := make([]string, len(pkg.Files));
i := 0;
for filename, f := range pkg.Files {
r.addFile(f);
filenames[i] = filename;
i++;
}
return r.newDoc(pkg.Name, importpath, pkg.Path, filenames);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Conversion to external representation // Conversion to external representation
...@@ -402,6 +421,8 @@ func makeBugDocs(v *vector.Vector) []string { ...@@ -402,6 +421,8 @@ func makeBugDocs(v *vector.Vector) []string {
type PackageDoc struct { type PackageDoc struct {
PackageName string; PackageName string;
ImportPath string; ImportPath string;
FilePath string;
Filenames []string;
Doc string; Doc string;
Consts []*ValueDoc; Consts []*ValueDoc;
Types []*TypeDoc; Types []*TypeDoc;
...@@ -411,12 +432,15 @@ type PackageDoc struct { ...@@ -411,12 +432,15 @@ type PackageDoc struct {
} }
// Doc returns the accumulated documentation for the package. // newDoc returns the accumulated documentation for the package.
// //
func (doc *DocReader) Doc() *PackageDoc { func (doc *docReader) newDoc(pkgname, importpath, filepath string, filenames []string) *PackageDoc {
p := new(PackageDoc); p := new(PackageDoc);
p.PackageName = doc.name; p.PackageName = pkgname;
p.ImportPath = doc.path; p.ImportPath = importpath;
p.FilePath = filepath;
sort.SortStrings(filenames);
p.Filenames = filenames;
p.Doc = astComment(doc.doc); p.Doc = astComment(doc.doc);
p.Consts = makeValueDocs(doc.consts); p.Consts = makeValueDocs(doc.consts);
p.Vars = makeValueDocs(doc.vars); p.Vars = makeValueDocs(doc.vars);
......
...@@ -48,7 +48,7 @@ func check(t *testing.T, source, golden string, exports bool) { ...@@ -48,7 +48,7 @@ func check(t *testing.T, source, golden string, exports bool) {
// filter exports if necessary // filter exports if necessary
if exports { if exports {
ast.FilterExports(prog); // ignore result ast.FileExports(prog); // ignore result
prog.Comments = nil; // don't print comments that are not in AST prog.Comments = nil; // don't print comments that are not in AST
} }
......
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