| 
									
										
										
										
											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. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // TODO: printing is gone; install as "go/doc" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package doc | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2009-04-16 20:52:37 -07:00
										 |  |  | 	"container/vector"; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	"fmt"; | 
					
						
							| 
									
										
										
										
											2009-04-16 20:52:37 -07:00
										 |  |  | 	"go/ast"; | 
					
						
							|  |  |  | 	"go/token"; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	"io"; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	"once"; | 
					
						
							|  |  |  | 	"regexp"; | 
					
						
							|  |  |  | 	"sort"; | 
					
						
							|  |  |  | 	"strings"; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	"unicode"; | 
					
						
							|  |  |  | 	"utf8"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 	"astprinter"; | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Elementary support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func hasExportedNames(names []*ast.Ident) bool { | 
					
						
							|  |  |  | 	for i, name := range names { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		if name.IsExported() { | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 			return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 10:16:17 -07: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 15:00:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type typeDoc struct { | 
					
						
							| 
									
										
										
										
											2009-04-02 10:16:17 -07:00
										 |  |  | 	decl *ast.GenDecl;  // len(decl.Specs) == 1, and the element type is *ast.TypeSpec | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	factories map[string] *ast.FuncDecl; | 
					
						
							|  |  |  | 	methods map[string] *ast.FuncDecl; | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // DocReader accumulates documentation for a single package. | 
					
						
							|  |  |  | type DocReader struct { | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 	name string;  // package name | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	path string;  // import path | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 	doc ast.Comments;  // package documentation, if any | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	consts *vector.Vector;  // list of *ast.GenDecl | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 	types map[string] *typeDoc; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	vars *vector.Vector;  // list of *ast.GenDecl | 
					
						
							|  |  |  | 	funcs map[string] *ast.FuncDecl; | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Init initializes a DocReader to collect package documentation | 
					
						
							|  |  |  | // for the package with the given package name and import path. | 
					
						
							|  |  |  | func (doc *DocReader) Init(pkg, imp string) { | 
					
						
							|  |  |  | 	doc.name = pkg; | 
					
						
							|  |  |  | 	doc.path = imp; | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 	doc.consts = vector.New(0); | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 	doc.types = make(map[string] *typeDoc); | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 	doc.vars = vector.New(0); | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	doc.funcs = make(map[string] *ast.FuncDecl); | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		return string(t.Value); | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 	case *ast.StarExpr: | 
					
						
							|  |  |  | 		return baseTypeName(t.X); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ""; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (doc *DocReader) lookupTypeDoc(typ ast.Expr) *typeDoc { | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 	tdoc, found := doc.types[baseTypeName(typ)]; | 
					
						
							|  |  |  | 	if found { | 
					
						
							|  |  |  | 		return tdoc; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (doc *DocReader) addType(decl *ast.GenDecl) { | 
					
						
							| 
									
										
										
										
											2009-04-02 10:16:17 -07:00
										 |  |  | 	typ := decl.Specs[0].(*ast.TypeSpec); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	name := typ.Name.Value; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	tdoc := &typeDoc{decl, make(map[string] *ast.FuncDecl), make(map[string] *ast.FuncDecl)}; | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 	doc.types[name] = tdoc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (doc *DocReader) addFunc(fun *ast.FuncDecl) { | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	name := fun.Name.Value; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 	// determine if it should be associated with a type | 
					
						
							|  |  |  | 	var typ *typeDoc; | 
					
						
							|  |  |  | 	if fun.Recv != nil { | 
					
						
							|  |  |  | 		// method | 
					
						
							| 
									
										
										
										
											2009-04-02 22:39:52 -07:00
										 |  |  | 		// (all receiver types must be declared before they are used) | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 		typ = doc.lookupTypeDoc(fun.Recv.Type); | 
					
						
							|  |  |  | 		if typ != nil { | 
					
						
							| 
									
										
										
										
											2009-04-02 22:39:52 -07:00
										 |  |  | 			// type found (i.e., exported) | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			typ.methods[name] = fun; | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-02 22:39:52 -07:00
										 |  |  | 		// if the type wasn't found, it wasn't exported | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		// TODO: a non-exported type may still have exported functions | 
					
						
							|  |  |  | 		//       determine what to do in that case | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-02 22:39:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	// 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 { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 				typ.factories[name] = fun; | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 				return; | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// ordinary function | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	doc.funcs[name] = fun; | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (doc *DocReader) addDecl(decl ast.Decl) { | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 	switch d := decl.(type) { | 
					
						
							| 
									
										
										
										
											2009-04-02 10:16:17 -07: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) { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 					doc.consts.Push(d); | 
					
						
							| 
									
										
										
										
											2009-04-02 10:16:17 -07:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			case token.TYPE: | 
					
						
							|  |  |  | 				// types are handled individually | 
					
						
							|  |  |  | 				for i, spec := range d.Specs { | 
					
						
							|  |  |  | 					s := spec.(*ast.TypeSpec); | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 					if s.Name.IsExported() { | 
					
						
							| 
									
										
										
										
											2009-04-02 10:16:17 -07:00
										 |  |  | 						// 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) { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 					doc.vars.Push(d); | 
					
						
							| 
									
										
										
										
											2009-04-02 10:16:17 -07:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case *ast.FuncDecl: | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		if d.Name.IsExported() { | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 			doc.addFunc(d); | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // AddProgram adds the AST for a source file to the DocReader. | 
					
						
							|  |  |  | // Adding the same AST multiple times is a no-op. | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (doc *DocReader) AddProgram(prog *ast.Program) { | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	if doc.name != prog.Name.Value { | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 		panic("package names don't match"); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// add package documentation | 
					
						
							|  |  |  | 	// TODO what to do if there are multiple files? | 
					
						
							|  |  |  | 	if prog.Doc != nil { | 
					
						
							|  |  |  | 		doc.doc = prog.Doc | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 10:16:17 -07:00
										 |  |  | 	// add all exported declarations | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 	for i, decl := range prog.Decls { | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 		doc.addDecl(decl); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ---------------------------------------------------------------------------- | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Conversion to external representation | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func Regexp(s string) *regexp.Regexp { | 
					
						
							|  |  |  | 	re, err := regexp.Compile(s); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic("MakeRegexp ", s, " ", err.String()); | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	return re; | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	comment_markers *regexp.Regexp; | 
					
						
							|  |  |  | 	trailing_whitespace *regexp.Regexp; | 
					
						
							|  |  |  | 	comment_junk *regexp.Regexp; | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // TODO(rsc): Cannot use var initialization for regexps, | 
					
						
							|  |  |  | // because Regexp constructor needs threads. | 
					
						
							|  |  |  | func SetupRegexps() { | 
					
						
							|  |  |  | 	comment_markers = Regexp("^[ \t]*(// ?| ?\\* ?)"); | 
					
						
							|  |  |  | 	trailing_whitespace = Regexp("[ \t\r]+$"); | 
					
						
							|  |  |  | 	comment_junk = Regexp("^[ \t]*(/\\*|\\*/)[ \t]*$"); | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Aggregate comment text, without comment markers. | 
					
						
							|  |  |  | func comment(comments ast.Comments) string { | 
					
						
							|  |  |  | 	once.Do(SetupRegexps); | 
					
						
							|  |  |  | 	lines := make([]string, 0, 20); | 
					
						
							|  |  |  | 	for i, c := range comments { | 
					
						
							|  |  |  | 		// split on newlines | 
					
						
							|  |  |  | 		cl := strings.Split(string(c.Text), "\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// walk lines, stripping comment markers | 
					
						
							|  |  |  | 		w := 0; | 
					
						
							|  |  |  | 		for j, l := range cl { | 
					
						
							|  |  |  | 			// remove /* and */ lines | 
					
						
							|  |  |  | 			if comment_junk.Match(l) { | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			// strip trailing white space | 
					
						
							|  |  |  | 			m := trailing_whitespace.Execute(l); | 
					
						
							|  |  |  | 			if len(m) > 0 { | 
					
						
							|  |  |  | 				l = l[0 : m[1]]; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			// strip leading comment markers | 
					
						
							|  |  |  | 			m = comment_markers.Execute(l); | 
					
						
							|  |  |  | 			if len(m) > 0 { | 
					
						
							|  |  |  | 				l = l[m[1] : len(l)]; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			// throw away leading blank lines | 
					
						
							|  |  |  | 			if w == 0 && l == "" { | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			cl[w] = l; | 
					
						
							|  |  |  | 			w++; | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		// throw away trailing blank lines | 
					
						
							|  |  |  | 		for w > 0 && cl[w-1] == "" { | 
					
						
							|  |  |  | 			w--; | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		cl = cl[0 : w]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// add this comment to total list | 
					
						
							|  |  |  | 		// TODO: maybe separate with a single blank line | 
					
						
							|  |  |  | 		// if there is already a comment and len(cl) > 0? | 
					
						
							|  |  |  | 		for j, l := range cl { | 
					
						
							|  |  |  | 			n := len(lines); | 
					
						
							|  |  |  | 			if n+1 >= cap(lines) { | 
					
						
							|  |  |  | 				newlines := make([]string, n, 2*cap(lines)); | 
					
						
							|  |  |  | 				for k := range newlines { | 
					
						
							|  |  |  | 					newlines[k] = lines[k]; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				lines = newlines; | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			lines = lines[0 : n+1]; | 
					
						
							|  |  |  | 			lines[n] = l; | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	// add final "" entry to get trailing newline. | 
					
						
							|  |  |  | 	// loop always leaves room for one more. | 
					
						
							|  |  |  | 	n := len(lines); | 
					
						
							|  |  |  | 	lines = lines[0 : n+1]; | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	return strings.Join(lines, "\n"); | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // ValueDoc is the documentation for a group of declared | 
					
						
							|  |  |  | // values, either vars or consts. | 
					
						
							|  |  |  | type ValueDoc struct { | 
					
						
							|  |  |  | 	Doc string; | 
					
						
							|  |  |  | 	Decl *ast.GenDecl; | 
					
						
							|  |  |  | 	order int; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | type sortValueDoc []*ValueDoc | 
					
						
							|  |  |  | func (p sortValueDoc) Len() int            { return len(p); } | 
					
						
							|  |  |  | func (p sortValueDoc) Swap(i, j int)       { p[i], p[j] = p[j], p[i]; } | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func declName(d *ast.GenDecl) string { | 
					
						
							|  |  |  | 	if len(d.Specs) != 1 { | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-01 15:00:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	switch v := d.Specs[0].(type) { | 
					
						
							|  |  |  | 	case *ast.ValueSpec: | 
					
						
							|  |  |  | 		return v.Names[0].Value; | 
					
						
							|  |  |  | 	case *ast.TypeSpec: | 
					
						
							|  |  |  | 		return v.Name.Value; | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ""; | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (p sortValueDoc) Less(i, j int) bool { | 
					
						
							|  |  |  | 	// sort by name | 
					
						
							|  |  |  | 	// pull blocks (name = "") up to top | 
					
						
							|  |  |  | 	// in original order | 
					
						
							|  |  |  | 	if ni, nj := declName(p[i].Decl), declName(p[j].Decl); ni != nj { | 
					
						
							|  |  |  | 		return ni < nj; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return p[i].order < p[j].order; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func makeValueDocs(v *vector.Vector) []*ValueDoc { | 
					
						
							|  |  |  | 	d := make([]*ValueDoc, v.Len()); | 
					
						
							|  |  |  | 	for i := range d { | 
					
						
							|  |  |  | 		decl := v.At(i).(*ast.GenDecl); | 
					
						
							|  |  |  | 		d[i] = &ValueDoc{comment(decl.Doc), decl, i}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Sort(sortValueDoc(d)); | 
					
						
							|  |  |  | 	return d; | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // FuncDoc is the documentation for a func declaration, | 
					
						
							|  |  |  | // either a top-level function or a method function. | 
					
						
							|  |  |  | type FuncDoc struct { | 
					
						
							|  |  |  | 	Doc string; | 
					
						
							|  |  |  | 	Recv ast.Expr;	// TODO(rsc): Would like string here | 
					
						
							|  |  |  | 	Name string; | 
					
						
							|  |  |  | 	Decl *ast.FuncDecl; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type sortFuncDoc []*FuncDoc | 
					
						
							|  |  |  | func (p sortFuncDoc) Len() int            { return len(p); } | 
					
						
							|  |  |  | func (p sortFuncDoc) Swap(i, j int)       { p[i], p[j] = p[j], p[i]; } | 
					
						
							|  |  |  | func (p sortFuncDoc) Less(i, j int) bool  { return p[i].Name < p[j].Name; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func makeFuncDocs(m map[string] *ast.FuncDecl) []*FuncDoc { | 
					
						
							|  |  |  | 	d := make([]*FuncDoc, len(m)); | 
					
						
							|  |  |  | 	i := 0; | 
					
						
							|  |  |  | 	for name, f := range m { | 
					
						
							|  |  |  | 		doc := new(FuncDoc); | 
					
						
							|  |  |  | 		doc.Doc = comment(f.Doc); | 
					
						
							|  |  |  | 		if f.Recv != nil { | 
					
						
							|  |  |  | 			doc.Recv = f.Recv.Type; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		doc.Name = f.Name.Value; | 
					
						
							|  |  |  | 		doc.Decl = f; | 
					
						
							|  |  |  | 		d[i] = doc; | 
					
						
							|  |  |  | 		i++; | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	sort.Sort(sortFuncDoc(d)); | 
					
						
							|  |  |  | 	return d; | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // TypeDoc is the documentation for a declared type. | 
					
						
							|  |  |  | // Factories is a sorted list of factory functions that return that type. | 
					
						
							|  |  |  | // Methods is a sorted list of method functions on that type. | 
					
						
							|  |  |  | type TypeDoc struct { | 
					
						
							|  |  |  | 	Doc string; | 
					
						
							|  |  |  | 	Type *ast.TypeSpec; | 
					
						
							|  |  |  | 	Factories []*FuncDoc; | 
					
						
							|  |  |  | 	Methods []*FuncDoc; | 
					
						
							|  |  |  | 	Decl *ast.GenDecl; | 
					
						
							|  |  |  | 	order int; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type sortTypeDoc []*TypeDoc | 
					
						
							|  |  |  | func (p sortTypeDoc) Len() int            { return len(p); } | 
					
						
							|  |  |  | func (p sortTypeDoc) Swap(i, j int)       { p[i], p[j] = p[j], p[i]; } | 
					
						
							|  |  |  | func (p sortTypeDoc) Less(i, j int) bool { | 
					
						
							|  |  |  | 	// sort by name | 
					
						
							|  |  |  | 	// pull blocks (name = "") up to top | 
					
						
							|  |  |  | 	// in original order | 
					
						
							|  |  |  | 	if ni, nj := p[i].Type.Name.Value, p[j].Type.Name.Value; ni != nj { | 
					
						
							|  |  |  | 		return ni < nj; | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	return p[i].order < p[j].order; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-03-31 18:46:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // NOTE(rsc): This would appear not to be correct for type ( ) | 
					
						
							|  |  |  | // blocks, but the doc extractor above has split them into | 
					
						
							|  |  |  | // individual statements. | 
					
						
							|  |  |  | func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc { | 
					
						
							|  |  |  | 	d := make([]*TypeDoc, len(m)); | 
					
						
							|  |  |  | 	i := 0; | 
					
						
							|  |  |  | 	for name, old := range m { | 
					
						
							|  |  |  | 		typespec := old.decl.Specs[0].(*ast.TypeSpec); | 
					
						
							|  |  |  | 		t := new(TypeDoc); | 
					
						
							|  |  |  | 		t.Doc = comment(typespec.Doc); | 
					
						
							|  |  |  | 		t.Type = typespec; | 
					
						
							|  |  |  | 		t.Factories = makeFuncDocs(old.factories); | 
					
						
							|  |  |  | 		t.Methods = makeFuncDocs(old.methods); | 
					
						
							|  |  |  | 		t.Decl = old.decl; | 
					
						
							|  |  |  | 		t.order = i; | 
					
						
							|  |  |  | 		d[i] = t; | 
					
						
							|  |  |  | 		i++; | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	sort.Sort(sortTypeDoc(d)); | 
					
						
							|  |  |  | 	return d; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PackageDoc is the documentation for an entire package. | 
					
						
							|  |  |  | type PackageDoc struct { | 
					
						
							|  |  |  | 	PackageName string; | 
					
						
							|  |  |  | 	ImportPath string; | 
					
						
							|  |  |  | 	Doc string; | 
					
						
							|  |  |  | 	Consts []*ValueDoc; | 
					
						
							|  |  |  | 	Types []*TypeDoc; | 
					
						
							|  |  |  | 	Vars []*ValueDoc; | 
					
						
							|  |  |  | 	Funcs []*FuncDoc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Doc returns the accumulated documentation for the package. | 
					
						
							|  |  |  | func (doc *DocReader) Doc() *PackageDoc { | 
					
						
							|  |  |  | 	p := new(PackageDoc); | 
					
						
							|  |  |  | 	p.PackageName = doc.name; | 
					
						
							|  |  |  | 	p.ImportPath = doc.path; | 
					
						
							|  |  |  | 	p.Doc = comment(doc.doc); | 
					
						
							|  |  |  | 	p.Consts = makeValueDocs(doc.consts); | 
					
						
							|  |  |  | 	p.Vars = makeValueDocs(doc.vars); | 
					
						
							|  |  |  | 	p.Types = makeTypeDocs(doc.types); | 
					
						
							|  |  |  | 	p.Funcs = makeFuncDocs(doc.funcs); | 
					
						
							|  |  |  | 	return p; | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Filtering by name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func match(s string, a []string) bool { | 
					
						
							|  |  |  | 	for i, t := range a { | 
					
						
							|  |  |  | 		if s == t { | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func matchDecl(d *ast.GenDecl, names []string) bool { | 
					
						
							|  |  |  | 	for i, d := range d.Specs { | 
					
						
							|  |  |  | 		switch v := d.(type) { | 
					
						
							|  |  |  | 		case *ast.ValueSpec: | 
					
						
							|  |  |  | 			for j, name := range v.Names { | 
					
						
							|  |  |  | 				if match(name.Value, names) { | 
					
						
							|  |  |  | 					return true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case *ast.TypeSpec: | 
					
						
							|  |  |  | 			if match(v.Name.Value, names) { | 
					
						
							|  |  |  | 				return true; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-03-31 16:53:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func filterValueDocs(a []*ValueDoc, names []string) []*ValueDoc { | 
					
						
							|  |  |  | 	w := 0; | 
					
						
							|  |  |  | 	for i, vd := range a { | 
					
						
							|  |  |  | 		if matchDecl(vd.Decl, names) { | 
					
						
							|  |  |  | 			a[w] = vd; | 
					
						
							|  |  |  | 			w++; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	return a[0 : w]; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func filterTypeDocs(a []*TypeDoc, names []string) []*TypeDoc { | 
					
						
							|  |  |  | 	w := 0; | 
					
						
							|  |  |  | 	for i, td := range a { | 
					
						
							|  |  |  | 		if matchDecl(td.Decl, names) { | 
					
						
							|  |  |  | 			a[w] = td; | 
					
						
							|  |  |  | 			w++; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	return a[0 : w]; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func filterFuncDocs(a []*FuncDoc, names []string) []*FuncDoc { | 
					
						
							|  |  |  | 	w := 0; | 
					
						
							|  |  |  | 	for i, fd := range a { | 
					
						
							|  |  |  | 		if match(fd.Name, names) { | 
					
						
							|  |  |  | 			a[w] = fd; | 
					
						
							|  |  |  | 			w++; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	return a[0 : w]; | 
					
						
							| 
									
										
										
										
											2009-03-30 17:13:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Filter eliminates information from d that is not | 
					
						
							|  |  |  | // about one of the given names. | 
					
						
							|  |  |  | // TODO: Recognize "Type.Method" as a name. | 
					
						
							|  |  |  | func (p *PackageDoc) Filter(names []string) { | 
					
						
							|  |  |  | 	p.Consts = filterValueDocs(p.Consts, names); | 
					
						
							|  |  |  | 	p.Vars = filterValueDocs(p.Vars, names); | 
					
						
							|  |  |  | 	p.Types = filterTypeDocs(p.Types, names); | 
					
						
							|  |  |  | 	p.Funcs = filterFuncDocs(p.Funcs, names); | 
					
						
							|  |  |  | 	p.Doc = "";	// don't show top-level package doc | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 |