diff --git a/cmd/godoc/godoc_test.go b/cmd/godoc/godoc_test.go index 39ca16ae..095bed55 100644 --- a/cmd/godoc/godoc_test.go +++ b/cmd/godoc/godoc_test.go @@ -152,11 +152,49 @@ func TestWeb(t *testing.T) { } defer cmd.Process.Kill() waitForServer(t, addr) - tests := []struct{ path, substr string }{ - {"/", "Go is an open source programming language"}, - {"/pkg/fmt/", "Package fmt implements formatted I/O"}, - {"/src/fmt/", "scan_test.go"}, - {"/src/fmt/print.go", "// Println formats using"}, + tests := []struct { + path string + match []string + dontmatch []string + }{ + { + path: "/", + match: []string{"Go is an open source programming language"}, + }, + { + path: "/pkg/fmt/", + match: []string{"Package fmt implements formatted I/O"}, + }, + { + path: "/src/fmt/", + match: []string{"scan_test.go"}, + }, + { + path: "/src/fmt/print.go", + match: []string{"// Println formats using"}, + }, + { + path: "/pkg", + match: []string{ + "Standard library", + "Package fmt implements formatted I/O", + }, + dontmatch: []string{ + "internal/syscall", + "cmd/gc", + }, + }, + { + path: "/pkg/?m=all", + match: []string{ + "Standard library", + "Package fmt implements formatted I/O", + "internal/syscall", + }, + dontmatch: []string{ + "cmd/gc", + }, + }, } for _, test := range tests { url := fmt.Sprintf("http://%s%s", addr, test.path) @@ -170,9 +208,21 @@ func TestWeb(t *testing.T) { if err != nil { t.Errorf("GET %s: failed to read body: %s (response: %v)", url, err, resp) } - if bytes.Index(body, []byte(test.substr)) < 0 { - t.Errorf("GET %s: want substring %q in body, got:\n%s", - url, test.substr, string(body)) + isErr := false + for _, substr := range test.match { + if !bytes.Contains(body, []byte(substr)) { + t.Errorf("GET %s: wanted substring %q in body", url, substr) + isErr = true + } + } + for _, substr := range test.dontmatch { + if bytes.Contains(body, []byte(substr)) { + t.Errorf("GET %s: didn't want substring %q in body", url, substr) + isErr = true + } + } + if isErr { + t.Errorf("GET %s: got:\n%s", url, body) } } } diff --git a/godoc/cmdline_test.go b/godoc/cmdline_test.go index 2228a6ae..64e1c442 100644 --- a/godoc/cmdline_test.go +++ b/godoc/cmdline_test.go @@ -194,8 +194,19 @@ package main fs.Bind("/", mfs, "/", vfs.BindReplace) c := NewCorpus(fs) p := &Presentation{Corpus: c} - p.cmdHandler = handlerServer{p, c, "/cmd/", "/src/cmd"} - p.pkgHandler = handlerServer{p, c, "/pkg/", "/src"} + p.cmdHandler = handlerServer{ + p: p, + c: c, + pattern: "/cmd/", + fsRoot: "/src/cmd", + } + p.pkgHandler = handlerServer{ + p: p, + c: c, + pattern: "/pkg/", + fsRoot: "/src", + exclude: []string{"/src/cmd"}, + } p.initFuncMap() p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{$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 {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{with .Funcs}} diff --git a/godoc/dirtrees.go b/godoc/dirtrees.go index c38d3b2c..af8b02f0 100644 --- a/godoc/dirtrees.go +++ b/godoc/dirtrees.go @@ -280,8 +280,10 @@ type DirList struct { // listing creates a (linear) directory listing from a directory tree. // If skipRoot is set, the root directory itself is excluded from the list. +// If filter is set, only the directory entries whose paths match the filter +// are included. // -func (root *Directory) listing(skipRoot bool) *DirList { +func (root *Directory) listing(skipRoot bool, filter func(string) bool) *DirList { if root == nil { return nil } @@ -306,10 +308,12 @@ func (root *Directory) listing(skipRoot bool) *DirList { } // create list - list := make([]DirEntry, n) - i := 0 + list := make([]DirEntry, 0, n) for d := range root.iter(skipRoot) { - p := &list[i] + if filter != nil && !filter(d.Path) { + continue + } + var p DirEntry p.Depth = d.Depth - minDepth p.Height = maxHeight - p.Depth // the path is relative to root.Path - remove the root.Path @@ -322,7 +326,7 @@ func (root *Directory) listing(skipRoot bool) *DirList { p.Name = d.Name p.HasPkg = d.HasPkg p.Synopsis = d.Synopsis - i++ + list = append(list, p) } return &DirList{maxHeight, list} diff --git a/godoc/pres.go b/godoc/pres.go index 42c40f02..2bf71f18 100644 --- a/godoc/pres.go +++ b/godoc/pres.go @@ -115,8 +115,19 @@ func NewPresentation(c *Corpus) *Presentation { (*Presentation).SearchResultTxt, }, } - p.cmdHandler = handlerServer{p, c, "/cmd/", "/src/cmd"} - p.pkgHandler = handlerServer{p, c, "/pkg/", "/src"} + p.cmdHandler = handlerServer{ + p: p, + c: c, + pattern: "/cmd/", + fsRoot: "/src/cmd", + } + p.pkgHandler = handlerServer{ + p: p, + c: c, + pattern: "/pkg/", + fsRoot: "/src", + exclude: []string{"/src/cmd"}, + } p.cmdHandler.registerWithMux(p.mux) p.pkgHandler.registerWithMux(p.mux) p.mux.HandleFunc("/", p.ServeFile) diff --git a/godoc/server.go b/godoc/server.go index e81c6737..0373679c 100644 --- a/godoc/server.go +++ b/godoc/server.go @@ -36,9 +36,10 @@ import ( // This should probably merge into something else. type handlerServer struct { p *Presentation - c *Corpus // copy of p.Corpus - pattern string // url pattern; e.g. "/pkg/" - fsRoot string // file system root to which the pattern is mapped + c *Corpus // copy of p.Corpus + pattern string // url pattern; e.g. "/pkg/" + fsRoot string // file system root to which the pattern is mapped; e.g. "/src" + exclude []string // file system paths to exclude; e.g. "/src/cmd" } func (s *handlerServer) registerWithMux(mux *http.ServeMux) { @@ -66,7 +67,14 @@ func (h *handlerServer) GetPageInfo(abspath, relpath string, mode PageInfoMode) ctxt := build.Default ctxt.IsAbsPath = pathpkg.IsAbs ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) { - return h.c.fs.ReadDir(filepath.ToSlash(dir)) + f, err := h.c.fs.ReadDir(filepath.ToSlash(dir)) + filtered := make([]os.FileInfo, 0, len(f)) + for _, i := range f { + if mode&NoFiltering != 0 || i.Name() != "internal" { + filtered = append(filtered, i) + } + } + return filtered, err } ctxt.OpenFile = func(name string) (r io.ReadCloser, err error) { data, err := vfs.ReadFile(h.c.fs, filepath.ToSlash(name)) @@ -188,13 +196,35 @@ func (h *handlerServer) GetPageInfo(abspath, relpath string, mode PageInfoMode) dir = h.c.newDirectory(abspath, 1) timestamp = time.Now() } - info.Dirs = dir.listing(true) + info.Dirs = dir.listing(true, func(path string) bool { return h.includePath(path, mode) }) info.DirTime = timestamp info.DirFlat = mode&FlatDir != 0 return info } +func (h *handlerServer) includePath(path string, mode PageInfoMode) (r bool) { + // if the path is under one of the exclusion paths, don't list. + for _, e := range h.exclude { + if strings.HasPrefix(path, e) { + return false + } + } + + // if the path includes 'internal', don't list unless we are in the NoFiltering mode. + if mode&NoFiltering != 0 { + return true + } + if strings.Contains(path, "internal") { + for _, c := range strings.Split(path, string(os.PathSeparator)) { + if c == "internal" { + return false + } + } + } + return true +} + type funcsByName []*doc.Func func (s funcsByName) Len() int { return len(s) }