Skip to content

godoc/server: show internal packages switch #312

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 49 additions & 10 deletions cmd/godoc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ var (

// source code notes
notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show")

// show internal packages
showInternalPkg = flag.Bool("show_internal_pkg", false, "show internal packages always")
)

// An httpResponseRecorder is an http.ResponseWriter
Expand Down Expand Up @@ -266,6 +269,16 @@ func main() {
}
}

var goModRoot *mod
if goModFile != "" {
// Determine modules root
goModRoot, err = getGoModRoot(goModFile)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to determine the main module: %v", err)
os.Exit(1)
}
}

var typeAnalysis, pointerAnalysis bool
if *analysisFlag != "" {
for _, a := range strings.Split(*analysisFlag, ",") {
Expand All @@ -282,7 +295,7 @@ func main() {

var corpus *godoc.Corpus
if goModFile != "" {
corpus = godoc.NewCorpus(moduleFS{fs})
corpus = godoc.NewCorpus(moduleFS{fs, goModRoot})
} else {
corpus = godoc.NewCorpus(fs)
}
Expand Down Expand Up @@ -313,10 +326,15 @@ func main() {
pres = godoc.NewPresentation(corpus)
pres.ShowTimestamps = *showTimestamps
pres.ShowPlayground = *showPlayground
pres.ShowInternalPkg = *showInternalPkg
pres.DeclLinks = *declLinks
if *notesRx != "" {
pres.NotesRx = regexp.MustCompile(*notesRx)
}
// if go.mod is present exclude the module's cmd
if goModRoot != nil {
pres.AddPkgExclude("/src/" + goModRoot.Path + "/cmd")
}

readTemplates(pres)
registerHandlers(pres)
Expand Down Expand Up @@ -496,6 +514,28 @@ func buildList(goMod string) ([]mod, error) {
return mods, nil
}

func getGoModRoot(goMod string) (*mod, error) {
if goMod == os.DevNull {
// Empty build list.
return nil, nil
}

out, err := exec.Command("go", "list", "-m", "-json").Output()
if ee := (*exec.ExitError)(nil); xerrors.As(err, &ee) {
return nil, fmt.Errorf("go command exited unsuccessfully: %v\n%s", ee.ProcessState.String(), ee.Stderr)
} else if err != nil {
return nil, err
}
var m mod
dec := json.NewDecoder(bytes.NewReader(out)); ; {
err := dec.Decode(&m)
if err != nil {
return nil, err
}
}
return &m, nil
}

// moduleFS is a vfs.FileSystem wrapper used when godoc is running
// in module mode. It's needed so that packages inside modules are
// considered to be third party.
Expand All @@ -511,22 +551,21 @@ func buildList(goMod string) ([]mod, error) {
// general case. It should be replaced by a more direct solution
// for determining whether a package is third party or not.
//
type moduleFS struct{ vfs.FileSystem }
type moduleFS struct{
vfs.FileSystem
goModRoot *mod
}

func (moduleFS) RootType(path string) vfs.RootType {
func (fs moduleFS) RootType(path string) vfs.RootType {
if !strings.HasPrefix(path, "/src/") {
return ""
}
domain := path[len("/src/"):]
if i := strings.Index(domain, "/"); i >= 0 {
domain = domain[:i]
}
if !strings.Contains(domain, ".") {
// No dot in the first element of import path
if fs.goModRoot != nil && strings.HasPrefix(path[len("/src/"):], fs.goModRoot.Path) {
// Beginning with the modules root path
// suggests this is a package in GOROOT.
return vfs.RootTypeGoRoot
} else {
// A dot in the first element of import path
// Not beginning with the modules root path
// suggests this is a third party package.
return vfs.RootTypeGoPath
}
Expand Down
48 changes: 36 additions & 12 deletions godoc/dirtrees.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"log"
"os"
pathpkg "path"
"path/filepath"
"runtime"
"sort"
"strings"
Expand Down Expand Up @@ -332,7 +333,7 @@ func hasThirdParty(list []DirEntry) bool {
// If filter is set, only the directory entries whose paths match the filter
// are included.
//
func (dir *Directory) listing(skipRoot bool, filter func(string) bool) *DirList {
func (dir *Directory) listing(skipRoot bool, filter func(DirEntry) bool) *DirList {
if dir == nil {
return nil
}
Expand All @@ -356,27 +357,50 @@ func (dir *Directory) listing(skipRoot bool, filter func(string) bool) *DirList
return nil
}

// mark the first GOROOT item
foundGoRoot := false

// create list
list := make([]DirEntry, 0, n)
for d := range dir.iter(skipRoot) {
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
// prefix (the prefix should always be present but avoid
// crashes and check)
path := strings.TrimPrefix(d.Path, dir.Path)
// remove leading separator if any - path must be relative
path = strings.TrimPrefix(path, "/")
p.Path = path
p.Path = d.Path
p.Name = d.Name
p.HasPkg = d.HasPkg
p.Synopsis = d.Synopsis
p.RootType = d.RootType
list = append(list, p)
if filter == nil || filter(p) {
// the path is relative to root.Path - remove the root.Path
// prefix (the prefix should always be present but avoid
// crashes and check)
p.Path = strings.TrimPrefix(p.Path, dir.Path)
// remove leading separator if any - path must be relative
p.Path = strings.TrimPrefix(p.Path, "/")
// test if found first GOROOT item
if p.RootType == vfs.RootTypeGoRoot && !foundGoRoot && p.Depth > 0 {
// include the GoRoot path prefix as DirEntries to visualize tree in packageroot.html
var v DirEntry
v.Depth = 0
v.Path = p.Path
v.HasPkg = false
v.Synopsis = ""
v.RootType = p.RootType
s := strings.Split(filepath.Clean(p.Path), string(os.PathSeparator))
for _, c := range s[:len(s)-1] {
v.Height = maxHeight - v.Depth
v.Name = c
// add to list
list = append(list, v)
v.Depth++
}
}
// if any RootTypeGoRoot mark foundGoRoot
foundGoRoot = foundGoRoot || p.RootType == vfs.RootTypeGoRoot
// add to list
list = append(list, p)
}
}

return &DirList{maxHeight, list}
Expand Down
7 changes: 7 additions & 0 deletions godoc/pres.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ type Presentation struct {
ShowPlayground bool
DeclLinks bool

// ShowInternalPkg optionally shows the internal packages from GoMod module
ShowInternalPkg bool

// NotesRx optionally specifies a regexp to match
// notes to render in the output.
NotesRx *regexp.Regexp
Expand Down Expand Up @@ -151,6 +154,10 @@ func (p *Presentation) CmdFSRoot() string {
return p.cmdHandler.fsRoot
}

func (p* Presentation) AddPkgExclude(path string) {
p.pkgHandler.exclude = append(p.pkgHandler.exclude, path)
}

// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
// but this doesn't feel right.
func (p *Presentation) GetPkgPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
Expand Down
13 changes: 8 additions & 5 deletions godoc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,29 +217,32 @@ func (h *handlerServer) GetPageInfo(abspath, relpath string, mode PageInfoMode,
dir = h.c.newDirectory(abspath, 2)
timestamp = time.Now()
}
info.Dirs = dir.listing(true, func(path string) bool { return h.includePath(path, mode) })
info.Dirs = dir.listing(true, func(p DirEntry) bool { return h.includePath(p, mode) })

info.DirTime = timestamp
info.DirFlat = mode&FlatDir != 0

return info
}

func (h *handlerServer) includePath(path string, mode PageInfoMode) (r bool) {
func (h *handlerServer) includePath(d DirEntry, mode PageInfoMode) (r bool) {
// if the path is under one of the exclusion paths, don't list.
path := d.Path
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 the path includes special symbols, don't list unless we are in the NoFiltering mode.
if mode&NoFiltering != 0 {
return true
}
if strings.Contains(path, "internal") || strings.Contains(path, "vendor") {
if strings.Contains(path, "internal") || strings.Contains(path, "vendor") || strings.Contains(path, "third_party") {
for _, c := range strings.Split(filepath.Clean(path), string(os.PathSeparator)) {
if c == "internal" || c == "vendor" {
if c == "internal" {
return d.RootType == vfs.RootTypeGoRoot && h.p.ShowInternalPkg
} else if c == "vendor" || c == "third_party" {
return false
}
}
Expand Down