diff --git a/godoc/dirtrees.go b/godoc/dirtrees.go index 6f51e2f9..a7de71a5 100644 --- a/godoc/dirtrees.go +++ b/godoc/dirtrees.go @@ -16,6 +16,8 @@ import ( "runtime" "sort" "strings" + + "golang.org/x/tools/godoc/vfs" ) // Conventional name for directories containing test data. @@ -24,12 +26,13 @@ import ( const testdataDirName = "testdata" type Directory struct { - Depth int - Path string // directory path; includes Name - Name string // directory name - HasPkg bool // true if the directory contains at least one package - Synopsis string // package documentation, if any - Dirs []*Directory // subdirectories + Depth int + Path string // directory path; includes Name + Name string // directory name + HasPkg bool // true if the directory contains at least one package + Synopsis string // package documentation, if any + FsRootType string // string representation of vfs.RootType + Dirs []*Directory // subdirectories } func isGoFile(fi os.FileInfo) bool { @@ -195,12 +198,13 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i } return &Directory{ - Depth: depth, - Path: path, - Name: name, - HasPkg: hasPkgFiles && show, // TODO(bradfitz): add proper Hide field? - Synopsis: synopsis, - Dirs: dirs, + Depth: depth, + Path: path, + Name: name, + HasPkg: hasPkgFiles && show, // TODO(bradfitz): add proper Hide field? + Synopsis: synopsis, + FsRootType: string(b.c.fs.RootType(path)), + Dirs: dirs, } } @@ -298,12 +302,13 @@ func (dir *Directory) lookup(path string) *Directory { // are useful for presenting an entry in an indented fashion. // type DirEntry struct { - Depth int // >= 0 - Height int // = DirList.MaxHeight - Depth, > 0 - Path string // directory path; includes Name, relative to DirList root - Name string // directory name - HasPkg bool // true if the directory contains at least one package - Synopsis string // package documentation, if any + Depth int // >= 0 + Height int // = DirList.MaxHeight - Depth, > 0 + Path string // directory path; includes Name, relative to DirList root + Name string // directory name + HasPkg bool // true if the directory contains at least one package + Synopsis string // package documentation, if any + FsRootType string // string representation of vfs.RootType } type DirList struct { @@ -311,6 +316,17 @@ type DirList struct { List []DirEntry } +// hasThirdParty checks whether a list of directory entries has packages outside +// the standard library or not. +func hasThirdParty(list []DirEntry) bool { + for _, entry := range list { + if entry.FsRootType == string(vfs.RootTypeGoPath) { + return true + } + } + return false +} + // 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 @@ -359,6 +375,7 @@ func (root *Directory) listing(skipRoot bool, filter func(string) bool) *DirList p.Name = d.Name p.HasPkg = d.HasPkg p.Synopsis = d.Synopsis + p.FsRootType = d.FsRootType list = append(list, p) } diff --git a/godoc/godoc.go b/godoc/godoc.go index d6c27d0b..413d582d 100644 --- a/godoc/godoc.go +++ b/godoc/godoc.go @@ -106,6 +106,9 @@ func (p *Presentation) initFuncMap() { // formatting of PageInfoMode query string "modeQueryString": modeQueryString, + + // check whether to display third party section or not + "hasThirdParty": hasThirdParty, } if p.URLForSrc != nil { p.funcMap["srcLink"] = p.URLForSrc diff --git a/godoc/static/package.html b/godoc/static/package.html index 41b99b46..c531cbd5 100644 --- a/godoc/static/package.html +++ b/godoc/static/package.html @@ -249,59 +249,124 @@
-These packages are part of the Go Project but outside the main Go tree. diff --git a/godoc/static/static.go b/godoc/static/static.go index b958c3ce..180660d1 100644 --- a/godoc/static/static.go +++ b/godoc/static/static.go @@ -1869,59 +1869,124 @@ function cgAddChild(tree, ul, cgn) {
-These packages are part of the Go Project but outside the main Go tree. diff --git a/godoc/vfs/emptyvfs.go b/godoc/vfs/emptyvfs.go index 01b6942f..be9a87c5 100644 --- a/godoc/vfs/emptyvfs.go +++ b/godoc/vfs/emptyvfs.go @@ -57,6 +57,10 @@ func (e *emptyVFS) String() string { return "emptyVFS(/)" } +func (e *emptyVFS) RootType(path string) RootType { + return RootTypeStandAlone +} + // These functions below implement os.FileInfo for the single // empty emulated directory. diff --git a/godoc/vfs/gatefs/gatefs.go b/godoc/vfs/gatefs/gatefs.go index 7045a5ca..fe0462dc 100644 --- a/godoc/vfs/gatefs/gatefs.go +++ b/godoc/vfs/gatefs/gatefs.go @@ -37,6 +37,10 @@ func (fs gatefs) String() string { return fmt.Sprintf("gated(%s, %d)", fs.fs.String(), cap(fs.gate)) } +func (fs gatefs) RootType(path string) vfs.RootType { + return fs.fs.RootType(path) +} + func (fs gatefs) Open(p string) (vfs.ReadSeekCloser, error) { fs.enter() defer fs.leave() diff --git a/godoc/vfs/gatefs/gatefs_test.go b/godoc/vfs/gatefs/gatefs_test.go new file mode 100644 index 00000000..45f8f324 --- /dev/null +++ b/godoc/vfs/gatefs/gatefs_test.go @@ -0,0 +1,39 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gatefs_test + +import ( + "os" + "runtime" + "testing" + + "golang.org/x/tools/godoc/vfs" + "golang.org/x/tools/godoc/vfs/gatefs" +) + +func TestRootType(t *testing.T) { + goPath := os.Getenv("GOPATH") + var expectedType vfs.RootType + if goPath == "" { + expectedType = vfs.RootTypeStandAlone + } else { + expectedType = vfs.RootTypeGoPath + } + tests := []struct { + path string + fsType vfs.RootType + }{ + {runtime.GOROOT(), vfs.RootTypeGoRoot}, + {goPath, expectedType}, + {"/tmp/", vfs.RootTypeStandAlone}, + } + + for _, item := range tests { + fs := gatefs.New(vfs.OS(item.path), make(chan bool, 1)) + if fs.RootType("path") != item.fsType { + t.Errorf("unexpected fsType. Expected- %v, Got- %v", item.fsType, fs.RootType("path")) + } + } +} diff --git a/godoc/vfs/mapfs/mapfs.go b/godoc/vfs/mapfs/mapfs.go index 660b1ca7..4911a2d4 100644 --- a/godoc/vfs/mapfs/mapfs.go +++ b/godoc/vfs/mapfs/mapfs.go @@ -29,6 +29,13 @@ type mapFS map[string]string func (fs mapFS) String() string { return "mapfs" } +// RootType directly returns vfs.RootTypeAsset because +// mapFs is only used to return static assets and not for +// resolving Go files. +func (fs mapFS) RootType(p string) vfs.RootType { + return vfs.RootTypeAsset +} + func (fs mapFS) Close() error { return nil } func filename(p string) string { diff --git a/godoc/vfs/namespace.go b/godoc/vfs/namespace.go index ca1213eb..f3212ba6 100644 --- a/godoc/vfs/namespace.go +++ b/godoc/vfs/namespace.go @@ -381,6 +381,20 @@ func (ns NameSpace) ReadDir(path string) ([]os.FileInfo, error) { return all, nil } +// RootType returns the RootType for the given path in the namespace. +func (ns NameSpace) RootType(path string) RootType { + // We resolve the given path to a list of mountedFS and then return + // the root type for the filesystem which contains the path. + for _, m := range ns.resolve(path) { + _, err := m.fs.ReadDir(m.translate(path)) + // Found a match, return the filesystem's root type + if err == nil { + return m.fs.RootType(path) + } + } + return RootTypeStandAlone +} + // byName implements sort.Interface. type byName []os.FileInfo diff --git a/godoc/vfs/os.go b/godoc/vfs/os.go index fa981424..9001871d 100644 --- a/godoc/vfs/os.go +++ b/godoc/vfs/os.go @@ -6,10 +6,12 @@ package vfs import ( "fmt" + "go/build" "io/ioutil" "os" pathpkg "path" "path/filepath" + "runtime" ) // OS returns an implementation of FileSystem reading from the @@ -18,12 +20,44 @@ import ( // passed to Open has no way to specify a drive letter. Using a root // lets code refer to OS(`c:\`), OS(`d:\`) and so on. func OS(root string) FileSystem { - return osFS(root) + var t RootType + switch { + case root == runtime.GOROOT(): + t = RootTypeGoRoot + case isGoPath(root): + t = RootTypeGoPath + default: + t = RootTypeStandAlone + } + return osFS{rootPath: root, rootType: t} } -type osFS string +type osFS struct { + rootPath string + rootType RootType +} -func (root osFS) String() string { return "os(" + string(root) + ")" } +func isGoPath(path string) bool { + for _, bp := range filepath.SplitList(build.Default.GOPATH) { + for _, gp := range filepath.SplitList(path) { + if bp == gp { + return true + } + } + } + return false +} + +func (root osFS) String() string { return "os(" + root.rootPath + ")" } + +// RootType returns the root type for the filesystem. +// +// Note that we ignore the path argument because roottype is a property of +// this filesystem. But for other filesystems, the roottype might need to be +// dynamically deduced at call time. +func (root osFS) RootType(path string) RootType { + return root.rootType +} func (root osFS) resolve(path string) string { // Clean the path so that it cannot possibly begin with ../. @@ -32,7 +66,7 @@ func (root osFS) resolve(path string) string { // with .. in it, but be safe anyway. path = pathpkg.Clean("/" + path) - return filepath.Join(string(root), path) + return filepath.Join(root.rootPath, path) } func (root osFS) Open(path string) (ReadSeekCloser, error) { diff --git a/godoc/vfs/os_test.go b/godoc/vfs/os_test.go new file mode 100644 index 00000000..9307424a --- /dev/null +++ b/godoc/vfs/os_test.go @@ -0,0 +1,38 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vfs_test + +import ( + "os" + "runtime" + "testing" + + "golang.org/x/tools/godoc/vfs" +) + +func TestRootType(t *testing.T) { + goPath := os.Getenv("GOPATH") + var expectedType vfs.RootType + if goPath == "" { + expectedType = vfs.RootTypeStandAlone + } else { + expectedType = vfs.RootTypeGoPath + } + tests := []struct { + path string + fsType vfs.RootType + }{ + {runtime.GOROOT(), vfs.RootTypeGoRoot}, + {goPath, expectedType}, + {"/tmp/", vfs.RootTypeStandAlone}, + } + + for _, item := range tests { + fs := vfs.OS(item.path) + if fs.RootType("path") != item.fsType { + t.Errorf("unexpected fsType. Expected- %v, Got- %v", item.fsType, fs.RootType("path")) + } + } +} diff --git a/godoc/vfs/vfs.go b/godoc/vfs/vfs.go index ad06b1a1..72265d89 100644 --- a/godoc/vfs/vfs.go +++ b/godoc/vfs/vfs.go @@ -12,6 +12,18 @@ import ( "os" ) +// RootType indicates the type of files contained within a directory. +// +// The two main types are a GOROOT or a GOPATH directory. +type RootType string + +const ( + RootTypeGoRoot RootType = "GOROOT" + RootTypeGoPath RootType = "GOPATH" + RootTypeStandAlone RootType = "StandAlone" // used by emptyvfs + RootTypeAsset RootType = "Assets" // serves static assets using mapfs +) + // The FileSystem interface specifies the methods godoc is using // to access the file system for which it serves documentation. type FileSystem interface { @@ -19,6 +31,7 @@ type FileSystem interface { Lstat(path string) (os.FileInfo, error) Stat(path string) (os.FileInfo, error) ReadDir(path string) ([]os.FileInfo, error) + RootType(path string) RootType String() string } diff --git a/godoc/vfs/zipfs/zipfs.go b/godoc/vfs/zipfs/zipfs.go index e554446e..894a8377 100644 --- a/godoc/vfs/zipfs/zipfs.go +++ b/godoc/vfs/zipfs/zipfs.go @@ -20,9 +20,12 @@ package zipfs // import "golang.org/x/tools/godoc/vfs/zipfs" import ( "archive/zip" "fmt" + "go/build" "io" "os" "path" + "path/filepath" + "runtime" "sort" "strings" "time" @@ -81,6 +84,28 @@ func (fs *zipFS) String() string { return "zip(" + fs.name + ")" } +func (fs *zipFS) RootType(abspath string) vfs.RootType { + var t vfs.RootType + switch { + case abspath == runtime.GOROOT(): + t = vfs.RootTypeGoRoot + case isGoPath(abspath): + t = vfs.RootTypeGoPath + default: + t = vfs.RootTypeStandAlone + } + return t +} + +func isGoPath(path string) bool { + for _, p := range filepath.SplitList(build.Default.GOPATH) { + if p == path { + return true + } + } + return false +} + func (fs *zipFS) Close() error { fs.list = nil return fs.ReadCloser.Close()