From bd5d160bece4811f55c3db96cda99884a94ccec4 Mon Sep 17 00:00:00 2001 From: Heschi Kreinick Date: Tue, 10 Nov 2020 18:20:37 -0500 Subject: [PATCH] internal/lsp: add go get quick fix on failing imports With -mod=readonly set, we no longer automatically add new requires to go.mod, even the temporary one. We have the go mod tidy code lens, but that only works on saved files, even in 1.16 due to golang/go#42491. Plus we may remove the code lens's network access in the future. Add a simple quick fix for import errors that runs (the moral equivalent of) go get on the missing import. Change-Id: Id5764a37ce7db0dce5370da9d648462aefa2042b Reviewed-on: https://go-review.googlesource.com/c/tools/+/274121 Trust: Heschi Kreinick Run-TryBot: Heschi Kreinick gopls-CI: kokoro TryBot-Result: Go Bot Reviewed-by: Rebecca Stambler Reviewed-by: Robert Findley --- gopls/doc/commands.md | 6 ++++ gopls/internal/regtest/modfile_test.go | 48 ++++++++++++++++++++++++++ internal/lsp/cache/errors.go | 4 +-- internal/lsp/code_action.go | 43 +++++++++++++++++++++++ internal/lsp/command.go | 27 +++++++++++++++ internal/lsp/source/api_json.go | 2 +- internal/lsp/source/command.go | 7 ++++ 7 files changed, 134 insertions(+), 3 deletions(-) diff --git a/gopls/doc/commands.md b/gopls/doc/commands.md index a0ee8f66a4..f545551b72 100644 --- a/gopls/doc/commands.md +++ b/gopls/doc/commands.md @@ -47,6 +47,12 @@ undeclared_name adds a variable declaration for an undeclared name. +### **go get package** +Identifier: `gopls.go_get_package` + +go_get_package runs `go get` to fetch a package. + + ### **Add dependency** Identifier: `gopls.add_dependency` diff --git a/gopls/internal/regtest/modfile_test.go b/gopls/internal/regtest/modfile_test.go index 338296e78c..8a510ed546 100644 --- a/gopls/internal/regtest/modfile_test.go +++ b/gopls/internal/regtest/modfile_test.go @@ -110,6 +110,54 @@ func main() { }) } +func TestGoGetFix(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + const mod = ` +-- go.mod -- +module mod.com + +go 1.12 + +-- main.go -- +package main + +import "example.com/blah" + +var _ = blah.Name +` + + const want = `module mod.com + +go 1.12 + +require example.com v1.2.3 +` + + runModfileTest(t, mod, proxy, func(t *testing.T, env *Env) { + if strings.Contains(t.Name(), "workspace_module") { + t.Skip("workspace module mode doesn't set -mod=readonly") + } + env.OpenFile("main.go") + var d protocol.PublishDiagnosticsParams + env.Await( + OnceMet( + env.DiagnosticAtRegexp("main.go", `"example.com/blah"`), + ReadDiagnostics("main.go", &d), + ), + ) + var goGetDiag protocol.Diagnostic + for _, diag := range d.Diagnostics { + if strings.Contains(diag.Message, "could not import") { + goGetDiag = diag + } + } + env.ApplyQuickFixes("main.go", []protocol.Diagnostic{goGetDiag}) + if got := env.ReadWorkspaceFile("go.mod"); got != want { + t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(want, got)) + } + }) +} + // Tests that multiple missing dependencies gives good single fixes. func TestMissingDependencyFixes(t *testing.T) { testenv.NeedsGo1Point(t, 14) diff --git a/internal/lsp/cache/errors.go b/internal/lsp/cache/errors.go index 1f55a586b7..ffc236d26c 100644 --- a/internal/lsp/cache/errors.go +++ b/internal/lsp/cache/errors.go @@ -128,7 +128,7 @@ func sourceError(ctx context.Context, snapshot *snapshot, pkg *pkg, e interface{ msg = e.Message kind = source.Analysis category = e.Category - fixes, err = suggestedFixes(snapshot, pkg, e) + fixes, err = suggestedAnalysisFixes(snapshot, pkg, e) if err != nil { return nil, err } @@ -154,7 +154,7 @@ func sourceError(ctx context.Context, snapshot *snapshot, pkg *pkg, e interface{ }, nil } -func suggestedFixes(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic) ([]source.SuggestedFix, error) { +func suggestedAnalysisFixes(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic) ([]source.SuggestedFix, error) { var fixes []source.SuggestedFix for _, fix := range diag.SuggestedFixes { edits := make(map[span.URI][]protocol.TextEdit) diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go index 9987eab660..cfeaaa12a4 100644 --- a/internal/lsp/code_action.go +++ b/internal/lsp/code_action.go @@ -7,6 +7,7 @@ package lsp import ( "context" "fmt" + "regexp" "sort" "strings" @@ -103,6 +104,16 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara }) } } + + // Fix unresolved imports with "go get". This is separate from the + // goimports fixes because goimports will not remove an import + // that appears to be used, even if currently unresolved. + actions, err := goGetFixes(ctx, snapshot, fh.URI(), diagnostics) + if err != nil { + return nil, err + } + codeActions = append(codeActions, actions...) + // Send all of the import edits as one code action if the file is // being organized. if wanted[protocol.SourceOrganizeImports] && len(importEdits) > 0 { @@ -349,6 +360,38 @@ func diagnosticToAnalyzer(snapshot source.Snapshot, src, msg string) (analyzer * return nil } +var importErrorRe = regexp.MustCompile(`could not import ([^\s]+)`) + +func goGetFixes(ctx context.Context, snapshot source.Snapshot, uri span.URI, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error) { + if snapshot.GoModForFile(ctx, uri) == "" { + // Go get only supports module mode for now. + return nil, nil + } + + var actions []protocol.CodeAction + for _, diag := range diagnostics { + matches := importErrorRe.FindStringSubmatch(diag.Message) + if len(matches) == 0 { + return nil, nil + } + args, err := source.MarshalArgs(uri, matches[1]) + if err != nil { + return nil, err + } + actions = append(actions, protocol.CodeAction{ + Title: fmt.Sprintf("go get package %v", matches[1]), + Diagnostics: []protocol.Diagnostic{diag}, + Kind: protocol.QuickFix, + Command: &protocol.Command{ + Title: source.CommandGoGetPackage.Title, + Command: source.CommandGoGetPackage.ID(), + Arguments: args, + }, + }) + } + return actions, nil +} + func convenienceFixes(ctx context.Context, snapshot source.Snapshot, pkg source.Package, uri span.URI, rng protocol.Range) ([]protocol.CodeAction, error) { var analyzers []*analysis.Analyzer for _, a := range snapshot.View().Options().ConvenienceAnalyzers { diff --git a/internal/lsp/command.go b/internal/lsp/command.go index 6044d1305a..fddb71849a 100644 --- a/internal/lsp/command.go +++ b/internal/lsp/command.go @@ -12,6 +12,7 @@ import ( "io" "io/ioutil" "path/filepath" + "strings" "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/gocommand" @@ -228,6 +229,19 @@ func (s *Server) runCommand(ctx context.Context, work *workDone, command *source return err } return s.runGoGetModule(ctx, snapshot, uri.SpanURI(), addRequire, goCmdArgs) + case source.CommandGoGetPackage: + var uri protocol.DocumentURI + var pkg string + if err := source.UnmarshalArgs(args, &uri, &pkg); err != nil { + return err + } + snapshot, _, ok, release, err := s.beginFileRequest(ctx, uri, source.UnknownKind) + defer release() + if !ok { + return err + } + return s.runGoGetPackage(ctx, snapshot, uri.SpanURI(), pkg) + case source.CommandToggleDetails: var fileURI protocol.DocumentURI if err := source.UnmarshalArgs(args, &fileURI); err != nil { @@ -387,6 +401,19 @@ func (s *Server) runGoGenerate(ctx context.Context, snapshot source.Snapshot, di return nil } +func (s *Server) runGoGetPackage(ctx context.Context, snapshot source.Snapshot, uri span.URI, pkg string) error { + stdout, err := snapshot.RunGoCommandDirect(ctx, source.WriteTemporaryModFile|source.AllowNetwork, &gocommand.Invocation{ + Verb: "list", + Args: []string{"-f", "{{.Module.Path}}@{{.Module.Version}}", pkg}, + WorkingDir: filepath.Dir(uri.Filename()), + }) + if err != nil { + return err + } + ver := strings.TrimSpace(stdout.String()) + return s.runGoGetModule(ctx, snapshot, uri, true, []string{ver}) +} + func (s *Server) runGoGetModule(ctx context.Context, snapshot source.Snapshot, uri span.URI, addRequire bool, args []string) error { if addRequire { // Using go get to create a new dependency results in an diff --git a/internal/lsp/source/api_json.go b/internal/lsp/source/api_json.go index 7f4d164a04..6269bfd44b 100755 --- a/internal/lsp/source/api_json.go +++ b/internal/lsp/source/api_json.go @@ -2,4 +2,4 @@ package source -const GeneratedAPIJSON = "{\"Options\":{\"Debugging\":[{\"Name\":\"verboseOutput\",\"Type\":\"bool\",\"Doc\":\"verboseOutput enables additional debug logging.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"completionBudget\",\"Type\":\"time.Duration\",\"Doc\":\"completionBudget is the soft latency goal for completion requests. Most\\nrequests finish in a couple milliseconds, but in some cases deep\\ncompletions can take much longer. As we use up our budget we\\ndynamically reduce the search scope to ensure we return timely\\nresults. Zero means unlimited.\\n\",\"EnumValues\":null,\"Default\":\"\\\"100ms\\\"\"}],\"Experimental\":[{\"Name\":\"annotations\",\"Type\":\"map[string]bool\",\"Doc\":\"annotations suppress various kinds of optimization diagnostics\\nthat would be reported by the gc_details command.\\n * noNilcheck suppresses display of nilchecks.\\n * noEscape suppresses escape choices.\\n * noInline suppresses inlining choices.\\n * noBounds suppresses bounds checking diagnostics.\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"staticcheck\",\"Type\":\"bool\",\"Doc\":\"staticcheck enables additional analyses from staticcheck.io.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"semanticTokens\",\"Type\":\"bool\",\"Doc\":\"semanticTokens controls whether the LSP server will send\\nsemantic tokens to the client.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"expandWorkspaceToModule\",\"Type\":\"bool\",\"Doc\":\"expandWorkspaceToModule instructs `gopls` to adjust the scope of the\\nworkspace to find the best available module root. `gopls` first looks for\\na go.mod file in any parent directory of the workspace folder, expanding\\nthe scope to that directory if it exists. If no viable parent directory is\\nfound, gopls will check if there is exactly one child directory containing\\na go.mod file, narrowing the scope to that directory if it exists.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"experimentalWorkspaceModule\",\"Type\":\"bool\",\"Doc\":\"experimentalWorkspaceModule opts a user into the experimental support\\nfor multi-module workspaces.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"experimentalDiagnosticsDelay\",\"Type\":\"time.Duration\",\"Doc\":\"experimentalDiagnosticsDelay controls the amount of time that gopls waits\\nafter the most recent file modification before computing deep diagnostics.\\nSimple diagnostics (parsing and type-checking) are always run immediately\\non recently modified packages.\\n\\nThis option must be set to a valid duration string, for example `\\\"250ms\\\"`.\\n\",\"EnumValues\":null,\"Default\":\"\\\"0s\\\"\"},{\"Name\":\"experimentalPackageCacheKey\",\"Type\":\"bool\",\"Doc\":\"experimentalPackageCacheKey controls whether to use a coarser cache key\\nfor package type information to increase cache hits. This setting removes\\nthe user's environment, build flags, and working directory from the cache\\nkey, which should be a safe change as all relevant inputs into the type\\nchecking pass are already hashed into the key. This is temporarily guarded\\nby an experiment because caching behavior is subtle and difficult to\\ncomprehensively test.\\n\",\"EnumValues\":null,\"Default\":\"true\"}],\"User\":[{\"Name\":\"buildFlags\",\"Type\":\"[]string\",\"Doc\":\"buildFlags is the set of flags passed on to the build system when invoked.\\nIt is applied to queries like `go list`, which is used when discovering files.\\nThe most common use is to set `-tags`.\\n\",\"EnumValues\":null,\"Default\":\"[]\"},{\"Name\":\"env\",\"Type\":\"map[string]string\",\"Doc\":\"env adds environment variables to external commands run by `gopls`, most notably `go list`.\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"hoverKind\",\"Type\":\"enum\",\"Doc\":\"hoverKind controls the information that appears in the hover text.\\nSingleLine and Structured are intended for use only by authors of editor plugins.\\n\",\"EnumValues\":[{\"Value\":\"\\\"FullDocumentation\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"NoDocumentation\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"SingleLine\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Structured\\\"\",\"Doc\":\"`\\\"Structured\\\"` is an experimental setting that returns a structured hover format.\\nThis format separates the signature from the documentation, so that the client\\ncan do more manipulation of these fields.\\n\\nThis should only be used by clients that support this behavior.\\n\"},{\"Value\":\"\\\"SynopsisDocumentation\\\"\",\"Doc\":\"\"}],\"Default\":\"\\\"FullDocumentation\\\"\"},{\"Name\":\"usePlaceholders\",\"Type\":\"bool\",\"Doc\":\"placeholders enables placeholders for function parameters or struct fields in completion responses.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"linkTarget\",\"Type\":\"string\",\"Doc\":\"linkTarget controls where documentation links go.\\nIt might be one of:\\n\\n* `\\\"godoc.org\\\"`\\n* `\\\"pkg.go.dev\\\"`\\n\\nIf company chooses to use its own `godoc.org`, its address can be used as well.\\n\",\"EnumValues\":null,\"Default\":\"\\\"pkg.go.dev\\\"\"},{\"Name\":\"local\",\"Type\":\"string\",\"Doc\":\"local is the equivalent of the `goimports -local` flag, which puts imports beginning with this string after 3rd-party packages.\\nIt should be the prefix of the import path whose imports should be grouped separately.\\n\",\"EnumValues\":null,\"Default\":\"\\\"\\\"\"},{\"Name\":\"gofumpt\",\"Type\":\"bool\",\"Doc\":\"gofumpt indicates if we should run gofumpt formatting.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"analyses\",\"Type\":\"map[string]bool\",\"Doc\":\"analyses specify analyses that the user would like to enable or disable.\\nA map of the names of analysis passes that should be enabled/disabled.\\nA full list of analyzers that gopls uses can be found [here](analyzers.md)\\n\\nExample Usage:\\n```json5\\n...\\n\\\"analyses\\\": {\\n \\\"unreachable\\\": false, // Disable the unreachable analyzer.\\n \\\"unusedparams\\\": true // Enable the unusedparams analyzer.\\n}\\n...\\n```\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"codelenses\",\"Type\":\"map[string]bool\",\"Doc\":\"codelenses overrides the enabled/disabled state of code lenses. See the \\\"Code Lenses\\\"\\nsection of settings.md for the list of supported lenses.\\n\\nExample Usage:\\n```json5\\n\\\"gopls\\\": {\\n...\\n \\\"codelens\\\": {\\n \\\"generate\\\": false, // Don't show the `go generate` lens.\\n \\\"gc_details\\\": true // Show a code lens toggling the display of gc's choices.\\n }\\n...\\n}\\n```\\n\",\"EnumValues\":null,\"Default\":\"{\\\"gc_details\\\":false,\\\"generate\\\":true,\\\"regenerate_cgo\\\":true,\\\"tidy\\\":true,\\\"upgrade_dependency\\\":true,\\\"vendor\\\":true}\"},{\"Name\":\"linksInHover\",\"Type\":\"bool\",\"Doc\":\"linksInHover toggles the presence of links to documentation in hover.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"importShortcut\",\"Type\":\"enum\",\"Doc\":\"importShortcut specifies whether import statements should link to\\ndocumentation or go to definitions.\\n\",\"EnumValues\":[{\"Value\":\"\\\"Both\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Definition\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Link\\\"\",\"Doc\":\"\"}],\"Default\":\"\\\"Both\\\"\"},{\"Name\":\"matcher\",\"Type\":\"enum\",\"Doc\":\"matcher sets the algorithm that is used when calculating completion candidates.\\n\",\"EnumValues\":[{\"Value\":\"\\\"CaseInsensitive\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"CaseSensitive\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Fuzzy\\\"\",\"Doc\":\"\"}],\"Default\":\"\\\"Fuzzy\\\"\"},{\"Name\":\"symbolMatcher\",\"Type\":\"enum\",\"Doc\":\"symbolMatcher sets the algorithm that is used when finding workspace symbols.\\n\",\"EnumValues\":[{\"Value\":\"\\\"CaseInsensitive\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"CaseSensitive\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Fuzzy\\\"\",\"Doc\":\"\"}],\"Default\":\"\\\"Fuzzy\\\"\"},{\"Name\":\"symbolStyle\",\"Type\":\"enum\",\"Doc\":\"symbolStyle controls how symbols are qualified in symbol responses.\\n\\nExample Usage:\\n```json5\\n\\\"gopls\\\": {\\n...\\n \\\"symbolStyle\\\": \\\"dynamic\\\",\\n...\\n}\\n```\\n\",\"EnumValues\":[{\"Value\":\"\\\"Dynamic\\\"\",\"Doc\":\"`\\\"Dynamic\\\"` uses whichever qualifier results in the highest scoring\\nmatch for the given symbol query. Here a \\\"qualifier\\\" is any \\\"/\\\" or \\\".\\\"\\ndelimited suffix of the fully qualified symbol. i.e. \\\"to/pkg.Foo.Field\\\" or\\njust \\\"Foo.Field\\\".\\n\"},{\"Value\":\"\\\"Full\\\"\",\"Doc\":\"`\\\"Full\\\"` is fully qualified symbols, i.e.\\n\\\"path/to/pkg.Foo.Field\\\".\\n\"},{\"Value\":\"\\\"Package\\\"\",\"Doc\":\"`\\\"Package\\\"` is package qualified symbols i.e.\\n\\\"pkg.Foo.Field\\\".\\n\"}],\"Default\":\"\\\"Dynamic\\\"\"}]},\"Commands\":[{\"Command\":\"gopls.generate\",\"Title\":\"Run go generate\",\"Doc\":\"generate runs `go generate` for a given directory.\\n\"},{\"Command\":\"gopls.fill_struct\",\"Title\":\"Fill struct\",\"Doc\":\"fill_struct is a gopls command to fill a struct with default\\nvalues.\\n\"},{\"Command\":\"gopls.regenerate_cgo\",\"Title\":\"Regenerate cgo\",\"Doc\":\"regenerate_cgo regenerates cgo definitions.\\n\"},{\"Command\":\"gopls.test\",\"Title\":\"Run test(s)\",\"Doc\":\"test runs `go test` for a specific test function.\\n\"},{\"Command\":\"gopls.tidy\",\"Title\":\"Run go mod tidy\",\"Doc\":\"tidy runs `go mod tidy` for a module.\\n\"},{\"Command\":\"gopls.update_go_sum\",\"Title\":\"Update go.sum\",\"Doc\":\"update_go_sum updates the go.sum file for a module.\\n\"},{\"Command\":\"gopls.undeclared_name\",\"Title\":\"Undeclared name\",\"Doc\":\"undeclared_name adds a variable declaration for an undeclared\\nname.\\n\"},{\"Command\":\"gopls.add_dependency\",\"Title\":\"Add dependency\",\"Doc\":\"add_dependency adds a dependency.\\n\"},{\"Command\":\"gopls.upgrade_dependency\",\"Title\":\"Upgrade dependency\",\"Doc\":\"upgrade_dependency upgrades a dependency.\\n\"},{\"Command\":\"gopls.remove_dependency\",\"Title\":\"Remove dependency\",\"Doc\":\"remove_dependency removes a dependency.\\n\"},{\"Command\":\"gopls.vendor\",\"Title\":\"Run go mod vendor\",\"Doc\":\"vendor runs `go mod vendor` for a module.\\n\"},{\"Command\":\"gopls.extract_variable\",\"Title\":\"Extract to variable\",\"Doc\":\"extract_variable extracts an expression to a variable.\\n\"},{\"Command\":\"gopls.extract_function\",\"Title\":\"Extract to function\",\"Doc\":\"extract_function extracts statements to a function.\\n\"},{\"Command\":\"gopls.gc_details\",\"Title\":\"Toggle gc_details\",\"Doc\":\"gc_details controls calculation of gc annotations.\\n\"},{\"Command\":\"gopls.generate_gopls_mod\",\"Title\":\"Generate gopls.mod\",\"Doc\":\"generate_gopls_mod (re)generates the gopls.mod file.\\n\"}],\"Lenses\":[{\"Lens\":\"generate\",\"Title\":\"Run go generate\",\"Doc\":\"generate runs `go generate` for a given directory.\\n\"},{\"Lens\":\"regenerate_cgo\",\"Title\":\"Regenerate cgo\",\"Doc\":\"regenerate_cgo regenerates cgo definitions.\\n\"},{\"Lens\":\"test\",\"Title\":\"Run test(s)\",\"Doc\":\"test runs `go test` for a specific test function.\\n\"},{\"Lens\":\"tidy\",\"Title\":\"Run go mod tidy\",\"Doc\":\"tidy runs `go mod tidy` for a module.\\n\"},{\"Lens\":\"upgrade_dependency\",\"Title\":\"Upgrade dependency\",\"Doc\":\"upgrade_dependency upgrades a dependency.\\n\"},{\"Lens\":\"vendor\",\"Title\":\"Run go mod vendor\",\"Doc\":\"vendor runs `go mod vendor` for a module.\\n\"},{\"Lens\":\"gc_details\",\"Title\":\"Toggle gc_details\",\"Doc\":\"gc_details controls calculation of gc annotations.\\n\"}]}" +const GeneratedAPIJSON = "{\"Options\":{\"Debugging\":[{\"Name\":\"verboseOutput\",\"Type\":\"bool\",\"Doc\":\"verboseOutput enables additional debug logging.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"completionBudget\",\"Type\":\"time.Duration\",\"Doc\":\"completionBudget is the soft latency goal for completion requests. Most\\nrequests finish in a couple milliseconds, but in some cases deep\\ncompletions can take much longer. As we use up our budget we\\ndynamically reduce the search scope to ensure we return timely\\nresults. Zero means unlimited.\\n\",\"EnumValues\":null,\"Default\":\"\\\"100ms\\\"\"}],\"Experimental\":[{\"Name\":\"annotations\",\"Type\":\"map[string]bool\",\"Doc\":\"annotations suppress various kinds of optimization diagnostics\\nthat would be reported by the gc_details command.\\n * noNilcheck suppresses display of nilchecks.\\n * noEscape suppresses escape choices.\\n * noInline suppresses inlining choices.\\n * noBounds suppresses bounds checking diagnostics.\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"staticcheck\",\"Type\":\"bool\",\"Doc\":\"staticcheck enables additional analyses from staticcheck.io.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"semanticTokens\",\"Type\":\"bool\",\"Doc\":\"semanticTokens controls whether the LSP server will send\\nsemantic tokens to the client.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"expandWorkspaceToModule\",\"Type\":\"bool\",\"Doc\":\"expandWorkspaceToModule instructs `gopls` to adjust the scope of the\\nworkspace to find the best available module root. `gopls` first looks for\\na go.mod file in any parent directory of the workspace folder, expanding\\nthe scope to that directory if it exists. If no viable parent directory is\\nfound, gopls will check if there is exactly one child directory containing\\na go.mod file, narrowing the scope to that directory if it exists.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"experimentalWorkspaceModule\",\"Type\":\"bool\",\"Doc\":\"experimentalWorkspaceModule opts a user into the experimental support\\nfor multi-module workspaces.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"experimentalDiagnosticsDelay\",\"Type\":\"time.Duration\",\"Doc\":\"experimentalDiagnosticsDelay controls the amount of time that gopls waits\\nafter the most recent file modification before computing deep diagnostics.\\nSimple diagnostics (parsing and type-checking) are always run immediately\\non recently modified packages.\\n\\nThis option must be set to a valid duration string, for example `\\\"250ms\\\"`.\\n\",\"EnumValues\":null,\"Default\":\"\\\"0s\\\"\"},{\"Name\":\"experimentalPackageCacheKey\",\"Type\":\"bool\",\"Doc\":\"experimentalPackageCacheKey controls whether to use a coarser cache key\\nfor package type information to increase cache hits. This setting removes\\nthe user's environment, build flags, and working directory from the cache\\nkey, which should be a safe change as all relevant inputs into the type\\nchecking pass are already hashed into the key. This is temporarily guarded\\nby an experiment because caching behavior is subtle and difficult to\\ncomprehensively test.\\n\",\"EnumValues\":null,\"Default\":\"true\"}],\"User\":[{\"Name\":\"buildFlags\",\"Type\":\"[]string\",\"Doc\":\"buildFlags is the set of flags passed on to the build system when invoked.\\nIt is applied to queries like `go list`, which is used when discovering files.\\nThe most common use is to set `-tags`.\\n\",\"EnumValues\":null,\"Default\":\"[]\"},{\"Name\":\"env\",\"Type\":\"map[string]string\",\"Doc\":\"env adds environment variables to external commands run by `gopls`, most notably `go list`.\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"hoverKind\",\"Type\":\"enum\",\"Doc\":\"hoverKind controls the information that appears in the hover text.\\nSingleLine and Structured are intended for use only by authors of editor plugins.\\n\",\"EnumValues\":[{\"Value\":\"\\\"FullDocumentation\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"NoDocumentation\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"SingleLine\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Structured\\\"\",\"Doc\":\"`\\\"Structured\\\"` is an experimental setting that returns a structured hover format.\\nThis format separates the signature from the documentation, so that the client\\ncan do more manipulation of these fields.\\n\\nThis should only be used by clients that support this behavior.\\n\"},{\"Value\":\"\\\"SynopsisDocumentation\\\"\",\"Doc\":\"\"}],\"Default\":\"\\\"FullDocumentation\\\"\"},{\"Name\":\"usePlaceholders\",\"Type\":\"bool\",\"Doc\":\"placeholders enables placeholders for function parameters or struct fields in completion responses.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"linkTarget\",\"Type\":\"string\",\"Doc\":\"linkTarget controls where documentation links go.\\nIt might be one of:\\n\\n* `\\\"godoc.org\\\"`\\n* `\\\"pkg.go.dev\\\"`\\n\\nIf company chooses to use its own `godoc.org`, its address can be used as well.\\n\",\"EnumValues\":null,\"Default\":\"\\\"pkg.go.dev\\\"\"},{\"Name\":\"local\",\"Type\":\"string\",\"Doc\":\"local is the equivalent of the `goimports -local` flag, which puts imports beginning with this string after 3rd-party packages.\\nIt should be the prefix of the import path whose imports should be grouped separately.\\n\",\"EnumValues\":null,\"Default\":\"\\\"\\\"\"},{\"Name\":\"gofumpt\",\"Type\":\"bool\",\"Doc\":\"gofumpt indicates if we should run gofumpt formatting.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"analyses\",\"Type\":\"map[string]bool\",\"Doc\":\"analyses specify analyses that the user would like to enable or disable.\\nA map of the names of analysis passes that should be enabled/disabled.\\nA full list of analyzers that gopls uses can be found [here](analyzers.md)\\n\\nExample Usage:\\n```json5\\n...\\n\\\"analyses\\\": {\\n \\\"unreachable\\\": false, // Disable the unreachable analyzer.\\n \\\"unusedparams\\\": true // Enable the unusedparams analyzer.\\n}\\n...\\n```\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"codelenses\",\"Type\":\"map[string]bool\",\"Doc\":\"codelenses overrides the enabled/disabled state of code lenses. See the \\\"Code Lenses\\\"\\nsection of settings.md for the list of supported lenses.\\n\\nExample Usage:\\n```json5\\n\\\"gopls\\\": {\\n...\\n \\\"codelens\\\": {\\n \\\"generate\\\": false, // Don't show the `go generate` lens.\\n \\\"gc_details\\\": true // Show a code lens toggling the display of gc's choices.\\n }\\n...\\n}\\n```\\n\",\"EnumValues\":null,\"Default\":\"{\\\"gc_details\\\":false,\\\"generate\\\":true,\\\"regenerate_cgo\\\":true,\\\"tidy\\\":true,\\\"upgrade_dependency\\\":true,\\\"vendor\\\":true}\"},{\"Name\":\"linksInHover\",\"Type\":\"bool\",\"Doc\":\"linksInHover toggles the presence of links to documentation in hover.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"importShortcut\",\"Type\":\"enum\",\"Doc\":\"importShortcut specifies whether import statements should link to\\ndocumentation or go to definitions.\\n\",\"EnumValues\":[{\"Value\":\"\\\"Both\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Definition\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Link\\\"\",\"Doc\":\"\"}],\"Default\":\"\\\"Both\\\"\"},{\"Name\":\"matcher\",\"Type\":\"enum\",\"Doc\":\"matcher sets the algorithm that is used when calculating completion candidates.\\n\",\"EnumValues\":[{\"Value\":\"\\\"CaseInsensitive\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"CaseSensitive\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Fuzzy\\\"\",\"Doc\":\"\"}],\"Default\":\"\\\"Fuzzy\\\"\"},{\"Name\":\"symbolMatcher\",\"Type\":\"enum\",\"Doc\":\"symbolMatcher sets the algorithm that is used when finding workspace symbols.\\n\",\"EnumValues\":[{\"Value\":\"\\\"CaseInsensitive\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"CaseSensitive\\\"\",\"Doc\":\"\"},{\"Value\":\"\\\"Fuzzy\\\"\",\"Doc\":\"\"}],\"Default\":\"\\\"Fuzzy\\\"\"},{\"Name\":\"symbolStyle\",\"Type\":\"enum\",\"Doc\":\"symbolStyle controls how symbols are qualified in symbol responses.\\n\\nExample Usage:\\n```json5\\n\\\"gopls\\\": {\\n...\\n \\\"symbolStyle\\\": \\\"dynamic\\\",\\n...\\n}\\n```\\n\",\"EnumValues\":[{\"Value\":\"\\\"Dynamic\\\"\",\"Doc\":\"`\\\"Dynamic\\\"` uses whichever qualifier results in the highest scoring\\nmatch for the given symbol query. Here a \\\"qualifier\\\" is any \\\"/\\\" or \\\".\\\"\\ndelimited suffix of the fully qualified symbol. i.e. \\\"to/pkg.Foo.Field\\\" or\\njust \\\"Foo.Field\\\".\\n\"},{\"Value\":\"\\\"Full\\\"\",\"Doc\":\"`\\\"Full\\\"` is fully qualified symbols, i.e.\\n\\\"path/to/pkg.Foo.Field\\\".\\n\"},{\"Value\":\"\\\"Package\\\"\",\"Doc\":\"`\\\"Package\\\"` is package qualified symbols i.e.\\n\\\"pkg.Foo.Field\\\".\\n\"}],\"Default\":\"\\\"Dynamic\\\"\"}]},\"Commands\":[{\"Command\":\"gopls.generate\",\"Title\":\"Run go generate\",\"Doc\":\"generate runs `go generate` for a given directory.\\n\"},{\"Command\":\"gopls.fill_struct\",\"Title\":\"Fill struct\",\"Doc\":\"fill_struct is a gopls command to fill a struct with default\\nvalues.\\n\"},{\"Command\":\"gopls.regenerate_cgo\",\"Title\":\"Regenerate cgo\",\"Doc\":\"regenerate_cgo regenerates cgo definitions.\\n\"},{\"Command\":\"gopls.test\",\"Title\":\"Run test(s)\",\"Doc\":\"test runs `go test` for a specific test function.\\n\"},{\"Command\":\"gopls.tidy\",\"Title\":\"Run go mod tidy\",\"Doc\":\"tidy runs `go mod tidy` for a module.\\n\"},{\"Command\":\"gopls.update_go_sum\",\"Title\":\"Update go.sum\",\"Doc\":\"update_go_sum updates the go.sum file for a module.\\n\"},{\"Command\":\"gopls.undeclared_name\",\"Title\":\"Undeclared name\",\"Doc\":\"undeclared_name adds a variable declaration for an undeclared\\nname.\\n\"},{\"Command\":\"gopls.go_get_package\",\"Title\":\"go get package\",\"Doc\":\"go_get_package runs `go get` to fetch a package.\\n\"},{\"Command\":\"gopls.add_dependency\",\"Title\":\"Add dependency\",\"Doc\":\"add_dependency adds a dependency.\\n\"},{\"Command\":\"gopls.upgrade_dependency\",\"Title\":\"Upgrade dependency\",\"Doc\":\"upgrade_dependency upgrades a dependency.\\n\"},{\"Command\":\"gopls.remove_dependency\",\"Title\":\"Remove dependency\",\"Doc\":\"remove_dependency removes a dependency.\\n\"},{\"Command\":\"gopls.vendor\",\"Title\":\"Run go mod vendor\",\"Doc\":\"vendor runs `go mod vendor` for a module.\\n\"},{\"Command\":\"gopls.extract_variable\",\"Title\":\"Extract to variable\",\"Doc\":\"extract_variable extracts an expression to a variable.\\n\"},{\"Command\":\"gopls.extract_function\",\"Title\":\"Extract to function\",\"Doc\":\"extract_function extracts statements to a function.\\n\"},{\"Command\":\"gopls.gc_details\",\"Title\":\"Toggle gc_details\",\"Doc\":\"gc_details controls calculation of gc annotations.\\n\"},{\"Command\":\"gopls.generate_gopls_mod\",\"Title\":\"Generate gopls.mod\",\"Doc\":\"generate_gopls_mod (re)generates the gopls.mod file.\\n\"}],\"Lenses\":[{\"Lens\":\"generate\",\"Title\":\"Run go generate\",\"Doc\":\"generate runs `go generate` for a given directory.\\n\"},{\"Lens\":\"regenerate_cgo\",\"Title\":\"Regenerate cgo\",\"Doc\":\"regenerate_cgo regenerates cgo definitions.\\n\"},{\"Lens\":\"test\",\"Title\":\"Run test(s)\",\"Doc\":\"test runs `go test` for a specific test function.\\n\"},{\"Lens\":\"tidy\",\"Title\":\"Run go mod tidy\",\"Doc\":\"tidy runs `go mod tidy` for a module.\\n\"},{\"Lens\":\"upgrade_dependency\",\"Title\":\"Upgrade dependency\",\"Doc\":\"upgrade_dependency upgrades a dependency.\\n\"},{\"Lens\":\"vendor\",\"Title\":\"Run go mod vendor\",\"Doc\":\"vendor runs `go mod vendor` for a module.\\n\"},{\"Lens\":\"gc_details\",\"Title\":\"Toggle gc_details\",\"Doc\":\"gc_details controls calculation of gc annotations.\\n\"}]}" diff --git a/internal/lsp/source/command.go b/internal/lsp/source/command.go index 564b555eb6..16d57fff94 100644 --- a/internal/lsp/source/command.go +++ b/internal/lsp/source/command.go @@ -64,6 +64,7 @@ var Commands = []*Command{ CommandTidy, CommandUpdateGoSum, CommandUndeclaredName, + CommandGoGetPackage, CommandAddDependency, CommandUpgradeDependency, CommandRemoveDependency, @@ -100,6 +101,12 @@ var ( Title: "Run go mod vendor", } + // CommandGoGetPackage runs `go get` to fetch a package. + CommandGoGetPackage = &Command{ + Name: "go_get_package", + Title: "go get package", + } + // CommandUpdateGoSum updates the go.sum file for a module. CommandUpdateGoSum = &Command{ Name: "update_go_sum",