gopls/internal/server: consolidate showMessageRequest in prompt.go

For golang/go#75447

Change-Id: I784c6c885d1ddc88cfcf6056e85218ce19047ba7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/726560
Auto-Submit: Ethan Lee <ethanalee@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Hongxiang Jiang <hxjiang@golang.org>
diff --git a/gopls/internal/server/prompt.go b/gopls/internal/server/prompt.go
index b0392b2..9c74daf 100644
--- a/gopls/internal/server/prompt.go
+++ b/gopls/internal/server/prompt.go
@@ -304,20 +304,9 @@
 `
 	}
 	// TODO(rfindley): investigate a "tell me more" action in combination with ShowDocument.
-	params := &protocol.ShowMessageRequestParams{
-		Type:    protocol.Info,
-		Message: prompt,
-		Actions: []protocol.MessageActionItem{
-			{Title: TelemetryYes},
-			{Title: TelemetryNo},
-		},
-	}
-
-	item, err := s.client.ShowMessageRequest(ctx, params)
+	action, err := showMessageRequest(ctx, s.client, protocol.Info, prompt, TelemetryYes, TelemetryNo)
 	if err != nil {
 		errorf("ShowMessageRequest failed: %v", err)
-		// Defensive: ensure item == nil for the logic below.
-		item = nil
 	}
 
 	message := func(typ protocol.MessageType, msg string) {
@@ -328,12 +317,12 @@
 	}
 
 	result := pFailed
-	if item == nil {
+	if action == "" {
 		// e.g. dialog was dismissed
 		errorf("no response")
 	} else {
 		// Response matches MessageActionItem.Title.
-		switch item.Title {
+		switch action {
 		case TelemetryYes:
 			result = pYes
 			if err := s.setTelemetryMode("on"); err == nil {
@@ -347,8 +336,8 @@
 		case TelemetryNo:
 			result = pNo
 		default:
-			errorf("unrecognized response %q", item.Title)
-			message(protocol.Error, fmt.Sprintf("Unrecognized response %q", item.Title))
+			errorf("unrecognized response %q", action)
+			message(protocol.Error, fmt.Sprintf("Unrecognized response %q", action))
 		}
 	}
 	resultContent := fmt.Appendf(nil, "%s %d %d %d", result, attempts, creationTime, token)
@@ -357,6 +346,37 @@
 	}
 }
 
+// showMessageRequest causes the client to show a prompt that the user can respond to.
+// It returns the title of the action the user selected, or "" if the user dismissed the prompt.
+func showMessageRequest(ctx context.Context, cli protocol.Client, typ protocol.MessageType, message string, actions ...string) (string, error) {
+	var actionItems []protocol.MessageActionItem
+	for _, action := range actions {
+		actionItems = append(actionItems, protocol.MessageActionItem{Title: action})
+	}
+	params := &protocol.ShowMessageRequestParams{
+		Type:    typ,
+		Message: message,
+		Actions: actionItems,
+	}
+	// Timeout used to wait for the user to respond to a message request.
+	const timeout = 15 * time.Second
+	ctx, cancel := context.WithTimeout(ctx, timeout)
+	defer cancel()
+
+	result, err := cli.ShowMessageRequest(ctx, params)
+
+	if err != nil {
+		if cause := context.Cause(ctx); cause == context.DeadlineExceeded || cause == context.Canceled {
+			return "", nil
+		}
+		return "", err
+	}
+	if result == nil {
+		return "", nil // User dismissed the notification
+	}
+	return result.Title, nil
+}
+
 func telemetryOnMessage(linkify bool) string {
 	format := `Thank you. Telemetry uploading is now enabled.
 
diff --git a/gopls/internal/server/vulncheck_prompt.go b/gopls/internal/server/vulncheck_prompt.go
index 8074f97..cd5e6b6 100644
--- a/gopls/internal/server/vulncheck_prompt.go
+++ b/gopls/internal/server/vulncheck_prompt.go
@@ -8,10 +8,8 @@
 	"context"
 	"crypto/sha256"
 	"encoding/hex"
-	"errors"
 	"fmt"
 	"os"
-	"time"
 
 	"golang.org/x/mod/modfile"
 	"golang.org/x/tools/gopls/internal/filecache"
@@ -48,36 +46,6 @@
 	return hex.EncodeToString(h.Sum(nil)), nil
 }
 
-// showMessageRequest causes the client to show a prompt that the user can respond to.
-func showMessageRequest(ctx context.Context, cli protocol.Client, typ protocol.MessageType, message string, actions ...string) (string, error) {
-	var actionItems []protocol.MessageActionItem
-	for _, action := range actions {
-		actionItems = append(actionItems, protocol.MessageActionItem{Title: action})
-	}
-	params := &protocol.ShowMessageRequestParams{
-		Type:    typ,
-		Message: message,
-		Actions: actionItems,
-	}
-	// Timeout used to wait for the user to respond to a message request.
-	const timeout = 15 * time.Second
-	ctx, cancel := context.WithTimeout(ctx, timeout)
-	defer cancel()
-
-	result, err := cli.ShowMessageRequest(ctx, params)
-
-	if err != nil {
-		if errors.Is(err, context.DeadlineExceeded) {
-			return "", nil
-		}
-		return "", err
-	}
-	if result == nil {
-		return "", nil // User dismissed the notification
-	}
-	return result.Title, nil
-}
-
 func (s *server) checkGoModDeps(ctx context.Context, uri protocol.DocumentURI) {
 	if s.Options().Vulncheck != settings.ModeVulncheckPrompt {
 		return