internal/lsp: clear diagnostics when all files from a package are closed

This change clears diagnostics for a package when all files from that
package have been closed in the editor. This prevents the user from
seeing irrelevant diagnostics.

Change-Id: I63d337805835dc45086cfba684d810f86ade5655
Reviewed-on: https://go-review.googlesource.com/c/tools/+/182463
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/internal/lsp/text_synchronization.go b/internal/lsp/text_synchronization.go
index fbbd82d..b0ffc3f 100644
--- a/internal/lsp/text_synchronization.go
+++ b/internal/lsp/text_synchronization.go
@@ -10,6 +10,7 @@
 
 	"golang.org/x/tools/internal/jsonrpc2"
 	"golang.org/x/tools/internal/lsp/protocol"
+	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/span"
 )
 
@@ -104,5 +105,47 @@
 	uri := span.NewURI(params.TextDocument.URI)
 	s.session.DidClose(uri)
 	view := s.session.ViewOf(uri)
-	return view.SetContent(ctx, uri, nil)
+	if err := view.SetContent(ctx, uri, nil); err != nil {
+		return err
+	}
+	// If the current file was the only open file for its package,
+	// clear out all diagnostics for the package.
+	f, err := view.GetFile(ctx, uri)
+	if err != nil {
+		s.session.Logger().Errorf(ctx, "no file for %s: %v", uri, err)
+		return nil
+	}
+	// For non-Go files, don't return any diagnostics.
+	gof, ok := f.(source.GoFile)
+	if !ok {
+		return nil
+	}
+	pkg := gof.GetPackage(ctx)
+	if pkg == nil {
+		s.session.Logger().Errorf(ctx, "no package available for %s", uri)
+		return nil
+	}
+	reports := make(map[span.URI][]source.Diagnostic)
+	clearDiagnostics := true
+	for _, filename := range pkg.GetFilenames() {
+		uri := span.NewURI(filename)
+		reports[uri] = []source.Diagnostic{}
+		if span.CompareURI(uri, gof.URI()) == 0 {
+			continue
+		}
+		// If other files from this package are open.
+		if s.session.IsOpen(uri) {
+			clearDiagnostics = false
+		}
+	}
+	// We still have open files for this package, so don't clear diagnostics.
+	if !clearDiagnostics {
+		return nil
+	}
+	for uri, diagnostics := range reports {
+		if err := s.publishDiagnostics(ctx, view, uri, diagnostics); err != nil {
+			s.session.Logger().Errorf(ctx, "failed to clear diagnostics for %s: %v", uri, err)
+		}
+	}
+	return nil
 }