-
Notifications
You must be signed in to change notification settings - Fork 11
/
dirs.go
115 lines (103 loc) · 2.79 KB
/
dirs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// 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.Printf("error opening %s: %v", dir, err)
return // TODO? There may be entry before the error.
}
entries, err := fd.Readdir(0)
fd.Close()
if err != nil {
log.Printf("error reading %s: %v", dir, err)
return // TODO? There may be entry before the error.
}
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
}
}
}
}