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