Commit 06946aad authored by Rob Pike's avatar Rob Pike

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/9471Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent c26fc88d
...@@ -2,14 +2,26 @@ ...@@ -2,14 +2,26 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Doc (usually run as go doc) accepts zero or one argument, interpreted as: // Doc (usually run as go doc) accepts zero, one or two arguments.
//
// Zero arguments:
// go doc // go doc
// Show the documentation for the package in the current directory.
//
// One argument:
// go doc <pkg> // go doc <pkg>
// go doc <sym>[.<method>] // go doc <sym>[.<method>]
// go doc [<pkg>].<sym>[.<method>] // go doc [<pkg>].<sym>[.<method>]
// The first item in this list that succeeds is the one whose documentation // The first item in this list that succeeds is the one whose documentation
// is printed. If there is no argument, the package in the current directory // is printed. If there is a symbol but no package, the package in the current
// is chosen. // directory is chosen.
//
// Two arguments:
// go doc <pkg> <sym>[.<method>]
//
// Show the documentation for the package, symbol, and method. The
// first argument must be a full package path. This is similar to the
// command-line usage for the godoc command.
// //
// For complete documentation, run "go help doc". // For complete documentation, run "go help doc".
package main package main
...@@ -28,14 +40,10 @@ import ( ...@@ -28,14 +40,10 @@ import (
) )
var ( var (
unexported bool unexported = flag.Bool("u", false, "show unexported symbols as well as exported")
matchCase = flag.Bool("c", false, "symbol matching honors case (paths not affected)")
) )
func init() {
flag.BoolVar(&unexported, "unexported", false, "show unexported symbols as well as exported")
flag.BoolVar(&unexported, "u", false, "shorthand for -unexported")
}
// usage is a replacement usage function for the flags package. // usage is a replacement usage function for the flags package.
func usage() { func usage() {
fmt.Fprintf(os.Stderr, "Usage of [go] doc:\n") fmt.Fprintf(os.Stderr, "Usage of [go] doc:\n")
...@@ -43,6 +51,7 @@ func usage() { ...@@ -43,6 +51,7 @@ func usage() {
fmt.Fprintf(os.Stderr, "\tgo doc <pkg>\n") fmt.Fprintf(os.Stderr, "\tgo doc <pkg>\n")
fmt.Fprintf(os.Stderr, "\tgo doc <sym>[.<method>]\n") fmt.Fprintf(os.Stderr, "\tgo doc <sym>[.<method>]\n")
fmt.Fprintf(os.Stderr, "\tgo doc [<pkg>].<sym>[.<method>]\n") fmt.Fprintf(os.Stderr, "\tgo doc [<pkg>].<sym>[.<method>]\n")
fmt.Fprintf(os.Stderr, "\tgo doc <pkg> <sym>[.<method>]\n")
fmt.Fprintf(os.Stderr, "For more information run\n") fmt.Fprintf(os.Stderr, "For more information run\n")
fmt.Fprintf(os.Stderr, "\tgo help doc\n\n") fmt.Fprintf(os.Stderr, "\tgo help doc\n\n")
fmt.Fprintf(os.Stderr, "Flags:\n") fmt.Fprintf(os.Stderr, "Flags:\n")
...@@ -55,9 +64,9 @@ func main() { ...@@ -55,9 +64,9 @@ func main() {
log.SetPrefix("doc: ") log.SetPrefix("doc: ")
flag.Usage = usage flag.Usage = usage
flag.Parse() flag.Parse()
buildPackage, symbol := parseArg() buildPackage, userPath, symbol := parseArgs()
symbol, method := parseSymbol(symbol) symbol, method := parseSymbol(symbol)
pkg := parsePackage(buildPackage) pkg := parsePackage(buildPackage, userPath)
switch { switch {
case symbol == "": case symbol == "":
pkg.packageDoc() pkg.packageDoc()
...@@ -69,18 +78,27 @@ func main() { ...@@ -69,18 +78,27 @@ func main() {
} }
} }
// parseArg analyzes the argument (if any) and returns the package // parseArgs analyzes the arguments (if any) and returns the package
// it represents and the symbol (possibly with a .method) within that // it represents, the part of the argument the user used to identify
// package. parseSymbol is used to analyze the symbol itself. // the path (or "" if it's the current package) and the symbol
func parseArg() (*build.Package, string) { // (possibly with a .method) within that package.
// parseSymbol is used to analyze the symbol itself.
func parseArgs() (*build.Package, string, string) {
switch flag.NArg() { switch flag.NArg() {
default: default:
usage() usage()
case 0: case 0:
// Easy: current directory. // Easy: current directory.
return importDir("."), "" return importDir("."), "", ""
case 1: case 1:
// Done below. // Done below.
case 2:
// Package must be importable.
pkg, err := build.Import(flag.Arg(0), "", build.ImportComment)
if err != nil {
log.Fatal(err)
}
return pkg, flag.Arg(0), flag.Arg(1)
} }
// Usual case: one argument. // Usual case: one argument.
arg := flag.Arg(0) arg := flag.Arg(0)
...@@ -90,17 +108,16 @@ func parseArg() (*build.Package, string) { ...@@ -90,17 +108,16 @@ func parseArg() (*build.Package, string) {
// package paths as their prefix. // package paths as their prefix.
pkg, err := build.Import(arg, "", build.ImportComment) pkg, err := build.Import(arg, "", build.ImportComment)
if err == nil { if err == nil {
return pkg, "" return pkg, arg, ""
} }
// Another disambiguator: If the symbol starts with an upper // Another disambiguator: If the symbol starts with an upper
// case letter, it can only be a symbol in the current directory. // case letter, it can only be a symbol in the current directory.
// Kills the problem caused by case-insensitive file systems // Kills the problem caused by case-insensitive file systems
// matching an upper case name as a package name. // matching an upper case name as a package name.
if isUpper(arg) { if isUpper(arg) {
println("HERE", arg)
pkg, err := build.ImportDir(".", build.ImportComment) pkg, err := build.ImportDir(".", build.ImportComment)
if err == nil { if err == nil {
return pkg, arg return pkg, "", arg
} }
} }
// If it has a slash, it must be a package path but there is a symbol. // If it has a slash, it must be a package path but there is a symbol.
...@@ -125,16 +142,13 @@ func parseArg() (*build.Package, string) { ...@@ -125,16 +142,13 @@ func parseArg() (*build.Package, string) {
// Have we identified a package already? // Have we identified a package already?
pkg, err := build.Import(arg[0:period], "", build.ImportComment) pkg, err := build.Import(arg[0:period], "", build.ImportComment)
if err == nil { if err == nil {
return pkg, symbol return pkg, arg[0:period], symbol
} }
// See if we have the basename or tail of a package, as in json for encoding/json // See if we have the basename or tail of a package, as in json for encoding/json
// or ivy/value for robpike.io/ivy/value. // or ivy/value for robpike.io/ivy/value.
path := findPackage(arg[0:period]) path := findPackage(arg[0:period])
if path != "" { if path != "" {
return importDir(path), symbol return importDir(path), arg[0:period], symbol
}
if path != "" {
return importDir(path), symbol
} }
} }
// If it has a slash, we've failed. // If it has a slash, we've failed.
...@@ -142,7 +156,7 @@ func parseArg() (*build.Package, string) { ...@@ -142,7 +156,7 @@ func parseArg() (*build.Package, string) {
log.Fatalf("no such package %s", arg[0:period]) log.Fatalf("no such package %s", arg[0:period])
} }
// Guess it's a symbol in the current directory. // Guess it's a symbol in the current directory.
return importDir("."), arg return importDir("."), "", arg
} }
// importDir is just an error-catching wrapper for build.ImportDir. // importDir is just an error-catching wrapper for build.ImportDir.
...@@ -194,7 +208,7 @@ func isIdentifier(name string) { ...@@ -194,7 +208,7 @@ func isIdentifier(name string) {
// If the unexported flag (-u) is true, isExported returns true because // If the unexported flag (-u) is true, isExported returns true because
// it means that we treat the name as if it is exported. // it means that we treat the name as if it is exported.
func isExported(name string) bool { func isExported(name string) bool {
return unexported || isUpper(name) return *unexported || isUpper(name)
} }
// isUpper reports whether the name starts with an upper case letter. // isUpper reports whether the name starts with an upper case letter.
......
...@@ -21,16 +21,18 @@ import ( ...@@ -21,16 +21,18 @@ import (
type Package struct { type Package struct {
name string // Package name, json for encoding/json. name string // Package name, json for encoding/json.
userPath string // String the user used to find this package.
pkg *ast.Package // Parsed package. pkg *ast.Package // Parsed package.
file *ast.File // Merged from all files in the package file *ast.File // Merged from all files in the package
doc *doc.Package doc *doc.Package
build *build.Package build *build.Package
fs *token.FileSet // Needed for printing. fs *token.FileSet // Needed for printing.
buf bytes.Buffer
} }
// parsePackage turns the build package we found into a parsed package // parsePackage turns the build package we found into a parsed package
// we can then use to generate documentation. // we can then use to generate documentation.
func parsePackage(pkg *build.Package) *Package { func parsePackage(pkg *build.Package, userPath string) *Package {
fs := token.NewFileSet() fs := token.NewFileSet()
// include tells parser.ParseDir which files to include. // include tells parser.ParseDir which files to include.
// That means the file must be in the build package's GoFiles or CgoFiles // That means the file must be in the build package's GoFiles or CgoFiles
...@@ -74,6 +76,7 @@ func parsePackage(pkg *build.Package) *Package { ...@@ -74,6 +76,7 @@ func parsePackage(pkg *build.Package) *Package {
return &Package{ return &Package{
name: pkg.Name, name: pkg.Name,
userPath: userPath,
pkg: astPkg, pkg: astPkg,
file: ast.MergePackageFiles(astPkg, 0), file: ast.MergePackageFiles(astPkg, 0),
doc: docPkg, doc: docPkg,
...@@ -82,26 +85,44 @@ func parsePackage(pkg *build.Package) *Package { ...@@ -82,26 +85,44 @@ func parsePackage(pkg *build.Package) *Package {
} }
} }
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. // emit prints the node.
func (pkg *Package) emit(comment string, node ast.Node) { func (pkg *Package) emit(comment string, node ast.Node) {
if node != nil { if node != nil {
formatBuf.Reset() err := format.Node(&pkg.buf, pkg.fs, node)
if comment != "" {
doc.ToText(&formatBuf, comment, "", "\t", 80)
}
err := format.Node(&formatBuf, pkg.fs, node)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
if formatBuf.Len() > 0 && formatBuf.Bytes()[formatBuf.Len()-1] != '\n' { if comment != "" {
formatBuf.WriteRune('\n') 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. // formatNode is a helper function for printing.
func (pkg *Package) formatNode(node ast.Node) []byte { func (pkg *Package) formatNode(node ast.Node) []byte {
formatBuf.Reset() formatBuf.Reset()
...@@ -137,7 +158,7 @@ func (pkg *Package) oneLineValueGenDecl(decl *ast.GenDecl) { ...@@ -137,7 +158,7 @@ func (pkg *Package) oneLineValueGenDecl(decl *ast.GenDecl) {
if i < len(valueSpec.Values) && valueSpec.Values[i] != nil { if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
val = fmt.Sprintf(" = %s", pkg.formatNode(valueSpec.Values[i])) 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 break
} }
} }
...@@ -148,33 +169,46 @@ func (pkg *Package) oneLineTypeDecl(spec *ast.TypeSpec) { ...@@ -148,33 +169,46 @@ func (pkg *Package) oneLineTypeDecl(spec *ast.TypeSpec) {
spec.Comment = nil spec.Comment = nil
switch spec.Type.(type) { switch spec.Type.(type) {
case *ast.InterfaceType: case *ast.InterfaceType:
fmt.Printf("type %s interface { ... }\n", spec.Name) pkg.Printf("type %s interface { ... }\n", spec.Name)
case *ast.StructType: case *ast.StructType:
fmt.Printf("type %s struct { ... }\n", spec.Name) pkg.Printf("type %s struct { ... }\n", spec.Name)
default: 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). // packageDoc prints the docs for the package (package doc plus one-liners of the rest).
// TODO: Sort the output.
func (pkg *Package) packageDoc() { 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 importPath := pkg.build.ImportComment
if importPath == "" { if importPath == "" {
importPath = pkg.build.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 { 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. // 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 ...@@ -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. // symbolDoc prints the docs for symbol. There may be multiple matches.
// If symbol matches a type, output includes its methods factories and associated constants. // If symbol matches a type, output includes its methods factories and associated constants.
func (pkg *Package) symbolDoc(symbol string) { func (pkg *Package) symbolDoc(symbol string) {
defer pkg.flush()
found := false found := false
// Functions. // Functions.
for _, fun := range pkg.findFuncs(symbol) { for _, fun := range pkg.findFuncs(symbol) {
if !found {
pkg.packageClause(true)
}
// Symbol is a function. // Symbol is a function.
decl := fun.Decl decl := fun.Decl
decl.Body = nil decl.Body = nil
...@@ -278,11 +316,17 @@ func (pkg *Package) symbolDoc(symbol string) { ...@@ -278,11 +316,17 @@ func (pkg *Package) symbolDoc(symbol string) {
values := pkg.findValues(symbol, pkg.doc.Consts) values := pkg.findValues(symbol, pkg.doc.Consts)
values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...) values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...)
for _, value := range values { for _, value := range values {
if !found {
pkg.packageClause(true)
}
pkg.emit(value.Doc, value.Decl) pkg.emit(value.Doc, value.Decl)
found = true found = true
} }
// Types. // Types.
for _, typ := range pkg.findTypes(symbol) { for _, typ := range pkg.findTypes(symbol) {
if !found {
pkg.packageClause(true)
}
decl := typ.Decl decl := typ.Decl
spec := pkg.findTypeSpec(decl, typ.Name) spec := pkg.findTypeSpec(decl, typ.Name)
trimUnexportedFields(spec) trimUnexportedFields(spec)
...@@ -306,7 +350,7 @@ func (pkg *Package) symbolDoc(symbol string) { ...@@ -306,7 +350,7 @@ func (pkg *Package) symbolDoc(symbol string) {
// trimUnexportedFields modifies spec in place to elide unexported fields (unless // trimUnexportedFields modifies spec in place to elide unexported fields (unless
// the unexported flag is set). If spec is not a structure declartion, nothing happens. // the unexported flag is set). If spec is not a structure declartion, nothing happens.
func trimUnexportedFields(spec *ast.TypeSpec) { func trimUnexportedFields(spec *ast.TypeSpec) {
if unexported { if *unexported {
// We're printing all fields. // We're printing all fields.
return return
} }
...@@ -349,6 +393,7 @@ func trimUnexportedFields(spec *ast.TypeSpec) { ...@@ -349,6 +393,7 @@ func trimUnexportedFields(spec *ast.TypeSpec) {
// methodDoc prints the docs for matches of symbol.method. // methodDoc prints the docs for matches of symbol.method.
func (pkg *Package) methodDoc(symbol, method string) { func (pkg *Package) methodDoc(symbol, method string) {
defer pkg.flush()
types := pkg.findTypes(symbol) types := pkg.findTypes(symbol)
if types == nil { if types == nil {
log.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath) 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 { ...@@ -376,6 +421,9 @@ func match(user, program string) bool {
if !isExported(program) { if !isExported(program) {
return false return false
} }
if *matchCase {
return user == program
}
for _, u := range user { for _, u := range user {
p, w := utf8.DecodeRuneInString(program) p, w := utf8.DecodeRuneInString(program)
program = program[w:] program = program[w:]
......
...@@ -188,41 +188,52 @@ Show documentation for package or symbol ...@@ -188,41 +188,52 @@ Show documentation for package or symbol
Usage: Usage:
go doc [-u] [package|[package.]symbol[.method]] go doc [-u] [-c] [package|[package.]symbol[.method]]
Doc accepts at most one argument, indicating either a package, a symbol within a Doc prints the documentation comments associated with the item identified by its
package, or a method of a symbol. arguments (a package, const, func, type, var, or method) followed by a one-line
summary of each of the first-level items "under" that item (package-level declarations
for a package, methods for a type, etc.).
Doc accepts zero, one, or two arguments.
Given no arguments, that is, when run as
go doc go doc
it prints the package documentation for the package in the current directory.
When run with one argument, the argument is treated as a Go-syntax-like representation
of the item to be documented. What the argument selects depends on what is installed
in GOROOT and GOPATH, as well as the form of the argument, which is schematically
one of these:
go doc <pkg> go doc <pkg>
go doc <sym>[.<method>] go doc <sym>[.<method>]
go doc [<pkg>].<sym>[.<method>] go doc [<pkg>].<sym>[.<method>]
Doc interprets the argument to see what it represents, determined by its syntax The first item in this list matched by the argument is the one whose documentation
and which packages and symbols are present in the source directories of GOROOT and is printed. (See the examples below.) For packages, the order of scanning is
GOPATH. determined lexically, but the GOROOT tree is always scanned before GOPATH.
The first item in this list that succeeds is the one whose documentation is printed.
For packages, the order of scanning is determined lexically, however the GOROOT
tree is always scanned before GOPATH.
If there is no package specified or matched, the package in the current directory If there is no package specified or matched, the package in the current directory
is selected, so "go doc" shows the documentation for the current package and is selected, so "go doc Foo" shows the documentation for symbol Foo in the current
"go doc Foo" shows the documentation for symbol Foo in the current package. package.
Doc prints the documentation comments associated with the top-level item the The package path must be either a qualified path or a proper suffix of a path. The
argument identifies (package, type, method) followed by a one-line summary of each go tool's usual package mechanism does not apply: package path elements like . and
of the first-level items "under" that item (package-level declarations for a ... are not implemented by go doc.
package, methods for a type, etc.).
The package paths must be either a qualified path or a proper suffix of a path When run with two arguments, the first must be a full package path (not just a
(see examples below). The go tool's usual package mechanism does not apply: package suffix), and the second is a symbol or symbol and method; this is similar to the
path elements like . and ... are not implemented by go doc. syntax accepted by godoc:
When matching symbols, lower-case letters match either case but upper-case letters go doc <pkg> <sym>[.<method>]
match exactly. This means that there may be multiple matches in a package if
different symbols have different cases. If this occurs, documentation for all In all forms, when matching symbols, lower-case letters in the argument match
matches is printed. either case but upper-case letters match exactly. This means that there may be
multiple matches of a lower-case argument in a package if different symbols have
different cases. If this occurs, documentation for all matches is printed.
Examples: Examples:
go doc go doc
...@@ -238,8 +249,17 @@ Examples: ...@@ -238,8 +249,17 @@ Examples:
Show documentation and method summary for json.Number. Show documentation and method summary for json.Number.
go doc json.Number.Int64 (or go doc json.number.int64) go doc json.Number.Int64 (or go doc json.number.int64)
Show documentation for json.Number's Int64 method. Show documentation for json.Number's Int64 method.
go doc template.new
Show documentation for html/template's New function.
(html/template is lexically before text/template)
go doc text/template.new # One argument
Show documentation for text/template's New function.
go doc text/template new # Two arguments
Show documentation for text/template's New function.
Flags: Flags:
-c
Respect case when matching symbols.
-u -u
Show documentation for unexported as well as exported Show documentation for unexported as well as exported
symbols and methods. symbols and methods.
......
...@@ -6,43 +6,55 @@ package main ...@@ -6,43 +6,55 @@ package main
var cmdDoc = &Command{ var cmdDoc = &Command{
Run: runDoc, Run: runDoc,
UsageLine: "doc [-u] [package|[package.]symbol[.method]]", UsageLine: "doc [-u] [-c] [package|[package.]symbol[.method]]",
CustomFlags: true, CustomFlags: true,
Short: "show documentation for package or symbol", Short: "show documentation for package or symbol",
Long: ` Long: `
Doc accepts at most one argument, indicating either a package, a symbol within a
package, or a method of a symbol. Doc prints the documentation comments associated with the item identified by its
arguments (a package, const, func, type, var, or method) followed by a one-line
summary of each of the first-level items "under" that item (package-level declarations
for a package, methods for a type, etc.).
Doc accepts zero, one, or two arguments.
Given no arguments, that is, when run as
go doc go doc
it prints the package documentation for the package in the current directory.
When run with one argument, the argument is treated as a Go-syntax-like representation
of the item to be documented. What the argument selects depends on what is installed
in GOROOT and GOPATH, as well as the form of the argument, which is schematically
one of these:
go doc <pkg> go doc <pkg>
go doc <sym>[.<method>] go doc <sym>[.<method>]
go doc [<pkg>].<sym>[.<method>] go doc [<pkg>].<sym>[.<method>]
Doc interprets the argument to see what it represents, determined by its syntax The first item in this list matched by the argument is the one whose documentation
and which packages and symbols are present in the source directories of GOROOT and is printed. (See the examples below.) For packages, the order of scanning is
GOPATH. determined lexically, but the GOROOT tree is always scanned before GOPATH.
The first item in this list that succeeds is the one whose documentation is printed.
For packages, the order of scanning is determined lexically, however the GOROOT
tree is always scanned before GOPATH.
If there is no package specified or matched, the package in the current directory If there is no package specified or matched, the package in the current directory
is selected, so "go doc" shows the documentation for the current package and is selected, so "go doc Foo" shows the documentation for symbol Foo in the current
"go doc Foo" shows the documentation for symbol Foo in the current package. package.
The package path must be either a qualified path or a proper suffix of a path. The
go tool's usual package mechanism does not apply: package path elements like . and
... are not implemented by go doc.
Doc prints the documentation comments associated with the top-level item the When run with two arguments, the first must be a full package path (not just a
argument identifies (package, type, method) followed by a one-line summary of each suffix), and the second is a symbol or symbol and method; this is similar to the
of the first-level items "under" that item (package-level declarations for a syntax accepted by godoc:
package, methods for a type, etc.).
The package paths must be either a qualified path or a proper suffix of a path go doc <pkg> <sym>[.<method>]
(see examples below). The go tool's usual package mechanism does not apply: package
path elements like . and ... are not implemented by go doc.
When matching symbols, lower-case letters match either case but upper-case letters In all forms, when matching symbols, lower-case letters in the argument match
match exactly. This means that there may be multiple matches in a package if either case but upper-case letters match exactly. This means that there may be
different symbols have different cases. If this occurs, documentation for all multiple matches of a lower-case argument in a package if different symbols have
matches is printed. different cases. If this occurs, documentation for all matches is printed.
Examples: Examples:
go doc go doc
...@@ -58,8 +70,17 @@ Examples: ...@@ -58,8 +70,17 @@ Examples:
Show documentation and method summary for json.Number. Show documentation and method summary for json.Number.
go doc json.Number.Int64 (or go doc json.number.int64) go doc json.Number.Int64 (or go doc json.number.int64)
Show documentation for json.Number's Int64 method. Show documentation for json.Number's Int64 method.
go doc template.new
Show documentation for html/template's New function.
(html/template is lexically before text/template)
go doc text/template.new # One argument
Show documentation for text/template's New function.
go doc text/template new # Two arguments
Show documentation for text/template's New function.
Flags: Flags:
-c
Respect case when matching symbols.
-u -u
Show documentation for unexported as well as exported Show documentation for unexported as well as exported
symbols and methods. symbols and methods.
......
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