.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / 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 float64
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() float64 {
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() float64 {
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                 filesByURI:           make(map[span.URI]*fileBase),
204                 filesByBase:          make(map[string][]*fileBase),
205                 rootURI:              root,
206                 workspaceInformation: *ws,
207                 tempWorkspace:        tempWorkspace,
208         }
209         v.importsState = &importsState{
210                 ctx: backgroundCtx,
211                 processEnv: &imports.ProcessEnv{
212                         GocmdRunner: s.gocmdRunner,
213                 },
214         }
215         v.snapshot = &snapshot{
216                 id:                snapshotID,
217                 view:              v,
218                 backgroundCtx:     backgroundCtx,
219                 cancel:            cancel,
220                 initializeOnce:    &sync.Once{},
221                 generation:        s.cache.store.Generation(generationName(v, 0)),
222                 packages:          make(map[packageKey]*packageHandle),
223                 ids:               make(map[span.URI][]packageID),
224                 metadata:          make(map[packageID]*metadata),
225                 files:             make(map[span.URI]source.VersionedFileHandle),
226                 goFiles:           make(map[parseKey]*parseGoHandle),
227                 importedBy:        make(map[packageID][]packageID),
228                 actions:           make(map[actionKey]*actionHandle),
229                 workspacePackages: make(map[packageID]packagePath),
230                 unloadableFiles:   make(map[span.URI]struct{}),
231                 parseModHandles:   make(map[span.URI]*parseModHandle),
232                 modTidyHandles:    make(map[span.URI]*modTidyHandle),
233                 modUpgradeHandles: make(map[span.URI]*modUpgradeHandle),
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                         if _, err := view.getFile(c.URI); err != nil {
458                                 return nil, nil, err
459                         }
460                         if _, ok := views[view]; !ok {
461                                 views[view] = make(map[span.URI]*fileChange)
462                         }
463                         if fh, ok := overlays[c.URI]; ok {
464                                 views[view][c.URI] = &fileChange{
465                                         content:    fh.text,
466                                         exists:     true,
467                                         fileHandle: fh,
468                                 }
469                         } else {
470                                 fsFile, err := s.cache.getFile(ctx, c.URI)
471                                 if err != nil {
472                                         return nil, nil, err
473                                 }
474                                 content, err := fsFile.Read()
475                                 fh := &closedFile{fsFile}
476                                 views[view][c.URI] = &fileChange{
477                                         content:    content,
478                                         exists:     err == nil,
479                                         fileHandle: fh,
480                                 }
481                         }
482                 }
483         }
484
485         var releases []func()
486         viewToSnapshot := map[*View]*snapshot{}
487         for view, changed := range views {
488                 snapshot, release := view.invalidateContent(ctx, changed, forceReloadMetadata)
489                 releases = append(releases, release)
490                 viewToSnapshot[view] = snapshot
491         }
492
493         // We only want to diagnose each changed file once, in the view to which
494         // it "most" belongs. We do this by picking the best view for each URI,
495         // and then aggregating the set of snapshots and their URIs (to avoid
496         // diagnosing the same snapshot multiple times).
497         snapshotURIs := map[source.Snapshot][]span.URI{}
498         for _, mod := range changes {
499                 viewSlice, ok := affectedViews[mod.URI]
500                 if !ok || len(viewSlice) == 0 {
501                         continue
502                 }
503                 view := bestViewForURI(mod.URI, viewSlice)
504                 snapshot, ok := viewToSnapshot[view]
505                 if !ok {
506                         panic(fmt.Sprintf("no snapshot for view %s", view.Folder()))
507                 }
508                 snapshotURIs[snapshot] = append(snapshotURIs[snapshot], mod.URI)
509         }
510         return snapshotURIs, releases, nil
511 }
512
513 func (s *Session) ExpandModificationsToDirectories(ctx context.Context, changes []source.FileModification) []source.FileModification {
514         var snapshots []*snapshot
515         for _, v := range s.views {
516                 snapshot, release := v.getSnapshot(ctx)
517                 defer release()
518                 snapshots = append(snapshots, snapshot)
519         }
520         knownDirs := knownDirectories(ctx, snapshots)
521         var result []source.FileModification
522         for _, c := range changes {
523                 if _, ok := knownDirs[c.URI]; !ok {
524                         result = append(result, c)
525                         continue
526                 }
527                 affectedFiles := knownFilesInDir(ctx, snapshots, c.URI)
528                 var fileChanges []source.FileModification
529                 for uri := range affectedFiles {
530                         fileChanges = append(fileChanges, source.FileModification{
531                                 URI:        uri,
532                                 Action:     c.Action,
533                                 LanguageID: "",
534                                 OnDisk:     c.OnDisk,
535                                 // changes to directories cannot include text or versions
536                         })
537                 }
538                 result = append(result, fileChanges...)
539         }
540         return result
541 }
542
543 // knownDirectories returns all of the directories known to the given
544 // snapshots, including workspace directories and their subdirectories.
545 func knownDirectories(ctx context.Context, snapshots []*snapshot) map[span.URI]struct{} {
546         result := map[span.URI]struct{}{}
547         for _, snapshot := range snapshots {
548                 dirs := snapshot.workspace.dirs(ctx, snapshot)
549                 for _, dir := range dirs {
550                         result[dir] = struct{}{}
551                 }
552                 subdirs := snapshot.allKnownSubdirs(ctx)
553                 for dir := range subdirs {
554                         result[dir] = struct{}{}
555                 }
556         }
557         return result
558 }
559
560 // knownFilesInDir returns the files known to the snapshots in the session.
561 // It does not respect symlinks.
562 func knownFilesInDir(ctx context.Context, snapshots []*snapshot, dir span.URI) map[span.URI]struct{} {
563         files := map[span.URI]struct{}{}
564
565         for _, snapshot := range snapshots {
566                 for _, uri := range snapshot.knownFilesInDir(ctx, dir) {
567                         files[uri] = struct{}{}
568                 }
569         }
570         return files
571 }
572
573 func (s *Session) updateOverlays(ctx context.Context, changes []source.FileModification) (map[span.URI]*overlay, error) {
574         s.overlayMu.Lock()
575         defer s.overlayMu.Unlock()
576
577         for _, c := range changes {
578                 // Don't update overlays for metadata invalidations.
579                 if c.Action == source.InvalidateMetadata {
580                         continue
581                 }
582
583                 o, ok := s.overlays[c.URI]
584
585                 // If the file is not opened in an overlay and the change is on disk,
586                 // there's no need to update an overlay. If there is an overlay, we
587                 // may need to update the overlay's saved value.
588                 if !ok && c.OnDisk {
589                         continue
590                 }
591
592                 // Determine the file kind on open, otherwise, assume it has been cached.
593                 var kind source.FileKind
594                 switch c.Action {
595                 case source.Open:
596                         kind = source.DetectLanguage(c.LanguageID, c.URI.Filename())
597                 default:
598                         if !ok {
599                                 return nil, errors.Errorf("updateOverlays: modifying unopened overlay %v", c.URI)
600                         }
601                         kind = o.kind
602                 }
603                 if kind == source.UnknownKind {
604                         return nil, errors.Errorf("updateOverlays: unknown file kind for %s", c.URI)
605                 }
606
607                 // Closing a file just deletes its overlay.
608                 if c.Action == source.Close {
609                         delete(s.overlays, c.URI)
610                         continue
611                 }
612
613                 // If the file is on disk, check if its content is the same as in the
614                 // overlay. Saves and on-disk file changes don't come with the file's
615                 // content.
616                 text := c.Text
617                 if text == nil && (c.Action == source.Save || c.OnDisk) {
618                         if !ok {
619                                 return nil, fmt.Errorf("no known content for overlay for %s", c.Action)
620                         }
621                         text = o.text
622                 }
623                 // On-disk changes don't come with versions.
624                 version := c.Version
625                 if c.OnDisk {
626                         version = o.version
627                 }
628                 hash := hashContents(text)
629                 var sameContentOnDisk bool
630                 switch c.Action {
631                 case source.Delete:
632                         // Do nothing. sameContentOnDisk should be false.
633                 case source.Save:
634                         // Make sure the version and content (if present) is the same.
635                         if o.version != version {
636                                 return nil, errors.Errorf("updateOverlays: saving %s at version %v, currently at %v", c.URI, c.Version, o.version)
637                         }
638                         if c.Text != nil && o.hash != hash {
639                                 return nil, errors.Errorf("updateOverlays: overlay %s changed on save", c.URI)
640                         }
641                         sameContentOnDisk = true
642                 default:
643                         fh, err := s.cache.getFile(ctx, c.URI)
644                         if err != nil {
645                                 return nil, err
646                         }
647                         _, readErr := fh.Read()
648                         sameContentOnDisk = (readErr == nil && fh.FileIdentity().Hash == hash)
649                 }
650                 o = &overlay{
651                         session: s,
652                         uri:     c.URI,
653                         version: version,
654                         text:    text,
655                         kind:    kind,
656                         hash:    hash,
657                         saved:   sameContentOnDisk,
658                 }
659                 s.overlays[c.URI] = o
660         }
661
662         // Get the overlays for each change while the session's overlay map is
663         // locked.
664         overlays := make(map[span.URI]*overlay)
665         for _, c := range changes {
666                 if o, ok := s.overlays[c.URI]; ok {
667                         overlays[c.URI] = o
668                 }
669         }
670         return overlays, nil
671 }
672
673 func (s *Session) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
674         if overlay := s.readOverlay(uri); overlay != nil {
675                 return overlay, nil
676         }
677         // Fall back to the cache-level file system.
678         return s.cache.getFile(ctx, uri)
679 }
680
681 func (s *Session) readOverlay(uri span.URI) *overlay {
682         s.overlayMu.Lock()
683         defer s.overlayMu.Unlock()
684
685         if overlay, ok := s.overlays[uri]; ok {
686                 return overlay
687         }
688         return nil
689 }
690
691 func (s *Session) Overlays() []source.Overlay {
692         s.overlayMu.Lock()
693         defer s.overlayMu.Unlock()
694
695         overlays := make([]source.Overlay, 0, len(s.overlays))
696         for _, overlay := range s.overlays {
697                 overlays = append(overlays, overlay)
698         }
699         return overlays
700 }
701
702 func (s *Session) FileWatchingGlobPatterns(ctx context.Context) map[string]struct{} {
703         patterns := map[string]struct{}{}
704         for _, view := range s.views {
705                 snapshot, release := view.getSnapshot(ctx)
706                 for k, v := range snapshot.fileWatchingGlobPatterns(ctx) {
707                         patterns[k] = v
708                 }
709                 release()
710         }
711         return patterns
712 }