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