Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / cache / imports.go
1 package cache
2
3 import (
4         "context"
5         "fmt"
6         "reflect"
7         "sync"
8         "time"
9
10         "golang.org/x/tools/internal/event"
11         "golang.org/x/tools/internal/event/keys"
12         "golang.org/x/tools/internal/imports"
13         "golang.org/x/tools/internal/lsp/source"
14         "golang.org/x/tools/internal/span"
15 )
16
17 type importsState struct {
18         ctx context.Context
19
20         mu                      sync.Mutex
21         processEnv              *imports.ProcessEnv
22         cleanupProcessEnv       func()
23         cacheRefreshDuration    time.Duration
24         cacheRefreshTimer       *time.Timer
25         cachedModFileIdentifier string
26         cachedBuildFlags        []string
27 }
28
29 func (s *importsState) runProcessEnvFunc(ctx context.Context, snapshot *snapshot, fn func(*imports.Options) error) error {
30         s.mu.Lock()
31         defer s.mu.Unlock()
32
33         // Use temporary go.mod files, but always go to disk for the contents.
34         // Rebuilding the cache is expensive, and we don't want to do it for
35         // transient changes.
36         var modFH source.FileHandle
37         var gosum []byte
38         var modFileIdentifier string
39         var err error
40         // TODO(rfindley): Change the goimports logic to use a persistent workspace
41         // module for workspace module mode.
42         //
43         // Get the go.mod file that corresponds to this view's root URI. This is
44         // broken because it assumes that the view's root is a module, but this is
45         // not more broken than the previous state--it is a temporary hack that
46         // should be removed ASAP.
47         var matchURI span.URI
48         for modURI := range snapshot.workspace.activeModFiles() {
49                 if dirURI(modURI) == snapshot.view.rootURI {
50                         matchURI = modURI
51                 }
52         }
53         // TODO(rFindley): should it be an error if matchURI is empty?
54         if matchURI != "" {
55                 modFH, err = snapshot.GetFile(ctx, matchURI)
56                 if err != nil {
57                         return err
58                 }
59                 modFileIdentifier = modFH.FileIdentity().Hash
60                 gosum = snapshot.goSum(ctx, matchURI)
61         }
62         // v.goEnv is immutable -- changes make a new view. Options can change.
63         // We can't compare build flags directly because we may add -modfile.
64         snapshot.view.optionsMu.Lock()
65         localPrefix := snapshot.view.options.Local
66         currentBuildFlags := snapshot.view.options.BuildFlags
67         changed := !reflect.DeepEqual(currentBuildFlags, s.cachedBuildFlags) ||
68                 snapshot.view.options.VerboseOutput != (s.processEnv.Logf != nil) ||
69                 modFileIdentifier != s.cachedModFileIdentifier
70         snapshot.view.optionsMu.Unlock()
71
72         // If anything relevant to imports has changed, clear caches and
73         // update the processEnv. Clearing caches blocks on any background
74         // scans.
75         if changed {
76                 // As a special case, skip cleanup the first time -- we haven't fully
77                 // initialized the environment yet and calling GetResolver will do
78                 // unnecessary work and potentially mess up the go.mod file.
79                 if s.cleanupProcessEnv != nil {
80                         if resolver, err := s.processEnv.GetResolver(); err == nil {
81                                 resolver.(*imports.ModuleResolver).ClearForNewMod()
82                         }
83                         s.cleanupProcessEnv()
84                 }
85                 s.cachedModFileIdentifier = modFileIdentifier
86                 s.cachedBuildFlags = currentBuildFlags
87                 s.cleanupProcessEnv, err = s.populateProcessEnv(ctx, snapshot, modFH, gosum)
88                 if err != nil {
89                         return err
90                 }
91         }
92
93         // Run the user function.
94         opts := &imports.Options{
95                 // Defaults.
96                 AllErrors:   true,
97                 Comments:    true,
98                 Fragment:    true,
99                 FormatOnly:  false,
100                 TabIndent:   true,
101                 TabWidth:    8,
102                 Env:         s.processEnv,
103                 LocalPrefix: localPrefix,
104         }
105
106         if err := fn(opts); err != nil {
107                 return err
108         }
109
110         if s.cacheRefreshTimer == nil {
111                 // Don't refresh more than twice per minute.
112                 delay := 30 * time.Second
113                 // Don't spend more than a couple percent of the time refreshing.
114                 if adaptive := 50 * s.cacheRefreshDuration; adaptive > delay {
115                         delay = adaptive
116                 }
117                 s.cacheRefreshTimer = time.AfterFunc(delay, s.refreshProcessEnv)
118         }
119
120         return nil
121 }
122
123 // populateProcessEnv sets the dynamically configurable fields for the view's
124 // process environment. Assumes that the caller is holding the s.view.importsMu.
125 func (s *importsState) populateProcessEnv(ctx context.Context, snapshot *snapshot, modFH source.FileHandle, gosum []byte) (cleanup func(), err error) {
126         cleanup = func() {}
127         pe := s.processEnv
128
129         snapshot.view.optionsMu.Lock()
130         pe.BuildFlags = append([]string(nil), snapshot.view.options.BuildFlags...)
131         if snapshot.view.options.VerboseOutput {
132                 pe.Logf = func(format string, args ...interface{}) {
133                         event.Log(ctx, fmt.Sprintf(format, args...))
134                 }
135         } else {
136                 pe.Logf = nil
137         }
138         snapshot.view.optionsMu.Unlock()
139
140         pe.Env = map[string]string{}
141         for k, v := range snapshot.view.goEnv {
142                 pe.Env[k] = v
143         }
144         pe.Env["GO111MODULE"] = snapshot.view.go111module
145
146         var modURI span.URI
147         var modContent []byte
148         if modFH != nil {
149                 modURI = modFH.URI()
150                 modContent, err = modFH.Read()
151                 if err != nil {
152                         return nil, err
153                 }
154         }
155         modmod, err := snapshot.needsModEqualsMod(ctx, modURI, modContent)
156         if err != nil {
157                 return cleanup, err
158         }
159         if modmod {
160                 pe.ModFlag = "mod"
161         }
162
163         // Add -modfile to the build flags, if we are using it.
164         if snapshot.workspaceMode()&tempModfile != 0 && modFH != nil {
165                 var tmpURI span.URI
166                 tmpURI, cleanup, err = tempModFile(modFH, gosum)
167                 if err != nil {
168                         return nil, err
169                 }
170                 pe.ModFile = tmpURI.Filename()
171         }
172
173         return cleanup, nil
174 }
175
176 func (s *importsState) refreshProcessEnv() {
177         start := time.Now()
178
179         s.mu.Lock()
180         env := s.processEnv
181         if resolver, err := s.processEnv.GetResolver(); err == nil {
182                 resolver.ClearForNewScan()
183         }
184         s.mu.Unlock()
185
186         event.Log(s.ctx, "background imports cache refresh starting")
187         if err := imports.PrimeCache(context.Background(), env); err == nil {
188                 event.Log(s.ctx, fmt.Sprintf("background refresh finished after %v", time.Since(start)))
189         } else {
190                 event.Log(s.ctx, fmt.Sprintf("background refresh finished after %v", time.Since(start)), keys.Err.Of(err))
191         }
192         s.mu.Lock()
193         s.cacheRefreshDuration = time.Since(start)
194         s.cacheRefreshTimer = nil
195         s.mu.Unlock()
196 }