зеркало из https://github.com/golang/tools.git
internal/lsp: convert refactor code actions to use codeAction/resolve
Allow apply fix and change signature commands to return edits instead of applying the edits. Added a marker test for removing parameters using the new resolve logic. We probably want most refactoring code actions to work both ways. This also updates the fill struct test to make sure that the capabilities of the editor are being correctly respected. For golang/go#64510 Change-Id: If58f7bdff52ec8e1621c007d029c5b9b60bbdd3a Reviewed-on: https://go-review.googlesource.com/c/tools/+/548276 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Родитель
f2d3f78aad
Коммит
592d9e1e87
|
@ -81,6 +81,47 @@ Args:
|
|||
"character": uint32,
|
||||
},
|
||||
},
|
||||
// Whether to resolve and return the edits.
|
||||
"ResolveEdits": bool,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// Holds changes to existing resources.
|
||||
"changes": map[golang.org/x/tools/gopls/internal/lsp/protocol.DocumentURI][]golang.org/x/tools/gopls/internal/lsp/protocol.TextEdit,
|
||||
// Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes
|
||||
// are either an array of `TextDocumentEdit`s to express changes to n different text documents
|
||||
// where each text document edit addresses a specific version of a text document. Or it can contain
|
||||
// above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations.
|
||||
//
|
||||
// Whether a client supports versioned document edits is expressed via
|
||||
// `workspace.workspaceEdit.documentChanges` client capability.
|
||||
//
|
||||
// If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then
|
||||
// only plain `TextEdit`s using the `changes` property are supported.
|
||||
"documentChanges": []{
|
||||
"TextDocumentEdit": {
|
||||
"textDocument": { ... },
|
||||
"edits": { ... },
|
||||
},
|
||||
"RenameFile": {
|
||||
"kind": string,
|
||||
"oldUri": string,
|
||||
"newUri": string,
|
||||
"options": { ... },
|
||||
"ResourceOperation": { ... },
|
||||
},
|
||||
},
|
||||
// A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and
|
||||
// delete file / folder operations.
|
||||
//
|
||||
// Whether clients honor this property depends on the client capability `workspace.changeAnnotationSupport`.
|
||||
//
|
||||
// @since 3.16.0
|
||||
"changeAnnotations": map[string]golang.org/x/tools/gopls/internal/lsp/protocol.ChangeAnnotation,
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -101,6 +142,47 @@ Args:
|
|||
"end": { ... },
|
||||
},
|
||||
},
|
||||
// Whether to resolve and return the edits.
|
||||
"ResolveEdits": bool,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// Holds changes to existing resources.
|
||||
"changes": map[golang.org/x/tools/gopls/internal/lsp/protocol.DocumentURI][]golang.org/x/tools/gopls/internal/lsp/protocol.TextEdit,
|
||||
// Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes
|
||||
// are either an array of `TextDocumentEdit`s to express changes to n different text documents
|
||||
// where each text document edit addresses a specific version of a text document. Or it can contain
|
||||
// above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations.
|
||||
//
|
||||
// Whether a client supports versioned document edits is expressed via
|
||||
// `workspace.workspaceEdit.documentChanges` client capability.
|
||||
//
|
||||
// If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then
|
||||
// only plain `TextEdit`s using the `changes` property are supported.
|
||||
"documentChanges": []{
|
||||
"TextDocumentEdit": {
|
||||
"textDocument": { ... },
|
||||
"edits": { ... },
|
||||
},
|
||||
"RenameFile": {
|
||||
"kind": string,
|
||||
"oldUri": string,
|
||||
"newUri": string,
|
||||
"options": { ... },
|
||||
"ResourceOperation": { ... },
|
||||
},
|
||||
},
|
||||
// A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and
|
||||
// delete file / folder operations.
|
||||
//
|
||||
// Whether clients honor this property depends on the client capability `workspace.changeAnnotationSupport`.
|
||||
//
|
||||
// @since 3.16.0
|
||||
"changeAnnotations": map[string]golang.org/x/tools/gopls/internal/lsp/protocol.ChangeAnnotation,
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -118,13 +118,13 @@ func Dispatch(ctx context.Context, params *protocol.ExecuteCommandParams, s Inte
|
|||
if err := UnmarshalArgs(params.Arguments, &a0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, s.ApplyFix(ctx, a0)
|
||||
return s.ApplyFix(ctx, a0)
|
||||
case "gopls.change_signature":
|
||||
var a0 ChangeSignatureArgs
|
||||
if err := UnmarshalArgs(params.Arguments, &a0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, s.ChangeSignature(ctx, a0)
|
||||
return s.ChangeSignature(ctx, a0)
|
||||
case "gopls.check_upgrades":
|
||||
var a0 CheckUpgradesArgs
|
||||
if err := UnmarshalArgs(params.Arguments, &a0); err != nil {
|
||||
|
|
|
@ -44,7 +44,7 @@ type Interface interface {
|
|||
// ApplyFix: Apply a fix
|
||||
//
|
||||
// Applies a fix to a region of source code.
|
||||
ApplyFix(context.Context, ApplyFixArgs) error
|
||||
ApplyFix(context.Context, ApplyFixArgs) (*protocol.WorkspaceEdit, error)
|
||||
|
||||
// Test: Run test(s) (legacy)
|
||||
//
|
||||
|
@ -216,7 +216,7 @@ type Interface interface {
|
|||
//
|
||||
// This command is experimental, currently only supporting parameter removal.
|
||||
// Its signature will certainly change in the future (pun intended).
|
||||
ChangeSignature(context.Context, ChangeSignatureArgs) error
|
||||
ChangeSignature(context.Context, ChangeSignatureArgs) (*protocol.WorkspaceEdit, error)
|
||||
|
||||
// DiagnoseFiles: Cause server to publish diagnostics for the specified files.
|
||||
//
|
||||
|
@ -257,6 +257,8 @@ type ApplyFixArgs struct {
|
|||
URI protocol.DocumentURI
|
||||
// The document range to scan for fixes.
|
||||
Range protocol.Range
|
||||
// Whether to resolve and return the edits.
|
||||
ResolveEdits bool
|
||||
}
|
||||
|
||||
type URIArg struct {
|
||||
|
@ -500,6 +502,8 @@ type AddTelemetryCountersArgs struct {
|
|||
// ChangeSignatureArgs specifies a "change signature" refactoring to perform.
|
||||
type ChangeSignatureArgs struct {
|
||||
RemoveParameter protocol.Location
|
||||
// Whether to resolve and return the edits.
|
||||
ResolveEdits bool
|
||||
}
|
||||
|
||||
// DiagnoseFilesArgs specifies a set of files for which diagnostics are wanted.
|
||||
|
|
|
@ -63,6 +63,7 @@ var renameProp = map[prop]string{
|
|||
{"CancelParams", "id"}: "interface{}",
|
||||
{"Command", "arguments"}: "[]json.RawMessage",
|
||||
{"CompletionItem", "textEdit"}: "TextEdit",
|
||||
{"CodeAction", "data"}: "json.RawMessage", // delay unmarshalling commands
|
||||
{"Diagnostic", "code"}: "interface{}",
|
||||
{"Diagnostic", "data"}: "json.RawMessage", // delay unmarshalling quickfixes
|
||||
|
||||
|
|
|
@ -489,7 +489,7 @@ type CodeAction struct {
|
|||
// a `textDocument/codeAction` and a `codeAction/resolve` request.
|
||||
//
|
||||
// @since 3.16.0
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Data *json.RawMessage `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// The Client Capabilities of a {@link CodeActionRequest}.
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"sort"
|
||||
|
@ -24,6 +26,7 @@ import (
|
|||
"golang.org/x/tools/gopls/internal/mod"
|
||||
"golang.org/x/tools/gopls/internal/settings"
|
||||
"golang.org/x/tools/gopls/internal/util/bug"
|
||||
"golang.org/x/tools/gopls/internal/util/slices"
|
||||
"golang.org/x/tools/internal/event"
|
||||
"golang.org/x/tools/internal/event/tag"
|
||||
"golang.org/x/tools/internal/imports"
|
||||
|
@ -179,7 +182,7 @@ func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionPara
|
|||
}
|
||||
|
||||
if want[protocol.RefactorExtract] {
|
||||
extractions, err := refactorExtract(pgf, params.Range)
|
||||
extractions, err := refactorExtract(pgf, params.Range, snapshot.Options())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -226,19 +229,15 @@ func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionPara
|
|||
return protocol.CodeAction{}, false, nil
|
||||
}
|
||||
cmd, err := command.NewApplyFixCommand(d.Message, command.ApplyFixArgs{
|
||||
URI: pgf.URI,
|
||||
Fix: string(settings.StubMethods),
|
||||
Range: pd.Range,
|
||||
URI: pgf.URI,
|
||||
Fix: string(settings.StubMethods),
|
||||
Range: pd.Range,
|
||||
ResolveEdits: supportsResolveEdits(snapshot.Options()),
|
||||
})
|
||||
if err != nil {
|
||||
return protocol.CodeAction{}, false, err
|
||||
}
|
||||
return protocol.CodeAction{
|
||||
Title: d.Message,
|
||||
Kind: protocol.QuickFix,
|
||||
Command: &cmd,
|
||||
Diagnostics: []protocol.Diagnostic{pd},
|
||||
}, true, nil
|
||||
return newCodeAction(d.Message, protocol.QuickFix, &cmd, nil, snapshot.Options()), true, nil
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -249,7 +248,7 @@ func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionPara
|
|||
}
|
||||
|
||||
if want[protocol.RefactorRewrite] {
|
||||
rewrites, err := refactorRewrite(snapshot, pkg, pgf, fh, params.Range)
|
||||
rewrites, err := refactorRewrite(snapshot, pkg, pgf, fh, params.Range, snapshot.Options())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -281,6 +280,54 @@ func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionPara
|
|||
}
|
||||
}
|
||||
|
||||
// ResolveCodeAction resolves missing Edit information (that is, computes the
|
||||
// details of the necessary patch) in the given code action using the provided
|
||||
// Data field of the CodeAction, which should contain the raw json of a protocol.Command.
|
||||
//
|
||||
// This should be called by the client before applying code actions, when the
|
||||
// client has code action resolve support.
|
||||
//
|
||||
// This feature allows capable clients to preview and selectively apply the diff
|
||||
// instead of applying the whole thing unconditionally through workspace/applyEdit.
|
||||
func (s *server) ResolveCodeAction(ctx context.Context, ca *protocol.CodeAction) (*protocol.CodeAction, error) {
|
||||
ctx, done := event.Start(ctx, "lsp.Server.resolveCodeAction")
|
||||
defer done()
|
||||
|
||||
// Only resolve the code action if there is Data provided.
|
||||
// TODO(suzmue): publish protocol.unmarshalParams as protocol.UnmarshalJSON
|
||||
// and use it consistently where we need to unmarshal to handle all null checks.
|
||||
if ca.Data != nil && len(*ca.Data) != 0 && !bytes.Equal(*ca.Data, []byte("null")) {
|
||||
var cmd protocol.Command
|
||||
if err := json.Unmarshal(*ca.Data, &cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := &protocol.ExecuteCommandParams{
|
||||
Command: cmd.Command,
|
||||
Arguments: cmd.Arguments,
|
||||
}
|
||||
|
||||
handler := &commandHandler{
|
||||
s: s,
|
||||
params: params,
|
||||
}
|
||||
edit, err := command.Dispatch(ctx, params, handler)
|
||||
if err != nil {
|
||||
|
||||
return nil, err
|
||||
}
|
||||
var ok bool
|
||||
if ca.Edit, ok = edit.(*protocol.WorkspaceEdit); !ok {
|
||||
return nil, fmt.Errorf("unable to resolve code action %q", ca.Title)
|
||||
}
|
||||
}
|
||||
return ca, nil
|
||||
}
|
||||
|
||||
func supportsResolveEdits(options *settings.Options) bool {
|
||||
return options.CodeActionResolveOptions != nil && slices.Contains(options.CodeActionResolveOptions, "edit")
|
||||
}
|
||||
|
||||
func (s *server) findMatchingDiagnostics(uri protocol.DocumentURI, pd protocol.Diagnostic) []*cache.Diagnostic {
|
||||
s.diagnosticsMu.Lock()
|
||||
defer s.diagnosticsMu.Unlock()
|
||||
|
@ -367,7 +414,7 @@ func fixedByImportFix(fix *imports.ImportFix, diagnostics []protocol.Diagnostic)
|
|||
return results
|
||||
}
|
||||
|
||||
func refactorExtract(pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.CodeAction, error) {
|
||||
func refactorExtract(pgf *source.ParsedGoFile, rng protocol.Range, options *settings.Options) ([]protocol.CodeAction, error) {
|
||||
if rng.Start == rng.End {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -380,9 +427,10 @@ func refactorExtract(pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.C
|
|||
var commands []protocol.Command
|
||||
if _, ok, methodOk, _ := source.CanExtractFunction(pgf.Tok, start, end, pgf.Src, pgf.File); ok {
|
||||
cmd, err := command.NewApplyFixCommand("Extract function", command.ApplyFixArgs{
|
||||
URI: puri,
|
||||
Fix: string(settings.ExtractFunction),
|
||||
Range: rng,
|
||||
URI: puri,
|
||||
Fix: string(settings.ExtractFunction),
|
||||
Range: rng,
|
||||
ResolveEdits: supportsResolveEdits(options),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -390,9 +438,10 @@ func refactorExtract(pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.C
|
|||
commands = append(commands, cmd)
|
||||
if methodOk {
|
||||
cmd, err := command.NewApplyFixCommand("Extract method", command.ApplyFixArgs{
|
||||
URI: puri,
|
||||
Fix: string(settings.ExtractMethod),
|
||||
Range: rng,
|
||||
URI: puri,
|
||||
Fix: string(settings.ExtractMethod),
|
||||
Range: rng,
|
||||
ResolveEdits: supportsResolveEdits(options),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -402,9 +451,10 @@ func refactorExtract(pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.C
|
|||
}
|
||||
if _, _, ok, _ := source.CanExtractVariable(start, end, pgf.File); ok {
|
||||
cmd, err := command.NewApplyFixCommand("Extract variable", command.ApplyFixArgs{
|
||||
URI: puri,
|
||||
Fix: string(settings.ExtractVariable),
|
||||
Range: rng,
|
||||
URI: puri,
|
||||
Fix: string(settings.ExtractVariable),
|
||||
Range: rng,
|
||||
ResolveEdits: supportsResolveEdits(options),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -413,16 +463,31 @@ func refactorExtract(pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.C
|
|||
}
|
||||
var actions []protocol.CodeAction
|
||||
for i := range commands {
|
||||
actions = append(actions, protocol.CodeAction{
|
||||
Title: commands[i].Title,
|
||||
Kind: protocol.RefactorExtract,
|
||||
Command: &commands[i],
|
||||
})
|
||||
actions = append(actions, newCodeAction(commands[i].Title, protocol.RefactorExtract, &commands[i], nil, options))
|
||||
}
|
||||
return actions, nil
|
||||
}
|
||||
|
||||
func refactorRewrite(snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.ParsedGoFile, fh file.Handle, rng protocol.Range) (_ []protocol.CodeAction, rerr error) {
|
||||
func newCodeAction(title string, kind protocol.CodeActionKind, cmd *protocol.Command, diagnostics []protocol.Diagnostic, options *settings.Options) protocol.CodeAction {
|
||||
action := protocol.CodeAction{
|
||||
Title: title,
|
||||
Kind: kind,
|
||||
Diagnostics: diagnostics,
|
||||
}
|
||||
if !supportsResolveEdits(options) {
|
||||
action.Command = cmd
|
||||
} else {
|
||||
data, err := json.Marshal(cmd)
|
||||
if err != nil {
|
||||
panic("unable to marshal")
|
||||
}
|
||||
msg := json.RawMessage(data)
|
||||
action.Data = &msg
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
func refactorRewrite(snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.ParsedGoFile, fh file.Handle, rng protocol.Range, options *settings.Options) (_ []protocol.CodeAction, rerr error) {
|
||||
// golang/go#61693: code actions were refactored to run outside of the
|
||||
// analysis framework, but as a result they lost their panic recovery.
|
||||
//
|
||||
|
@ -442,15 +507,12 @@ func refactorRewrite(snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.P
|
|||
URI: pgf.URI,
|
||||
Range: rng,
|
||||
},
|
||||
ResolveEdits: supportsResolveEdits(options),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
actions = append(actions, protocol.CodeAction{
|
||||
Title: "Refactor: remove unused parameter",
|
||||
Kind: protocol.RefactorRewrite,
|
||||
Command: &cmd,
|
||||
})
|
||||
actions = append(actions, newCodeAction("Refactor: remove unused parameter", protocol.RefactorRewrite, &cmd, nil, options))
|
||||
}
|
||||
|
||||
if action, ok := source.ConvertStringLiteral(pgf, fh, rng); ok {
|
||||
|
@ -465,9 +527,10 @@ func refactorRewrite(snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.P
|
|||
var commands []protocol.Command
|
||||
if _, ok, _ := source.CanInvertIfCondition(pgf.File, start, end); ok {
|
||||
cmd, err := command.NewApplyFixCommand("Invert if condition", command.ApplyFixArgs{
|
||||
URI: pgf.URI,
|
||||
Fix: string(settings.InvertIfCondition),
|
||||
Range: rng,
|
||||
URI: pgf.URI,
|
||||
Fix: string(settings.InvertIfCondition),
|
||||
Range: rng,
|
||||
ResolveEdits: supportsResolveEdits(options),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -487,9 +550,10 @@ func refactorRewrite(snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.P
|
|||
return nil, err
|
||||
}
|
||||
cmd, err := command.NewApplyFixCommand(d.Message, command.ApplyFixArgs{
|
||||
URI: pgf.URI,
|
||||
Fix: string(settings.FillStruct),
|
||||
Range: rng,
|
||||
URI: pgf.URI,
|
||||
Fix: string(settings.FillStruct),
|
||||
Range: rng,
|
||||
ResolveEdits: supportsResolveEdits(options),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -499,11 +563,7 @@ func refactorRewrite(snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.P
|
|||
}
|
||||
|
||||
for i := range commands {
|
||||
actions = append(actions, protocol.CodeAction{
|
||||
Title: commands[i].Title,
|
||||
Kind: protocol.RefactorRewrite,
|
||||
Command: &commands[i],
|
||||
})
|
||||
actions = append(actions, newCodeAction(commands[i].Title, protocol.RefactorRewrite, &commands[i], nil, options))
|
||||
}
|
||||
|
||||
if snapshot.Options().IsAnalyzerEnabled(infertypeargs.Analyzer.Name) {
|
||||
|
|
|
@ -199,8 +199,9 @@ func (c *commandHandler) run(ctx context.Context, cfg commandConfig, run command
|
|||
return runcmd()
|
||||
}
|
||||
|
||||
func (c *commandHandler) ApplyFix(ctx context.Context, args command.ApplyFixArgs) error {
|
||||
return c.run(ctx, commandConfig{
|
||||
func (c *commandHandler) ApplyFix(ctx context.Context, args command.ApplyFixArgs) (*protocol.WorkspaceEdit, error) {
|
||||
var result *protocol.WorkspaceEdit
|
||||
err := c.run(ctx, commandConfig{
|
||||
// Note: no progress here. Applying fixes should be quick.
|
||||
forURI: args.URI,
|
||||
}, func(ctx context.Context, deps commandDeps) error {
|
||||
|
@ -215,10 +216,15 @@ func (c *commandHandler) ApplyFix(ctx context.Context, args command.ApplyFixArgs
|
|||
TextDocumentEdit: &edit,
|
||||
})
|
||||
}
|
||||
edit := protocol.WorkspaceEdit{
|
||||
DocumentChanges: changes,
|
||||
}
|
||||
if args.ResolveEdits {
|
||||
result = &edit
|
||||
return nil
|
||||
}
|
||||
r, err := c.s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
|
||||
Edit: protocol.WorkspaceEdit{
|
||||
DocumentChanges: changes,
|
||||
},
|
||||
Edit: edit,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -228,6 +234,7 @@ func (c *commandHandler) ApplyFix(ctx context.Context, args command.ApplyFixArgs
|
|||
}
|
||||
return nil
|
||||
})
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *commandHandler) RegenerateCgo(ctx context.Context, args command.URIArg) error {
|
||||
|
@ -1266,8 +1273,9 @@ func showDocumentImpl(ctx context.Context, cli protocol.Client, url protocol.URI
|
|||
}
|
||||
}
|
||||
|
||||
func (c *commandHandler) ChangeSignature(ctx context.Context, args command.ChangeSignatureArgs) error {
|
||||
return c.run(ctx, commandConfig{
|
||||
func (c *commandHandler) ChangeSignature(ctx context.Context, args command.ChangeSignatureArgs) (*protocol.WorkspaceEdit, error) {
|
||||
var result *protocol.WorkspaceEdit
|
||||
err := c.run(ctx, commandConfig{
|
||||
forURI: args.RemoveParameter.URI,
|
||||
}, func(ctx context.Context, deps commandDeps) error {
|
||||
// For now, gopls only supports removing unused parameters.
|
||||
|
@ -1275,10 +1283,15 @@ func (c *commandHandler) ChangeSignature(ctx context.Context, args command.Chang
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
edit := protocol.WorkspaceEdit{
|
||||
DocumentChanges: changes,
|
||||
}
|
||||
if args.ResolveEdits {
|
||||
result = &edit
|
||||
return nil
|
||||
}
|
||||
r, err := c.s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
|
||||
Edit: protocol.WorkspaceEdit{
|
||||
DocumentChanges: changes,
|
||||
},
|
||||
Edit: edit,
|
||||
})
|
||||
if !r.Applied {
|
||||
return fmt.Errorf("failed to apply edits: %v", r.FailureReason)
|
||||
|
@ -1286,6 +1299,7 @@ func (c *commandHandler) ChangeSignature(ctx context.Context, args command.Chang
|
|||
|
||||
return nil
|
||||
})
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *commandHandler) DiagnoseFiles(ctx context.Context, args command.DiagnoseFilesArgs) error {
|
||||
|
|
|
@ -114,6 +114,7 @@ func (s *server) Initialize(ctx context.Context, params *protocol.ParamInitializ
|
|||
// Using CodeActionOptions is only valid if codeActionLiteralSupport is set.
|
||||
codeActionProvider = &protocol.CodeActionOptions{
|
||||
CodeActionKinds: s.getSupportedCodeActions(),
|
||||
ResolveProvider: true,
|
||||
}
|
||||
}
|
||||
var renameOpts interface{} = true
|
||||
|
|
|
@ -102,10 +102,6 @@ func (s *server) Resolve(context.Context, *protocol.InlayHint) (*protocol.InlayH
|
|||
return nil, notImplemented("Resolve")
|
||||
}
|
||||
|
||||
func (s *server) ResolveCodeAction(context.Context, *protocol.CodeAction) (*protocol.CodeAction, error) {
|
||||
return nil, notImplemented("ResolveCodeAction")
|
||||
}
|
||||
|
||||
func (s *server) ResolveCodeLens(context.Context, *protocol.CodeLens) (*protocol.CodeLens, error) {
|
||||
return nil, notImplemented("ResolveCodeLens")
|
||||
}
|
||||
|
|
|
@ -739,16 +739,18 @@ var GeneratedAPIJSON = &APIJSON{
|
|||
ArgDoc: "{\n\t// Names and Values must have the same length.\n\t\"Names\": []string,\n\t\"Values\": []int64,\n}",
|
||||
},
|
||||
{
|
||||
Command: "gopls.apply_fix",
|
||||
Title: "Apply a fix",
|
||||
Doc: "Applies a fix to a region of source code.",
|
||||
ArgDoc: "{\n\t// The fix to apply.\n\t\"Fix\": string,\n\t// The file URI for the document to fix.\n\t\"URI\": string,\n\t// The document range to scan for fixes.\n\t\"Range\": {\n\t\t\"start\": {\n\t\t\t\"line\": uint32,\n\t\t\t\"character\": uint32,\n\t\t},\n\t\t\"end\": {\n\t\t\t\"line\": uint32,\n\t\t\t\"character\": uint32,\n\t\t},\n\t},\n}",
|
||||
Command: "gopls.apply_fix",
|
||||
Title: "Apply a fix",
|
||||
Doc: "Applies a fix to a region of source code.",
|
||||
ArgDoc: "{\n\t// The fix to apply.\n\t\"Fix\": string,\n\t// The file URI for the document to fix.\n\t\"URI\": string,\n\t// The document range to scan for fixes.\n\t\"Range\": {\n\t\t\"start\": {\n\t\t\t\"line\": uint32,\n\t\t\t\"character\": uint32,\n\t\t},\n\t\t\"end\": {\n\t\t\t\"line\": uint32,\n\t\t\t\"character\": uint32,\n\t\t},\n\t},\n\t// Whether to resolve and return the edits.\n\t\"ResolveEdits\": bool,\n}",
|
||||
ResultDoc: "{\n\t// Holds changes to existing resources.\n\t\"changes\": map[golang.org/x/tools/gopls/internal/lsp/protocol.DocumentURI][]golang.org/x/tools/gopls/internal/lsp/protocol.TextEdit,\n\t// Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes\n\t// are either an array of `TextDocumentEdit`s to express changes to n different text documents\n\t// where each text document edit addresses a specific version of a text document. Or it can contain\n\t// above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations.\n\t//\n\t// Whether a client supports versioned document edits is expressed via\n\t// `workspace.workspaceEdit.documentChanges` client capability.\n\t//\n\t// If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then\n\t// only plain `TextEdit`s using the `changes` property are supported.\n\t\"documentChanges\": []{\n\t\t\"TextDocumentEdit\": {\n\t\t\t\"textDocument\": { ... },\n\t\t\t\"edits\": { ... },\n\t\t},\n\t\t\"RenameFile\": {\n\t\t\t\"kind\": string,\n\t\t\t\"oldUri\": string,\n\t\t\t\"newUri\": string,\n\t\t\t\"options\": { ... },\n\t\t\t\"ResourceOperation\": { ... },\n\t\t},\n\t},\n\t// A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and\n\t// delete file / folder operations.\n\t//\n\t// Whether clients honor this property depends on the client capability `workspace.changeAnnotationSupport`.\n\t//\n\t// @since 3.16.0\n\t\"changeAnnotations\": map[string]golang.org/x/tools/gopls/internal/lsp/protocol.ChangeAnnotation,\n}",
|
||||
},
|
||||
{
|
||||
Command: "gopls.change_signature",
|
||||
Title: "Perform a \"change signature\" refactoring",
|
||||
Doc: "This command is experimental, currently only supporting parameter removal.\nIts signature will certainly change in the future (pun intended).",
|
||||
ArgDoc: "{\n\t\"RemoveParameter\": {\n\t\t\"uri\": string,\n\t\t\"range\": {\n\t\t\t\"start\": { ... },\n\t\t\t\"end\": { ... },\n\t\t},\n\t},\n}",
|
||||
Command: "gopls.change_signature",
|
||||
Title: "Perform a \"change signature\" refactoring",
|
||||
Doc: "This command is experimental, currently only supporting parameter removal.\nIts signature will certainly change in the future (pun intended).",
|
||||
ArgDoc: "{\n\t\"RemoveParameter\": {\n\t\t\"uri\": string,\n\t\t\"range\": {\n\t\t\t\"start\": { ... },\n\t\t\t\"end\": { ... },\n\t\t},\n\t},\n\t// Whether to resolve and return the edits.\n\t\"ResolveEdits\": bool,\n}",
|
||||
ResultDoc: "{\n\t// Holds changes to existing resources.\n\t\"changes\": map[golang.org/x/tools/gopls/internal/lsp/protocol.DocumentURI][]golang.org/x/tools/gopls/internal/lsp/protocol.TextEdit,\n\t// Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes\n\t// are either an array of `TextDocumentEdit`s to express changes to n different text documents\n\t// where each text document edit addresses a specific version of a text document. Or it can contain\n\t// above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations.\n\t//\n\t// Whether a client supports versioned document edits is expressed via\n\t// `workspace.workspaceEdit.documentChanges` client capability.\n\t//\n\t// If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then\n\t// only plain `TextEdit`s using the `changes` property are supported.\n\t\"documentChanges\": []{\n\t\t\"TextDocumentEdit\": {\n\t\t\t\"textDocument\": { ... },\n\t\t\t\"edits\": { ... },\n\t\t},\n\t\t\"RenameFile\": {\n\t\t\t\"kind\": string,\n\t\t\t\"oldUri\": string,\n\t\t\t\"newUri\": string,\n\t\t\t\"options\": { ... },\n\t\t\t\"ResourceOperation\": { ... },\n\t\t},\n\t},\n\t// A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and\n\t// delete file / folder operations.\n\t//\n\t// Whether clients honor this property depends on the client capability `workspace.changeAnnotationSupport`.\n\t//\n\t// @since 3.16.0\n\t\"changeAnnotations\": map[string]golang.org/x/tools/gopls/internal/lsp/protocol.ChangeAnnotation,\n}",
|
||||
},
|
||||
{
|
||||
Command: "gopls.check_upgrades",
|
||||
|
|
|
@ -131,6 +131,7 @@ type ClientOptions struct {
|
|||
CompletionTags bool
|
||||
CompletionDeprecated bool
|
||||
SupportedResourceOperations []protocol.ResourceOperationKind
|
||||
CodeActionResolveOptions []string
|
||||
}
|
||||
|
||||
// ServerOptions holds LSP-specific configuration that is provided by the
|
||||
|
@ -748,6 +749,11 @@ func (o *Options) ForClientCapabilities(clientName *protocol.ClientInfo, caps pr
|
|||
} else if caps.TextDocument.Completion.CompletionItem.DeprecatedSupport {
|
||||
o.CompletionDeprecated = true
|
||||
}
|
||||
|
||||
// Check if the client supports code actions resolving.
|
||||
if caps.TextDocument.CodeAction.DataSupport && caps.TextDocument.CodeAction.ResolveSupport != nil {
|
||||
o.CodeActionResolveOptions = caps.TextDocument.CodeAction.ResolveSupport.Properties
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) Clone() *Options {
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/test/integration/fake/glob"
|
||||
"golang.org/x/tools/gopls/internal/util/pathutil"
|
||||
"golang.org/x/tools/gopls/internal/util/slices"
|
||||
"golang.org/x/tools/internal/jsonrpc2"
|
||||
"golang.org/x/tools/internal/jsonrpc2/servertest"
|
||||
"golang.org/x/tools/internal/xcontext"
|
||||
|
@ -263,42 +264,11 @@ func (e *Editor) initialize(ctx context.Context) error {
|
|||
params.InitializationOptions = makeSettings(e.sandbox, config)
|
||||
params.WorkspaceFolders = makeWorkspaceFolders(e.sandbox, config.WorkspaceFolders)
|
||||
|
||||
// Set various client capabilities that are sought by gopls.
|
||||
params.Capabilities.Workspace.Configuration = true // support workspace/configuration
|
||||
params.Capabilities.Window.WorkDoneProgress = true // support window/workDoneProgress
|
||||
params.Capabilities.TextDocument.Completion.CompletionItem.TagSupport = &protocol.CompletionItemTagOptions{}
|
||||
params.Capabilities.TextDocument.Completion.CompletionItem.TagSupport.ValueSet = []protocol.CompletionItemTag{protocol.ComplDeprecated}
|
||||
params.Capabilities.TextDocument.Completion.CompletionItem.SnippetSupport = true
|
||||
params.Capabilities.TextDocument.SemanticTokens.Requests.Full = &protocol.Or_ClientSemanticTokensRequestOptions_full{Value: true}
|
||||
params.Capabilities.TextDocument.SemanticTokens.TokenTypes = []string{
|
||||
"namespace", "type", "class", "enum", "interface",
|
||||
"struct", "typeParameter", "parameter", "variable", "property", "enumMember",
|
||||
"event", "function", "method", "macro", "keyword", "modifier", "comment",
|
||||
"string", "number", "regexp", "operator",
|
||||
}
|
||||
params.Capabilities.TextDocument.SemanticTokens.TokenModifiers = []string{
|
||||
"declaration", "definition", "readonly", "static",
|
||||
"deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary",
|
||||
}
|
||||
// The LSP tests have historically enabled this flag,
|
||||
// but really we should test both ways for older editors.
|
||||
params.Capabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport = true
|
||||
// Glob pattern watching is enabled.
|
||||
params.Capabilities.Workspace.DidChangeWatchedFiles.DynamicRegistration = true
|
||||
// "rename" operations are used for package renaming.
|
||||
//
|
||||
// TODO(rfindley): add support for other resource operations (create, delete, ...)
|
||||
params.Capabilities.Workspace.WorkspaceEdit = &protocol.WorkspaceEditClientCapabilities{
|
||||
ResourceOperations: []protocol.ResourceOperationKind{
|
||||
"rename",
|
||||
},
|
||||
}
|
||||
// Apply capabilities overlay.
|
||||
if config.CapabilitiesJSON != nil {
|
||||
if err := json.Unmarshal(config.CapabilitiesJSON, ¶ms.Capabilities); err != nil {
|
||||
return fmt.Errorf("unmarshalling EditorConfig.CapabilitiesJSON: %v", err)
|
||||
}
|
||||
capabilities, err := clientCapabilities(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshalling EditorConfig.CapabilitiesJSON: %v", err)
|
||||
}
|
||||
params.Capabilities = capabilities
|
||||
|
||||
trace := protocol.TraceValues("messages")
|
||||
params.Trace = &trace
|
||||
|
@ -325,6 +295,48 @@ func (e *Editor) initialize(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func clientCapabilities(cfg EditorConfig) (protocol.ClientCapabilities, error) {
|
||||
var capabilities protocol.ClientCapabilities
|
||||
// Set various client capabilities that are sought by gopls.
|
||||
capabilities.Workspace.Configuration = true // support workspace/configuration
|
||||
capabilities.TextDocument.Completion.CompletionItem.TagSupport = &protocol.CompletionItemTagOptions{}
|
||||
capabilities.TextDocument.Completion.CompletionItem.TagSupport.ValueSet = []protocol.CompletionItemTag{protocol.ComplDeprecated}
|
||||
capabilities.TextDocument.Completion.CompletionItem.SnippetSupport = true
|
||||
capabilities.TextDocument.SemanticTokens.Requests.Full = &protocol.Or_ClientSemanticTokensRequestOptions_full{Value: true}
|
||||
capabilities.Window.WorkDoneProgress = true // support window/workDoneProgress
|
||||
capabilities.TextDocument.SemanticTokens.TokenTypes = []string{
|
||||
"namespace", "type", "class", "enum", "interface",
|
||||
"struct", "typeParameter", "parameter", "variable", "property", "enumMember",
|
||||
"event", "function", "method", "macro", "keyword", "modifier", "comment",
|
||||
"string", "number", "regexp", "operator",
|
||||
}
|
||||
capabilities.TextDocument.SemanticTokens.TokenModifiers = []string{
|
||||
"declaration", "definition", "readonly", "static",
|
||||
"deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary",
|
||||
}
|
||||
// The LSP tests have historically enabled this flag,
|
||||
// but really we should test both ways for older editors.
|
||||
capabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport = true
|
||||
// Glob pattern watching is enabled.
|
||||
capabilities.Workspace.DidChangeWatchedFiles.DynamicRegistration = true
|
||||
// "rename" operations are used for package renaming.
|
||||
//
|
||||
// TODO(rfindley): add support for other resource operations (create, delete, ...)
|
||||
capabilities.Workspace.WorkspaceEdit = &protocol.WorkspaceEditClientCapabilities{
|
||||
ResourceOperations: []protocol.ResourceOperationKind{
|
||||
"rename",
|
||||
},
|
||||
}
|
||||
|
||||
// Apply capabilities overlay.
|
||||
if cfg.CapabilitiesJSON != nil {
|
||||
if err := json.Unmarshal(cfg.CapabilitiesJSON, &capabilities); err != nil {
|
||||
return protocol.ClientCapabilities{}, fmt.Errorf("unmarshalling EditorConfig.CapabilitiesJSON: %v", err)
|
||||
}
|
||||
}
|
||||
return capabilities, nil
|
||||
}
|
||||
|
||||
// marshalUnmarshal is a helper to json Marshal and then Unmarshal as a
|
||||
// different type. Used to work around cases where our protocol types are not
|
||||
// specific.
|
||||
|
@ -902,6 +914,21 @@ func (e *Editor) ApplyQuickFixes(ctx context.Context, loc protocol.Location, dia
|
|||
|
||||
// ApplyCodeAction applies the given code action.
|
||||
func (e *Editor) ApplyCodeAction(ctx context.Context, action protocol.CodeAction) error {
|
||||
// Resolve the code actions if necessary and supported.
|
||||
if action.Edit == nil {
|
||||
editSupport, err := e.EditResolveSupport()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if editSupport {
|
||||
ca, err := e.Server.ResolveCodeAction(ctx, &action)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
action.Edit = ca.Edit
|
||||
}
|
||||
}
|
||||
|
||||
if action.Edit != nil {
|
||||
for _, change := range action.Edit.DocumentChanges {
|
||||
if change.TextDocumentEdit != nil {
|
||||
|
@ -932,11 +959,11 @@ func (e *Editor) ApplyCodeAction(ctx context.Context, action protocol.CodeAction
|
|||
|
||||
// GetQuickFixes returns the available quick fix code actions.
|
||||
func (e *Editor) GetQuickFixes(ctx context.Context, loc protocol.Location, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error) {
|
||||
return e.getCodeActions(ctx, loc, diagnostics, protocol.QuickFix, protocol.SourceFixAll)
|
||||
return e.CodeActions(ctx, loc, diagnostics, protocol.QuickFix, protocol.SourceFixAll)
|
||||
}
|
||||
|
||||
func (e *Editor) applyCodeActions(ctx context.Context, loc protocol.Location, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) (int, error) {
|
||||
actions, err := e.getCodeActions(ctx, loc, diagnostics, only...)
|
||||
actions, err := e.CodeActions(ctx, loc, diagnostics, only...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -963,7 +990,7 @@ func (e *Editor) applyCodeActions(ctx context.Context, loc protocol.Location, di
|
|||
return applied, nil
|
||||
}
|
||||
|
||||
func (e *Editor) getCodeActions(ctx context.Context, loc protocol.Location, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) ([]protocol.CodeAction, error) {
|
||||
func (e *Editor) CodeActions(ctx context.Context, loc protocol.Location, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) ([]protocol.CodeAction, error) {
|
||||
if e.Server == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -1471,6 +1498,14 @@ func (e *Editor) CodeAction(ctx context.Context, loc protocol.Location, diagnost
|
|||
return lens, nil
|
||||
}
|
||||
|
||||
func (e *Editor) EditResolveSupport() (bool, error) {
|
||||
capabilities, err := clientCapabilities(e.Config())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return capabilities.TextDocument.CodeAction.ResolveSupport != nil && slices.Contains(capabilities.TextDocument.CodeAction.ResolveSupport.Properties, "edit"), nil
|
||||
}
|
||||
|
||||
// Hover triggers a hover at the given position in an open buffer.
|
||||
func (e *Editor) Hover(ctx context.Context, loc protocol.Location) (*protocol.MarkupContent, protocol.Location, error) {
|
||||
if err := e.checkBufferLocation(loc); err != nil {
|
||||
|
|
|
@ -7,14 +7,24 @@ package misc
|
|||
import (
|
||||
"testing"
|
||||
|
||||
. "golang.org/x/tools/gopls/internal/test/integration"
|
||||
"golang.org/x/tools/gopls/internal/test/compare"
|
||||
. "golang.org/x/tools/gopls/internal/test/integration"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
)
|
||||
|
||||
// A basic test for fillstruct, now that it uses a command.
|
||||
// A basic test for fillstruct, now that it uses a command and supports resolve edits.
|
||||
func TestFillStruct(t *testing.T) {
|
||||
tc := []struct {
|
||||
name string
|
||||
capabilities string
|
||||
wantCommand bool
|
||||
}{
|
||||
{"default", "{}", true},
|
||||
{"no data", `{ "textDocument": {"codeAction": { "resolveSupport": { "properties": ["edit"] } } } }`, true},
|
||||
{"resolve support", `{ "textDocument": {"codeAction": { "dataSupport": true, "resolveSupport": { "properties": ["edit"] } } } }`, false},
|
||||
}
|
||||
|
||||
const basic = `
|
||||
-- go.mod --
|
||||
module mod.com
|
||||
|
@ -32,12 +42,35 @@ func Foo() {
|
|||
_ = Info{}
|
||||
}
|
||||
`
|
||||
Run(t, basic, func(t *testing.T, env *Env) {
|
||||
env.OpenFile("main.go")
|
||||
if err := env.Editor.RefactorRewrite(env.Ctx, env.RegexpSearch("main.go", "Info{}")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := `package main
|
||||
|
||||
for _, tt := range tc {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
runner := WithOptions(CapabilitiesJSON([]byte(tt.capabilities)))
|
||||
|
||||
runner.Run(t, basic, func(t *testing.T, env *Env) {
|
||||
env.OpenFile("main.go")
|
||||
fixes, err := env.Editor.CodeActions(env.Ctx, env.RegexpSearch("main.go", "Info{}"), nil, protocol.RefactorRewrite)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(fixes) != 1 {
|
||||
t.Fatalf("expected 1 code action, got %v", len(fixes))
|
||||
}
|
||||
if tt.wantCommand {
|
||||
if fixes[0].Command == nil || fixes[0].Data != nil {
|
||||
t.Errorf("expected code action to have command not data, got %v", fixes[0])
|
||||
}
|
||||
} else {
|
||||
if fixes[0].Command != nil || fixes[0].Data == nil {
|
||||
t.Errorf("expected code action to have command not data, got %v", fixes[0])
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the code action (handles resolving the code action), and check that the result is correct.
|
||||
if err := env.Editor.RefactorRewrite(env.Ctx, env.RegexpSearch("main.go", "Info{}")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := `package main
|
||||
|
||||
type Info struct {
|
||||
WordCounts map[string]int
|
||||
|
@ -51,10 +84,12 @@ func Foo() {
|
|||
}
|
||||
}
|
||||
`
|
||||
if got := env.BufferText("main.go"); got != want {
|
||||
t.Fatalf("TestFillStruct failed:\n%s", compare.Text(want, got))
|
||||
}
|
||||
})
|
||||
if got := env.BufferText("main.go"); got != want {
|
||||
t.Fatalf("TestFillStruct failed:\n%s", compare.Text(want, got))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFillReturns(t *testing.T) {
|
||||
|
|
|
@ -73,6 +73,13 @@ func ClientName(name string) RunOption {
|
|||
})
|
||||
}
|
||||
|
||||
// CapabilitiesJSON sets the capabalities json.
|
||||
func CapabilitiesJSON(capabilities []byte) RunOption {
|
||||
return optionSetter(func(opts *runConfig) {
|
||||
opts.editor.CapabilitiesJSON = capabilities
|
||||
})
|
||||
}
|
||||
|
||||
// Settings sets user-provided configuration for the LSP server.
|
||||
//
|
||||
// As a special case, the env setting must not be provided via Settings: use
|
||||
|
|
|
@ -1919,6 +1919,23 @@ func codeActionChanges(env *integration.Env, uri protocol.DocumentURI, rng proto
|
|||
// applied in that order. But since applyDocumentChanges(env,
|
||||
// action.Edit.DocumentChanges) doesn't compose, for now we
|
||||
// assert that actions return one or the other.
|
||||
|
||||
// Resolve code action edits first if the client has resolve support
|
||||
// and the code action has no edits.
|
||||
if action.Edit == nil {
|
||||
editSupport, err := env.Editor.EditResolveSupport()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if editSupport {
|
||||
resolved, err := env.Editor.Server.ResolveCodeAction(env.Ctx, &action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
action.Edit = resolved.Edit
|
||||
}
|
||||
}
|
||||
|
||||
if action.Edit != nil {
|
||||
if action.Edit.Changes != nil {
|
||||
env.T.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Edit.Changes", action.Kind, action.Title)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
This test checks the behavior of the 'extract variable' code action.
|
||||
See extract_variable_resolve.txt for the same test with resolve support.
|
||||
|
||||
-- flags --
|
||||
-ignore_extra_diags
|
||||
|
|
81
gopls/internal/test/marker/testdata/codeaction/extract_variable_resolve.txt
поставляемый
Normal file
81
gopls/internal/test/marker/testdata/codeaction/extract_variable_resolve.txt
поставляемый
Normal file
|
@ -0,0 +1,81 @@
|
|||
This test checks the behavior of the 'extract variable' code action, with resolve support.
|
||||
See extract_variable.txt for the same test without resolve support.
|
||||
|
||||
-- capabilities.json --
|
||||
{
|
||||
"textDocument": {
|
||||
"codeAction": {
|
||||
"dataSupport": true,
|
||||
"resolveSupport": {
|
||||
"properties": ["edit"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-- flags --
|
||||
-ignore_extra_diags
|
||||
|
||||
-- basic_lit.go --
|
||||
package extract
|
||||
|
||||
func _() {
|
||||
var _ = 1 + 2 //@codeactionedit("1", "refactor.extract", basic_lit1)
|
||||
var _ = 3 + 4 //@codeactionedit("3 + 4", "refactor.extract", basic_lit2)
|
||||
}
|
||||
|
||||
-- @basic_lit1/basic_lit.go --
|
||||
@@ -4 +4,2 @@
|
||||
- var _ = 1 + 2 //@codeactionedit("1", "refactor.extract", basic_lit1)
|
||||
+ x := 1
|
||||
+ var _ = x + 2 //@codeactionedit("1", "refactor.extract", basic_lit1)
|
||||
-- @basic_lit2/basic_lit.go --
|
||||
@@ -5 +5,2 @@
|
||||
- var _ = 3 + 4 //@codeactionedit("3 + 4", "refactor.extract", basic_lit2)
|
||||
+ x := 3 + 4
|
||||
+ var _ = x //@codeactionedit("3 + 4", "refactor.extract", basic_lit2)
|
||||
-- func_call.go --
|
||||
package extract
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
x0 := append([]int{}, 1) //@codeactionedit("append([]int{}, 1)", "refactor.extract", func_call1)
|
||||
str := "1"
|
||||
b, err := strconv.Atoi(str) //@codeactionedit("strconv.Atoi(str)", "refactor.extract", func_call2)
|
||||
}
|
||||
|
||||
-- @func_call1/func_call.go --
|
||||
@@ -6 +6,2 @@
|
||||
- x0 := append([]int{}, 1) //@codeactionedit("append([]int{}, 1)", "refactor.extract", func_call1)
|
||||
+ x := append([]int{}, 1)
|
||||
+ x0 := x //@codeactionedit("append([]int{}, 1)", "refactor.extract", func_call1)
|
||||
-- @func_call2/func_call.go --
|
||||
@@ -8 +8,2 @@
|
||||
- b, err := strconv.Atoi(str) //@codeactionedit("strconv.Atoi(str)", "refactor.extract", func_call2)
|
||||
+ x, x1 := strconv.Atoi(str)
|
||||
+ b, err := x, x1 //@codeactionedit("strconv.Atoi(str)", "refactor.extract", func_call2)
|
||||
-- scope.go --
|
||||
package extract
|
||||
|
||||
import "go/ast"
|
||||
|
||||
func _() {
|
||||
x0 := 0
|
||||
if true {
|
||||
y := ast.CompositeLit{} //@codeactionedit("ast.CompositeLit{}", "refactor.extract", scope1)
|
||||
}
|
||||
if true {
|
||||
x1 := !false //@codeactionedit("!false", "refactor.extract", scope2)
|
||||
}
|
||||
}
|
||||
|
||||
-- @scope1/scope.go --
|
||||
@@ -8 +8,2 @@
|
||||
- y := ast.CompositeLit{} //@codeactionedit("ast.CompositeLit{}", "refactor.extract", scope1)
|
||||
+ x := ast.CompositeLit{}
|
||||
+ y := x //@codeactionedit("ast.CompositeLit{}", "refactor.extract", scope1)
|
||||
-- @scope2/scope.go --
|
||||
@@ -11 +11,2 @@
|
||||
- x1 := !false //@codeactionedit("!false", "refactor.extract", scope2)
|
||||
+ x := !false
|
||||
+ x1 := x //@codeactionedit("!false", "refactor.extract", scope2)
|
|
@ -1,4 +1,5 @@
|
|||
This test checks the behavior of the 'fill struct' code action.
|
||||
See fill_struct_resolve.txt for same test with resolve support.
|
||||
|
||||
-- flags --
|
||||
-ignore_extra_diags
|
||||
|
@ -89,11 +90,11 @@ type funStruct struct {
|
|||
|
||||
var _ = funStruct{} //@codeactionedit("}", "refactor.rewrite", a22)
|
||||
|
||||
type funStructCompex struct {
|
||||
type funStructComplex struct {
|
||||
fn func(i int, s string) (string, int)
|
||||
}
|
||||
|
||||
var _ = funStructCompex{} //@codeactionedit("}", "refactor.rewrite", a23)
|
||||
var _ = funStructComplex{} //@codeactionedit("}", "refactor.rewrite", a23)
|
||||
|
||||
type funStructEmpty struct {
|
||||
fn func()
|
||||
|
@ -120,8 +121,8 @@ var _ = funStructEmpty{} //@codeactionedit("}", "refactor.rewrite", a24)
|
|||
+} //@codeactionedit("}", "refactor.rewrite", a22)
|
||||
-- @a23/a2.go --
|
||||
@@ -23 +23,4 @@
|
||||
-var _ = funStructCompex{} //@codeactionedit("}", "refactor.rewrite", a23)
|
||||
+var _ = funStructCompex{
|
||||
-var _ = funStructComplex{} //@codeactionedit("}", "refactor.rewrite", a23)
|
||||
+var _ = funStructComplex{
|
||||
+ fn: func(i int, s string) (string, int) {
|
||||
+ },
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a23)
|
||||
|
|
|
@ -0,0 +1,586 @@
|
|||
This test checks the behavior of the 'fill struct' code action, with resolve support.
|
||||
See fill_struct.txt for same test without resolve support.
|
||||
|
||||
-- capabilities.json --
|
||||
{
|
||||
"textDocument": {
|
||||
"codeAction": {
|
||||
"dataSupport": true,
|
||||
"resolveSupport": {
|
||||
"properties": ["edit"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-- flags --
|
||||
-ignore_extra_diags
|
||||
|
||||
-- go.mod --
|
||||
module golang.org/lsptests/fillstruct
|
||||
|
||||
go 1.18
|
||||
|
||||
-- data/data.go --
|
||||
package data
|
||||
|
||||
type B struct {
|
||||
ExportedInt int
|
||||
unexportedInt int
|
||||
}
|
||||
|
||||
-- a.go --
|
||||
package fillstruct
|
||||
|
||||
import (
|
||||
"golang.org/lsptests/fillstruct/data"
|
||||
)
|
||||
|
||||
type basicStruct struct {
|
||||
foo int
|
||||
}
|
||||
|
||||
var _ = basicStruct{} //@codeactionedit("}", "refactor.rewrite", a1)
|
||||
|
||||
type twoArgStruct struct {
|
||||
foo int
|
||||
bar string
|
||||
}
|
||||
|
||||
var _ = twoArgStruct{} //@codeactionedit("}", "refactor.rewrite", a2)
|
||||
|
||||
type nestedStruct struct {
|
||||
bar string
|
||||
basic basicStruct
|
||||
}
|
||||
|
||||
var _ = nestedStruct{} //@codeactionedit("}", "refactor.rewrite", a3)
|
||||
|
||||
var _ = data.B{} //@codeactionedit("}", "refactor.rewrite", a4)
|
||||
-- @a1/a.go --
|
||||
@@ -11 +11,3 @@
|
||||
-var _ = basicStruct{} //@codeactionedit("}", "refactor.rewrite", a1)
|
||||
+var _ = basicStruct{
|
||||
+ foo: 0,
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a1)
|
||||
-- @a2/a.go --
|
||||
@@ -18 +18,4 @@
|
||||
-var _ = twoArgStruct{} //@codeactionedit("}", "refactor.rewrite", a2)
|
||||
+var _ = twoArgStruct{
|
||||
+ foo: 0,
|
||||
+ bar: "",
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a2)
|
||||
-- @a3/a.go --
|
||||
@@ -25 +25,4 @@
|
||||
-var _ = nestedStruct{} //@codeactionedit("}", "refactor.rewrite", a3)
|
||||
+var _ = nestedStruct{
|
||||
+ bar: "",
|
||||
+ basic: basicStruct{},
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a3)
|
||||
-- @a4/a.go --
|
||||
@@ -27 +27,3 @@
|
||||
-var _ = data.B{} //@codeactionedit("}", "refactor.rewrite", a4)
|
||||
+var _ = data.B{
|
||||
+ ExportedInt: 0,
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a4)
|
||||
-- a2.go --
|
||||
package fillstruct
|
||||
|
||||
type typedStruct struct {
|
||||
m map[string]int
|
||||
s []int
|
||||
c chan int
|
||||
c1 <-chan int
|
||||
a [2]string
|
||||
}
|
||||
|
||||
var _ = typedStruct{} //@codeactionedit("}", "refactor.rewrite", a21)
|
||||
|
||||
type funStruct struct {
|
||||
fn func(i int) int
|
||||
}
|
||||
|
||||
var _ = funStruct{} //@codeactionedit("}", "refactor.rewrite", a22)
|
||||
|
||||
type funStructComplex struct {
|
||||
fn func(i int, s string) (string, int)
|
||||
}
|
||||
|
||||
var _ = funStructComplex{} //@codeactionedit("}", "refactor.rewrite", a23)
|
||||
|
||||
type funStructEmpty struct {
|
||||
fn func()
|
||||
}
|
||||
|
||||
var _ = funStructEmpty{} //@codeactionedit("}", "refactor.rewrite", a24)
|
||||
|
||||
-- @a21/a2.go --
|
||||
@@ -11 +11,7 @@
|
||||
-var _ = typedStruct{} //@codeactionedit("}", "refactor.rewrite", a21)
|
||||
+var _ = typedStruct{
|
||||
+ m: map[string]int{},
|
||||
+ s: []int{},
|
||||
+ c: make(chan int),
|
||||
+ c1: make(<-chan int),
|
||||
+ a: [2]string{},
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a21)
|
||||
-- @a22/a2.go --
|
||||
@@ -17 +17,4 @@
|
||||
-var _ = funStruct{} //@codeactionedit("}", "refactor.rewrite", a22)
|
||||
+var _ = funStruct{
|
||||
+ fn: func(i int) int {
|
||||
+ },
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a22)
|
||||
-- @a23/a2.go --
|
||||
@@ -23 +23,4 @@
|
||||
-var _ = funStructComplex{} //@codeactionedit("}", "refactor.rewrite", a23)
|
||||
+var _ = funStructComplex{
|
||||
+ fn: func(i int, s string) (string, int) {
|
||||
+ },
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a23)
|
||||
-- @a24/a2.go --
|
||||
@@ -29 +29,4 @@
|
||||
-var _ = funStructEmpty{} //@codeactionedit("}", "refactor.rewrite", a24)
|
||||
+var _ = funStructEmpty{
|
||||
+ fn: func() {
|
||||
+ },
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a24)
|
||||
-- a3.go --
|
||||
package fillstruct
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
type Foo struct {
|
||||
A int
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
X *Foo
|
||||
Y *Foo
|
||||
}
|
||||
|
||||
var _ = Bar{} //@codeactionedit("}", "refactor.rewrite", a31)
|
||||
|
||||
type importedStruct struct {
|
||||
m map[*ast.CompositeLit]ast.Field
|
||||
s []ast.BadExpr
|
||||
a [3]token.Token
|
||||
c chan ast.EmptyStmt
|
||||
fn func(ast_decl ast.DeclStmt) ast.Ellipsis
|
||||
st ast.CompositeLit
|
||||
}
|
||||
|
||||
var _ = importedStruct{} //@codeactionedit("}", "refactor.rewrite", a32)
|
||||
|
||||
type pointerBuiltinStruct struct {
|
||||
b *bool
|
||||
s *string
|
||||
i *int
|
||||
}
|
||||
|
||||
var _ = pointerBuiltinStruct{} //@codeactionedit("}", "refactor.rewrite", a33)
|
||||
|
||||
var _ = []ast.BasicLit{
|
||||
{}, //@codeactionedit("}", "refactor.rewrite", a34)
|
||||
}
|
||||
|
||||
var _ = []ast.BasicLit{{}} //@codeactionedit("}", "refactor.rewrite", a35)
|
||||
-- @a31/a3.go --
|
||||
@@ -17 +17,4 @@
|
||||
-var _ = Bar{} //@codeactionedit("}", "refactor.rewrite", a31)
|
||||
+var _ = Bar{
|
||||
+ X: &Foo{},
|
||||
+ Y: &Foo{},
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a31)
|
||||
-- @a32/a3.go --
|
||||
@@ -28 +28,9 @@
|
||||
-var _ = importedStruct{} //@codeactionedit("}", "refactor.rewrite", a32)
|
||||
+var _ = importedStruct{
|
||||
+ m: map[*ast.CompositeLit]ast.Field{},
|
||||
+ s: []ast.BadExpr{},
|
||||
+ a: [3]token.Token{},
|
||||
+ c: make(chan ast.EmptyStmt),
|
||||
+ fn: func(ast_decl ast.DeclStmt) ast.Ellipsis {
|
||||
+ },
|
||||
+ st: ast.CompositeLit{},
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a32)
|
||||
-- @a33/a3.go --
|
||||
@@ -36 +36,5 @@
|
||||
-var _ = pointerBuiltinStruct{} //@codeactionedit("}", "refactor.rewrite", a33)
|
||||
+var _ = pointerBuiltinStruct{
|
||||
+ b: new(bool),
|
||||
+ s: new(string),
|
||||
+ i: new(int),
|
||||
+} //@codeactionedit("}", "refactor.rewrite", a33)
|
||||
-- @a34/a3.go --
|
||||
@@ -39 +39,5 @@
|
||||
- {}, //@codeactionedit("}", "refactor.rewrite", a34)
|
||||
+ {
|
||||
+ ValuePos: 0,
|
||||
+ Kind: 0,
|
||||
+ Value: "",
|
||||
+ }, //@codeactionedit("}", "refactor.rewrite", a34)
|
||||
-- @a35/a3.go --
|
||||
@@ -42 +42,5 @@
|
||||
-var _ = []ast.BasicLit{{}} //@codeactionedit("}", "refactor.rewrite", a35)
|
||||
+var _ = []ast.BasicLit{{
|
||||
+ ValuePos: 0,
|
||||
+ Kind: 0,
|
||||
+ Value: "",
|
||||
+}} //@codeactionedit("}", "refactor.rewrite", a35)
|
||||
-- a4.go --
|
||||
package fillstruct
|
||||
|
||||
import "go/ast"
|
||||
|
||||
type iStruct struct {
|
||||
X int
|
||||
}
|
||||
|
||||
type sStruct struct {
|
||||
str string
|
||||
}
|
||||
|
||||
type multiFill struct {
|
||||
num int
|
||||
strin string
|
||||
arr []int
|
||||
}
|
||||
|
||||
type assignStruct struct {
|
||||
n ast.Node
|
||||
}
|
||||
|
||||
func fill() {
|
||||
var x int
|
||||
var _ = iStruct{} //@codeactionedit("}", "refactor.rewrite", a41)
|
||||
|
||||
var s string
|
||||
var _ = sStruct{} //@codeactionedit("}", "refactor.rewrite", a42)
|
||||
|
||||
var n int
|
||||
_ = []int{}
|
||||
if true {
|
||||
arr := []int{1, 2}
|
||||
}
|
||||
var _ = multiFill{} //@codeactionedit("}", "refactor.rewrite", a43)
|
||||
|
||||
var node *ast.CompositeLit
|
||||
var _ = assignStruct{} //@codeactionedit("}", "refactor.rewrite", a45)
|
||||
}
|
||||
|
||||
-- @a41/a4.go --
|
||||
@@ -25 +25,3 @@
|
||||
- var _ = iStruct{} //@codeactionedit("}", "refactor.rewrite", a41)
|
||||
+ var _ = iStruct{
|
||||
+ X: x,
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", a41)
|
||||
-- @a42/a4.go --
|
||||
@@ -28 +28,3 @@
|
||||
- var _ = sStruct{} //@codeactionedit("}", "refactor.rewrite", a42)
|
||||
+ var _ = sStruct{
|
||||
+ str: s,
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", a42)
|
||||
-- @a43/a4.go --
|
||||
@@ -35 +35,5 @@
|
||||
- var _ = multiFill{} //@codeactionedit("}", "refactor.rewrite", a43)
|
||||
+ var _ = multiFill{
|
||||
+ num: n,
|
||||
+ strin: s,
|
||||
+ arr: []int{},
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", a43)
|
||||
-- @a45/a4.go --
|
||||
@@ -38 +38,3 @@
|
||||
- var _ = assignStruct{} //@codeactionedit("}", "refactor.rewrite", a45)
|
||||
+ var _ = assignStruct{
|
||||
+ n: node,
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", a45)
|
||||
-- fill_struct.go --
|
||||
package fillstruct
|
||||
|
||||
type StructA struct {
|
||||
unexportedIntField int
|
||||
ExportedIntField int
|
||||
MapA map[int]string
|
||||
Array []int
|
||||
StructB
|
||||
}
|
||||
|
||||
type StructA2 struct {
|
||||
B *StructB
|
||||
}
|
||||
|
||||
type StructA3 struct {
|
||||
B StructB
|
||||
}
|
||||
|
||||
func fill() {
|
||||
a := StructA{} //@codeactionedit("}", "refactor.rewrite", fill_struct1)
|
||||
b := StructA2{} //@codeactionedit("}", "refactor.rewrite", fill_struct2)
|
||||
c := StructA3{} //@codeactionedit("}", "refactor.rewrite", fill_struct3)
|
||||
if true {
|
||||
_ = StructA3{} //@codeactionedit("}", "refactor.rewrite", fill_struct4)
|
||||
}
|
||||
}
|
||||
|
||||
-- @fill_struct1/fill_struct.go --
|
||||
@@ -20 +20,7 @@
|
||||
- a := StructA{} //@codeactionedit("}", "refactor.rewrite", fill_struct1)
|
||||
+ a := StructA{
|
||||
+ unexportedIntField: 0,
|
||||
+ ExportedIntField: 0,
|
||||
+ MapA: map[int]string{},
|
||||
+ Array: []int{},
|
||||
+ StructB: StructB{},
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct1)
|
||||
-- @fill_struct2/fill_struct.go --
|
||||
@@ -21 +21,3 @@
|
||||
- b := StructA2{} //@codeactionedit("}", "refactor.rewrite", fill_struct2)
|
||||
+ b := StructA2{
|
||||
+ B: &StructB{},
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct2)
|
||||
-- @fill_struct3/fill_struct.go --
|
||||
@@ -22 +22,3 @@
|
||||
- c := StructA3{} //@codeactionedit("}", "refactor.rewrite", fill_struct3)
|
||||
+ c := StructA3{
|
||||
+ B: StructB{},
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct3)
|
||||
-- @fill_struct4/fill_struct.go --
|
||||
@@ -24 +24,3 @@
|
||||
- _ = StructA3{} //@codeactionedit("}", "refactor.rewrite", fill_struct4)
|
||||
+ _ = StructA3{
|
||||
+ B: StructB{},
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct4)
|
||||
-- fill_struct_anon.go --
|
||||
package fillstruct
|
||||
|
||||
type StructAnon struct {
|
||||
a struct{}
|
||||
b map[string]interface{}
|
||||
c map[string]struct {
|
||||
d int
|
||||
e bool
|
||||
}
|
||||
}
|
||||
|
||||
func fill() {
|
||||
_ := StructAnon{} //@codeactionedit("}", "refactor.rewrite", fill_struct_anon)
|
||||
}
|
||||
-- @fill_struct_anon/fill_struct_anon.go --
|
||||
@@ -13 +13,5 @@
|
||||
- _ := StructAnon{} //@codeactionedit("}", "refactor.rewrite", fill_struct_anon)
|
||||
+ _ := StructAnon{
|
||||
+ a: struct{}{},
|
||||
+ b: map[string]interface{}{},
|
||||
+ c: map[string]struct{d int; e bool}{},
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct_anon)
|
||||
-- fill_struct_nested.go --
|
||||
package fillstruct
|
||||
|
||||
type StructB struct {
|
||||
StructC
|
||||
}
|
||||
|
||||
type StructC struct {
|
||||
unexportedInt int
|
||||
}
|
||||
|
||||
func nested() {
|
||||
c := StructB{
|
||||
StructC: StructC{}, //@codeactionedit("}", "refactor.rewrite", fill_nested)
|
||||
}
|
||||
}
|
||||
|
||||
-- @fill_nested/fill_struct_nested.go --
|
||||
@@ -13 +13,3 @@
|
||||
- StructC: StructC{}, //@codeactionedit("}", "refactor.rewrite", fill_nested)
|
||||
+ StructC: StructC{
|
||||
+ unexportedInt: 0,
|
||||
+ }, //@codeactionedit("}", "refactor.rewrite", fill_nested)
|
||||
-- fill_struct_package.go --
|
||||
package fillstruct
|
||||
|
||||
import (
|
||||
h2 "net/http"
|
||||
|
||||
"golang.org/lsptests/fillstruct/data"
|
||||
)
|
||||
|
||||
func unexported() {
|
||||
a := data.B{} //@codeactionedit("}", "refactor.rewrite", fill_struct_package1)
|
||||
_ = h2.Client{} //@codeactionedit("}", "refactor.rewrite", fill_struct_package2)
|
||||
}
|
||||
-- @fill_struct_package1/fill_struct_package.go --
|
||||
@@ -10 +10,3 @@
|
||||
- a := data.B{} //@codeactionedit("}", "refactor.rewrite", fill_struct_package1)
|
||||
+ a := data.B{
|
||||
+ ExportedInt: 0,
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct_package1)
|
||||
-- @fill_struct_package2/fill_struct_package.go --
|
||||
@@ -11 +11,7 @@
|
||||
- _ = h2.Client{} //@codeactionedit("}", "refactor.rewrite", fill_struct_package2)
|
||||
+ _ = h2.Client{
|
||||
+ Transport: nil,
|
||||
+ CheckRedirect: func(req *h2.Request, via []*h2.Request) error {
|
||||
+ },
|
||||
+ Jar: nil,
|
||||
+ Timeout: 0,
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct_package2)
|
||||
-- fill_struct_partial.go --
|
||||
package fillstruct
|
||||
|
||||
type StructPartialA struct {
|
||||
PrefilledInt int
|
||||
UnfilledInt int
|
||||
StructPartialB
|
||||
}
|
||||
|
||||
type StructPartialB struct {
|
||||
PrefilledInt int
|
||||
UnfilledInt int
|
||||
}
|
||||
|
||||
func fill() {
|
||||
a := StructPartialA{
|
||||
PrefilledInt: 5,
|
||||
} //@codeactionedit("}", "refactor.rewrite", fill_struct_partial1)
|
||||
b := StructPartialB{
|
||||
/* this comment should disappear */
|
||||
PrefilledInt: 7, // This comment should be blown away.
|
||||
/* As should
|
||||
this one */
|
||||
} //@codeactionedit("}", "refactor.rewrite", fill_struct_partial2)
|
||||
}
|
||||
|
||||
-- @fill_struct_partial1/fill_struct_partial.go --
|
||||
@@ -16 +16,3 @@
|
||||
- PrefilledInt: 5,
|
||||
+ PrefilledInt: 5,
|
||||
+ UnfilledInt: 0,
|
||||
+ StructPartialB: StructPartialB{},
|
||||
-- @fill_struct_partial2/fill_struct_partial.go --
|
||||
@@ -19,4 +19,2 @@
|
||||
- /* this comment should disappear */
|
||||
- PrefilledInt: 7, // This comment should be blown away.
|
||||
- /* As should
|
||||
- this one */
|
||||
+ PrefilledInt: 7,
|
||||
+ UnfilledInt: 0,
|
||||
-- fill_struct_spaces.go --
|
||||
package fillstruct
|
||||
|
||||
type StructD struct {
|
||||
ExportedIntField int
|
||||
}
|
||||
|
||||
func spaces() {
|
||||
d := StructD{} //@codeactionedit("}", "refactor.rewrite", fill_struct_spaces)
|
||||
}
|
||||
|
||||
-- @fill_struct_spaces/fill_struct_spaces.go --
|
||||
@@ -8 +8,3 @@
|
||||
- d := StructD{} //@codeactionedit("}", "refactor.rewrite", fill_struct_spaces)
|
||||
+ d := StructD{
|
||||
+ ExportedIntField: 0,
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct_spaces)
|
||||
-- fill_struct_unsafe.go --
|
||||
package fillstruct
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type unsafeStruct struct {
|
||||
x int
|
||||
p unsafe.Pointer
|
||||
}
|
||||
|
||||
func fill() {
|
||||
_ := unsafeStruct{} //@codeactionedit("}", "refactor.rewrite", fill_struct_unsafe)
|
||||
}
|
||||
|
||||
-- @fill_struct_unsafe/fill_struct_unsafe.go --
|
||||
@@ -11 +11,4 @@
|
||||
- _ := unsafeStruct{} //@codeactionedit("}", "refactor.rewrite", fill_struct_unsafe)
|
||||
+ _ := unsafeStruct{
|
||||
+ x: 0,
|
||||
+ p: nil,
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", fill_struct_unsafe)
|
||||
-- typeparams.go --
|
||||
package fillstruct
|
||||
|
||||
type emptyStructWithTypeParams[A any] struct{}
|
||||
|
||||
var _ = emptyStructWithTypeParams[int]{} // no suggested fix
|
||||
|
||||
type basicStructWithTypeParams[T any] struct {
|
||||
foo T
|
||||
}
|
||||
|
||||
var _ = basicStructWithTypeParams[int]{} //@codeactionedit("}", "refactor.rewrite", typeparams1)
|
||||
|
||||
type twoArgStructWithTypeParams[F, B any] struct {
|
||||
foo F
|
||||
bar B
|
||||
}
|
||||
|
||||
var _ = twoArgStructWithTypeParams[string, int]{} //@codeactionedit("}", "refactor.rewrite", typeparams2)
|
||||
|
||||
var _ = twoArgStructWithTypeParams[int, string]{
|
||||
bar: "bar",
|
||||
} //@codeactionedit("}", "refactor.rewrite", typeparams3)
|
||||
|
||||
type nestedStructWithTypeParams struct {
|
||||
bar string
|
||||
basic basicStructWithTypeParams[int]
|
||||
}
|
||||
|
||||
var _ = nestedStructWithTypeParams{} //@codeactionedit("}", "refactor.rewrite", typeparams4)
|
||||
|
||||
func _[T any]() {
|
||||
type S struct{ t T }
|
||||
_ = S{} //@codeactionedit("}", "refactor.rewrite", typeparams5)
|
||||
}
|
||||
-- @typeparams1/typeparams.go --
|
||||
@@ -11 +11,3 @@
|
||||
-var _ = basicStructWithTypeParams[int]{} //@codeactionedit("}", "refactor.rewrite", typeparams1)
|
||||
+var _ = basicStructWithTypeParams[int]{
|
||||
+ foo: 0,
|
||||
+} //@codeactionedit("}", "refactor.rewrite", typeparams1)
|
||||
-- @typeparams2/typeparams.go --
|
||||
@@ -18 +18,4 @@
|
||||
-var _ = twoArgStructWithTypeParams[string, int]{} //@codeactionedit("}", "refactor.rewrite", typeparams2)
|
||||
+var _ = twoArgStructWithTypeParams[string, int]{
|
||||
+ foo: "",
|
||||
+ bar: 0,
|
||||
+} //@codeactionedit("}", "refactor.rewrite", typeparams2)
|
||||
-- @typeparams3/typeparams.go --
|
||||
@@ -21 +21 @@
|
||||
+ foo: 0,
|
||||
-- @typeparams4/typeparams.go --
|
||||
@@ -29 +29,4 @@
|
||||
-var _ = nestedStructWithTypeParams{} //@codeactionedit("}", "refactor.rewrite", typeparams4)
|
||||
+var _ = nestedStructWithTypeParams{
|
||||
+ bar: "",
|
||||
+ basic: basicStructWithTypeParams{},
|
||||
+} //@codeactionedit("}", "refactor.rewrite", typeparams4)
|
||||
-- @typeparams5/typeparams.go --
|
||||
@@ -33 +33,3 @@
|
||||
- _ = S{} //@codeactionedit("}", "refactor.rewrite", typeparams5)
|
||||
+ _ = S{
|
||||
+ t: *new(T),
|
||||
+ } //@codeactionedit("}", "refactor.rewrite", typeparams5)
|
||||
-- issue63921.go --
|
||||
package fillstruct
|
||||
|
||||
// Test for golang/go#63921: fillstruct panicked with invalid fields.
|
||||
type invalidStruct struct {
|
||||
F int
|
||||
Undefined
|
||||
}
|
||||
|
||||
func _() {
|
||||
// Note: the golden content for issue63921 is empty: fillstruct produces no
|
||||
// edits, but does not panic.
|
||||
invalidStruct{} //@codeactionedit("}", "refactor.rewrite", issue63921)
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
This test exercises the refactoring to remove unused parameters.
|
||||
See removeparam_resolve.txt for same test with resolve support.
|
||||
|
||||
-- go.mod --
|
||||
module unused.mod
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
This test exercises the refactoring to remove unused parameters, with resolve support.
|
||||
See removeparam.txt for same test without resolve support.
|
||||
|
||||
-- capabilities.json --
|
||||
{
|
||||
"textDocument": {
|
||||
"codeAction": {
|
||||
"dataSupport": true,
|
||||
"resolveSupport": {
|
||||
"properties": ["edit"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-- go.mod --
|
||||
module unused.mod
|
||||
|
||||
go 1.18
|
||||
|
||||
-- a/a.go --
|
||||
package a
|
||||
|
||||
func A(x, unused int) int { //@codeaction("unused", "unused", "refactor.rewrite", a)
|
||||
return x
|
||||
}
|
||||
|
||||
-- @a/a/a.go --
|
||||
package a
|
||||
|
||||
func A(x int) int { //@codeaction("unused", "unused", "refactor.rewrite", a)
|
||||
return x
|
||||
}
|
||||
|
||||
-- a/a2.go --
|
||||
package a
|
||||
|
||||
func _() {
|
||||
A(1, 2)
|
||||
}
|
||||
|
||||
-- a/a_test.go --
|
||||
package a
|
||||
|
||||
func _() {
|
||||
A(1, 2)
|
||||
}
|
||||
|
||||
-- a/a_x_test.go --
|
||||
package a_test
|
||||
|
||||
import "unused.mod/a"
|
||||
|
||||
func _() {
|
||||
a.A(1, 2)
|
||||
}
|
||||
|
||||
-- b/b.go --
|
||||
package b
|
||||
|
||||
import "unused.mod/a"
|
||||
|
||||
func f() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func g() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func _() {
|
||||
a.A(f(), 1)
|
||||
}
|
||||
|
||||
-- @a/a/a2.go --
|
||||
package a
|
||||
|
||||
func _() {
|
||||
A(1)
|
||||
}
|
||||
-- @a/a/a_test.go --
|
||||
package a
|
||||
|
||||
func _() {
|
||||
A(1)
|
||||
}
|
||||
-- @a/a/a_x_test.go --
|
||||
package a_test
|
||||
|
||||
import "unused.mod/a"
|
||||
|
||||
func _() {
|
||||
a.A(1)
|
||||
}
|
||||
-- @a/b/b.go --
|
||||
package b
|
||||
|
||||
import "unused.mod/a"
|
||||
|
||||
func f() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func g() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func _() {
|
||||
a.A(f())
|
||||
}
|
||||
-- field/field.go --
|
||||
package field
|
||||
|
||||
func Field(x int, field int) { //@codeaction("int", "int", "refactor.rewrite", field)
|
||||
}
|
||||
|
||||
func _() {
|
||||
Field(1, 2)
|
||||
}
|
||||
-- @field/field/field.go --
|
||||
package field
|
||||
|
||||
func Field(field int) { //@codeaction("int", "int", "refactor.rewrite", field)
|
||||
}
|
||||
|
||||
func _() {
|
||||
Field(2)
|
||||
}
|
||||
-- ellipsis/ellipsis.go --
|
||||
package ellipsis
|
||||
|
||||
func Ellipsis(...any) { //@codeaction("any", "any", "refactor.rewrite", ellipsis)
|
||||
}
|
||||
|
||||
func _() {
|
||||
// TODO(rfindley): investigate the broken formatting resulting from these inlinings.
|
||||
Ellipsis()
|
||||
Ellipsis(1)
|
||||
Ellipsis(1, 2)
|
||||
Ellipsis(1, f(), g())
|
||||
Ellipsis(h())
|
||||
Ellipsis(i()...)
|
||||
}
|
||||
|
||||
func f() int
|
||||
func g() int
|
||||
func h() (int, int)
|
||||
func i() []any
|
||||
|
||||
-- @ellipsis/ellipsis/ellipsis.go --
|
||||
package ellipsis
|
||||
|
||||
func Ellipsis() { //@codeaction("any", "any", "refactor.rewrite", ellipsis)
|
||||
}
|
||||
|
||||
func _() {
|
||||
// TODO(rfindley): investigate the broken formatting resulting from these inlinings.
|
||||
Ellipsis()
|
||||
Ellipsis()
|
||||
Ellipsis()
|
||||
var _ []any = []any{1, f(), g()}
|
||||
Ellipsis()
|
||||
func(_ ...any) {
|
||||
Ellipsis()
|
||||
}(h())
|
||||
var _ []any = i()
|
||||
Ellipsis()
|
||||
}
|
||||
|
||||
func f() int
|
||||
func g() int
|
||||
func h() (int, int)
|
||||
func i() []any
|
||||
-- ellipsis2/ellipsis2.go --
|
||||
package ellipsis2
|
||||
|
||||
func Ellipsis2(_, _ int, rest ...int) { //@codeaction("_", "_", "refactor.rewrite", ellipsis2)
|
||||
}
|
||||
|
||||
func _() {
|
||||
Ellipsis2(1,2,3)
|
||||
Ellipsis2(h())
|
||||
Ellipsis2(1,2, []int{3, 4}...)
|
||||
}
|
||||
|
||||
func h() (int, int)
|
||||
|
||||
-- @ellipsis2/ellipsis2/ellipsis2.go --
|
||||
package ellipsis2
|
||||
|
||||
func Ellipsis2(_ int, rest ...int) { //@codeaction("_", "_", "refactor.rewrite", ellipsis2)
|
||||
}
|
||||
|
||||
func _() {
|
||||
Ellipsis2(2, []int{3}...)
|
||||
func(_, blank0 int, rest ...int) {
|
||||
Ellipsis2(blank0, rest...)
|
||||
}(h())
|
||||
Ellipsis2(2, []int{3, 4}...)
|
||||
}
|
||||
|
||||
func h() (int, int)
|
||||
-- overlapping/overlapping.go --
|
||||
package overlapping
|
||||
|
||||
func Overlapping(i int) int { //@codeactionerr(re"(i) int", re"(i) int", "refactor.rewrite", re"overlapping")
|
||||
return 0
|
||||
}
|
||||
|
||||
func _() {
|
||||
x := Overlapping(Overlapping(0))
|
||||
_ = x
|
||||
}
|
||||
|
||||
-- effects/effects.go --
|
||||
package effects
|
||||
|
||||
func effects(x, y int) int { //@codeaction("y", "y", "refactor.rewrite", effects)
|
||||
return x
|
||||
}
|
||||
|
||||
func f() int
|
||||
func g() int
|
||||
|
||||
func _() {
|
||||
effects(f(), g())
|
||||
effects(f(), g())
|
||||
}
|
||||
-- @effects/effects/effects.go --
|
||||
package effects
|
||||
|
||||
func effects(x int) int { //@codeaction("y", "y", "refactor.rewrite", effects)
|
||||
return x
|
||||
}
|
||||
|
||||
func f() int
|
||||
func g() int
|
||||
|
||||
func _() {
|
||||
var x, _ int = f(), g()
|
||||
effects(x)
|
||||
{
|
||||
var x, _ int = f(), g()
|
||||
effects(x)
|
||||
}
|
||||
}
|
||||
-- recursive/recursive.go --
|
||||
package recursive
|
||||
|
||||
func Recursive(x int) int { //@codeaction("x", "x", "refactor.rewrite", recursive)
|
||||
return Recursive(1)
|
||||
}
|
||||
|
||||
-- @recursive/recursive/recursive.go --
|
||||
package recursive
|
||||
|
||||
func Recursive() int { //@codeaction("x", "x", "refactor.rewrite", recursive)
|
||||
return Recursive()
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
This test exercises basic 'stub methods' functionality.
|
||||
See basic_resolve.txt for the same test with resolve support.
|
||||
|
||||
-- go.mod --
|
||||
module example.com
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
This test exercises basic 'stub methods' functionality, with resolve support.
|
||||
See basic.txt for the same test without resolve support.
|
||||
|
||||
-- capabilities.json --
|
||||
{
|
||||
"textDocument": {
|
||||
"codeAction": {
|
||||
"dataSupport": true,
|
||||
"resolveSupport": {
|
||||
"properties": ["edit"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-- go.mod --
|
||||
module example.com
|
||||
go 1.12
|
||||
|
||||
-- a/a.go --
|
||||
package a
|
||||
|
||||
type C int
|
||||
|
||||
var _ error = C(0) //@suggestedfix(re"C.0.", re"missing method Error", stub)
|
||||
-- @stub/a/a.go --
|
||||
@@ -5 +5,5 @@
|
||||
+// Error implements error.
|
||||
+func (c C) Error() string {
|
||||
+ panic("unimplemented")
|
||||
+}
|
||||
+
|
Загрузка…
Ссылка в новой задаче