|  | // Copyright 2015 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 main | 
|  |  | 
|  | import ( | 
|  | "go/build" | 
|  | "log" | 
|  | "os" | 
|  | "path" | 
|  | "path/filepath" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | // Dirs is a structure for scanning the directory tree. | 
|  | // Its Next method returns the next Go source directory it finds. | 
|  | // Although it can be used to scan the tree multiple times, it | 
|  | // only walks the tree once, caching the data it finds. | 
|  | type Dirs struct { | 
|  | scan   chan string // directories generated by walk. | 
|  | paths  []string    // Cache of known paths. | 
|  | offset int         // Counter for Next. | 
|  | } | 
|  |  | 
|  | var dirs Dirs | 
|  |  | 
|  | func init() { | 
|  | dirs.paths = make([]string, 0, 1000) | 
|  | dirs.scan = make(chan string) | 
|  | go dirs.walk() | 
|  | } | 
|  |  | 
|  | // Reset puts the scan back at the beginning. | 
|  | func (d *Dirs) Reset() { | 
|  | d.offset = 0 | 
|  | } | 
|  |  | 
|  | // Next returns the next directory in the scan. The boolean | 
|  | // is false when the scan is done. | 
|  | func (d *Dirs) Next() (string, bool) { | 
|  | if d.offset < len(d.paths) { | 
|  | path := d.paths[d.offset] | 
|  | d.offset++ | 
|  | return path, true | 
|  | } | 
|  | path, ok := <-d.scan | 
|  | if !ok { | 
|  | return "", false | 
|  | } | 
|  | d.paths = append(d.paths, path) | 
|  | d.offset++ | 
|  | return path, ok | 
|  | } | 
|  |  | 
|  | // walk walks the trees in GOROOT and GOPATH. | 
|  | func (d *Dirs) walk() { | 
|  | d.bfsWalkRoot(build.Default.GOROOT) | 
|  | for _, root := range splitGopath() { | 
|  | d.bfsWalkRoot(root) | 
|  | } | 
|  | close(d.scan) | 
|  | } | 
|  |  | 
|  | // bfsWalkRoot walks a single directory hierarchy in breadth-first lexical order. | 
|  | // Each Go source directory it finds is delivered on d.scan. | 
|  | func (d *Dirs) bfsWalkRoot(root string) { | 
|  | root = path.Join(root, "src") | 
|  |  | 
|  | // this is the queue of directories to examine in this pass. | 
|  | this := []string{} | 
|  | // next is the queue of directories to examine in the next pass. | 
|  | next := []string{root} | 
|  |  | 
|  | for len(next) > 0 { | 
|  | this, next = next, this[0:0] | 
|  | for _, dir := range this { | 
|  | fd, err := os.Open(dir) | 
|  | if err != nil { | 
|  | log.Print(err) | 
|  | continue | 
|  | } | 
|  | entries, err := fd.Readdir(0) | 
|  | fd.Close() | 
|  | if err != nil { | 
|  | log.Print(err) | 
|  | continue | 
|  | } | 
|  | hasGoFiles := false | 
|  | for _, entry := range entries { | 
|  | name := entry.Name() | 
|  | // For plain files, remember if this directory contains any .go | 
|  | // source files, but ignore them otherwise. | 
|  | if !entry.IsDir() { | 
|  | if !hasGoFiles && strings.HasSuffix(name, ".go") { | 
|  | hasGoFiles = true | 
|  | } | 
|  | continue | 
|  | } | 
|  | // Entry is a directory. | 
|  | // No .git or other dot nonsense please. | 
|  | if strings.HasPrefix(name, ".") { | 
|  | continue | 
|  | } | 
|  | // Remember this (fully qualified) directory for the next pass. | 
|  | next = append(next, filepath.Join(dir, name)) | 
|  | } | 
|  | if hasGoFiles { | 
|  | // It's a candidate. | 
|  | d.scan <- dir | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  | } |