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 / session.go
1 // Copyright 2019 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 cache
6
7 import (
8         "context"
9         "fmt"
10         "os"
11         "strconv"
12         "strings"
13         "sync"
14         "sync/atomic"
15
16         "golang.org/x/tools/internal/event"
17         "golang.org/x/tools/internal/gocommand"
18         "golang.org/x/tools/internal/imports"
19         "golang.org/x/tools/internal/lsp/source"
20         "golang.org/x/tools/internal/span"
21         "golang.org/x/tools/internal/xcontext"
22         errors "golang.org/x/xerrors"
23 )
24
25 type Session struct {
26         cache *Cache
27         id    string
28
29         optionsMu sync.Mutex
30         options   *source.Options
31
32         viewMu  sync.Mutex
33         views   []*View
34         viewMap map[span.URI]*View
35
36         overlayMu sync.Mutex
37         overlays  map[span.URI]*overlay
38
39         // gocmdRunner guards go command calls from concurrency errors.
40         gocmdRunner *gocommand.Runner
41 }
42
43 type overlay struct {
44         session *Session
45         uri     span.URI
46         text    []byte
47         hash    string
48         version float64
49         kind    source.FileKind
50
51         // saved is true if a file matches the state on disk,
52         // and therefore does not need to be part of the overlay sent to go/packages.
53         saved bool
54 }
55
56 func (o *overlay) Read() ([]byte, error) {
57         return o.text, nil
58 }
59
60 func (o *overlay) FileIdentity() source.FileIdentity {
61         return source.FileIdentity{
62                 URI:  o.uri,
63                 Hash: o.hash,
64                 Kind: o.kind,
65         }
66 }
67
68 func (o *overlay) VersionedFileIdentity() source.VersionedFileIdentity {
69         return source.VersionedFileIdentity{
70                 URI:       o.uri,
71                 SessionID: o.session.id,
72                 Version:   o.version,
73         }
74 }
75
76 func (o *overlay) Kind() source.FileKind {
77         return o.kind
78 }
79
80 func (o *overlay) URI() span.URI {
81         return o.uri
82 }
83
84 func (o *overlay) Version() float64 {
85         return o.version
86 }
87
88 func (o *overlay) Session() string {
89         return o.session.id
90 }
91
92 func (o *overlay) Saved() bool {
93         return o.saved
94 }
95
96 // closedFile implements LSPFile for a file that the editor hasn't told us about.
97 type closedFile struct {
98         source.FileHandle
99 }
100
101 func (c *closedFile) VersionedFileIdentity() source.VersionedFileIdentity {
102         return source.VersionedFileIdentity{
103                 URI:       c.FileHandle.URI(),
104                 SessionID: "",
105                 Version:   0,
106         }
107 }
108
109 func (c *closedFile) Session() string {
110         return ""
111 }
112
113 func (c *closedFile) Version() float64 {
114         return 0
115 }
116
117 func (s *Session) ID() string     { return s.id }
118 func (s *Session) String() string { return s.id }
119
120 func (s *Session) Options() *source.Options {
121         s.optionsMu.Lock()
122         defer s.optionsMu.Unlock()
123         return s.options
124 }
125
126 func (s *Session) SetOptions(options *source.Options) {
127         s.optionsMu.Lock()
128         defer s.optionsMu.Unlock()
129         s.options = options
130 }
131
132 func (s *Session) Shutdown(ctx context.Context) {
133         s.viewMu.Lock()
134         defer s.viewMu.Unlock()
135         for _, view := range s.views {
136                 view.shutdown(ctx)
137         }
138         s.views = nil
139         s.viewMap = nil
140         event.Log(ctx, "Shutdown session", KeyShutdownSession.Of(s))
141 }
142
143 func (s *Session) Cache() interface{} {
144         return s.cache
145 }
146
147 func (s *Session) NewView(ctx context.Context, name string, folder, tempWorkspace span.URI, options *source.Options) (source.View, source.Snapshot, func(), error) {
148         s.viewMu.Lock()
149         defer s.viewMu.Unlock()
150         view, snapshot, release, err := s.createView(ctx, name, folder, tempWorkspace, options, 0)
151         if err != nil {
152                 return nil, nil, func() {}, err
153         }
154         s.views = append(s.views, view)
155         // we always need to drop the view map
156         s.viewMap = make(map[span.URI]*View)
157         return view, snapshot, release, nil
158 }
159
160 func (s *Session) createView(ctx context.Context, name string, folder, tempWorkspace span.URI, options *source.Options, snapshotID uint64) (*View, *snapshot, func(), error) {
161         index := atomic.AddInt64(&viewIndex, 1)
162
163         if s.cache.options != nil {
164                 s.cache.options(options)
165         }
166
167         // Set the module-specific information.
168         ws, err := s.getWorkspaceInformation(ctx, folder, options)
169         if err != nil {
170                 return nil, nil, func() {}, err
171         }
172
173         // Build the gopls workspace, collecting active modules in the view.
174         workspace, err := newWorkspace(ctx, ws.rootURI, s, options.ExperimentalWorkspaceModule)
175         if err != nil {
176                 return nil, nil, func() {}, err
177         }
178
179         // We want a true background context and not a detached context here
180         // the spans need to be unrelated and no tag values should pollute it.
181         baseCtx := event.Detach(xcontext.Detach(ctx))
182         backgroundCtx, cancel := context.WithCancel(baseCtx)
183
184         v := &View{
185                 session:              s,
186                 initialWorkspaceLoad: make(chan struct{}),
187                 initializationSema:   make(chan struct{}, 1),
188                 id:                   strconv.FormatInt(index, 10),
189                 options:              options,
190                 baseCtx:              baseCtx,
191                 backgroundCtx:        backgroundCtx,
192                 cancel:               cancel,
193                 name:                 name,
194                 folder:               folder,
195                 filesByURI:           make(map[span.URI]*fileBase),
196                 filesByBase:          make(map[string][]*fileBase),
197                 workspaceInformation: *ws,
198                 tempWorkspace:        tempWorkspace,
199         }
200         v.importsState = &importsState{
201                 ctx: backgroundCtx,
202                 processEnv: &imports.ProcessEnv{
203                         GocmdRunner: s.gocmdRunner,
204                         WorkingDir:  folder.Filename(),
205                         Env:         ws.goEnv,
206                 },
207         }
208         v.snapshot = &snapshot{
209                 id:                snapshotID,
210                 view:              v,
211                 initializeOnce:    &sync.Once{},
212                 generation:        s.cache.store.Generation(generationName(v, 0)),
213                 packages:          make(map[packageKey]*packageHandle),
214                 ids:               make(map[span.URI][]packageID),
215                 metadata:          make(map[packageID]*metadata),
216                 files:             make(map[span.URI]source.VersionedFileHandle),
217                 goFiles:           make(map[parseKey]*parseGoHandle),
218                 importedBy:        make(map[packageID][]packageID),
219                 actions:           make(map[actionKey]*actionHandle),
220                 workspacePackages: make(map[packageID]packagePath),
221                 unloadableFiles:   make(map[span.URI]struct{}),
222                 parseModHandles:   make(map[span.URI]*parseModHandle),
223                 modTidyHandles:    make(map[span.URI]*modTidyHandle),
224                 modUpgradeHandles: make(map[span.URI]*modUpgradeHandle),
225                 modWhyHandles:     make(map[span.URI]*modWhyHandle),
226                 workspace:         workspace,
227         }
228
229         // Initialize the view without blocking.
230         initCtx, initCancel := context.WithCancel(xcontext.Detach(ctx))
231         v.initCancelFirstAttempt = initCancel
232         snapshot := v.snapshot
233         release := snapshot.generation.Acquire(initCtx)
234         go func() {
235                 snapshot.initialize(initCtx, true)
236                 if v.tempWorkspace != "" {
237                         var err error
238                         if err = os.Mkdir(v.tempWorkspace.Filename(), 0700); err == nil {
239                                 var wsdir span.URI
240                                 wsdir, err = snapshot.getWorkspaceDir(initCtx)
241                                 if err == nil {
242                                         err = copyWorkspace(v.tempWorkspace, wsdir)
243                                 }
244                         }
245                         if err != nil {
246                                 event.Error(initCtx, "creating workspace dir", err)
247                         }
248                 }
249                 release()
250         }()
251         return v, snapshot, snapshot.generation.Acquire(ctx), nil
252 }
253
254 // View returns the view by name.
255 func (s *Session) View(name string) source.View {
256         s.viewMu.Lock()
257         defer s.viewMu.Unlock()
258         for _, view := range s.views {
259                 if view.Name() == name {
260                         return view
261                 }
262         }
263         return nil
264 }
265
266 // ViewOf returns a view corresponding to the given URI.
267 // If the file is not already associated with a view, pick one using some heuristics.
268 func (s *Session) ViewOf(uri span.URI) (source.View, error) {
269         return s.viewOf(uri)
270 }
271
272 func (s *Session) viewOf(uri span.URI) (*View, error) {
273         s.viewMu.Lock()
274         defer s.viewMu.Unlock()
275
276         // Check if we already know this file.
277         if v, found := s.viewMap[uri]; found {
278                 return v, nil
279         }
280         // Pick the best view for this file and memoize the result.
281         v, err := s.bestView(uri)
282         if err != nil {
283                 return nil, err
284         }
285         s.viewMap[uri] = v
286         return v, nil
287 }
288
289 func (s *Session) viewsOf(uri span.URI) []*View {
290         s.viewMu.Lock()
291         defer s.viewMu.Unlock()
292
293         var views []*View
294         for _, view := range s.views {
295                 if strings.HasPrefix(string(uri), string(view.Folder())) {
296                         views = append(views, view)
297                 }
298         }
299         return views
300 }
301
302 func (s *Session) Views() []source.View {
303         s.viewMu.Lock()
304         defer s.viewMu.Unlock()
305         result := make([]source.View, len(s.views))
306         for i, v := range s.views {
307                 result[i] = v
308         }
309         return result
310 }
311
312 // bestView finds the best view to associate a given URI with.
313 // viewMu must be held when calling this method.
314 func (s *Session) bestView(uri span.URI) (*View, error) {
315         if len(s.views) == 0 {
316                 return nil, errors.Errorf("no views in the session")
317         }
318         // we need to find the best view for this file
319         var longest *View
320         for _, view := range s.views {
321                 if longest != nil && len(longest.Folder()) > len(view.Folder()) {
322                         continue
323                 }
324                 if view.contains(uri) {
325                         longest = view
326                 }
327         }
328         if longest != nil {
329                 return longest, nil
330         }
331         // Try our best to return a view that knows the file.
332         for _, view := range s.views {
333                 if view.knownFile(uri) {
334                         return view, nil
335                 }
336         }
337         // TODO: are there any more heuristics we can use?
338         return s.views[0], nil
339 }
340
341 func (s *Session) removeView(ctx context.Context, view *View) error {
342         s.viewMu.Lock()
343         defer s.viewMu.Unlock()
344         i, err := s.dropView(ctx, view)
345         if err != nil {
346                 return err
347         }
348         // delete this view... we don't care about order but we do want to make
349         // sure we can garbage collect the view
350         s.views[i] = s.views[len(s.views)-1]
351         s.views[len(s.views)-1] = nil
352         s.views = s.views[:len(s.views)-1]
353         return nil
354 }
355
356 func (s *Session) updateView(ctx context.Context, view *View, options *source.Options) (*View, error) {
357         s.viewMu.Lock()
358         defer s.viewMu.Unlock()
359         i, err := s.dropView(ctx, view)
360         if err != nil {
361                 return nil, err
362         }
363         // Preserve the snapshot ID if we are recreating the view.
364         view.snapshotMu.Lock()
365         snapshotID := view.snapshot.id
366         view.snapshotMu.Unlock()
367         v, _, release, err := s.createView(ctx, view.name, view.folder, view.tempWorkspace, options, snapshotID)
368         release()
369         if err != nil {
370                 // we have dropped the old view, but could not create the new one
371                 // this should not happen and is very bad, but we still need to clean
372                 // up the view array if it happens
373                 s.views[i] = s.views[len(s.views)-1]
374                 s.views[len(s.views)-1] = nil
375                 s.views = s.views[:len(s.views)-1]
376                 return nil, err
377         }
378         // substitute the new view into the array where the old view was
379         s.views[i] = v
380         return v, nil
381 }
382
383 func (s *Session) dropView(ctx context.Context, v *View) (int, error) {
384         // we always need to drop the view map
385         s.viewMap = make(map[span.URI]*View)
386         for i := range s.views {
387                 if v == s.views[i] {
388                         // we found the view, drop it and return the index it was found at
389                         s.views[i] = nil
390                         v.shutdown(ctx)
391                         return i, nil
392                 }
393         }
394         return -1, errors.Errorf("view %s for %v not found", v.Name(), v.Folder())
395 }
396
397 func (s *Session) ModifyFiles(ctx context.Context, changes []source.FileModification) error {
398         _, _, releases, _, err := s.DidModifyFiles(ctx, changes)
399         for _, release := range releases {
400                 release()
401         }
402         return err
403 }
404
405 type fileChange struct {
406         content    []byte
407         exists     bool
408         fileHandle source.VersionedFileHandle
409 }
410
411 func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModification) (map[span.URI]source.View, map[source.View]source.Snapshot, []func(), []span.URI, error) {
412         views := make(map[*View]map[span.URI]*fileChange)
413         bestViews := map[span.URI]source.View{}
414         // Keep track of deleted files so that we can clear their diagnostics.
415         // A file might be re-created after deletion, so only mark files that
416         // have truly been deleted.
417         deletions := map[span.URI]struct{}{}
418
419         overlays, err := s.updateOverlays(ctx, changes)
420         if err != nil {
421                 return nil, nil, nil, nil, err
422         }
423         var forceReloadMetadata bool
424         for _, c := range changes {
425                 if c.Action == source.InvalidateMetadata {
426                         forceReloadMetadata = true
427                 }
428
429                 // Build the list of affected views.
430                 bestView, err := s.viewOf(c.URI)
431                 if err != nil {
432                         return nil, nil, nil, nil, err
433                 }
434                 bestViews[c.URI] = bestView
435
436                 var changedViews []*View
437                 for _, view := range s.views {
438                         // Don't propagate changes that are outside of the view's scope
439                         // or knowledge.
440                         if !view.relevantChange(c) {
441                                 continue
442                         }
443                         changedViews = append(changedViews, view)
444                 }
445                 // If no view matched the change, assign it to the best view.
446                 if len(changedViews) == 0 {
447                         changedViews = append(changedViews, bestView)
448                 }
449
450                 // Apply the changes to all affected views.
451                 for _, view := range changedViews {
452                         // Make sure that the file is added to the view.
453                         if _, err := view.getFile(c.URI); err != nil {
454                                 return nil, nil, nil, nil, err
455                         }
456                         if _, ok := views[view]; !ok {
457                                 views[view] = make(map[span.URI]*fileChange)
458                         }
459                         if fh, ok := overlays[c.URI]; ok {
460                                 views[view][c.URI] = &fileChange{
461                                         content:    fh.text,
462                                         exists:     true,
463                                         fileHandle: fh,
464                                 }
465                                 delete(deletions, c.URI)
466                         } else {
467                                 fsFile, err := s.cache.getFile(ctx, c.URI)
468                                 if err != nil {
469                                         return nil, nil, nil, nil, err
470                                 }
471                                 content, err := fsFile.Read()
472                                 fh := &closedFile{fsFile}
473                                 views[view][c.URI] = &fileChange{
474                                         content:    content,
475                                         exists:     err == nil,
476                                         fileHandle: fh,
477                                 }
478                                 if err != nil {
479                                         deletions[c.URI] = struct{}{}
480                                 }
481                         }
482                 }
483         }
484
485         snapshots := map[source.View]source.Snapshot{}
486         var releases []func()
487         for view, changed := range views {
488                 snapshot, release := view.invalidateContent(ctx, changed, forceReloadMetadata)
489                 snapshots[view] = snapshot
490                 releases = append(releases, release)
491         }
492         var deletionsSlice []span.URI
493         for uri := range deletions {
494                 deletionsSlice = append(deletionsSlice, uri)
495         }
496         return bestViews, snapshots, releases, deletionsSlice, nil
497 }
498
499 func (s *Session) updateOverlays(ctx context.Context, changes []source.FileModification) (map[span.URI]*overlay, error) {
500         s.overlayMu.Lock()
501         defer s.overlayMu.Unlock()
502
503         for _, c := range changes {
504                 // Don't update overlays for metadata invalidations.
505                 if c.Action == source.InvalidateMetadata {
506                         continue
507                 }
508
509                 o, ok := s.overlays[c.URI]
510
511                 // If the file is not opened in an overlay and the change is on disk,
512                 // there's no need to update an overlay. If there is an overlay, we
513                 // may need to update the overlay's saved value.
514                 if !ok && c.OnDisk {
515                         continue
516                 }
517
518                 // Determine the file kind on open, otherwise, assume it has been cached.
519                 var kind source.FileKind
520                 switch c.Action {
521                 case source.Open:
522                         kind = source.DetectLanguage(c.LanguageID, c.URI.Filename())
523                 default:
524                         if !ok {
525                                 return nil, errors.Errorf("updateOverlays: modifying unopened overlay %v", c.URI)
526                         }
527                         kind = o.kind
528                 }
529                 if kind == source.UnknownKind {
530                         return nil, errors.Errorf("updateOverlays: unknown file kind for %s", c.URI)
531                 }
532
533                 // Closing a file just deletes its overlay.
534                 if c.Action == source.Close {
535                         delete(s.overlays, c.URI)
536                         continue
537                 }
538
539                 // If the file is on disk, check if its content is the same as in the
540                 // overlay. Saves and on-disk file changes don't come with the file's
541                 // content.
542                 text := c.Text
543                 if text == nil && (c.Action == source.Save || c.OnDisk) {
544                         if !ok {
545                                 return nil, fmt.Errorf("no known content for overlay for %s", c.Action)
546                         }
547                         text = o.text
548                 }
549                 // On-disk changes don't come with versions.
550                 version := c.Version
551                 if c.OnDisk {
552                         version = o.version
553                 }
554                 hash := hashContents(text)
555                 var sameContentOnDisk bool
556                 switch c.Action {
557                 case source.Delete:
558                         // Do nothing. sameContentOnDisk should be false.
559                 case source.Save:
560                         // Make sure the version and content (if present) is the same.
561                         if o.version != version {
562                                 return nil, errors.Errorf("updateOverlays: saving %s at version %v, currently at %v", c.URI, c.Version, o.version)
563                         }
564                         if c.Text != nil && o.hash != hash {
565                                 return nil, errors.Errorf("updateOverlays: overlay %s changed on save", c.URI)
566                         }
567                         sameContentOnDisk = true
568                 default:
569                         fh, err := s.cache.getFile(ctx, c.URI)
570                         if err != nil {
571                                 return nil, err
572                         }
573                         _, readErr := fh.Read()
574                         sameContentOnDisk = (readErr == nil && fh.FileIdentity().Hash == hash)
575                 }
576                 o = &overlay{
577                         session: s,
578                         uri:     c.URI,
579                         version: version,
580                         text:    text,
581                         kind:    kind,
582                         hash:    hash,
583                         saved:   sameContentOnDisk,
584                 }
585                 s.overlays[c.URI] = o
586         }
587
588         // Get the overlays for each change while the session's overlay map is
589         // locked.
590         overlays := make(map[span.URI]*overlay)
591         for _, c := range changes {
592                 if o, ok := s.overlays[c.URI]; ok {
593                         overlays[c.URI] = o
594                 }
595         }
596         return overlays, nil
597 }
598
599 func (s *Session) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
600         if overlay := s.readOverlay(uri); overlay != nil {
601                 return overlay, nil
602         }
603         // Fall back to the cache-level file system.
604         return s.cache.getFile(ctx, uri)
605 }
606
607 func (s *Session) readOverlay(uri span.URI) *overlay {
608         s.overlayMu.Lock()
609         defer s.overlayMu.Unlock()
610
611         if overlay, ok := s.overlays[uri]; ok {
612                 return overlay
613         }
614         return nil
615 }
616
617 func (s *Session) Overlays() []source.Overlay {
618         s.overlayMu.Lock()
619         defer s.overlayMu.Unlock()
620
621         overlays := make([]source.Overlay, 0, len(s.overlays))
622         for _, overlay := range s.overlays {
623                 overlays = append(overlays, overlay)
624         }
625         return overlays
626 }