cmd/doc: allow go doc -http without package in current directory

go doc tries to find a package to display documentation for. In the case
that no package is provided, it uses "." just like go list does. So if
go doc -http is run without any arguments, it tries to show the
documentation for the package in the current directory. As a special
case, if no arguments are provided, allow no package to match the
current directory and just open the root pkgsite page.

For #68106

Change-Id: I6d65b160a838591db953fac630eced6b09106877
Reviewed-on: https://go-review.googlesource.com/c/go/+/675075
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: Michael Matloob <matloob@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Michael Matloob 2025-05-21 12:18:03 -04:00
parent 8b45a3f78b
commit 2a5ac1a993
1 changed files with 40 additions and 20 deletions

View File

@ -122,8 +122,21 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
}
}
if serveHTTP {
// We want to run the logic below to determine a match for a symbol, method,
// or field, but not actually print the documentation to the output.
// Special case: if there are no arguments to go doc -http, allow
// there to be no package in the current directory. We'll still try
// to open the page for the documentation of the package in the current
// directory, but if one doesn't exist, fall back to opening the home page.
if len(flagSet.Args()) == 0 {
var path string
if importPath, err := runCmd("go", "list"); err == nil {
path = importPath
}
return doPkgsite(path)
}
// If args are provided, we need to figure out which page to open on the pkgsite
// instance. Run the logic below to determine a match for a symbol, method,
// or field, but don't actually print the documentation to the output.
writer = io.Discard
}
var paths []string
@ -179,44 +192,41 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
}
if found {
if serveHTTP {
return doPkgsite(userPath, pkg, symbol, method)
path, err := objectPath(userPath, pkg, symbol, method)
if err != nil {
return err
}
return doPkgsite(path)
}
return nil
}
}
}
func listUserPath(userPath string) (string, error) {
func runCmd(cmdline ...string) (string, error) {
var stdout, stderr strings.Builder
cmd := exec.Command("go", "list", userPath)
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return "", fmt.Errorf("go doc: go list %s: %v\n%s\n", userPath, err, stderr.String())
return "", fmt.Errorf("go doc: %s: %v\n%s\n", strings.Join(cmdline, " "), err, stderr.String())
}
return strings.TrimSpace(stdout.String()), nil
}
func doPkgsite(userPath string, pkg *Package, symbol, method string) error {
port, err := pickUnusedPort()
if err != nil {
return fmt.Errorf("failed to find port for documentation server: %v", err)
}
addr := fmt.Sprintf("localhost:%d", port)
// Assemble url to open on the browser, to point to documentation of
// the requested object.
importPath := pkg.build.ImportPath
if importPath == "." {
func objectPath(userPath string, pkg *Package, symbol, method string) (string, error) {
var err error
path := pkg.build.ImportPath
if path == "." {
// go/build couldn't determine the import path, probably
// because this was a relative path into a module. Use
// go list to get the import path.
importPath, err = listUserPath(userPath)
path, err = runCmd("go", "list", userPath)
if err != nil {
return err
return "", err
}
}
path := path.Join("http://"+addr, importPath)
object := symbol
if symbol != "" && method != "" {
object = symbol + "." + method
@ -224,6 +234,16 @@ func doPkgsite(userPath string, pkg *Package, symbol, method string) error {
if object != "" {
path = path + "#" + object
}
return path, nil
}
func doPkgsite(urlPath string) error {
port, err := pickUnusedPort()
if err != nil {
return fmt.Errorf("failed to find port for documentation server: %v", err)
}
addr := fmt.Sprintf("localhost:%d", port)
path := path.Join("http://"+addr, urlPath)
// Turn off the default signal handler for SIGINT (and SIGQUIT on Unix)
// and instead wait for the child process to handle the signal and