gopls/internal/lsp: remove extra newlines in vulncheck diagnostics

Diagnostics are populated with vuln details that are already
formatted with newlines to limit line length. The client UI has
its own wrapping logic, so we can remove this to allow the client
to do its own formatting.

Change-Id: Ida0ce71886add995fad7815e6a302d4b44de65e8
Reviewed-on: https://go-review.googlesource.com/c/tools/+/436775
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
diff --git a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagnostics.go
index 2b15ba7..61cf594 100644
--- a/gopls/internal/lsp/mod/diagnostics.go
+++ b/gopls/internal/lsp/mod/diagnostics.go
@@ -7,6 +7,7 @@
 package mod
 
 import (
+	"bytes"
 	"context"
 	"fmt"
 
@@ -215,14 +216,13 @@
 			}
 
 			vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{
-				URI:      fh.URI(),
-				Range:    rng,
-				Severity: severity,
-				Source:   source.Vulncheck,
-				Code:     v.ID,
-				CodeHref: v.URL,
-				// TODO(suzmue): replace the newlines in v.Details to allow the editor to handle formatting.
-				Message:        fmt.Sprintf("%s has a known vulnerability: %s", v.ModPath, v.Details),
+				URI:            fh.URI(),
+				Range:          rng,
+				Severity:       severity,
+				Source:         source.Vulncheck,
+				Code:           v.ID,
+				CodeHref:       v.URL,
+				Message:        formatMessage(&v),
 				SuggestedFixes: fixes,
 			})
 		}
@@ -231,3 +231,14 @@
 
 	return vulnDiagnostics, nil
 }
+
+func formatMessage(v *command.Vuln) string {
+	details := []byte(v.Details)
+	// Remove any new lines that are not preceded or followed by a new line.
+	for i, r := range details {
+		if r == '\n' && i > 0 && details[i-1] != '\n' && i+1 < len(details) && details[i+1] != '\n' {
+			details[i] = ' '
+		}
+	}
+	return fmt.Sprintf("%s has a known vulnerability: %s", v.ModPath, string(bytes.TrimSpace(details)))
+}
diff --git a/gopls/internal/regtest/misc/testdata/vulndb/golang.org/bmod.json b/gopls/internal/regtest/misc/testdata/vulndb/golang.org/bmod.json
index b08b20d..5e69cd9 100644
--- a/gopls/internal/regtest/misc/testdata/vulndb/golang.org/bmod.json
+++ b/gopls/internal/regtest/misc/testdata/vulndb/golang.org/bmod.json
@@ -1,7 +1,7 @@
 [
 	{
 		"id":"GO-2022-99",
-		"details": "vuln in bmod",
+		"details": "vuln in bmod\n\nThis is a long description\nof this vulnerability.\n",
 		"affected":[
 			{
 				"package":{"name":"golang.org/bmod","ecosystem":"Go"},
diff --git a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/misc/vuln_test.go
index b0fb1f9..180431c 100644
--- a/gopls/internal/regtest/misc/vuln_test.go
+++ b/gopls/internal/regtest/misc/vuln_test.go
@@ -255,7 +255,7 @@
 			ShownMessage("Found"),
 			OnceMet(
 				env.DiagnosticAtRegexpWithMessage("go.mod", `golang.org/amod`, "golang.org/amod has a known vulnerability: vuln in amod"),
-				env.DiagnosticAtRegexpWithMessage("go.mod", `golang.org/bmod`, "golang.org/bmod has a known vulnerability: vuln in bmod"),
+				env.DiagnosticAtRegexpWithMessage("go.mod", `golang.org/bmod`, "golang.org/bmod has a known vulnerability: vuln in bmod\n\nThis is a long description of this vulnerability."),
 				ReadDiagnostics("go.mod", d),
 			),
 		)