aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/doc/pkg.go
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2015-04-28 20:55:01 -0700
committerRob Pike <r@golang.org>2015-04-29 19:12:53 +0000
commit06946aad1973c42e26ddb0890c57ef37fecbb48a (patch)
tree25e4da2e1d90fae96bfe92e2911194a6a8b8093c /src/cmd/doc/pkg.go
parentc26fc88d56ee4f93c98fc8923fe256121e6199cf (diff)
downloadgo-06946aad1973c42e26ddb0890c57ef37fecbb48a.tar.xz
cmd/go: better UI for go doc
Print it out much like godoc so there isn't a single block of text. Print the symbol before its comment and indent the comment so individual symbols separate visually. Buffer the output. Add a -c option to force case-sensitive matching. Allow two arguments, like godoc, to help disambiguate cases where path and symbol may be confused. Improve the documentation printed by go help doc. Change-Id: If687aad04bbacdf7dbe4bf7636de9fe96f756fd0 Reviewed-on: https://go-review.googlesource.com/9471 Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/cmd/doc/pkg.go')
-rw-r--r--src/cmd/doc/pkg.go124
1 files changed, 86 insertions, 38 deletions
diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index e08b756239..580a91f7e6 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -20,17 +20,19 @@ import (
)
type Package struct {
- name string // Package name, json for encoding/json.
- pkg *ast.Package // Parsed package.
- file *ast.File // Merged from all files in the package
- doc *doc.Package
- build *build.Package
- fs *token.FileSet // Needed for printing.
+ name string // Package name, json for encoding/json.
+ userPath string // String the user used to find this package.
+ pkg *ast.Package // Parsed package.
+ file *ast.File // Merged from all files in the package
+ doc *doc.Package
+ build *build.Package
+ fs *token.FileSet // Needed for printing.
+ buf bytes.Buffer
}
// parsePackage turns the build package we found into a parsed package
// we can then use to generate documentation.
-func parsePackage(pkg *build.Package) *Package {
+func parsePackage(pkg *build.Package, userPath string) *Package {
fs := token.NewFileSet()
// include tells parser.ParseDir which files to include.
// That means the file must be in the build package's GoFiles or CgoFiles
@@ -73,35 +75,54 @@ func parsePackage(pkg *build.Package) *Package {
}
return &Package{
- name: pkg.Name,
- pkg: astPkg,
- file: ast.MergePackageFiles(astPkg, 0),
- doc: docPkg,
- build: pkg,
- fs: fs,
+ name: pkg.Name,
+ userPath: userPath,
+ pkg: astPkg,
+ file: ast.MergePackageFiles(astPkg, 0),
+ doc: docPkg,
+ build: pkg,
+ fs: fs,
}
}
-var formatBuf bytes.Buffer // One instance to minimize allocation. TODO: Buffer all output.
+func (pkg *Package) Printf(format string, args ...interface{}) {
+ fmt.Fprintf(&pkg.buf, format, args...)
+}
+
+func (pkg *Package) flush() {
+ _, err := os.Stdout.Write(pkg.buf.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ pkg.buf.Reset() // Not needed, but it's a flush.
+}
+
+var newlineBytes = []byte("\n\n") // We never ask for more than 2.
+
+// newlines guarantees there are n newlines at the end of the buffer.
+func (pkg *Package) newlines(n int) {
+ for !bytes.HasSuffix(pkg.buf.Bytes(), newlineBytes[:n]) {
+ pkg.buf.WriteRune('\n')
+ }
+}
// emit prints the node.
func (pkg *Package) emit(comment string, node ast.Node) {
if node != nil {
- formatBuf.Reset()
- if comment != "" {
- doc.ToText(&formatBuf, comment, "", "\t", 80)
- }
- err := format.Node(&formatBuf, pkg.fs, node)
+ err := format.Node(&pkg.buf, pkg.fs, node)
if err != nil {
log.Fatal(err)
}
- if formatBuf.Len() > 0 && formatBuf.Bytes()[formatBuf.Len()-1] != '\n' {
- formatBuf.WriteRune('\n')
+ if comment != "" {
+ pkg.newlines(1)
+ doc.ToText(&pkg.buf, comment, " ", "\t", 80)
}
- os.Stdout.Write(formatBuf.Bytes())
+ pkg.newlines(1)
}
}
+var formatBuf bytes.Buffer // Reusable to avoid allocation.
+
// formatNode is a helper function for printing.
func (pkg *Package) formatNode(node ast.Node) []byte {
formatBuf.Reset()
@@ -137,7 +158,7 @@ func (pkg *Package) oneLineValueGenDecl(decl *ast.GenDecl) {
if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
val = fmt.Sprintf(" = %s", pkg.formatNode(valueSpec.Values[i]))
}
- fmt.Printf("%s %s%s%s%s\n", decl.Tok, valueSpec.Names[0], typ, val, dotDotDot)
+ pkg.Printf("%s %s%s%s%s\n", decl.Tok, valueSpec.Names[0], typ, val, dotDotDot)
break
}
}
@@ -148,33 +169,46 @@ func (pkg *Package) oneLineTypeDecl(spec *ast.TypeSpec) {
spec.Comment = nil
switch spec.Type.(type) {
case *ast.InterfaceType:
- fmt.Printf("type %s interface { ... }\n", spec.Name)
+ pkg.Printf("type %s interface { ... }\n", spec.Name)
case *ast.StructType:
- fmt.Printf("type %s struct { ... }\n", spec.Name)
+ pkg.Printf("type %s struct { ... }\n", spec.Name)
default:
- fmt.Printf("type %s %s\n", spec.Name, pkg.formatNode(spec.Type))
+ pkg.Printf("type %s %s\n", spec.Name, pkg.formatNode(spec.Type))
}
}
// packageDoc prints the docs for the package (package doc plus one-liners of the rest).
-// TODO: Sort the output.
func (pkg *Package) packageDoc() {
- // Package comment.
+ defer pkg.flush()
+ pkg.packageClause(false)
+
+ doc.ToText(&pkg.buf, pkg.doc.Doc, "", "\t", 80)
+ pkg.newlines(2)
+
+ pkg.valueSummary(pkg.doc.Consts)
+ pkg.valueSummary(pkg.doc.Vars)
+ pkg.funcSummary(pkg.doc.Funcs)
+ pkg.typeSummary()
+}
+
+// packageClause prints the package clause.
+// The argument boolean, if true, suppresses the output if the
+// user's argument is identical to the actual package path or
+// is empty, meaning it's the current directory.
+func (pkg *Package) packageClause(checkUserPath bool) {
+ if checkUserPath {
+ if pkg.userPath == "" || pkg.userPath == pkg.build.ImportPath {
+ return
+ }
+ }
importPath := pkg.build.ImportComment
if importPath == "" {
importPath = pkg.build.ImportPath
}
- fmt.Printf("package %s // import %q\n\n", pkg.name, importPath)
+ pkg.Printf("package %s // import %q\n\n", pkg.name, importPath)
if importPath != pkg.build.ImportPath {
- fmt.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
+ pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
}
- doc.ToText(os.Stdout, pkg.doc.Doc, "", "\t", 80)
- fmt.Print("\n")
-
- pkg.valueSummary(pkg.doc.Consts)
- pkg.valueSummary(pkg.doc.Vars)
- pkg.funcSummary(pkg.doc.Funcs)
- pkg.typeSummary()
}
// valueSummary prints a one-line summary for each set of values and constants.
@@ -265,9 +299,13 @@ func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec
// symbolDoc prints the docs for symbol. There may be multiple matches.
// If symbol matches a type, output includes its methods factories and associated constants.
func (pkg *Package) symbolDoc(symbol string) {
+ defer pkg.flush()
found := false
// Functions.
for _, fun := range pkg.findFuncs(symbol) {
+ if !found {
+ pkg.packageClause(true)
+ }
// Symbol is a function.
decl := fun.Decl
decl.Body = nil
@@ -278,11 +316,17 @@ func (pkg *Package) symbolDoc(symbol string) {
values := pkg.findValues(symbol, pkg.doc.Consts)
values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...)
for _, value := range values {
+ if !found {
+ pkg.packageClause(true)
+ }
pkg.emit(value.Doc, value.Decl)
found = true
}
// Types.
for _, typ := range pkg.findTypes(symbol) {
+ if !found {
+ pkg.packageClause(true)
+ }
decl := typ.Decl
spec := pkg.findTypeSpec(decl, typ.Name)
trimUnexportedFields(spec)
@@ -306,7 +350,7 @@ func (pkg *Package) symbolDoc(symbol string) {
// trimUnexportedFields modifies spec in place to elide unexported fields (unless
// the unexported flag is set). If spec is not a structure declartion, nothing happens.
func trimUnexportedFields(spec *ast.TypeSpec) {
- if unexported {
+ if *unexported {
// We're printing all fields.
return
}
@@ -349,6 +393,7 @@ func trimUnexportedFields(spec *ast.TypeSpec) {
// methodDoc prints the docs for matches of symbol.method.
func (pkg *Package) methodDoc(symbol, method string) {
+ defer pkg.flush()
types := pkg.findTypes(symbol)
if types == nil {
log.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
@@ -376,6 +421,9 @@ func match(user, program string) bool {
if !isExported(program) {
return false
}
+ if *matchCase {
+ return user == program
+ }
for _, u := range user {
p, w := utf8.DecodeRuneInString(program)
program = program[w:]