internal/lsp: add initial workspace load notification

Add workspace package load (IWL) notification.

Closes golang/go#40632

Change-Id: I4d4c6fba1ece08bb0d6df52da2e6f08c959fd1ae
Reviewed-on: https://go-review.googlesource.com/c/tools/+/248623
Run-TryBot: Danish Dua <danishdua@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index c8c4ea8..3500bff 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -488,7 +488,7 @@
 func (s *snapshot) CachedImportPaths(ctx context.Context) (map[string]source.Package, error) {
 	// Don't reload workspace package metadata.
 	// This function is meant to only return currently cached information.
-	s.view.awaitInitialized(ctx)
+	s.view.AwaitInitialized(ctx)
 
 	s.mu.Lock()
 	defer s.mu.Unlock()
@@ -667,7 +667,7 @@
 
 func (s *snapshot) awaitLoaded(ctx context.Context) error {
 	// Do not return results until the snapshot's view has been initialized.
-	s.view.awaitInitialized(ctx)
+	s.view.AwaitInitialized(ctx)
 
 	if err := s.reloadWorkspace(ctx); err != nil {
 		return err
@@ -1161,7 +1161,7 @@
 }
 
 func (s *snapshot) BuiltinPackage(ctx context.Context) (*source.BuiltinPackage, error) {
-	s.view.awaitInitialized(ctx)
+	s.view.AwaitInitialized(ctx)
 
 	if s.builtin == nil {
 		return nil, errors.Errorf("no builtin package for view %s", s.view.name)
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index cbb9d1a..48b3148 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -654,7 +654,8 @@
 	})
 }
 
-func (v *View) awaitInitialized(ctx context.Context) {
+// AwaitInitialized waits until a view is initialized
+func (v *View) AwaitInitialized(ctx context.Context) {
 	select {
 	case <-ctx.Done():
 		return
@@ -677,7 +678,7 @@
 	v.cancelBackground()
 
 	// Do not clone a snapshot until its view has finished initializing.
-	v.awaitInitialized(ctx)
+	v.AwaitInitialized(ctx)
 
 	// This should be the only time we hold the view's snapshot lock for any period of time.
 	v.snapshotMu.Lock()
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index 6661eb1..d499d8f 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -197,11 +197,18 @@
 	dirsToWatch := map[span.URI]struct{}{}
 	for _, folder := range folders {
 		uri := span.URIFromURI(folder.URI)
+		work := s.progress.start(ctx, "Setting up workspace", "Loading packages...", nil, nil)
 		view, snapshot, release, err := s.addView(ctx, folder.Name, uri)
 		if err != nil {
 			viewErrors[uri] = err
+			work.end(ctx, fmt.Sprintf("Error loading packages: %s", err))
 			continue
 		}
+		go func() {
+			view.AwaitInitialized(ctx)
+			work.end(ctx, "Finished loading packages.")
+		}()
+
 		for _, dir := range snapshot.WorkspaceDirectories(ctx) {
 			dirsToWatch[dir] = struct{}{}
 		}
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index 6f84d4b..8515220 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -141,6 +141,9 @@
 	// Shutdown closes this view, and detaches it from its session.
 	Shutdown(ctx context.Context)
 
+	// AwaitInitialized waits until a view is initialized
+	AwaitInitialized(ctx context.Context)
+
 	// WriteEnv writes the view-specific environment to the io.Writer.
 	WriteEnv(ctx context.Context, w io.Writer) error