internal/lsp: cancel the initial workspace load when view shuts down

The error messages from view cancellation clutter up the logs when
testing, especially if you're running a single subtest.

A few quick staticcheck fixes in the CL also.

Change-Id: Ia1ed5360ac754023c589ed526ec0ed3e94a79b2f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/237637
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 34448af..44636bc 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -159,7 +159,9 @@
 	}
 
 	// Initialize the view without blocking.
-	go v.initialize(xcontext.Detach(ctx), v.snapshot)
+	initCtx, initCancel := context.WithCancel(xcontext.Detach(ctx))
+	v.initCancel = initCancel
+	go v.initialize(initCtx, v.snapshot)
 	return v, v.snapshot, nil
 }
 
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index d4a59db..a5bf043 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -87,6 +87,7 @@
 	// If we failed to load, we don't re-try to avoid too many go/packages calls.
 	initializeOnce sync.Once
 	initialized    chan struct{}
+	initCancel     context.CancelFunc
 
 	// initializedErr needs no mutex, since any access to it happens after it
 	// has been set.
@@ -525,7 +526,9 @@
 }
 
 func (v *View) shutdown(ctx context.Context) {
-	// TODO: Cancel the view's initialization.
+	// Cancel the initial workspace load if it is still running.
+	v.initCancel()
+
 	v.mu.Lock()
 	defer v.mu.Unlock()
 	if v.cancel != nil {
@@ -561,6 +564,9 @@
 		defer close(v.initialized)
 
 		if err := s.load(ctx, viewLoadScope("LOAD_VIEW"), packagePath("builtin")); err != nil {
+			if ctx.Err() != nil {
+				return
+			}
 			v.initializedErr = err
 			event.Error(ctx, "initial workspace load failed", err)
 		}
diff --git a/internal/lsp/cmd/capabilities_test.go b/internal/lsp/cmd/capabilities_test.go
index aebed67..7b6d3bd 100644
--- a/internal/lsp/cmd/capabilities_test.go
+++ b/internal/lsp/cmd/capabilities_test.go
@@ -143,6 +143,12 @@
 			t.Errorf("textEdit is not a *protocol.TextEdit, instead it is %T", textEdit)
 		}
 	}
+	if err := c.Server.Shutdown(ctx); err != nil {
+		t.Fatal(err)
+	}
+	if err := c.Server.Exit(ctx); err != nil {
+		t.Fatal(err)
+	}
 }
 
 func validateCapabilities(result *protocol.InitializeResult) error {
diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go
index f8e72f0..023729d 100644
--- a/internal/lsp/cmd/cmd.go
+++ b/internal/lsp/cmd/cmd.go
@@ -496,3 +496,8 @@
 	//TODO: right now calling exit terminates the process, we should rethink that
 	//server.Exit(ctx)
 }
+
+// Implement io.Closer.
+func (c *cmdClient) Close() error {
+	return nil
+}
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index 89dff8d..c2051ce 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -308,10 +308,12 @@
 func (s *Server) exit(ctx context.Context) error {
 	s.stateMu.Lock()
 	defer s.stateMu.Unlock()
-	//TODO: would be nice to have a better way to find the conn close method
+
+	// TODO: We need a better way to find the conn close method.
 	s.client.(io.Closer).Close()
+
 	if s.state != serverShutDown {
-		//TODO: we shoud be able to do better than this
+		// TODO: We should be able to do better than this.
 		os.Exit(1)
 	}
 	// we don't terminate the process on a normal exit, we just allow it to
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index d3be652..ff31a10 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -55,20 +55,21 @@
 		options := tests.DefaultOptions()
 		session.SetOptions(options)
 		options.Env = datum.Config.Env
-		v, snapshot, err := session.NewView(ctx, datum.Config.Dir, span.URIFromPath(datum.Config.Dir), options)
+		view, snapshot, err := session.NewView(ctx, datum.Config.Dir, span.URIFromPath(datum.Config.Dir), options)
 		if err != nil {
 			t.Fatal(err)
 		}
+		defer view.Shutdown(ctx)
 
 		// Enable type error analyses for tests.
 		// TODO(golang/go#38212): Delete this once they are enabled by default.
 		tests.EnableAllAnalyzers(snapshot, &options)
-		v.SetOptions(ctx, options)
+		view.SetOptions(ctx, options)
 
 		// Check to see if the -modfile flag is available, this is basically a check
 		// to see if the go version >= 1.14. Otherwise, the modfile specific tests
 		// will always fail if this flag is not available.
-		for _, flag := range v.Snapshot().Config(ctx).BuildFlags {
+		for _, flag := range view.Snapshot().Config(ctx).BuildFlags {
 			if strings.Contains(flag, "-modfile=") {
 				datum.ModfileFlagAvailable = true
 				break
@@ -385,9 +386,8 @@
 		},
 		Range: rng,
 	})
-
-	if len(actions) == 0 {
-		return
+	if err != nil {
+		t.Fatal(err)
 	}
 	for _, action := range actions {
 		// There may be more code actions available at spn (Span),
@@ -409,7 +409,7 @@
 		}
 		return
 	}
-	t.Fatalf("expected code action: %v but none", title)
+	t.Fatalf("expected code action %q, but got none", title)
 }
 
 func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string) {
diff --git a/internal/lsp/source/format.go b/internal/lsp/source/format.go
index 9ff9b3b..183fd8e 100644
--- a/internal/lsp/source/format.go
+++ b/internal/lsp/source/format.go
@@ -56,7 +56,7 @@
 }
 
 func formatSource(ctx context.Context, fh FileHandle) ([]byte, error) {
-	ctx, done := event.Start(ctx, "source.formatSource")
+	_, done := event.Start(ctx, "source.formatSource")
 	defer done()
 
 	data, err := fh.Read()
@@ -212,7 +212,7 @@
 }
 
 func computeTextEdits(ctx context.Context, view View, fh FileHandle, m *protocol.ColumnMapper, formatted string) ([]protocol.TextEdit, error) {
-	ctx, done := event.Start(ctx, "source.computeTextEdits")
+	_, done := event.Start(ctx, "source.computeTextEdits")
 	defer done()
 
 	data, err := fh.Read()
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index af702f1..93b4114 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -56,6 +56,8 @@
 		if err != nil {
 			t.Fatal(err)
 		}
+		defer view.Shutdown(ctx)
+
 		// Enable type error analyses for tests.
 		// TODO(golang/go#38212): Delete this once they are enabled by default.
 		tests.EnableAllAnalyzers(snapshot, &options)