]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/doc: allow go doc -http without package in current directory
authorMichael Matloob <matloob@golang.org>
Wed, 21 May 2025 16:18:03 +0000 (12:18 -0400)
committerMichael Matloob <matloob@golang.org>
Wed, 21 May 2025 16:58:00 +0000 (09:58 -0700)
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>

src/cmd/doc/main.go

index 4c47b4bcfc63b037463bbc5fe79aabaf20b9f98d..03654e5824537401046c6324c60db6a258f69fe8 100644 (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