]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/doc: add -all flag to print all documentation for package
authorRob Pike <r@golang.org>
Sat, 13 Oct 2018 10:06:43 +0000 (21:06 +1100)
committerRob Pike <r@golang.org>
Wed, 17 Oct 2018 08:36:13 +0000 (08:36 +0000)
Unlike the one for the old godoc, you need the -u flag to see
unexported symbols. This seems like the right behavior: it's
consistent.

For now at least, the argument must be a package, not a symbol.
This is also different from old godoc.

Required a little refactoring but also cleaned up a few things.

Update #25595

Leaving the bug open for now until we tackle
go doc -all symbol

Change-Id: Ibc1975bfa592cb1e92513eb2e5e9e11e01a60095
Reviewed-on: https://go-review.googlesource.com/c/141977
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
src/cmd/doc/doc_test.go
src/cmd/doc/main.go
src/cmd/doc/pkg.go
src/cmd/go/alldocs.go
src/cmd/go/internal/doc/doc.go

index 64b1fb596b44e9ed53dc528a5c7125dac39be2af..80fdb5c72e2860e235bb53243879253172e1cbf5 100644 (file)
@@ -147,6 +147,69 @@ var tests = []test{
                        `type T1 T2`,                       // Type alias does not display as type declaration.
                },
        },
+       // Package dump -all
+       {
+               "full package",
+               []string{"-all", p},
+               []string{
+                       `package pkg .*import`,
+                       `Package comment`,
+                       `CONSTANTS`,
+                       `Comment before ConstOne`,
+                       `ConstOne = 1`,
+                       `ConstTwo = 2 // Comment on line with ConstTwo`,
+                       `ConstFive`,
+                       `ConstSix`,
+                       `Const block where first entry is unexported`,
+                       `ConstLeft2, constRight2 uint64`,
+                       `constLeft3, ConstRight3`,
+                       `ConstLeft4, ConstRight4`,
+                       `Duplicate = iota`,
+                       `const CaseMatch = 1`,
+                       `const Casematch = 2`,
+                       `const ExportedConstant = 1`,
+                       `const MultiLineConst = `,
+                       `MultiLineString1`,
+                       `VARIABLES`,
+                       `Comment before VarOne`,
+                       `VarOne = 1`,
+                       `Comment about block of variables`,
+                       `VarFive = 5`,
+                       `var ExportedVariable = 1`,
+                       `var LongLine = newLongLine\(`,
+                       `var MultiLineVar = map\[struct {`,
+                       `FUNCTIONS`,
+                       `func ExportedFunc\(a int\) bool`,
+                       `Comment about exported function`,
+                       `func MultiLineFunc\(x interface`,
+                       `func ReturnUnexported\(\) unexportedType`,
+                       `TYPES`,
+                       `type ExportedInterface interface`,
+                       `type ExportedStructOneField struct`,
+                       `type ExportedType struct`,
+                       `Comment about exported type`,
+                       `const ConstGroup4 ExportedType = ExportedType`,
+                       `ExportedTypedConstant ExportedType = iota`,
+                       `Constants tied to ExportedType`,
+                       `func ExportedTypeConstructor\(\) \*ExportedType`,
+                       `Comment about constructor for exported type`,
+                       `func ReturnExported\(\) ExportedType`,
+                       `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+                       `Comment about exported method`,
+                       `type T1 = T2`,
+                       `type T2 int`,
+               },
+               []string{
+                       `constThree`,
+                       `_, _ uint64 = 2 \* iota, 1 << iota`,
+                       `constLeft1, constRight1`,
+                       `duplicate`,
+                       `varFour`,
+                       `func internalFunc`,
+                       `unexportedField`,
+                       `func \(unexportedType\)`,
+               },
+       },
        // Package dump -u
        {
                "full package with u",
@@ -164,6 +227,58 @@ var tests = []test{
                        `MultiLine(String|Method|Field)`,   // No data from multi line portions.
                },
        },
+       // Package dump -u -all
+       {
+               "full package",
+               []string{"-u", "-all", p},
+               []string{
+                       `package pkg .*import`,
+                       `Package comment`,
+                       `CONSTANTS`,
+                       `Comment before ConstOne`,
+                       `ConstOne += 1`,
+                       `ConstTwo += 2 // Comment on line with ConstTwo`,
+                       `constThree = 3 // Comment on line with constThree`,
+                       `ConstFive`,
+                       `const internalConstant += 2`,
+                       `Comment about internal constant`,
+                       `VARIABLES`,
+                       `Comment before VarOne`,
+                       `VarOne += 1`,
+                       `Comment about block of variables`,
+                       `varFour += 4`,
+                       `VarFive += 5`,
+                       `varSix += 6`,
+                       `var ExportedVariable = 1`,
+                       `var LongLine = newLongLine\(`,
+                       `var MultiLineVar = map\[struct {`,
+                       `var internalVariable = 2`,
+                       `Comment about internal variable`,
+                       `FUNCTIONS`,
+                       `func ExportedFunc\(a int\) bool`,
+                       `Comment about exported function`,
+                       `func MultiLineFunc\(x interface`,
+                       `func internalFunc\(a int\) bool`,
+                       `Comment about internal function`,
+                       `func newLongLine\(ss .*string\)`,
+                       `TYPES`,
+                       `type ExportedType struct`,
+                       `type T1 = T2`,
+                       `type T2 int`,
+                       `type unexportedType int`,
+                       `Comment about unexported type`,
+                       `ConstGroup1 unexportedType = iota`,
+                       `ConstGroup2`,
+                       `ConstGroup3`,
+                       `ExportedTypedConstant_unexported unexportedType = iota`,
+                       `Constants tied to unexportedType`,
+                       `const unexportedTypedConstant unexportedType = 1`,
+                       `func ReturnUnexported\(\) unexportedType`,
+                       `func \(unexportedType\) ExportedMethod\(\) bool`,
+                       `func \(unexportedType\) unexportedMethod\(\) bool`,
+               },
+               nil,
+       },
 
        // Single constant.
        {
@@ -361,7 +476,6 @@ var tests = []test{
                        `io.Reader.*Comment on line with embedded Reader`,
                },
                []string{
-                       `int.*embedded`,                 // No unexported embedded field.
                        `Comment about exported method`, // No comment about exported method.
                        `unexportedMethod`,              // No unexported method.
                        `unexportedTypedConstant`,       // No unexported constant.
index a3e09d3f87115fec84538a405790e1ed99dbdcaa..614f19438c8cd41316e997a68679bd45792be270 100644 (file)
@@ -31,6 +31,9 @@
 // The -src flag causes doc to print the full source code for the symbol, such
 // as the body of a struct, function or method.
 //
+// The -all flag causes doc to print all documentation for the package and
+// all its visible symbols. The argument must identify a package.
+//
 // For complete documentation, run "go help doc".
 package main
 
@@ -52,6 +55,7 @@ import (
 var (
        unexported bool // -u flag
        matchCase  bool // -c flag
+       showAll    bool // -all flag
        showCmd    bool // -cmd flag
        showSrc    bool // -src flag
 )
@@ -88,6 +92,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
        matchCase = false
        flagSet.BoolVar(&unexported, "u", false, "show unexported symbols as well as exported")
        flagSet.BoolVar(&matchCase, "c", false, "symbol matching honors case (paths not affected)")
+       flagSet.BoolVar(&showAll, "all", false, "show all documentation for package")
        flagSet.BoolVar(&showCmd, "cmd", false, "show symbols with package docs even if package is a command")
        flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
        flagSet.Parse(args)
@@ -127,6 +132,15 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
                        unexported = true
                }
 
+               // We have a package.
+               if showAll {
+                       if symbol != "" {
+                               return fmt.Errorf("-all valid only for package, not symbol: %s", symbol)
+                       }
+                       pkg.allDoc()
+                       return
+               }
+
                switch {
                case symbol == "":
                        pkg.packageDoc() // The package exists, so we got some output.
index 154fb7b45fcfcd2682f8715ab2afa9e41a71ec3f..bb1b99853776067eeacd4edc417bef3a5a53a0c0 100644 (file)
@@ -29,15 +29,18 @@ const (
 )
 
 type Package struct {
-       writer   io.Writer    // Destination for output.
-       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
+       writer               io.Writer    // Destination for output.
+       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
+       typedValue           map[*doc.Value]bool // Consts and vars related to types.
+       constructor          map[*doc.Func]bool  // Constructors.
+       packageClausePrinted bool                // Prevent repeated package clauses.
+       fs                   *token.FileSet      // Needed for printing.
+       buf                  bytes.Buffer
 }
 
 type PackageError string // type returned by pkg.Fatalf.
@@ -142,21 +145,38 @@ func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Packag
                mode |= doc.PreserveAST // See comment for Package.emit.
        }
        docPkg := doc.New(astPkg, pkg.ImportPath, mode)
+       typedValue := make(map[*doc.Value]bool)
+       constructor := make(map[*doc.Func]bool)
        for _, typ := range docPkg.Types {
                docPkg.Consts = append(docPkg.Consts, typ.Consts...)
+               for _, value := range typ.Consts {
+                       typedValue[value] = true
+               }
                docPkg.Vars = append(docPkg.Vars, typ.Vars...)
+               for _, value := range typ.Vars {
+                       typedValue[value] = true
+               }
                docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...)
+               for _, fun := range typ.Funcs {
+                       // We don't count it as a constructor bound to the type
+                       // if the type itself is not exported.
+                       if isExported(typ.Name) {
+                               constructor[fun] = true
+                       }
+               }
        }
 
        return &Package{
-               writer:   writer,
-               name:     pkg.Name,
-               userPath: userPath,
-               pkg:      astPkg,
-               file:     ast.MergePackageFiles(astPkg, 0),
-               doc:      docPkg,
-               build:    pkg,
-               fs:       fs,
+               writer:      writer,
+               name:        pkg.Name,
+               userPath:    userPath,
+               pkg:         astPkg,
+               file:        ast.MergePackageFiles(astPkg, 0),
+               doc:         docPkg,
+               typedValue:  typedValue,
+               constructor: constructor,
+               build:       pkg,
+               fs:          fs,
        }
 }
 
@@ -390,6 +410,68 @@ func joinStrings(ss []string) string {
        return strings.Join(ss, ", ")
 }
 
+// allDoc prints all the docs for the package.
+func (pkg *Package) allDoc() {
+       defer pkg.flush()
+       if pkg.showInternals() {
+               pkg.packageClause(false)
+       }
+
+       doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
+       pkg.newlines(1)
+
+       printed := make(map[*ast.GenDecl]bool)
+
+       hdr := ""
+       printHdr := func(s string) {
+               if hdr != s {
+                       pkg.Printf("\n%s\n\n", s)
+               }
+       }
+
+       // Constants.
+       for _, value := range pkg.doc.Consts {
+               // Constants and variables come in groups, and valueDoc prints
+               // all the items in the group. We only need to find one exported symbol.
+               for _, name := range value.Names {
+                       if isExported(name) && !pkg.typedValue[value] {
+                               printHdr("CONSTANTS")
+                               pkg.valueDoc(value, printed)
+                               break
+                       }
+               }
+       }
+
+       // Variables.
+       for _, value := range pkg.doc.Vars {
+               // Constants and variables come in groups, and valueDoc prints
+               // all the items in the group. We only need to find one exported symbol.
+               for _, name := range value.Names {
+                       if isExported(name) && !pkg.typedValue[value] {
+                               printHdr("VARIABLES")
+                               pkg.valueDoc(value, printed)
+                               break
+                       }
+               }
+       }
+
+       // Functions.
+       for _, fun := range pkg.doc.Funcs {
+               if isExported(fun.Name) && !pkg.constructor[fun] {
+                       printHdr("FUNCTIONS")
+                       pkg.emit(fun.Doc, fun.Decl)
+               }
+       }
+
+       // Types.
+       for _, typ := range pkg.doc.Types {
+               if isExported(typ.Name) {
+                       printHdr("TYPES")
+                       pkg.typeDoc(typ)
+               }
+       }
+}
+
 // packageDoc prints the docs for the package (package doc plus one-liners of the rest).
 func (pkg *Package) packageDoc() {
        defer pkg.flush()
@@ -426,6 +508,10 @@ func (pkg *Package) showInternals() bool {
 // 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 pkg.packageClausePrinted {
+               return
+       }
+
        if checkUserPath {
                if pkg.userPath == "" || pkg.userPath == pkg.build.ImportPath {
                        return
@@ -463,6 +549,7 @@ func (pkg *Package) packageClause(checkUserPath bool) {
        if !usingModules && importPath != pkg.build.ImportPath {
                pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
        }
+       pkg.packageClausePrinted = true
 }
 
 // valueSummary prints a one-line summary for each set of values and constants.
@@ -497,22 +584,10 @@ func (pkg *Package) valueSummary(values []*doc.Value, showGrouped bool) {
 // funcSummary prints a one-line summary for each function. Constructors
 // are printed by typeSummary, below, and so can be suppressed here.
 func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) {
-       // First, identify the constructors. Don't bother figuring out if they're exported.
-       var isConstructor map[*doc.Func]bool
-       if !showConstructors {
-               isConstructor = make(map[*doc.Func]bool)
-               for _, typ := range pkg.doc.Types {
-                       if isExported(typ.Name) {
-                               for _, f := range typ.Funcs {
-                                       isConstructor[f] = true
-                               }
-                       }
-               }
-       }
        for _, fun := range funcs {
                // Exported functions only. The go/doc package does not include methods here.
                if isExported(fun.Name) {
-                       if !isConstructor[fun] {
+                       if showConstructors || !pkg.constructor[fun] {
                                pkg.Printf("%s\n", pkg.oneLineNode(fun.Decl))
                        }
                }
@@ -629,80 +704,108 @@ func (pkg *Package) symbolDoc(symbol string) bool {
        // So we remember which declarations we've printed to avoid duplication.
        printed := make(map[*ast.GenDecl]bool)
        for _, value := range values {
-               // Print each spec only if there is at least one exported symbol in it.
-               // (See issue 11008.)
-               // TODO: Should we elide unexported symbols from a single spec?
-               // It's an unlikely scenario, probably not worth the trouble.
-               // TODO: Would be nice if go/doc did this for us.
-               specs := make([]ast.Spec, 0, len(value.Decl.Specs))
-               var typ ast.Expr
-               for _, spec := range value.Decl.Specs {
-                       vspec := spec.(*ast.ValueSpec)
+               pkg.valueDoc(value, printed)
+               found = true
+       }
+       // Types.
+       for _, typ := range pkg.findTypes(symbol) {
+               pkg.typeDoc(typ)
+               found = true
+       }
+       if !found {
+               // See if there are methods.
+               if !pkg.printMethodDoc("", symbol) {
+                       return false
+               }
+       }
+       return true
+}
 
-                       // The type name may carry over from a previous specification in the
-                       // case of constants and iota.
-                       if vspec.Type != nil {
-                               typ = vspec.Type
-                       }
+// valueDoc prints the docs for a constant or variable.
+func (pkg *Package) valueDoc(value *doc.Value, printed map[*ast.GenDecl]bool) {
+       if printed[value.Decl] {
+               return
+       }
+       // Print each spec only if there is at least one exported symbol in it.
+       // (See issue 11008.)
+       // TODO: Should we elide unexported symbols from a single spec?
+       // It's an unlikely scenario, probably not worth the trouble.
+       // TODO: Would be nice if go/doc did this for us.
+       specs := make([]ast.Spec, 0, len(value.Decl.Specs))
+       var typ ast.Expr
+       for _, spec := range value.Decl.Specs {
+               vspec := spec.(*ast.ValueSpec)
+
+               // The type name may carry over from a previous specification in the
+               // case of constants and iota.
+               if vspec.Type != nil {
+                       typ = vspec.Type
+               }
 
-                       for _, ident := range vspec.Names {
-                               if showSrc || isExported(ident.Name) {
-                                       if vspec.Type == nil && vspec.Values == nil && typ != nil {
-                                               // This a standalone identifier, as in the case of iota usage.
-                                               // Thus, assume the type comes from the previous type.
-                                               vspec.Type = &ast.Ident{
-                                                       Name:    pkg.oneLineNode(typ),
-                                                       NamePos: vspec.End() - 1,
-                                               }
+               for _, ident := range vspec.Names {
+                       if showSrc || isExported(ident.Name) {
+                               if vspec.Type == nil && vspec.Values == nil && typ != nil {
+                                       // This a standalone identifier, as in the case of iota usage.
+                                       // Thus, assume the type comes from the previous type.
+                                       vspec.Type = &ast.Ident{
+                                               Name:    pkg.oneLineNode(typ),
+                                               NamePos: vspec.End() - 1,
                                        }
-
-                                       specs = append(specs, vspec)
-                                       typ = nil // Only inject type on first exported identifier
-                                       break
                                }
+
+                               specs = append(specs, vspec)
+                               typ = nil // Only inject type on first exported identifier
+                               break
                        }
                }
-               if len(specs) == 0 || printed[value.Decl] {
-                       continue
-               }
-               value.Decl.Specs = specs
-               if !found {
-                       pkg.packageClause(true)
-               }
-               pkg.emit(value.Doc, value.Decl)
-               printed[value.Decl] = true
-               found = true
        }
-       // Types.
-       for _, typ := range pkg.findTypes(symbol) {
-               if !found {
-                       pkg.packageClause(true)
-               }
-               decl := typ.Decl
-               spec := pkg.findTypeSpec(decl, typ.Name)
-               trimUnexportedElems(spec)
-               // If there are multiple types defined, reduce to just this one.
-               if len(decl.Specs) > 1 {
-                       decl.Specs = []ast.Spec{spec}
+       if len(specs) == 0 {
+               return
+       }
+       value.Decl.Specs = specs
+       pkg.emit(value.Doc, value.Decl)
+       printed[value.Decl] = true
+}
+
+// typeDoc prints the docs for a type, including constructors and other items
+// related to it.
+func (pkg *Package) typeDoc(typ *doc.Type) {
+       decl := typ.Decl
+       spec := pkg.findTypeSpec(decl, typ.Name)
+       trimUnexportedElems(spec)
+       // If there are multiple types defined, reduce to just this one.
+       if len(decl.Specs) > 1 {
+               decl.Specs = []ast.Spec{spec}
+       }
+       pkg.emit(typ.Doc, decl)
+       pkg.newlines(2)
+       // Show associated methods, constants, etc.
+       if showAll {
+               printed := make(map[*ast.GenDecl]bool)
+               // We can use append here to print consts, then vars. Ditto for funcs and methods.
+               values := typ.Consts
+               values = append(values, typ.Vars...)
+               for _, value := range values {
+                       for _, name := range value.Names {
+                               if isExported(name) {
+                                       pkg.valueDoc(value, printed)
+                                       break
+                               }
+                       }
                }
-               pkg.emit(typ.Doc, decl)
-               // Show associated methods, constants, etc.
-               if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 {
-                       pkg.Printf("\n")
+               funcs := typ.Funcs
+               funcs = append(funcs, typ.Methods...)
+               for _, fun := range funcs {
+                       if isExported(fun.Name) {
+                               pkg.emit(fun.Doc, fun.Decl)
+                       }
                }
+       } else {
                pkg.valueSummary(typ.Consts, true)
                pkg.valueSummary(typ.Vars, true)
                pkg.funcSummary(typ.Funcs, true)
                pkg.funcSummary(typ.Methods, true)
-               found = true
-       }
-       if !found {
-               // See if there are methods.
-               if !pkg.printMethodDoc("", symbol) {
-                       return false
-               }
        }
-       return true
 }
 
 // trimUnexportedElems modifies spec in place to elide unexported fields from
index 33cb6cd3b37e771940e6ef67f128cd7dcbb715a9..7866b397939e1e8b4f6964a222f65d02e316c695 100644 (file)
 //     cd go/src/encoding/json; go doc decode
 //
 // Flags:
+//     -all
+//             Show all the documentation for the package.
 //     -c
 //             Respect case when matching symbols.
 //     -cmd
index 262bbb3ecb64b4172a874f00588cb7e3aec20328..bad05ff9128cb523fa6553b353b7117fab81ab9c 100644 (file)
@@ -106,6 +106,8 @@ Examples:
        cd go/src/encoding/json; go doc decode
 
 Flags:
+       -all
+               Show all the documentation for the package.
        -c
                Respect case when matching symbols.
        -cmd