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
5package godoc
6
7import (
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.
22type 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.
117func 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
131func (c *Corpus) CurrentIndex() (*Index, time.Time) {
132	v, t := c.searchIndex.Get()
133	idx, _ := v.(*Index)
134	return idx, t
135}
136
137func (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.
144func (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
157func (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