diff --git a/godoc/cmdline.go b/godoc/cmdline.go index 7b7d7ba9..d0396a04 100644 --- a/godoc/cmdline.go +++ b/godoc/cmdline.go @@ -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) } diff --git a/godoc/cmdline_test.go b/godoc/cmdline_test.go index 303aeafc..52c706d2 100644 --- a/godoc/cmdline_test.go +++ b/godoc/cmdline_test.go @@ -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", diff --git a/godoc/godoc.go b/godoc/godoc.go index cfa8c2f3..7964b524 100644 --- a/godoc/godoc.go +++ b/godoc/godoc.go @@ -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 diff --git a/godoc/parser.go b/godoc/parser.go index a27d4fdc..4762bef2 100644 --- a/godoc/parser.go +++ b/godoc/parser.go @@ -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 diff --git a/godoc/server.go b/godoc/server.go index f152adc3..33467304 100644 --- a/godoc/server.go +++ b/godoc/server.go @@ -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: diff --git a/godoc/static/package.txt b/godoc/static/package.txt index d191621c..44ab380e 100644 --- a/godoc/static/package.txt +++ b/godoc/static/package.txt @@ -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"}} diff --git a/godoc/static/static.go b/godoc/static/static.go index ad409d3f..21cb1d79 100644 --- a/godoc/static/static.go +++ b/godoc/static/static.go @@ -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"}}