github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/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  	"log"
    10  	"os"
    11  	"path"
    12  	"path/filepath"
    13  	"strings"
    14  )
    15  
    16  // Dirs is a structure for scanning the directory tree.
    17  // Its Next method returns the next Go source directory it finds.
    18  // Although it can be used to scan the tree multiple times, it
    19  // only walks the tree once, caching the data it finds.
    20  type Dirs struct {
    21  	scan   chan string // directories generated by walk.
    22  	paths  []string    // Cache of known paths.
    23  	offset int         // Counter for Next.
    24  }
    25  
    26  var dirs Dirs
    27  
    28  func init() {
    29  	dirs.paths = make([]string, 0, 1000)
    30  	dirs.scan = make(chan string)
    31  	go dirs.walk()
    32  }
    33  
    34  // Reset puts the scan back at the beginning.
    35  func (d *Dirs) Reset() {
    36  	d.offset = 0
    37  }
    38  
    39  // Next returns the next directory in the scan. The boolean
    40  // is false when the scan is done.
    41  func (d *Dirs) Next() (string, bool) {
    42  	if d.offset < len(d.paths) {
    43  		path := d.paths[d.offset]
    44  		d.offset++
    45  		return path, true
    46  	}
    47  	path, ok := <-d.scan
    48  	if !ok {
    49  		return "", false
    50  	}
    51  	d.paths = append(d.paths, path)
    52  	d.offset++
    53  	return path, ok
    54  }
    55  
    56  // walk walks the trees in GOROOT and GOPATH.
    57  func (d *Dirs) walk() {
    58  	d.bfsWalkRoot(build.Default.GOROOT)
    59  	for _, root := range splitGopath() {
    60  		d.bfsWalkRoot(root)
    61  	}
    62  	close(d.scan)
    63  }
    64  
    65  // bfsWalkRoot walks a single directory hierarchy in breadth-first lexical order.
    66  // Each Go source directory it finds is delivered on d.scan.
    67  func (d *Dirs) bfsWalkRoot(root string) {
    68  	root = path.Join(root, "src")
    69  
    70  	// this is the queue of directories to examine in this pass.
    71  	this := []string{}
    72  	// next is the queue of directories to examine in the next pass.
    73  	next := []string{root}
    74  
    75  	for len(next) > 0 {
    76  		this, next = next, this[0:0]
    77  		for _, dir := range this {
    78  			fd, err := os.Open(dir)
    79  			if err != nil {
    80  				log.Printf("error opening %s: %v", dir, err)
    81  				return // TODO? There may be entry before the error.
    82  			}
    83  			entries, err := fd.Readdir(0)
    84  			fd.Close()
    85  			if err != nil {
    86  				log.Printf("error reading %s: %v", dir, err)
    87  				return // TODO? There may be entry before the error.
    88  			}
    89  			hasGoFiles := false
    90  			for _, entry := range entries {
    91  				name := entry.Name()
    92  				// For plain files, remember if this directory contains any .go
    93  				// source files, but ignore them otherwise.
    94  				if !entry.IsDir() {
    95  					if !hasGoFiles && strings.HasSuffix(name, ".go") {
    96  						hasGoFiles = true
    97  					}
    98  					continue
    99  				}
   100  				// Entry is a directory.
   101  				// No .git or other dot nonsense please.
   102  				if strings.HasPrefix(name, ".") {
   103  					continue
   104  				}
   105  				// Remember this (fully qualified) directory for the next pass.
   106  				next = append(next, filepath.Join(dir, name))
   107  			}
   108  			if hasGoFiles {
   109  				// It's a candidate.
   110  				d.scan <- dir
   111  			}
   112  		}
   113  
   114  	}
   115  }