mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/doc: add -all flag to print all documentation for package
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>
This commit is contained in:
parent
ee76992200
commit
101a677ebf
5 changed files with 327 additions and 92 deletions
|
|
@ -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,71 +704,12 @@ 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)
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
pkg.valueDoc(value, printed)
|
||||
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}
|
||||
}
|
||||
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")
|
||||
}
|
||||
pkg.valueSummary(typ.Consts, true)
|
||||
pkg.valueSummary(typ.Vars, true)
|
||||
pkg.funcSummary(typ.Funcs, true)
|
||||
pkg.funcSummary(typ.Methods, true)
|
||||
pkg.typeDoc(typ)
|
||||
found = true
|
||||
}
|
||||
if !found {
|
||||
|
|
@ -705,6 +721,93 @@ func (pkg *Package) symbolDoc(symbol string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
specs = append(specs, vspec)
|
||||
typ = nil // Only inject type on first exported identifier
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// trimUnexportedElems modifies spec in place to elide unexported fields from
|
||||
// structs and methods from interfaces (unless the unexported flag is set or we
|
||||
// are asked to show the original source).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue