github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/cmd/doc/dirs.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "go/build" 9 "os" 10 "path" 11 "path/filepath" 12 "strings" 13 ) 14 15 // Dirs is a structure for scanning the directory tree. 16 // Its Next method returns the next Go source directory it finds. 17 // Although it can be used to scan the tree multiple times, it 18 // only walks the tree once, caching the data it finds. 19 type Dirs struct { 20 scan chan string // directories generated by walk. 21 paths []string // Cache of known paths. 22 offset int // Counter for Next. 23 } 24 25 var dirs Dirs 26 27 func init() { 28 dirs.paths = make([]string, 0, 1000) 29 dirs.scan = make(chan string) 30 go dirs.walk() 31 } 32 33 // Reset puts the scan back at the beginning. 34 func (d *Dirs) Reset() { 35 d.offset = 0 36 } 37 38 // Next returns the next directory in the scan. The boolean 39 // is false when the scan is done. 40 func (d *Dirs) Next() (string, bool) { 41 if d.offset < len(d.paths) { 42 path := d.paths[d.offset] 43 d.offset++ 44 return path, true 45 } 46 path, ok := <-d.scan 47 if !ok { 48 return "", false 49 } 50 d.paths = append(d.paths, path) 51 d.offset++ 52 return path, ok 53 } 54 55 // walk walks the trees in GOROOT and GOPATH. 56 func (d *Dirs) walk() { 57 d.walkRoot(build.Default.GOROOT) 58 for _, root := range splitGopath() { 59 d.walkRoot(root) 60 } 61 close(d.scan) 62 } 63 64 // walkRoot walks a single directory. Each Go source directory it finds is 65 // delivered on d.scan. 66 func (d *Dirs) walkRoot(root string) { 67 root = path.Join(root, "src") 68 slashDot := string(filepath.Separator) + "." 69 // We put a slash on the pkg so can use simple string comparison below 70 // yet avoid inadvertent matches, like /foobar matching bar. 71 72 visit := func(pathName string, f os.FileInfo, err error) error { 73 if err != nil { 74 return nil 75 } 76 // One package per directory. Ignore the files themselves. 77 if !f.IsDir() { 78 return nil 79 } 80 // No .git or other dot nonsense please. 81 if strings.Contains(pathName, slashDot) { 82 return filepath.SkipDir 83 } 84 // Does the directory contain any Go files? If so, it's a candidate. 85 if hasGoFiles(pathName) { 86 d.scan <- pathName 87 return nil 88 } 89 return nil 90 } 91 92 filepath.Walk(root, visit) 93 } 94 95 // hasGoFiles tests whether the directory contains at least one file with ".go" 96 // extension 97 func hasGoFiles(path string) bool { 98 dir, err := os.Open(path) 99 if err != nil { 100 // ignore unreadable directories 101 return false 102 } 103 defer dir.Close() 104 105 names, err := dir.Readdirnames(0) 106 if err != nil { 107 // ignore unreadable directories 108 return false 109 } 110 111 for _, name := range names { 112 if strings.HasSuffix(name, ".go") { 113 return true 114 } 115 } 116 117 return false 118 }