internal/lsp: add an option to get notified of bug reports

Our mechanism for reporting internal bugs doesn't work unless we
actually notice them. Add an undocumented option to receive showMessage
dialogs on the first bug occurring server-side.

Change-Id: I259a4c13161271c350fae06dc6ab0e1621725c92
Reviewed-on: https://go-review.googlesource.com/c/tools/+/399624
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/gopls/internal/regtest/debug/debug_test.go b/gopls/internal/regtest/debug/debug_test.go
new file mode 100644
index 0000000..d60b3f7
--- /dev/null
+++ b/gopls/internal/regtest/debug/debug_test.go
@@ -0,0 +1,34 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package debug
+
+import (
+	"testing"
+
+	"golang.org/x/tools/gopls/internal/hooks"
+	"golang.org/x/tools/internal/lsp/bug"
+	. "golang.org/x/tools/internal/lsp/regtest"
+)
+
+func TestMain(m *testing.M) {
+	Main(m, hooks.Options)
+}
+
+func TestBugNotification(t *testing.T) {
+	// Verify that a properly configured session gets notified of a bug on the
+	// server.
+	WithOptions(
+		Modes(Singleton), // must be in-process to receive the bug report below
+		EditorConfig{
+			Settings: map[string]interface{}{
+				"showBugReports": true,
+			},
+		},
+	).Run(t, "", func(t *testing.T, env *Env) {
+		const desc = "got a bug"
+		bug.Report(desc, nil)
+		env.Await(ShownMessage(desc))
+	})
+}
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index aeb6c5b..ab74778 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -9,6 +9,7 @@
 	"context"
 	"encoding/json"
 	"fmt"
+	"log"
 	"os"
 	"path"
 	"path/filepath"
@@ -16,6 +17,7 @@
 
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/jsonrpc2"
+	"golang.org/x/tools/internal/lsp/bug"
 	"golang.org/x/tools/internal/lsp/debug"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
@@ -55,6 +57,21 @@
 	}
 	options.ForClientCapabilities(params.Capabilities)
 
+	if options.ShowBugReports {
+		// Report the next bug that occurs on the server.
+		bugCh := bug.Notify()
+		go func() {
+			b := <-bugCh
+			msg := &protocol.ShowMessageParams{
+				Type:    protocol.Error,
+				Message: fmt.Sprintf("A bug occurred on the server: %s\nLocation:%s", b.Description, b.Key),
+			}
+			if err := s.eventuallyShowMessage(context.Background(), msg); err != nil {
+				log.Printf("error showing bug: %v", err)
+			}
+		}()
+	}
+
 	folders := params.WorkspaceFolders
 	if len(folders) == 0 {
 		if params.RootURI != "" {
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index de82cb2..d1d34ef 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -541,11 +541,17 @@
 	// }
 	// ```
 	//
-	// At the location of the `<>` in this program, deep completion would suggest the result `x.str`.
+	// At the location of the `<>` in this program, deep completion would suggest
+	// the result `x.str`.
 	DeepCompletion bool
 
 	// TempModfile controls the use of the -modfile flag in Go 1.14.
 	TempModfile bool
+
+	// ShowBugReports causes a message to be shown when the first bug is reported
+	// on the server.
+	// This option applies only during initialization.
+	ShowBugReports bool
 }
 
 type ImportShortcut string
@@ -953,6 +959,9 @@
 	case "tempModfile":
 		result.setBool(&o.TempModfile)
 
+	case "showBugReports":
+		result.setBool(&o.ShowBugReports)
+
 	case "gofumpt":
 		result.setBool(&o.Gofumpt)