Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / godoc / dirtrees.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/godoc/dirtrees.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/godoc/dirtrees.go
new file mode 100644 (file)
index 0000000..82c9a06
--- /dev/null
@@ -0,0 +1,383 @@
+// Copyright 2010 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.
+
+// This file contains the code dealing with package directory trees.
+
+package godoc
+
+import (
+       "go/doc"
+       "go/parser"
+       "go/token"
+       "log"
+       "os"
+       pathpkg "path"
+       "runtime"
+       "sort"
+       "strings"
+
+       "golang.org/x/tools/godoc/vfs"
+)
+
+// Conventional name for directories containing test data.
+// Excluded from directory trees.
+//
+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
+       RootType vfs.RootType // root type of the filesystem containing the directory
+       Dirs     []*Directory // subdirectories
+}
+
+func isGoFile(fi os.FileInfo) bool {
+       name := fi.Name()
+       return !fi.IsDir() &&
+               len(name) > 0 && name[0] != '.' && // ignore .files
+               pathpkg.Ext(name) == ".go"
+}
+
+func isPkgFile(fi os.FileInfo) bool {
+       return isGoFile(fi) &&
+               !strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
+}
+
+func isPkgDir(fi os.FileInfo) bool {
+       name := fi.Name()
+       return fi.IsDir() && len(name) > 0 &&
+               name[0] != '_' && name[0] != '.' // ignore _files and .files
+}
+
+type treeBuilder struct {
+       c        *Corpus
+       maxDepth int
+}
+
+// ioGate is a semaphore controlling VFS activity (ReadDir, parseFile, etc).
+// Send before an operation and receive after.
+var ioGate = make(chan struct{}, 20)
+
+// workGate controls the number of concurrent workers. Too many concurrent
+// workers and performance degrades and the race detector gets overwhelmed. If
+// we cannot check out a concurrent worker, work is performed by the main thread
+// instead of spinning up another goroutine.
+var workGate = make(chan struct{}, runtime.NumCPU()*4)
+
+func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory {
+       if name == testdataDirName {
+               return nil
+       }
+
+       if depth >= b.maxDepth {
+               // return a dummy directory so that the parent directory
+               // doesn't get discarded just because we reached the max
+               // directory depth
+               return &Directory{
+                       Depth: depth,
+                       Path:  path,
+                       Name:  name,
+               }
+       }
+
+       var synopses [3]string // prioritized package documentation (0 == highest priority)
+
+       show := true // show in package listing
+       hasPkgFiles := false
+       haveSummary := false
+
+       if hook := b.c.SummarizePackage; hook != nil {
+               if summary, show0, ok := hook(strings.TrimPrefix(path, "/src/")); ok {
+                       hasPkgFiles = true
+                       show = show0
+                       synopses[0] = summary
+                       haveSummary = true
+               }
+       }
+
+       ioGate <- struct{}{}
+       list, err := b.c.fs.ReadDir(path)
+       <-ioGate
+       if err != nil {
+               // TODO: propagate more. See golang.org/issue/14252.
+               // For now:
+               if b.c.Verbose {
+                       log.Printf("newDirTree reading %s: %v", path, err)
+               }
+       }
+
+       // determine number of subdirectories and if there are package files
+       var dirchs []chan *Directory
+       var dirs []*Directory
+
+       for _, d := range list {
+               filename := pathpkg.Join(path, d.Name())
+               switch {
+               case isPkgDir(d):
+                       name := d.Name()
+                       select {
+                       case workGate <- struct{}{}:
+                               ch := make(chan *Directory, 1)
+                               dirchs = append(dirchs, ch)
+                               go func() {
+                                       ch <- b.newDirTree(fset, filename, name, depth+1)
+                                       <-workGate
+                               }()
+                       default:
+                               // no free workers, do work synchronously
+                               dir := b.newDirTree(fset, filename, name, depth+1)
+                               if dir != nil {
+                                       dirs = append(dirs, dir)
+                               }
+                       }
+               case !haveSummary && isPkgFile(d):
+                       // looks like a package file, but may just be a file ending in ".go";
+                       // don't just count it yet (otherwise we may end up with hasPkgFiles even
+                       // though the directory doesn't contain any real package files - was bug)
+                       // no "optimal" package synopsis yet; continue to collect synopses
+                       ioGate <- struct{}{}
+                       const flags = parser.ParseComments | parser.PackageClauseOnly
+                       file, err := b.c.parseFile(fset, filename, flags)
+                       <-ioGate
+                       if err != nil {
+                               if b.c.Verbose {
+                                       log.Printf("Error parsing %v: %v", filename, err)
+                               }
+                               break
+                       }
+
+                       hasPkgFiles = true
+                       if file.Doc != nil {
+                               // prioritize documentation
+                               i := -1
+                               switch file.Name.Name {
+                               case name:
+                                       i = 0 // normal case: directory name matches package name
+                               case "main":
+                                       i = 1 // directory contains a main package
+                               default:
+                                       i = 2 // none of the above
+                               }
+                               if 0 <= i && i < len(synopses) && synopses[i] == "" {
+                                       synopses[i] = doc.Synopsis(file.Doc.Text())
+                               }
+                       }
+                       haveSummary = synopses[0] != ""
+               }
+       }
+
+       // create subdirectory tree
+       for _, ch := range dirchs {
+               if d := <-ch; d != nil {
+                       dirs = append(dirs, d)
+               }
+       }
+
+       // We need to sort the dirs slice because
+       // it is appended again after reading from dirchs.
+       sort.Slice(dirs, func(i, j int) bool {
+               return dirs[i].Name < dirs[j].Name
+       })
+
+       // if there are no package files and no subdirectories
+       // containing package files, ignore the directory
+       if !hasPkgFiles && len(dirs) == 0 {
+               return nil
+       }
+
+       // select the highest-priority synopsis for the directory entry, if any
+       synopsis := ""
+       for _, synopsis = range synopses {
+               if synopsis != "" {
+                       break
+               }
+       }
+
+       return &Directory{
+               Depth:    depth,
+               Path:     path,
+               Name:     name,
+               HasPkg:   hasPkgFiles && show, // TODO(bradfitz): add proper Hide field?
+               Synopsis: synopsis,
+               RootType: b.c.fs.RootType(path),
+               Dirs:     dirs,
+       }
+}
+
+// newDirectory creates a new package directory tree with at most maxDepth
+// levels, anchored at root. The result tree is pruned such that it only
+// contains directories that contain package files or that contain
+// subdirectories containing package files (transitively). If a non-nil
+// pathFilter is provided, directory paths additionally must be accepted
+// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is
+// provided for maxDepth, nodes at larger depths are pruned as well; they
+// are assumed to contain package files even if their contents are not known
+// (i.e., in this case the tree may contain directories w/o any package files).
+//
+func (c *Corpus) newDirectory(root string, maxDepth int) *Directory {
+       // The root could be a symbolic link so use Stat not Lstat.
+       d, err := c.fs.Stat(root)
+       // If we fail here, report detailed error messages; otherwise
+       // is is hard to see why a directory tree was not built.
+       switch {
+       case err != nil:
+               log.Printf("newDirectory(%s): %s", root, err)
+               return nil
+       case root != "/" && !isPkgDir(d):
+               log.Printf("newDirectory(%s): not a package directory", root)
+               return nil
+       case root == "/" && !d.IsDir():
+               log.Printf("newDirectory(%s): not a directory", root)
+               return nil
+       }
+       if maxDepth < 0 {
+               maxDepth = 1e6 // "infinity"
+       }
+       b := treeBuilder{c, maxDepth}
+       // the file set provided is only for local parsing, no position
+       // information escapes and thus we don't need to save the set
+       return b.newDirTree(token.NewFileSet(), root, d.Name(), 0)
+}
+
+func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) {
+       if dir != nil {
+               if !skipRoot {
+                       c <- dir
+               }
+               for _, d := range dir.Dirs {
+                       d.walk(c, false)
+               }
+       }
+}
+
+func (dir *Directory) iter(skipRoot bool) <-chan *Directory {
+       c := make(chan *Directory)
+       go func() {
+               dir.walk(c, skipRoot)
+               close(c)
+       }()
+       return c
+}
+
+func (dir *Directory) lookupLocal(name string) *Directory {
+       for _, d := range dir.Dirs {
+               if d.Name == name {
+                       return d
+               }
+       }
+       return nil
+}
+
+func splitPath(p string) []string {
+       p = strings.TrimPrefix(p, "/")
+       if p == "" {
+               return nil
+       }
+       return strings.Split(p, "/")
+}
+
+// lookup looks for the *Directory for a given path, relative to dir.
+func (dir *Directory) lookup(path string) *Directory {
+       d := splitPath(dir.Path)
+       p := splitPath(path)
+       i := 0
+       for i < len(d) {
+               if i >= len(p) || d[i] != p[i] {
+                       return nil
+               }
+               i++
+       }
+       for dir != nil && i < len(p) {
+               dir = dir.lookupLocal(p[i])
+               i++
+       }
+       return dir
+}
+
+// DirEntry describes a directory entry. The Depth and Height values
+// 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
+       RootType vfs.RootType // root type of the filesystem containing the direntry
+}
+
+type DirList struct {
+       MaxHeight int // directory tree height, > 0
+       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.RootType == 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
+// are included.
+//
+func (dir *Directory) listing(skipRoot bool, filter func(string) bool) *DirList {
+       if dir == nil {
+               return nil
+       }
+
+       // determine number of entries n and maximum height
+       n := 0
+       minDepth := 1 << 30 // infinity
+       maxDepth := 0
+       for d := range dir.iter(skipRoot) {
+               n++
+               if minDepth > d.Depth {
+                       minDepth = d.Depth
+               }
+               if maxDepth < d.Depth {
+                       maxDepth = d.Depth
+               }
+       }
+       maxHeight := maxDepth - minDepth + 1
+
+       if n == 0 {
+               return nil
+       }
+
+       // 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.Name = d.Name
+               p.HasPkg = d.HasPkg
+               p.Synopsis = d.Synopsis
+               p.RootType = d.RootType
+               list = append(list, p)
+       }
+
+       return &DirList{maxHeight, list}
+}