Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / source / command.go
1 // Copyright 2020 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package source
6
7 import (
8         "context"
9         "fmt"
10         "go/ast"
11         "go/token"
12         "go/types"
13
14         "golang.org/x/tools/go/analysis"
15         "golang.org/x/tools/internal/lsp/analysis/fillstruct"
16         "golang.org/x/tools/internal/lsp/analysis/undeclaredname"
17         "golang.org/x/tools/internal/lsp/protocol"
18         "golang.org/x/tools/internal/span"
19         errors "golang.org/x/xerrors"
20 )
21
22 type Command struct {
23         Title string
24         Name  string
25
26         // Synchronous controls whether the command executes synchronously within the
27         // ExecuteCommand request (applying suggested fixes is always synchronous).
28         Synchronous bool
29
30         // appliesFn is an optional field to indicate whether or not a command can
31         // be applied to the given inputs. If it returns false, we should not
32         // suggest this command for these inputs.
33         appliesFn AppliesFunc
34
35         // suggestedFixFn is an optional field to generate the edits that the
36         // command produces for the given inputs.
37         suggestedFixFn SuggestedFixFunc
38 }
39
40 // CommandPrefix is the prefix of all command names gopls uses externally.
41 const CommandPrefix = "gopls."
42
43 // ID adds the CommandPrefix to the command name, in order to avoid
44 // collisions with other language servers.
45 func (c Command) ID() string {
46         return CommandPrefix + c.Name
47 }
48
49 type AppliesFunc func(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info) bool
50
51 // SuggestedFixFunc is a function used to get the suggested fixes for a given
52 // gopls command, some of which are provided by go/analysis.Analyzers. Some of
53 // the analyzers in internal/lsp/analysis are not efficient enough to include
54 // suggested fixes with their diagnostics, so we have to compute them
55 // separately. Such analyzers should provide a function with a signature of
56 // SuggestedFixFunc.
57 type SuggestedFixFunc func(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error)
58
59 // Commands are the commands currently supported by gopls.
60 var Commands = []*Command{
61         CommandGenerate,
62         CommandFillStruct,
63         CommandRegenerateCgo,
64         CommandTest,
65         CommandTidy,
66         CommandUndeclaredName,
67         CommandUpgradeDependency,
68         CommandVendor,
69         CommandExtractVariable,
70         CommandExtractFunction,
71         CommandToggleDetails,
72         CommandGenerateGoplsMod,
73 }
74
75 var (
76         // CommandTest runs `go test` for a specific test function.
77         CommandTest = &Command{
78                 Name:  "test",
79                 Title: "Run test(s)",
80         }
81
82         // CommandGenerate runs `go generate` for a given directory.
83         CommandGenerate = &Command{
84                 Name:  "generate",
85                 Title: "Run go generate",
86         }
87
88         // CommandTidy runs `go mod tidy` for a module.
89         CommandTidy = &Command{
90                 Name:  "tidy",
91                 Title: "Run go mod tidy",
92         }
93
94         // CommandVendor runs `go mod vendor` for a module.
95         CommandVendor = &Command{
96                 Name:  "vendor",
97                 Title: "Run go mod vendor",
98         }
99
100         // CommandUpgradeDependency upgrades a dependency.
101         CommandUpgradeDependency = &Command{
102                 Name:  "upgrade_dependency",
103                 Title: "Upgrade dependency",
104         }
105
106         // CommandRegenerateCgo regenerates cgo definitions.
107         CommandRegenerateCgo = &Command{
108                 Name:  "regenerate_cgo",
109                 Title: "Regenerate cgo",
110         }
111
112         // CommandToggleDetails controls calculation of gc annotations.
113         CommandToggleDetails = &Command{
114                 Name:  "gc_details",
115                 Title: "Toggle gc_details",
116         }
117
118         // CommandFillStruct is a gopls command to fill a struct with default
119         // values.
120         CommandFillStruct = &Command{
121                 Name:           "fill_struct",
122                 Title:          "Fill struct",
123                 suggestedFixFn: fillstruct.SuggestedFix,
124         }
125
126         // CommandUndeclaredName adds a variable declaration for an undeclared
127         // name.
128         CommandUndeclaredName = &Command{
129                 Name:           "undeclared_name",
130                 Title:          "Undeclared name",
131                 suggestedFixFn: undeclaredname.SuggestedFix,
132         }
133
134         // CommandExtractVariable extracts an expression to a variable.
135         CommandExtractVariable = &Command{
136                 Name:           "extract_variable",
137                 Title:          "Extract to variable",
138                 suggestedFixFn: extractVariable,
139                 appliesFn: func(_ *token.FileSet, rng span.Range, _ []byte, file *ast.File, _ *types.Package, _ *types.Info) bool {
140                         _, _, ok, _ := canExtractVariable(rng, file)
141                         return ok
142                 },
143         }
144
145         // CommandExtractFunction extracts statements to a function.
146         CommandExtractFunction = &Command{
147                 Name:           "extract_function",
148                 Title:          "Extract to function",
149                 suggestedFixFn: extractFunction,
150                 appliesFn: func(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, _ *types.Package, info *types.Info) bool {
151                         _, ok, _ := canExtractFunction(fset, rng, src, file, info)
152                         return ok
153                 },
154         }
155
156         // CommandGenerateGoplsMod (re)generates the gopls.mod file.
157         CommandGenerateGoplsMod = &Command{
158                 Name:        "generate_gopls_mod",
159                 Title:       "Generate gopls.mod",
160                 Synchronous: true,
161         }
162 )
163
164 // Applies reports whether the command c implements a suggested fix that is
165 // relevant to the given rng.
166 func (c *Command) Applies(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) bool {
167         // If there is no applies function, assume that the command applies.
168         if c.appliesFn == nil {
169                 return true
170         }
171         fset, rng, src, file, _, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng)
172         if err != nil {
173                 return false
174         }
175         return c.appliesFn(fset, rng, src, file, pkg, info)
176 }
177
178 // IsSuggestedFix reports whether the given command is intended to work as a
179 // suggested fix. Suggested fix commands are intended to return edits which are
180 // then applied to the workspace.
181 func (c *Command) IsSuggestedFix() bool {
182         return c.suggestedFixFn != nil
183 }
184
185 // SuggestedFix applies the command's suggested fix to the given file and
186 // range, returning the resulting edits.
187 func (c *Command) SuggestedFix(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) ([]protocol.TextDocumentEdit, error) {
188         if c.suggestedFixFn == nil {
189                 return nil, fmt.Errorf("no suggested fix function for %s", c.Name)
190         }
191         fset, rng, src, file, m, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng)
192         if err != nil {
193                 return nil, err
194         }
195         fix, err := c.suggestedFixFn(fset, rng, src, file, pkg, info)
196         if err != nil {
197                 return nil, err
198         }
199         if fix == nil {
200                 return nil, nil
201         }
202
203         var edits []protocol.TextDocumentEdit
204         for _, edit := range fix.TextEdits {
205                 rng := span.NewRange(fset, edit.Pos, edit.End)
206                 spn, err := rng.Span()
207                 if err != nil {
208                         return nil, err
209                 }
210                 clRng, err := m.Range(spn)
211                 if err != nil {
212                         return nil, err
213                 }
214                 edits = append(edits, protocol.TextDocumentEdit{
215                         TextDocument: protocol.VersionedTextDocumentIdentifier{
216                                 Version: fh.Version(),
217                                 TextDocumentIdentifier: protocol.TextDocumentIdentifier{
218                                         URI: protocol.URIFromSpanURI(fh.URI()),
219                                 },
220                         },
221                         Edits: []protocol.TextEdit{
222                                 {
223                                         Range:   clRng,
224                                         NewText: string(edit.NewText),
225                                 },
226                         },
227                 })
228         }
229         return edits, nil
230 }
231
232 // getAllSuggestedFixInputs is a helper function to collect all possible needed
233 // inputs for an AppliesFunc or SuggestedFixFunc.
234 func getAllSuggestedFixInputs(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, span.Range, []byte, *ast.File, *protocol.ColumnMapper, *types.Package, *types.Info, error) {
235         pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
236         if err != nil {
237                 return nil, span.Range{}, nil, nil, nil, nil, nil, errors.Errorf("getting file for Identifier: %w", err)
238         }
239         spn, err := pgf.Mapper.RangeSpan(pRng)
240         if err != nil {
241                 return nil, span.Range{}, nil, nil, nil, nil, nil, err
242         }
243         rng, err := spn.Range(pgf.Mapper.Converter)
244         if err != nil {
245                 return nil, span.Range{}, nil, nil, nil, nil, nil, err
246         }
247         src, err := fh.Read()
248         if err != nil {
249                 return nil, span.Range{}, nil, nil, nil, nil, nil, err
250         }
251         return snapshot.FileSet(), rng, src, pgf.File, pgf.Mapper, pkg.GetTypes(), pkg.GetTypesInfo(), nil
252 }