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