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