@@ -49,6 +49,9 @@ type snapshot struct {
49
49
// workspacePackages contains the workspace's packages, which are loaded
50
50
// when the view is created.
51
51
workspacePackages map [packageID ]packagePath
52
+
53
+ // unloadableFiles keeps track of files that we've failed to load.
54
+ unloadableFiles map [span.URI ]struct {}
52
55
}
53
56
54
57
type packageKey struct {
@@ -425,12 +428,17 @@ func (s *snapshot) addActionHandle(ah *actionHandle) {
425
428
s .actions [key ] = ah
426
429
}
427
430
428
- func (s * snapshot ) getMetadataForURI (uri span.URI ) (metadata []* metadata ) {
429
- // TODO(matloob): uri can be a file or directory. Should we update the mappings
430
- // to map directories to their contained packages?
431
+ func (s * snapshot ) getMetadataForURI (uri span.URI ) []* metadata {
431
432
s .mu .Lock ()
432
433
defer s .mu .Unlock ()
433
434
435
+ return s .getMetadataForURILocked (uri )
436
+ }
437
+
438
+ func (s * snapshot ) getMetadataForURILocked (uri span.URI ) (metadata []* metadata ) {
439
+ // TODO(matloob): uri can be a file or directory. Should we update the mappings
440
+ // to map directories to their contained packages?
441
+
434
442
for _ , id := range s .ids [uri ] {
435
443
if m , ok := s .metadata [id ]; ok {
436
444
metadata = append (metadata , m )
@@ -489,13 +497,6 @@ func (s *snapshot) isWorkspacePackage(id packageID) (packagePath, bool) {
489
497
return scope , ok
490
498
}
491
499
492
- func (s * snapshot ) setWorkspacePackage (id packageID , pkgPath packagePath ) {
493
- s .mu .Lock ()
494
- defer s .mu .Unlock ()
495
-
496
- s .workspacePackages [id ] = pkgPath
497
- }
498
-
499
500
func (s * snapshot ) getFileURIs () []span.URI {
500
501
s .mu .Lock ()
501
502
defer s .mu .Unlock ()
@@ -545,32 +546,115 @@ func (s *snapshot) awaitLoaded(ctx context.Context) error {
545
546
546
547
// reloadWorkspace reloads the metadata for all invalidated workspace packages.
547
548
func (s * snapshot ) reloadWorkspace (ctx context.Context ) error {
548
- scope := s .workspaceScope (ctx )
549
- if scope == nil {
550
- return nil
549
+ // See which of the workspace packages are missing metadata.
550
+ s .mu .Lock ()
551
+ var pkgPaths []interface {}
552
+ for id , pkgPath := range s .workspacePackages {
553
+ if s .metadata [id ] == nil {
554
+ pkgPaths = append (pkgPaths , pkgPath )
555
+ }
556
+ }
557
+ s .mu .Unlock ()
558
+
559
+ if len (pkgPaths ) > 0 {
560
+ if m , err := s .load (ctx , pkgPaths ... ); err == nil {
561
+ for _ , m := range m {
562
+ s .setWorkspacePackage (ctx , m )
563
+ }
564
+ }
565
+ }
566
+
567
+ // When we load ./... or a package path directly, we may not get packages
568
+ // that exist only in overlays. As a workaround, we search all of the files
569
+ // available in the snapshot and reload their metadata individually using a
570
+ // file= query if the metadata is unavailable.
571
+ if scopes := s .orphanedFileScopes (); len (scopes ) > 0 {
572
+ m , err := s .load (ctx , scopes ... )
573
+
574
+ // If we failed to load some files, i.e. they have no metadata,
575
+ // mark the failures so we don't bother retrying until the file's
576
+ // content changes.
577
+ //
578
+ // TODO(rstambler): This may be an overestimate if the load stopped
579
+ // early for an unrelated errors. Add a fallback?
580
+ //
581
+ // Check for context cancellation so that we don't incorrectly mark files
582
+ // as unloadable, but don't return before setting all workspace packages.
583
+ if ctx .Err () == nil && err != nil {
584
+ s .mu .Lock ()
585
+ for _ , scope := range scopes {
586
+ uri := span .URI (scope .(fileURI ))
587
+ if s .getMetadataForURILocked (uri ) == nil {
588
+ s .unloadableFiles [uri ] = struct {}{}
589
+ }
590
+ }
591
+ s .mu .Unlock ()
592
+ }
593
+ for _ , m := range m {
594
+ // If a package's files belong to this view, it is a workspace package
595
+ // and should be added to the set of workspace packages.
596
+ for _ , uri := range m .compiledGoFiles {
597
+ if ! contains (s .view .session .viewsOf (uri ), s .view ) {
598
+ continue
599
+ }
600
+ s .setWorkspacePackage (ctx , m )
601
+ }
602
+ }
551
603
}
552
- _ , err := s .load (ctx , scope )
553
- return err
604
+ // Create package handles for all of the workspace packages.
605
+ for _ , id := range s .workspacePackageIDs () {
606
+ if _ , err := s .packageHandle (ctx , id ); err != nil {
607
+ return err
608
+ }
609
+ }
610
+ return nil
554
611
}
555
612
556
- func (s * snapshot ) workspaceScope ( ctx context. Context ) interface {} {
613
+ func (s * snapshot ) orphanedFileScopes () [] interface {} {
557
614
s .mu .Lock ()
558
615
defer s .mu .Unlock ()
559
616
560
- var pkgPaths []packagePath
561
- for id , pkgPath := range s .workspacePackages {
562
- if s .metadata [id ] == nil {
563
- pkgPaths = append (pkgPaths , pkgPath )
617
+ scopeSet := make (map [span.URI ]struct {})
618
+ for uri , fh := range s .files {
619
+ // Don't try to reload metadata for go.mod files.
620
+ if fh .Identity ().Kind != source .Go {
621
+ continue
622
+ }
623
+ // Don't reload metadata for files we've already deemed unloadable.
624
+ if _ , ok := s .unloadableFiles [uri ]; ok {
625
+ continue
626
+ }
627
+ if s .getMetadataForURILocked (uri ) == nil {
628
+ scopeSet [uri ] = struct {}{}
564
629
}
565
630
}
566
- switch len (pkgPaths ) {
567
- case 0 :
568
- return nil
569
- case len (s .workspacePackages ):
570
- return directoryURI (s .view .folder )
571
- default :
572
- return pkgPaths
631
+ var scopes []interface {}
632
+ for uri := range scopeSet {
633
+ scopes = append (scopes , fileURI (uri ))
573
634
}
635
+ return scopes
636
+ }
637
+
638
+ func contains (views []* view , view * view ) bool {
639
+ for _ , v := range views {
640
+ if v == view {
641
+ return true
642
+ }
643
+ }
644
+ return false
645
+ }
646
+
647
+ func (s * snapshot ) setWorkspacePackage (ctx context.Context , m * metadata ) {
648
+ s .mu .Lock ()
649
+ defer s .mu .Unlock ()
650
+
651
+ // A test variant of a package can only be loaded directly by loading
652
+ // the non-test variant with -test. Track the import path of the non-test variant.
653
+ pkgPath := m .pkgPath
654
+ if m .forTest != "" {
655
+ pkgPath = m .forTest
656
+ }
657
+ s .workspacePackages [m .id ] = pkgPath
574
658
}
575
659
576
660
func (s * snapshot ) clone (ctx context.Context , withoutURIs []span.URI ) * snapshot {
@@ -587,12 +671,17 @@ func (s *snapshot) clone(ctx context.Context, withoutURIs []span.URI) *snapshot
587
671
actions : make (map [actionKey ]* actionHandle ),
588
672
files : make (map [span.URI ]source.FileHandle ),
589
673
workspacePackages : make (map [packageID ]packagePath ),
674
+ unloadableFiles : make (map [span.URI ]struct {}),
590
675
}
591
676
592
677
// Copy all of the FileHandles.
593
678
for k , v := range s .files {
594
679
result .files [k ] = v
595
680
}
681
+ // Copy the set of unloadable files.
682
+ for k , v := range s .unloadableFiles {
683
+ result .unloadableFiles [k ] = v
684
+ }
596
685
597
686
// transitiveIDs keeps track of transitive reverse dependencies.
598
687
// If an ID is present in the map, invalidate its types.
@@ -664,6 +753,8 @@ func (s *snapshot) clone(ctx context.Context, withoutURIs []span.URI) *snapshot
664
753
} else {
665
754
result .files [withoutURI ] = currentFH
666
755
}
756
+ // Make sure to remove the changed file from the unloadable set.
757
+ delete (result .unloadableFiles , withoutURI )
667
758
}
668
759
669
760
// Collect the IDs for the packages associated with the excluded URIs.
0 commit comments