godoc: support for multiple packages in a directory
- smartly select the "right" package
- provide a list of other packages

R=rsc
CC=golang-dev
https://golang.org/cl/466042
diff --git a/lib/godoc/package.html b/lib/godoc/package.html
index c0cd18f..d0a5970 100644
--- a/lib/godoc/package.html
+++ b/lib/godoc/package.html
@@ -80,6 +80,14 @@
 		{.end}
 	{.end}
 {.end}
+{.section PList}
+	<h2>Other packages</h2>
+	<p>
+	{.repeated section @}
+	<a href="?p={@|html}">{@|html}</a><br />
+	{.end}
+	</p>
+{.end}
 {.section Dirs}
 	<h2 id="Subdirectories">Subdirectories</h2>
 	<p>
diff --git a/lib/godoc/package.txt b/lib/godoc/package.txt
index b9203ff..124771e 100644
--- a/lib/godoc/package.txt
+++ b/lib/godoc/package.txt
@@ -75,6 +75,14 @@
 {.end}
 {.end}
 {.end}
+{.section PList}
+
+OTHER PACKAGES
+
+{.repeated section @}
+{@}
+{.end}
+{.end}
 {.section Dirs}
 {.section Dirs}
 
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index 04393c6..067f82a 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -1087,8 +1087,9 @@
 
 type PageInfo struct {
 	Dirname string          // directory containing the package
-	PAst    *ast.File       // nil if no AST with package exports
-	PDoc    *doc.PackageDoc // nil if no package documentation
+	PList   []string        // list of package names found
+	PAst    *ast.File       // nil if no single AST with package exports
+	PDoc    *doc.PackageDoc // nil if no single package documentation
 	Dirs    *DirList        // nil if no directory information
 	IsPkg   bool            // false if this is not documenting a real package
 }
@@ -1101,7 +1102,7 @@
 }
 
 
-// getPageInfo returns the PageInfo for a package directory dirname. If the
+// getPageInfo returns the PageInfo for a package directory abspath. If the
 // parameter genAST is set, an AST containing only the package exports is
 // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
 // is extracted from the AST. If the parameter try is set, no errors are
@@ -1109,7 +1110,7 @@
 // directory, PageInfo.PDoc and PageInfo.PExp are nil. If there are no sub-
 // directories, PageInfo.Dirs is nil.
 //
-func (h *httpHandler) getPageInfo(dirname, relpath string, genAST, try bool) PageInfo {
+func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, genAST, try bool) PageInfo {
 	// filter function to select the desired .go files
 	filter := func(d *os.Dir) bool {
 		// If we are looking at cmd documentation, only accept
@@ -1118,30 +1119,64 @@
 	}
 
 	// get package ASTs
-	pkgs, err := parser.ParseDir(dirname, filter, parser.ParseComments)
+	pkgs, err := parser.ParseDir(abspath, filter, parser.ParseComments)
 	if err != nil && !try {
 		// TODO: errors should be shown instead of an empty directory
 		log.Stderrf("parser.parseDir: %s", err)
 	}
-	if len(pkgs) != 1 && !try {
-		// TODO: should handle multiple packages,
-		//       error reporting disabled for now
-		// log.Stderrf("parser.parseDir: found %d packages", len(pkgs))
-	}
 
-	// Get the best matching package: either the first one, or the
-	// first one whose package name matches the directory name.
-	// The package name is the directory name within its parent.
-	_, pkgname := pathutil.Split(dirname)
-	var pkg *ast.Package
-	for _, p := range pkgs {
-		switch {
-		case pkg == nil:
+	// select package
+	var pkg *ast.Package // selected package
+	var plist []string   // list of other package (names), if any
+	if len(pkgs) == 1 {
+		// Exactly one package - select it.
+		for _, p := range pkgs {
 			pkg = p
-		case p.Name == pkgname:
-			pkg = p
-			break
 		}
+
+	} else if len(pkgs) > 1 {
+		// Multiple packages - select the best matching package: The
+		// 1st choice is the package with pkgname, the 2nd choice is
+		// the package with dirname, and the 3rd choice is a package
+		// that is not called "main" if there is exactly one such
+		// package. Otherwise, don't select a package.
+		dirpath, dirname := pathutil.Split(abspath)
+
+		// If the dirname is "go" we might be in a sub-directory for
+		// .go files - use the outer directory name instead for better
+		// results.
+		if dirname == "go" {
+			_, dirname = pathutil.Split(pathutil.Clean(dirpath))
+		}
+
+		var choice3 *ast.Package
+	loop:
+		for _, p := range pkgs {
+			switch {
+			case p.Name == pkgname:
+				pkg = p
+				break loop // 1st choice; we are done
+			case p.Name == dirname:
+				pkg = p // 2nd choice
+			case p.Name != "main":
+				choice3 = p
+			}
+		}
+		if pkg == nil && len(pkgs) == 2 {
+			pkg = choice3
+		}
+
+		// Compute the list of other packages
+		// (excluding the selected package, if any).
+		plist = make([]string, len(pkgs))
+		i := 0
+		for name, _ := range pkgs {
+			if pkg == nil || name != pkg.Name {
+				plist[i] = name
+				i++
+			}
+		}
+		plist = plist[0:i]
 	}
 
 	// compute package documentation
@@ -1163,16 +1198,16 @@
 		// (may still fail if the file system was updated and the
 		// new directory tree has not yet been computed)
 		// TODO(gri) Need to build directory tree for fsMap entries
-		dir = tree.(*Directory).lookup(dirname)
+		dir = tree.(*Directory).lookup(abspath)
 	}
 	if dir == nil {
 		// no directory tree present (either early after startup
 		// or command-line mode, or we don't build a tree for the
 		// directory; e.g. google3); compute one level for this page
-		dir = newDirectory(dirname, 1)
+		dir = newDirectory(abspath, 1)
 	}
 
-	return PageInfo{dirname, past, pdoc, dir.listing(true), h.isPkg}
+	return PageInfo{abspath, plist, past, pdoc, dir.listing(true), h.isPkg}
 }
 
 
@@ -1183,7 +1218,7 @@
 
 	relpath := r.URL.Path[len(h.pattern):]
 	abspath := absolutePath(relpath, h.fsRoot)
-	info := h.getPageInfo(abspath, relpath, r.FormValue("m") == "src", false)
+	info := h.getPageInfo(abspath, relpath, r.FormValue("p"), r.FormValue("m") == "src", false)
 
 	if r.FormValue("f") == "text" {
 		contents := applyTemplate(packageText, "packageText", info)
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 5436088..f7dc522 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -241,14 +241,16 @@
 		relpath = relativePath(path)
 	}
 
-	info := pkgHandler.getPageInfo(abspath, relpath, *genAST, true)
+	// TODO(gri): Provide a mechanism (flag?) to select a package
+	//            if there are multiple packages in a directory.
+	info := pkgHandler.getPageInfo(abspath, relpath, "", *genAST, true)
 
 	if info.PAst == nil && info.PDoc == nil && info.Dirs == nil {
 		// try again, this time assume it's a command
 		if len(path) > 0 && path[0] != '/' {
 			abspath = absolutePath(path, cmdHandler.fsRoot)
 		}
-		info = cmdHandler.getPageInfo(abspath, relpath, false, false)
+		info = cmdHandler.getPageInfo(abspath, relpath, "", false, false)
 	}
 
 	if info.PDoc != nil && flag.NArg() > 1 {