internal/lsp/cmd: change vulncheck to directly call the hook

Instead of invoking the command through the LSP custom command,
call the vulncheck command hook directly. That reduces the extra
overhead of bringing up the full gopls server & package loading.
The vulncheck hook loads packages again any way, so the benefit
of running the check inside gopls's custom command framework
is not huge any more.

Still `gopls vulncheck` is useful - editors don't need to install
another binary for vulncheck feature, and it will output the
result in the format easier to handle than what `govulncheck`
currently offers.

Updates golang/go#50577

Change-Id: Ia21e6d7e0c37c4a1b02dc8bbca860143524c3d1b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/404574
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/internal/lsp/cmd/usage/vulncheck.hlp b/internal/lsp/cmd/usage/vulncheck.hlp
index 4bfdc4b..99fe13c 100644
--- a/internal/lsp/cmd/usage/vulncheck.hlp
+++ b/internal/lsp/cmd/usage/vulncheck.hlp
@@ -5,5 +5,9 @@
 
 	WARNING: this command is experimental.
 
+	By default, the command outputs a JSON-encoded
+	golang.org/x/tools/internal/lsp/command.VulncheckResult
+	message.
+
 	Example:
 	$ gopls vulncheck <packages>
diff --git a/internal/lsp/cmd/vulncheck.go b/internal/lsp/cmd/vulncheck.go
index adf59ce..8173778 100644
--- a/internal/lsp/cmd/vulncheck.go
+++ b/internal/lsp/cmd/vulncheck.go
@@ -11,8 +11,10 @@
 	"fmt"
 	"os"
 
+	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/internal/lsp/command"
 	"golang.org/x/tools/internal/lsp/protocol"
+	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/tool"
 )
 
@@ -31,6 +33,10 @@
 	fmt.Fprint(f.Output(), `
 	WARNING: this command is experimental.
 
+	By default, the command outputs a JSON-encoded
+	golang.org/x/tools/internal/lsp/command.VulncheckResult
+	message.
+
 	Example:
 	$ gopls vulncheck <packages>
 `)
@@ -46,34 +52,32 @@
 		pattern = args[0]
 	}
 
-	conn, err := v.app.connect(ctx)
-	if err != nil {
-		return err
-	}
-	defer conn.terminate(ctx)
-
 	cwd, err := os.Getwd()
 	if err != nil {
-		return err
+		return tool.CommandLineErrorf("failed to get current directory: %v", err)
 	}
 
-	cmd, err := command.NewRunVulncheckExpCommand("", command.VulncheckArgs{
+	opts := source.DefaultOptions().Clone()
+	v.app.options(opts) // register hook
+	if opts == nil || opts.Hooks.Govulncheck == nil {
+		return tool.CommandLineErrorf("vulncheck feature is not available")
+	}
+
+	loadCfg := &packages.Config{
+		Context: ctx,
+	}
+
+	res, err := opts.Hooks.Govulncheck(ctx, loadCfg, command.VulncheckArgs{
 		Dir:     protocol.URIFromPath(cwd),
 		Pattern: pattern,
 	})
 	if err != nil {
 		return err
 	}
-
-	params := &protocol.ExecuteCommandParams{Command: cmd.Command, Arguments: cmd.Arguments}
-	res, err := conn.ExecuteCommand(ctx, params)
-	if err != nil {
-		return fmt.Errorf("executing server command: %v", err)
-	}
 	data, err := json.MarshalIndent(res, " ", " ")
 	if err != nil {
 		return fmt.Errorf("failed to decode results: %v", err)
 	}
-	fmt.Printf("%s\n", data)
+	fmt.Printf("%s", data)
 	return nil
 }