godoc: Fix commandline mode output.
Fix output of packages with multiple files. Remove extra output when filter is requested. Fixes golang/go#7115. R=bradfitz, adg CC=golang-codereviews https://golang.org/cl/52750044
This commit is contained in:
		
							parent
							
								
									bf4d636dc9
								
							
						
					
					
						commit
						2ca2bfc172
					
				| 
						 | 
				
			
			@ -4,11 +4,9 @@
 | 
			
		|||
package godoc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/build"
 | 
			
		||||
	"go/printer"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +104,8 @@ func CommandLine(w io.Writer, fs vfs.NameSpace, pres *Presentation, args []strin
 | 
			
		|||
 | 
			
		||||
	// If we have more than one argument, use the remaining arguments for filtering.
 | 
			
		||||
	if len(args) > 1 {
 | 
			
		||||
		filterInfo(pres, args[1:], info)
 | 
			
		||||
		info.IsFiltered = true
 | 
			
		||||
		filterInfo(args[1:], info)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packageText := pres.PackageText
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +144,9 @@ func paths(fs vfs.NameSpace, pres *Presentation, path string) (string, string) {
 | 
			
		|||
	return pathpkg.Join(pres.PkgFSRoot(), path), path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func filterInfo(pres *Presentation, args []string, info *PageInfo) {
 | 
			
		||||
// filterInfo updates info to include only the nodes that match the given
 | 
			
		||||
// filter args.
 | 
			
		||||
func filterInfo(args []string, info *PageInfo) {
 | 
			
		||||
	rx, err := makeRx(args)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("illegal regular expression from %v: %v", args, err)
 | 
			
		||||
| 
						 | 
				
			
			@ -154,29 +155,21 @@ func filterInfo(pres *Presentation, args []string, info *PageInfo) {
 | 
			
		|||
	filter := func(s string) bool { return rx.MatchString(s) }
 | 
			
		||||
	switch {
 | 
			
		||||
	case info.PAst != nil:
 | 
			
		||||
		cmap := ast.NewCommentMap(info.FSet, info.PAst, info.PAst.Comments)
 | 
			
		||||
		ast.FilterFile(info.PAst, filter)
 | 
			
		||||
		// Special case: Don't use templates for printing
 | 
			
		||||
		// so we only get the filtered declarations without
 | 
			
		||||
		// package clause or extra whitespace.
 | 
			
		||||
		for i, d := range info.PAst.Decls {
 | 
			
		||||
			// determine the comments associated with d only
 | 
			
		||||
			comments := cmap.Filter(d).Comments()
 | 
			
		||||
			cn := &printer.CommentedNode{Node: d, Comments: comments}
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				fmt.Println()
 | 
			
		||||
		newPAst := map[string]*ast.File{}
 | 
			
		||||
		for name, a := range info.PAst {
 | 
			
		||||
			cmap := ast.NewCommentMap(info.FSet, a, a.Comments)
 | 
			
		||||
			a.Comments = []*ast.CommentGroup{} // remove all comments.
 | 
			
		||||
			ast.FilterFile(a, filter)
 | 
			
		||||
			if len(a.Decls) > 0 {
 | 
			
		||||
				newPAst[name] = a
 | 
			
		||||
			}
 | 
			
		||||
			if pres.HTMLMode {
 | 
			
		||||
				var buf bytes.Buffer
 | 
			
		||||
				pres.WriteNode(&buf, info.FSet, cn)
 | 
			
		||||
				FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil)
 | 
			
		||||
			} else {
 | 
			
		||||
				pres.WriteNode(os.Stdout, info.FSet, cn)
 | 
			
		||||
			for _, d := range a.Decls {
 | 
			
		||||
				// add back the comments associated with d only
 | 
			
		||||
				comments := cmap.Filter(d).Comments()
 | 
			
		||||
				a.Comments = append(a.Comments, comments...)
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Println()
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
		info.PAst = newPAst // add only matching files.
 | 
			
		||||
	case info.PDoc != nil:
 | 
			
		||||
		info.PDoc.Filter(filter)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -157,6 +157,17 @@ func TestCommandLine(t *testing.T) {
 | 
			
		|||
	mfs := mapfs.New(map[string]string{
 | 
			
		||||
		"src/pkg/bar/bar.go": `// Package bar is an example.
 | 
			
		||||
package bar
 | 
			
		||||
`,
 | 
			
		||||
		"src/pkg/foo/foo.go": `// Package foo.
 | 
			
		||||
package foo
 | 
			
		||||
 | 
			
		||||
// First function is first.
 | 
			
		||||
func First() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Second function is second.
 | 
			
		||||
func Second() {
 | 
			
		||||
}
 | 
			
		||||
`,
 | 
			
		||||
		"src/cmd/go/doc.go": `// The go command
 | 
			
		||||
package main
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +183,10 @@ package main
 | 
			
		|||
	p.cmdHandler = handlerServer{p, c, "/cmd/", "/src/cmd"}
 | 
			
		||||
	p.pkgHandler = handlerServer{p, c, "/pkg/", "/src/pkg"}
 | 
			
		||||
	p.initFuncMap()
 | 
			
		||||
	p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{with .PAst}}{{node $ .}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{end}}`))
 | 
			
		||||
	p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{with .PAst}}{{range $filename, $ast := .}}{{$filename}}:
 | 
			
		||||
{{node $ $ast}}{{end}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{with .Funcs}}
 | 
			
		||||
{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}{{end}}{{end}}{{end}}`))
 | 
			
		||||
 | 
			
		||||
	for _, tc := range []struct {
 | 
			
		||||
		desc string
 | 
			
		||||
| 
						 | 
				
			
			@ -190,10 +204,20 @@ package main
 | 
			
		|||
			args: []string{"bar"},
 | 
			
		||||
			exp:  "PACKAGE Package bar is an example.\n",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "package w. filter",
 | 
			
		||||
			args: []string{"foo", "First"},
 | 
			
		||||
			exp:  "PACKAGE \nfunc First()\n    First function is first.\n",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "source mode",
 | 
			
		||||
			args: []string{"src/bar"},
 | 
			
		||||
			exp:  "// Package bar is an example.\npackage bar\n",
 | 
			
		||||
			exp:  "bar/bar.go:\n// Package bar is an example.\npackage bar\n",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "source mode w. filter",
 | 
			
		||||
			args: []string{"src/foo", "Second"},
 | 
			
		||||
			exp:  "// Second function is second.\nfunc Second() {\n}",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "command",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -219,12 +219,13 @@ type PageInfo struct {
 | 
			
		|||
	Err     error  // error or nil
 | 
			
		||||
 | 
			
		||||
	// package info
 | 
			
		||||
	FSet     *token.FileSet         // nil if no package documentation
 | 
			
		||||
	PDoc     *doc.Package           // nil if no package documentation
 | 
			
		||||
	Examples []*doc.Example         // nil if no example code
 | 
			
		||||
	Notes    map[string][]*doc.Note // nil if no package Notes
 | 
			
		||||
	PAst     *ast.File              // nil if no AST with package exports
 | 
			
		||||
	IsMain   bool                   // true for package main
 | 
			
		||||
	FSet       *token.FileSet         // nil if no package documentation
 | 
			
		||||
	PDoc       *doc.Package           // nil if no package documentation
 | 
			
		||||
	Examples   []*doc.Example         // nil if no example code
 | 
			
		||||
	Notes      map[string][]*doc.Note // nil if no package Notes
 | 
			
		||||
	PAst       map[string]*ast.File   // nil if no AST with package exports
 | 
			
		||||
	IsMain     bool                   // true for package main
 | 
			
		||||
	IsFiltered bool                   // true if results were filtered
 | 
			
		||||
 | 
			
		||||
	// directory info
 | 
			
		||||
	Dirs    *DirList  // nil if no directory information
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ func (c *Corpus) parseFile(fset *token.FileSet, filename string, mode parser.Mod
 | 
			
		|||
	return parser.ParseFile(fset, filename, src, mode)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Corpus) parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) {
 | 
			
		||||
func (c *Corpus) parseFiles(fset *token.FileSet, relpath string, abspath string, localnames []string) (map[string]*ast.File, error) {
 | 
			
		||||
	files := make(map[string]*ast.File)
 | 
			
		||||
	for _, f := range localnames {
 | 
			
		||||
		absname := pathpkg.Join(abspath, f)
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ func (c *Corpus) parseFiles(fset *token.FileSet, abspath string, localnames []st
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		files[absname] = file
 | 
			
		||||
		files[pathpkg.Join(relpath, f)] = file
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return files, nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ func (h *handlerServer) GetPageInfo(abspath, relpath string, mode PageInfoMode)
 | 
			
		|||
	if len(pkgfiles) > 0 {
 | 
			
		||||
		// build package AST
 | 
			
		||||
		fset := token.NewFileSet()
 | 
			
		||||
		files, err := h.c.parseFiles(fset, abspath, pkgfiles)
 | 
			
		||||
		files, err := h.c.parseFiles(fset, relpath, abspath, pkgfiles)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			info.Err = err
 | 
			
		||||
			return info
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +128,7 @@ func (h *handlerServer) GetPageInfo(abspath, relpath string, mode PageInfoMode)
 | 
			
		|||
 | 
			
		||||
			// collect examples
 | 
			
		||||
			testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...)
 | 
			
		||||
			files, err = h.c.parseFiles(fset, abspath, testfiles)
 | 
			
		||||
			files, err = h.c.parseFiles(fset, relpath, abspath, testfiles)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Println("parsing examples:", err)
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +156,7 @@ func (h *handlerServer) GetPageInfo(abspath, relpath string, mode PageInfoMode)
 | 
			
		|||
			if mode&NoFiltering == 0 {
 | 
			
		||||
				packageExports(fset, pkg)
 | 
			
		||||
			}
 | 
			
		||||
			info.PAst = ast.MergePackageFiles(pkg, 0)
 | 
			
		||||
			info.PAst = files
 | 
			
		||||
		}
 | 
			
		||||
		info.IsMain = pkgname == "main"
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +218,10 @@ func (h *handlerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
	var tabtitle, title, subtitle string
 | 
			
		||||
	switch {
 | 
			
		||||
	case info.PAst != nil:
 | 
			
		||||
		tabtitle = info.PAst.Name.Name
 | 
			
		||||
		for _, ast := range info.PAst {
 | 
			
		||||
			tabtitle = ast.Name.Name
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	case info.PDoc != nil:
 | 
			
		||||
		tabtitle = info.PDoc.Name
 | 
			
		||||
	default:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,51 +1,56 @@
 | 
			
		|||
{{with .PAst}}{{node $ .}}{{end}}{{/*
 | 
			
		||||
{{$info := .}}{{$filtered := .IsFiltered}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{if $filtered}}{{range .PAst}}{{range .Decls}}{{node $info .}}{{end}}{{end}}{{else}}{{with .PAst}}{{range $filename, $ast := .}}{{$filename}}:
 | 
			
		||||
{{node $ $ast}}{{end}}{{end}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .PDoc}}{{if $.IsMain}}COMMAND DOCUMENTATION
 | 
			
		||||
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{else}}PACKAGE DOCUMENTATION
 | 
			
		||||
{{else}}{{if not $filtered}}PACKAGE DOCUMENTATION
 | 
			
		||||
 | 
			
		||||
package {{.Name}}
 | 
			
		||||
    import "{{.ImportPath}}"
 | 
			
		||||
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{example_text $ "" "    "}}{{/*
 | 
			
		||||
{{example_text $ "" "    "}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .Consts}}
 | 
			
		||||
*/}}{{with .Consts}}{{if not $filtered}}
 | 
			
		||||
CONSTANTS
 | 
			
		||||
 | 
			
		||||
{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{end}}{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{end}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .Vars}}
 | 
			
		||||
*/}}{{with .Vars}}{{if not $filtered}}
 | 
			
		||||
VARIABLES
 | 
			
		||||
 | 
			
		||||
{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{end}}{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{end}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .Funcs}}
 | 
			
		||||
*/}}{{with .Funcs}}{{if not $filtered}}
 | 
			
		||||
FUNCTIONS
 | 
			
		||||
 | 
			
		||||
{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{end}}{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{example_text $ .Name "    "}}{{end}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .Types}}
 | 
			
		||||
*/}}{{with .Types}}{{if not $filtered}}
 | 
			
		||||
TYPES
 | 
			
		||||
 | 
			
		||||
{{range .}}{{$tname := .Name}}{{node $ .Decl}}
 | 
			
		||||
{{end}}{{range .}}{{$tname := .Name}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{range .Consts}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -751,54 +751,59 @@ $(document).ready(function() {
 | 
			
		|||
	{{end}}
 | 
			
		||||
{{end}}
 | 
			
		||||
`,
 | 
			
		||||
	"package.txt": `{{with .PAst}}{{node $ .}}{{end}}{{/*
 | 
			
		||||
	"package.txt": `{{$info := .}}{{$filtered := .IsFiltered}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{if $filtered}}{{range .PAst}}{{range .Decls}}{{node $info .}}{{end}}{{end}}{{else}}{{with .PAst}}{{range $filename, $ast := .}}{{$filename}}:
 | 
			
		||||
{{node $ $ast}}{{end}}{{end}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .PDoc}}{{if $.IsMain}}COMMAND DOCUMENTATION
 | 
			
		||||
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{else}}PACKAGE DOCUMENTATION
 | 
			
		||||
{{else}}{{if not $filtered}}PACKAGE DOCUMENTATION
 | 
			
		||||
 | 
			
		||||
package {{.Name}}
 | 
			
		||||
    import "{{.ImportPath}}"
 | 
			
		||||
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{example_text $ "" "    "}}{{/*
 | 
			
		||||
{{example_text $ "" "    "}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .Consts}}
 | 
			
		||||
*/}}{{with .Consts}}{{if not $filtered}}
 | 
			
		||||
CONSTANTS
 | 
			
		||||
 | 
			
		||||
{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{end}}{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{end}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .Vars}}
 | 
			
		||||
*/}}{{with .Vars}}{{if not $filtered}}
 | 
			
		||||
VARIABLES
 | 
			
		||||
 | 
			
		||||
{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{end}}{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{end}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .Funcs}}
 | 
			
		||||
*/}}{{with .Funcs}}{{if not $filtered}}
 | 
			
		||||
FUNCTIONS
 | 
			
		||||
 | 
			
		||||
{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{end}}{{range .}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{example_text $ .Name "    "}}{{end}}{{end}}{{/*
 | 
			
		||||
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
*/}}{{with .Types}}
 | 
			
		||||
*/}}{{with .Types}}{{if not $filtered}}
 | 
			
		||||
TYPES
 | 
			
		||||
 | 
			
		||||
{{range .}}{{$tname := .Name}}{{node $ .Decl}}
 | 
			
		||||
{{end}}{{range .}}{{$tname := .Name}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
{{range .Consts}}{{node $ .Decl}}
 | 
			
		||||
{{comment_text .Doc "    " "\t"}}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue