mirror of https://github.com/golang/go.git
internal/lsp: support configurable codeLens
Some code lenses may be undesirable for certain users or editors -- for example a code lens that runs tests, when VSCode already supports this functionality outside of the LSP. To handle such situations, support configuring code lenses via a new 'codelens' gopls option. Add support for code lens in regtests, and use this to test the new configuration. To achieve this, thread through a new 'EditorConfig' type that configures the fake editor's LSP session. It made sense to move the test Env overlay onto this config object as well. While looking at them, document some types in source.Options. Change-Id: I961077422a273829c5cbd83c3b87fae29f77eeda Reviewed-on: https://go-review.googlesource.com/c/tools/+/232680 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
480da3ebd7
commit
cb8d9cd245
|
|
@ -83,9 +83,15 @@ Example Usage:
|
|||
...
|
||||
```
|
||||
|
||||
### **staticcheck** *boolean*
|
||||
### **codelens** *map[string]bool*
|
||||
|
||||
If true, it enables the use of the staticcheck.io analyzers.
|
||||
Overrides the enabled/disabled state of various code lenses. Currently, we
|
||||
support two code lenses:
|
||||
|
||||
* `generate`: run `go generate` as specified by a `//go:generate` directive.
|
||||
* `upgrade.dependency`: upgrade a dependency listed in a `go.mod` file.
|
||||
|
||||
By default, both of these code lenses are enabled.
|
||||
|
||||
### **completionDocumentation** *boolean*
|
||||
|
||||
|
|
@ -129,3 +135,7 @@ At the location of the `<>` in this program, deep completion would suggest the r
|
|||
If true, this enables server side fuzzy matching of completion candidates.
|
||||
|
||||
Default: `true`.
|
||||
|
||||
### **staticcheck** *boolean*
|
||||
|
||||
If true, it enables the use of the staticcheck.io analyzers.
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ import (
|
|||
|
||||
func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
|
||||
switch params.Command {
|
||||
case "generate":
|
||||
case source.CommandGenerate:
|
||||
dir, recursive, err := getGenerateRequest(params.Arguments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go s.runGenerate(xcontext.Detach(ctx), dir, recursive)
|
||||
case "tidy":
|
||||
case source.CommandTidy:
|
||||
if len(params.Arguments) == 0 || len(params.Arguments) > 1 {
|
||||
return nil, errors.Errorf("expected one file URI for call to `go mod tidy`, got %v", params.Arguments)
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCom
|
|||
if _, err := gocmdRunner.Run(ctx, inv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "upgrade.dependency":
|
||||
case source.CommandUpgradeDependency:
|
||||
if len(params.Arguments) < 2 {
|
||||
return nil, errors.Errorf("expected one file URI and one dependency for call to `go get`, got %v", params.Arguments)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,10 @@ import (
|
|||
// Editor is a fake editor client. It keeps track of client state and can be
|
||||
// used for writing LSP tests.
|
||||
type Editor struct {
|
||||
// server, client, and sandbox are concurrency safe and written only at
|
||||
// construction, so do not require synchronization.
|
||||
Config EditorConfig
|
||||
|
||||
// server, client, and sandbox are concurrency safe and written only
|
||||
// at construction time, so do not require synchronization.
|
||||
server protocol.Server
|
||||
client *Client
|
||||
sandbox *Sandbox
|
||||
|
|
@ -49,11 +51,26 @@ func (b buffer) text() string {
|
|||
return strings.Join(b.content, "\n")
|
||||
}
|
||||
|
||||
// EditorConfig configures the editor's LSP session. This is similar to
|
||||
// source.UserOptions, but we use a separate type here so that we expose only
|
||||
// that configuration which we support.
|
||||
//
|
||||
// The zero value for EditorConfig should correspond to its defaults.
|
||||
type EditorConfig struct {
|
||||
Env []string
|
||||
|
||||
// CodeLens is a map defining whether codelens are enabled, keyed by the
|
||||
// codeLens command. CodeLens which are not present in this map are left in
|
||||
// their default state.
|
||||
CodeLens map[string]bool
|
||||
}
|
||||
|
||||
// NewEditor Creates a new Editor.
|
||||
func NewEditor(ws *Sandbox) *Editor {
|
||||
func NewEditor(ws *Sandbox, config EditorConfig) *Editor {
|
||||
return &Editor{
|
||||
buffers: make(map[string]buffer),
|
||||
sandbox: ws,
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,15 +122,24 @@ func (e *Editor) Client() *Client {
|
|||
}
|
||||
|
||||
func (e *Editor) configuration() map[string]interface{} {
|
||||
config := map[string]interface{}{
|
||||
"verboseWorkDoneProgress": true,
|
||||
}
|
||||
|
||||
envvars := e.sandbox.GoEnv()
|
||||
envvars = append(envvars, e.Config.Env...)
|
||||
env := map[string]interface{}{}
|
||||
for _, value := range e.sandbox.GoEnv() {
|
||||
for _, value := range envvars {
|
||||
kv := strings.SplitN(value, "=", 2)
|
||||
env[kv[0]] = kv[1]
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"env": env,
|
||||
"verboseWorkDoneProgress": true,
|
||||
config["env"] = env
|
||||
|
||||
if e.Config.CodeLens != nil {
|
||||
config["codelens"] = e.Config.CodeLens
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func (e *Editor) initialize(ctx context.Context) error {
|
||||
|
|
@ -235,9 +261,7 @@ func (e *Editor) CloseBuffer(ctx context.Context, path string) error {
|
|||
|
||||
if e.server != nil {
|
||||
if err := e.server.DidClose(ctx, &protocol.DidCloseTextDocumentParams{
|
||||
TextDocument: protocol.TextDocumentIdentifier{
|
||||
URI: e.sandbox.Workdir.URI(path),
|
||||
},
|
||||
TextDocument: e.textDocumentIdentifier(path),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("DidClose: %w", err)
|
||||
}
|
||||
|
|
@ -245,6 +269,12 @@ func (e *Editor) CloseBuffer(ctx context.Context, path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *Editor) textDocumentIdentifier(path string) protocol.TextDocumentIdentifier {
|
||||
return protocol.TextDocumentIdentifier{
|
||||
URI: e.sandbox.Workdir.URI(path),
|
||||
}
|
||||
}
|
||||
|
||||
// SaveBuffer writes the content of the buffer specified by the given path to
|
||||
// the filesystem.
|
||||
func (e *Editor) SaveBuffer(ctx context.Context, path string) error {
|
||||
|
|
@ -269,9 +299,7 @@ func (e *Editor) SaveBuffer(ctx context.Context, path string) error {
|
|||
}
|
||||
e.mu.Unlock()
|
||||
|
||||
docID := protocol.TextDocumentIdentifier{
|
||||
URI: e.sandbox.Workdir.URI(buf.path),
|
||||
}
|
||||
docID := e.textDocumentIdentifier(buf.path)
|
||||
if e.server != nil {
|
||||
if err := e.server.WillSave(ctx, &protocol.WillSaveTextDocumentParams{
|
||||
TextDocument: docID,
|
||||
|
|
@ -456,10 +484,8 @@ func (e *Editor) editBufferLocked(ctx context.Context, path string, edits []Edit
|
|||
}
|
||||
params := &protocol.DidChangeTextDocumentParams{
|
||||
TextDocument: protocol.VersionedTextDocumentIdentifier{
|
||||
Version: float64(buf.version),
|
||||
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
|
||||
URI: e.sandbox.Workdir.URI(buf.path),
|
||||
},
|
||||
Version: float64(buf.version),
|
||||
TextDocumentIdentifier: e.textDocumentIdentifier(buf.path),
|
||||
},
|
||||
ContentChanges: evts,
|
||||
}
|
||||
|
|
@ -614,3 +640,24 @@ func (e *Editor) RunGenerate(ctx context.Context, dir string) error {
|
|||
// the caller.
|
||||
return nil
|
||||
}
|
||||
|
||||
// CodeLens execute a codelens request on the server.
|
||||
func (e *Editor) CodeLens(ctx context.Context, path string) ([]protocol.CodeLens, error) {
|
||||
if e.server == nil {
|
||||
return nil, nil
|
||||
}
|
||||
e.mu.Lock()
|
||||
_, ok := e.buffers[path]
|
||||
e.mu.Unlock()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("buffer %q is not open", path)
|
||||
}
|
||||
params := &protocol.CodeLensParams{
|
||||
TextDocument: e.textDocumentIdentifier(path),
|
||||
}
|
||||
lens, err := e.server.CodeLens(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return lens, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ func TestClientEditing(t *testing.T) {
|
|||
}
|
||||
defer ws.Close()
|
||||
ctx := context.Background()
|
||||
editor := NewEditor(ws)
|
||||
editor := NewEditor(ws, EditorConfig{})
|
||||
if err := editor.OpenFile(ctx, "main.go"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ type Sandbox struct {
|
|||
name string
|
||||
gopath string
|
||||
basedir string
|
||||
env []string
|
||||
Proxy *Proxy
|
||||
Workdir *Workdir
|
||||
}
|
||||
|
|
@ -31,10 +30,9 @@ type Sandbox struct {
|
|||
// working directory populated by the txtar-encoded content in srctxt, and a
|
||||
// file-based module proxy populated with the txtar-encoded content in
|
||||
// proxytxt.
|
||||
func NewSandbox(name, srctxt, proxytxt string, inGopath bool, env ...string) (_ *Sandbox, err error) {
|
||||
func NewSandbox(name, srctxt, proxytxt string, inGopath bool) (_ *Sandbox, err error) {
|
||||
sb := &Sandbox{
|
||||
name: name,
|
||||
env: env,
|
||||
}
|
||||
defer func() {
|
||||
// Clean up if we fail at any point in this constructor.
|
||||
|
|
@ -103,12 +101,12 @@ func (sb *Sandbox) GOPATH() string {
|
|||
// GoEnv returns the default environment variables that can be used for
|
||||
// invoking Go commands in the sandbox.
|
||||
func (sb *Sandbox) GoEnv() []string {
|
||||
return append([]string{
|
||||
return []string{
|
||||
"GOPATH=" + sb.GOPATH(),
|
||||
"GOPROXY=" + sb.Proxy.GOPROXY(),
|
||||
"GO111MODULE=",
|
||||
"GOSUMDB=off",
|
||||
}, sb.env...)
|
||||
}
|
||||
}
|
||||
|
||||
// RunGoCommand executes a go command in the sandbox.
|
||||
|
|
|
|||
|
|
@ -218,13 +218,13 @@ func TestDebugInfoLifecycle(t *testing.T) {
|
|||
tsForwarder := servertest.NewPipeServer(clientCtx, forwarder)
|
||||
|
||||
conn1 := tsForwarder.Connect(clientCtx)
|
||||
ed1, err := fake.NewEditor(sb).Connect(clientCtx, conn1)
|
||||
ed1, err := fake.NewEditor(sb, fake.EditorConfig{}).Connect(clientCtx, conn1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ed1.Shutdown(clientCtx)
|
||||
conn2 := tsBackend.Connect(baseCtx)
|
||||
ed2, err := fake.NewEditor(sb).Connect(baseCtx, conn2)
|
||||
ed2, err := fake.NewEditor(sb, fake.EditorConfig{}).Connect(baseCtx, conn2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ import (
|
|||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
// CodeLens computes code lens for a go.mod file.
|
||||
func CodeLens(ctx context.Context, snapshot source.Snapshot, uri span.URI) ([]protocol.CodeLens, error) {
|
||||
if !snapshot.View().Options().EnabledCodeLens[source.CommandUpgradeDependency] {
|
||||
return nil, nil
|
||||
}
|
||||
realURI, _ := snapshot.View().ModFiles()
|
||||
if realURI == "" {
|
||||
return nil, nil
|
||||
|
|
@ -50,7 +54,7 @@ func CodeLens(ctx context.Context, snapshot source.Snapshot, uri span.URI) ([]pr
|
|||
Range: rng,
|
||||
Command: protocol.Command{
|
||||
Title: fmt.Sprintf("Upgrade dependency to %s", latest),
|
||||
Command: "upgrade.dependency",
|
||||
Command: source.CommandUpgradeDependency,
|
||||
Arguments: []interface{}{uri, dep},
|
||||
},
|
||||
})
|
||||
|
|
@ -67,7 +71,7 @@ func CodeLens(ctx context.Context, snapshot source.Snapshot, uri span.URI) ([]pr
|
|||
Range: rng,
|
||||
Command: protocol.Command{
|
||||
Title: "Upgrade all dependencies",
|
||||
Command: "upgrade.dependency",
|
||||
Command: source.CommandUpgradeDependency,
|
||||
Arguments: []interface{}{uri, strings.Join(append([]string{"-u"}, allUpgrades...), " ")},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2020 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 regtest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/fake"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
)
|
||||
|
||||
func TestDisablingCodeLens(t *testing.T) {
|
||||
const workspace = `
|
||||
-- go.mod --
|
||||
module codelens.test
|
||||
-- lib.go --
|
||||
package lib
|
||||
|
||||
type Number int
|
||||
|
||||
const (
|
||||
Zero Number = iota
|
||||
One
|
||||
Two
|
||||
)
|
||||
|
||||
//go:generate stringer -type=Number
|
||||
`
|
||||
tests := []struct {
|
||||
label string
|
||||
enabled map[string]bool
|
||||
wantCodeLens bool
|
||||
}{
|
||||
{
|
||||
label: "default",
|
||||
wantCodeLens: true,
|
||||
},
|
||||
{
|
||||
label: "generate disabled",
|
||||
enabled: map[string]bool{source.CommandGenerate: false},
|
||||
wantCodeLens: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.label, func(t *testing.T) {
|
||||
runner.Run(t, workspace, func(t *testing.T, env *Env) {
|
||||
env.OpenFile("lib.go")
|
||||
lens := env.CodeLens("lib.go")
|
||||
if gotCodeLens := len(lens) > 0; gotCodeLens != test.wantCodeLens {
|
||||
t.Errorf("got codeLens: %t, want %t", gotCodeLens, test.wantCodeLens)
|
||||
}
|
||||
}, WithEditorConfig(fake.EditorConfig{CodeLens: test.enabled}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -382,7 +382,7 @@ func _() {
|
|||
if err := env.Editor.OrganizeImports(env.Ctx, "main.go"); err == nil {
|
||||
t.Fatalf("organize imports should fail with an empty GOPATH")
|
||||
}
|
||||
}, WithEnv("GOPATH="))
|
||||
}, WithEditorConfig(fake.EditorConfig{Env: []string{"GOPATH="}}))
|
||||
}
|
||||
|
||||
// Tests golang/go#38669.
|
||||
|
|
@ -404,7 +404,7 @@ var X = 0
|
|||
env.OpenFile("main.go")
|
||||
env.OrganizeImports("main.go")
|
||||
env.Await(EmptyDiagnostics("main.go"))
|
||||
}, WithEnv("GOFLAGS=-tags=foo"))
|
||||
}, WithEditorConfig(fake.EditorConfig{Env: []string{"GOFLAGS=-tags=foo"}}))
|
||||
}
|
||||
|
||||
// Tests golang/go#38467.
|
||||
|
|
|
|||
|
|
@ -102,10 +102,10 @@ type condition struct {
|
|||
|
||||
// NewEnv creates a new test environment using the given scratch environment
|
||||
// and gopls server.
|
||||
func NewEnv(ctx context.Context, t *testing.T, scratch *fake.Sandbox, ts servertest.Connector) *Env {
|
||||
func NewEnv(ctx context.Context, t *testing.T, scratch *fake.Sandbox, ts servertest.Connector, editorConfig fake.EditorConfig) *Env {
|
||||
t.Helper()
|
||||
conn := ts.Connect(ctx)
|
||||
editor, err := fake.NewEditor(scratch).Connect(ctx, conn)
|
||||
editor, err := fake.NewEditor(scratch, editorConfig).Connect(ctx, conn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,12 +66,12 @@ type Runner struct {
|
|||
}
|
||||
|
||||
type runConfig struct {
|
||||
modes Mode
|
||||
proxyTxt string
|
||||
timeout time.Duration
|
||||
env []string
|
||||
skipCleanup bool
|
||||
gopath bool
|
||||
editorConfig fake.EditorConfig
|
||||
modes Mode
|
||||
proxyTxt string
|
||||
timeout time.Duration
|
||||
skipCleanup bool
|
||||
gopath bool
|
||||
}
|
||||
|
||||
func (r *Runner) defaultConfig() *runConfig {
|
||||
|
|
@ -113,11 +113,10 @@ func WithModes(modes Mode) RunOption {
|
|||
})
|
||||
}
|
||||
|
||||
// WithEnv overlays environment variables encoded by "<var>=<value" on top of
|
||||
// the default regtest environment.
|
||||
func WithEnv(env ...string) RunOption {
|
||||
// WithEditorConfig configures the editors LSP session.
|
||||
func WithEditorConfig(config fake.EditorConfig) RunOption {
|
||||
return optionSetter(func(opts *runConfig) {
|
||||
opts.env = env
|
||||
opts.editorConfig = config
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +167,7 @@ func (r *Runner) Run(t *testing.T, filedata string, test func(t *testing.T, e *E
|
|||
defer cancel()
|
||||
ctx = debug.WithInstance(ctx, "", "")
|
||||
|
||||
sandbox, err := fake.NewSandbox("regtest", filedata, config.proxyTxt, config.gopath, config.env...)
|
||||
sandbox, err := fake.NewSandbox("regtest", filedata, config.proxyTxt, config.gopath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -191,7 +190,7 @@ func (r *Runner) Run(t *testing.T, filedata string, test func(t *testing.T, e *E
|
|||
defer func() {
|
||||
ts.Close()
|
||||
}()
|
||||
env := NewEnv(ctx, t, sandbox, ts)
|
||||
env := NewEnv(ctx, t, sandbox, ts, config.editorConfig)
|
||||
defer func() {
|
||||
if t.Failed() && r.PrintGoroutinesOnFailure {
|
||||
pprof.Lookup("goroutine").WriteTo(os.Stderr, 1)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func runShared(t *testing.T, program string, testFunc func(env1 *Env, env2 *Env)
|
|||
runner.Run(t, sharedProgram, func(t *testing.T, env1 *Env) {
|
||||
// Create a second test session connected to the same workspace and server
|
||||
// as the first.
|
||||
env2 := NewEnv(env1.Ctx, t, env1.Sandbox, env1.Server)
|
||||
env2 := NewEnv(env1.Ctx, t, env1.Sandbox, env1.Server, env1.Editor.Config)
|
||||
testFunc(env1, env2)
|
||||
}, WithModes(modes))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ package regtest
|
|||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/fake"
|
||||
)
|
||||
|
||||
func TestBadGOPATH(t *testing.T) {
|
||||
|
|
@ -28,5 +30,7 @@ func _() {
|
|||
if err := env.Editor.OrganizeImports(env.Ctx, "main.go"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}, WithEnv(fmt.Sprintf("GOPATH=:/path/to/gopath")))
|
||||
}, WithEditorConfig(fake.EditorConfig{
|
||||
Env: []string{fmt.Sprintf("GOPATH=:/path/to/gopath")},
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,3 +166,14 @@ func (e *Env) CheckForFileChanges() {
|
|||
e.T.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// CodeLens calls textDocument/codeLens for the given path, calling t.Fatal on
|
||||
// any error.
|
||||
func (e *Env) CodeLens(path string) []protocol.CodeLens {
|
||||
e.T.Helper()
|
||||
lens, err := e.Editor.CodeLens(e.Ctx, path)
|
||||
if err != nil {
|
||||
e.T.Fatal(err)
|
||||
}
|
||||
return lens
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ import (
|
|||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
)
|
||||
|
||||
// CodeLens computes code lens for Go source code.
|
||||
func CodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) {
|
||||
if !snapshot.View().Options().EnabledCodeLens[CommandGenerate] {
|
||||
return nil, nil
|
||||
}
|
||||
f, _, m, _, err := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull).Parse(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -35,7 +39,7 @@ func CodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol
|
|||
Range: rng,
|
||||
Command: protocol.Command{
|
||||
Title: "run go generate",
|
||||
Command: "generate",
|
||||
Command: CommandGenerate,
|
||||
Arguments: []interface{}{dir, false},
|
||||
},
|
||||
},
|
||||
|
|
@ -43,7 +47,7 @@ func CodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol
|
|||
Range: rng,
|
||||
Command: protocol.Command{
|
||||
Title: "run go generate ./...",
|
||||
Command: "generate",
|
||||
Command: CommandGenerate,
|
||||
Arguments: []interface{}{dir, true},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -52,6 +52,18 @@ import (
|
|||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
// CommandGenerate is a gopls command to run `go generate` for a directory.
|
||||
CommandGenerate = "generate"
|
||||
// CommandTidy is a gopls command to run `go mod tidy` for a module.
|
||||
CommandTidy = "tidy"
|
||||
// CommandUpgradeDependency is a gopls command to upgrade a dependency.
|
||||
CommandUpgradeDependency = "upgrade.dependency"
|
||||
)
|
||||
|
||||
// DefaultOptions is the options that are used for Gopls execution independent
|
||||
// of any externally provided configuration (LSP initialization, command
|
||||
// invokation, etc.).
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
ClientOptions: ClientOptions{
|
||||
|
|
@ -76,9 +88,9 @@ func DefaultOptions() Options {
|
|||
Sum: {},
|
||||
},
|
||||
SupportedCommands: []string{
|
||||
"tidy", // for go.mod files
|
||||
"upgrade.dependency", // for go.mod dependency upgrades
|
||||
"generate", // for "go generate" commands
|
||||
CommandTidy, // for go.mod files
|
||||
CommandUpgradeDependency, // for go.mod dependency upgrades
|
||||
CommandGenerate, // for "go generate" commands
|
||||
},
|
||||
},
|
||||
UserOptions: UserOptions{
|
||||
|
|
@ -89,6 +101,10 @@ func DefaultOptions() Options {
|
|||
DeepCompletion: true,
|
||||
UnimportedCompletion: true,
|
||||
CompletionDocumentation: true,
|
||||
EnabledCodeLens: map[string]bool{
|
||||
CommandGenerate: true,
|
||||
CommandUpgradeDependency: true,
|
||||
},
|
||||
},
|
||||
DebuggingOptions: DebuggingOptions{
|
||||
CompletionBudget: 100 * time.Millisecond,
|
||||
|
|
@ -106,6 +122,8 @@ func DefaultOptions() Options {
|
|||
}
|
||||
}
|
||||
|
||||
// Options holds various configuration that affects Gopls execution, organized
|
||||
// by the nature or origin of the settings.
|
||||
type Options struct {
|
||||
ClientOptions
|
||||
ServerOptions
|
||||
|
|
@ -115,6 +133,8 @@ type Options struct {
|
|||
Hooks
|
||||
}
|
||||
|
||||
// ClientOptions holds LSP-specific configuration that is provided by the
|
||||
// client.
|
||||
type ClientOptions struct {
|
||||
InsertTextFormat protocol.InsertTextFormat
|
||||
ConfigurationSupported bool
|
||||
|
|
@ -125,11 +145,15 @@ type ClientOptions struct {
|
|||
HierarchicalDocumentSymbolSupport bool
|
||||
}
|
||||
|
||||
// ServerOptions holds LSP-specific configuration that is provided by the
|
||||
// server.
|
||||
type ServerOptions struct {
|
||||
SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool
|
||||
SupportedCommands []string
|
||||
}
|
||||
|
||||
// UserOptions holds custom Gopls configuration (not part of the LSP) that is
|
||||
// modified by the client.
|
||||
type UserOptions struct {
|
||||
// Env is the current set of environment overrides on this view.
|
||||
Env []string
|
||||
|
|
@ -140,9 +164,10 @@ type UserOptions struct {
|
|||
// HoverKind specifies the format of the content for hover requests.
|
||||
HoverKind HoverKind
|
||||
|
||||
// UserEnabledAnalyses specify analyses that the user would like to enable or disable.
|
||||
// A map of the names of analysis passes that should be enabled/disabled.
|
||||
// A full list of analyzers that gopls uses can be found [here](analyzers.md)
|
||||
// UserEnabledAnalyses specifies analyses that the user would like to enable
|
||||
// or disable. A map of the names of analysis passes that should be
|
||||
// enabled/disabled. A full list of analyzers that gopls uses can be found
|
||||
// [here](analyzers.md).
|
||||
//
|
||||
// Example Usage:
|
||||
// ...
|
||||
|
|
@ -152,6 +177,10 @@ type UserOptions struct {
|
|||
// }
|
||||
UserEnabledAnalyses map[string]bool
|
||||
|
||||
// EnabledCodeLens specifies which codelens are enabled, keyed by the gopls
|
||||
// command that they provide.
|
||||
EnabledCodeLens map[string]bool
|
||||
|
||||
// StaticCheck enables additional analyses from staticcheck.io.
|
||||
StaticCheck bool
|
||||
|
||||
|
|
@ -191,6 +220,8 @@ type completionOptions struct {
|
|||
budget time.Duration
|
||||
}
|
||||
|
||||
// Hooks contains configuration that is provided to the Gopls command by the
|
||||
// main package.
|
||||
type Hooks struct {
|
||||
GoDiff bool
|
||||
ComputeEdits diff.ComputeEdits
|
||||
|
|
@ -215,6 +246,8 @@ type ExperimentalOptions struct {
|
|||
VerboseWorkDoneProgress bool
|
||||
}
|
||||
|
||||
// DebuggingOptions should not affect the logical execution of Gopls, but may
|
||||
// be altered for debugging purposes.
|
||||
type DebuggingOptions struct {
|
||||
VerboseOutput bool
|
||||
|
||||
|
|
@ -395,15 +428,17 @@ func (o *Options) set(name string, value interface{}) OptionResult {
|
|||
o.LinkTarget = linkTarget
|
||||
|
||||
case "analyses":
|
||||
allAnalyses, ok := value.(map[string]interface{})
|
||||
if !ok {
|
||||
result.errorf("Invalid type %T for map[string]interface{} option %q", value, name)
|
||||
break
|
||||
}
|
||||
o.UserEnabledAnalyses = make(map[string]bool)
|
||||
for a, enabled := range allAnalyses {
|
||||
if enabled, ok := enabled.(bool); ok {
|
||||
o.UserEnabledAnalyses[a] = enabled
|
||||
result.setBoolMap(&o.UserEnabledAnalyses)
|
||||
|
||||
case "codelens":
|
||||
var lensOverrides map[string]bool
|
||||
result.setBoolMap(&lensOverrides)
|
||||
if result.Error == nil {
|
||||
if o.EnabledCodeLens == nil {
|
||||
o.EnabledCodeLens = make(map[string]bool)
|
||||
}
|
||||
for lens, enabled := range lensOverrides {
|
||||
o.EnabledCodeLens[lens] = enabled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -488,6 +523,24 @@ func (r *OptionResult) asBool() (bool, bool) {
|
|||
return b, true
|
||||
}
|
||||
|
||||
func (r *OptionResult) setBoolMap(bm *map[string]bool) {
|
||||
all, ok := r.Value.(map[string]interface{})
|
||||
if !ok {
|
||||
r.errorf("Invalid type %T for map[string]interface{} option %q", r.Value, r.Name)
|
||||
return
|
||||
}
|
||||
m := make(map[string]bool)
|
||||
for a, enabled := range all {
|
||||
if enabled, ok := enabled.(bool); ok {
|
||||
m[a] = enabled
|
||||
} else {
|
||||
r.errorf("Invalid type %d for map key %q in option %q", a, r.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
*bm = m
|
||||
}
|
||||
|
||||
func (r *OptionResult) asString() (string, bool) {
|
||||
b, ok := r.Value.(string)
|
||||
if !ok {
|
||||
|
|
|
|||
|
|
@ -360,9 +360,13 @@ func (fileID FileIdentity) String() string {
|
|||
type FileKind int
|
||||
|
||||
const (
|
||||
// Go is a normal go source file.
|
||||
Go = FileKind(iota)
|
||||
// Mod is a go.mod file.
|
||||
Mod
|
||||
// Sum is a go.sum file.
|
||||
Sum
|
||||
// UnknownKind is a file type we don't know about.
|
||||
UnknownKind
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue