1 | // Copyright 2013 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 godoc |
6 | |
7 | import ( |
8 | "errors" |
9 | "sync" |
10 | "time" |
11 | |
12 | "golang.org/x/tools/godoc/analysis" |
13 | "golang.org/x/tools/godoc/util" |
14 | "golang.org/x/tools/godoc/vfs" |
15 | ) |
16 | |
17 | // A Corpus holds all the state related to serving and indexing a |
18 | // collection of Go code. |
19 | // |
20 | // Construct a new Corpus with NewCorpus, then modify options, |
21 | // then call its Init method. |
22 | type Corpus struct { |
23 | fs vfs.FileSystem |
24 | |
25 | // Verbose logging. |
26 | Verbose bool |
27 | |
28 | // IndexEnabled controls whether indexing is enabled. |
29 | IndexEnabled bool |
30 | |
31 | // IndexFiles specifies a glob pattern specifying index files. |
32 | // If not empty, the index is read from these files in sorted |
33 | // order. |
34 | IndexFiles string |
35 | |
36 | // IndexThrottle specifies the indexing throttle value |
37 | // between 0.0 and 1.0. At 0.0, the indexer always sleeps. |
38 | // At 1.0, the indexer never sleeps. Because 0.0 is useless |
39 | // and redundant with setting IndexEnabled to false, the |
40 | // zero value for IndexThrottle means 0.9. |
41 | IndexThrottle float64 |
42 | |
43 | // IndexInterval specifies the time to sleep between reindexing |
44 | // all the sources. |
45 | // If zero, a default is used. If negative, the index is only |
46 | // built once. |
47 | IndexInterval time.Duration |
48 | |
49 | // IndexDocs enables indexing of Go documentation. |
50 | // This will produce search results for exported types, functions, |
51 | // methods, variables, and constants, and will link to the godoc |
52 | // documentation for those identifiers. |
53 | IndexDocs bool |
54 | |
55 | // IndexGoCode enables indexing of Go source code. |
56 | // This will produce search results for internal and external identifiers |
57 | // and will link to both declarations and uses of those identifiers in |
58 | // source code. |
59 | IndexGoCode bool |
60 | |
61 | // IndexFullText enables full-text indexing. |
62 | // This will provide search results for any matching text in any file that |
63 | // is indexed, including non-Go files (see whitelisted in index.go). |
64 | // Regexp searching is supported via full-text indexing. |
65 | IndexFullText bool |
66 | |
67 | // MaxResults optionally specifies the maximum results for indexing. |
68 | MaxResults int |
69 | |
70 | // SummarizePackage optionally specifies a function to |
71 | // summarize a package. It exists as an optimization to |
72 | // avoid reading files to parse package comments. |
73 | // |
74 | // If SummarizePackage returns false for ok, the caller |
75 | // ignores all return values and parses the files in the package |
76 | // as if SummarizePackage were nil. |
77 | // |
78 | // If showList is false, the package is hidden from the |
79 | // package listing. |
80 | SummarizePackage func(pkg string) (summary string, showList, ok bool) |
81 | |
82 | // IndexDirectory optionally specifies a function to determine |
83 | // whether the provided directory should be indexed. The dir |
84 | // will be of the form "/src/cmd/6a", "/doc/play", |
85 | // "/src/io", etc. |
86 | // If nil, all directories are indexed if indexing is enabled. |
87 | IndexDirectory func(dir string) bool |
88 | |
89 | // Send a value on this channel to trigger a metadata refresh. |
90 | // It is buffered so that if a signal is not lost if sent |
91 | // during a refresh. |
92 | refreshMetadataSignal chan bool |
93 | |
94 | // file system information |
95 | fsTree util.RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now) |
96 | fsModified util.RWValue // timestamp of last call to invalidateIndex |
97 | docMetadata util.RWValue // mapping from paths to *Metadata |
98 | |
99 | // SearchIndex is the search index in use. |
100 | searchIndex util.RWValue |
101 | |
102 | // Analysis is the result of type and pointer analysis. |
103 | Analysis analysis.Result |
104 | |
105 | // flag to check whether a corpus is initialized or not |
106 | initMu sync.RWMutex |
107 | initDone bool |
108 | |
109 | // pkgAPIInfo contains the information about which package API |
110 | // features were added in which version of Go. |
111 | pkgAPIInfo apiVersions |
112 | } |
113 | |
114 | // NewCorpus returns a new Corpus from a filesystem. |
115 | // The returned corpus has all indexing enabled and MaxResults set to 1000. |
116 | // Change or set any options on Corpus before calling the Corpus.Init method. |
117 | func NewCorpus(fs vfs.FileSystem) *Corpus { |
118 | c := &Corpus{ |
119 | fs: fs, |
120 | refreshMetadataSignal: make(chan bool, 1), |
121 | |
122 | MaxResults: 1000, |
123 | IndexEnabled: true, |
124 | IndexDocs: true, |
125 | IndexGoCode: true, |
126 | IndexFullText: true, |
127 | } |
128 | return c |
129 | } |
130 | |
131 | func (c *Corpus) CurrentIndex() (*Index, time.Time) { |
132 | v, t := c.searchIndex.Get() |
133 | idx, _ := v.(*Index) |
134 | return idx, t |
135 | } |
136 | |
137 | func (c *Corpus) FSModifiedTime() time.Time { |
138 | _, ts := c.fsModified.Get() |
139 | return ts |
140 | } |
141 | |
142 | // Init initializes Corpus, once options on Corpus are set. |
143 | // It must be called before any subsequent method calls. |
144 | func (c *Corpus) Init() error { |
145 | if err := c.initFSTree(); err != nil { |
146 | return err |
147 | } |
148 | c.updateMetadata() |
149 | go c.refreshMetadataLoop() |
150 | |
151 | c.initMu.Lock() |
152 | c.initDone = true |
153 | c.initMu.Unlock() |
154 | return nil |
155 | } |
156 | |
157 | func (c *Corpus) initFSTree() error { |
158 | dir := c.newDirectory("/", -1) |
159 | if dir == nil { |
160 | return errors.New("godoc: corpus fstree is nil") |
161 | } |
162 | c.fsTree.Set(dir) |
163 | c.invalidateIndex() |
164 | return nil |
165 | } |
166 |
Members