Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / 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         CommandUndeclaredName,
66         CommandAddDependency,
67         CommandUpgradeDependency,
68         CommandRemoveDependency,
69         CommandVendor,
70         CommandExtractVariable,
71         CommandExtractFunction,
72         CommandToggleDetails,
73         CommandGenerateGoplsMod,
74 }
75
76 var (
77         // CommandTest runs `go test` for a specific test function.
78         CommandTest = &Command{
79                 Name:  "test",
80                 Title: "Run test(s)",
81                 Async: true,
82         }
83
84         // CommandGenerate runs `go generate` for a given directory.
85         CommandGenerate = &Command{
86                 Name:  "generate",
87                 Title: "Run go generate",
88         }
89
90         // CommandTidy runs `go mod tidy` for a module.
91         CommandTidy = &Command{
92                 Name:  "tidy",
93                 Title: "Run go mod tidy",
94         }
95
96         // CommandVendor runs `go mod vendor` for a module.
97         CommandVendor = &Command{
98                 Name:  "vendor",
99                 Title: "Run go mod vendor",
100         }
101
102         // CommandAddDependency adds a dependency.
103         CommandAddDependency = &Command{
104                 Name:  "add_dependency",
105                 Title: "Add dependency",
106         }
107
108         // CommandUpgradeDependency upgrades a dependency.
109         CommandUpgradeDependency = &Command{
110                 Name:  "upgrade_dependency",
111                 Title: "Upgrade dependency",
112         }
113
114         // CommandRemoveDependency removes a dependency.
115         CommandRemoveDependency = &Command{
116                 Name:  "remove_dependency",
117                 Title: "Remove dependency",
118         }
119
120         // CommandRegenerateCgo regenerates cgo definitions.
121         CommandRegenerateCgo = &Command{
122                 Name:  "regenerate_cgo",
123                 Title: "Regenerate cgo",
124         }
125
126         // CommandToggleDetails controls calculation of gc annotations.
127         CommandToggleDetails = &Command{
128                 Name:  "gc_details",
129                 Title: "Toggle gc_details",
130         }
131
132         // CommandFillStruct is a gopls command to fill a struct with default
133         // values.
134         CommandFillStruct = &Command{
135                 Name:           "fill_struct",
136                 Title:          "Fill struct",
137                 suggestedFixFn: fillstruct.SuggestedFix,
138         }
139
140         // CommandUndeclaredName adds a variable declaration for an undeclared
141         // name.
142         CommandUndeclaredName = &Command{
143                 Name:           "undeclared_name",
144                 Title:          "Undeclared name",
145                 suggestedFixFn: undeclaredname.SuggestedFix,
146         }
147
148         // CommandExtractVariable extracts an expression to a variable.
149         CommandExtractVariable = &Command{
150                 Name:           "extract_variable",
151                 Title:          "Extract to variable",
152                 suggestedFixFn: extractVariable,
153                 appliesFn: func(_ *token.FileSet, rng span.Range, _ []byte, file *ast.File, _ *types.Package, _ *types.Info) bool {
154                         _, _, ok, _ := canExtractVariable(rng, file)
155                         return ok
156                 },
157         }
158
159         // CommandExtractFunction extracts statements to a function.
160         CommandExtractFunction = &Command{
161                 Name:           "extract_function",
162                 Title:          "Extract to function",
163                 suggestedFixFn: extractFunction,
164                 appliesFn: func(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, _ *types.Package, info *types.Info) bool {
165                         _, ok, _ := canExtractFunction(fset, rng, src, file, info)
166                         return ok
167                 },
168         }
169
170         // CommandGenerateGoplsMod (re)generates the gopls.mod file.
171         CommandGenerateGoplsMod = &Command{
172                 Name:  "generate_gopls_mod",
173                 Title: "Generate gopls.mod",
174         }
175 )
176
177 // Applies reports whether the command c implements a suggested fix that is
178 // relevant to the given rng.
179 func (c *Command) Applies(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) bool {
180         // If there is no applies function, assume that the command applies.
181         if c.appliesFn == nil {
182                 return true
183         }
184         fset, rng, src, file, _, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng)
185         if err != nil {
186                 return false
187         }
188         return c.appliesFn(fset, rng, src, file, pkg, info)
189 }
190
191 // IsSuggestedFix reports whether the given command is intended to work as a
192 // suggested fix. Suggested fix commands are intended to return edits which are
193 // then applied to the workspace.
194 func (c *Command) IsSuggestedFix() bool {
195         return c.suggestedFixFn != nil
196 }
197
198 // SuggestedFix applies the command's suggested fix to the given file and
199 // range, returning the resulting edits.
200 func (c *Command) SuggestedFix(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) ([]protocol.TextDocumentEdit, error) {
201         if c.suggestedFixFn == nil {
202                 return nil, fmt.Errorf("no suggested fix function for %s", c.Name)
203         }
204         fset, rng, src, file, m, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng)
205         if err != nil {
206                 return nil, err
207         }
208         fix, err := c.suggestedFixFn(fset, rng, src, file, pkg, info)
209         if err != nil {
210                 return nil, err
211         }
212         if fix == nil {
213                 return nil, nil
214         }
215
216         var edits []protocol.TextDocumentEdit
217         for _, edit := range fix.TextEdits {
218                 rng := span.NewRange(fset, edit.Pos, edit.End)
219                 spn, err := rng.Span()
220                 if err != nil {
221                         return nil, err
222                 }
223                 clRng, err := m.Range(spn)
224                 if err != nil {
225                         return nil, err
226                 }
227                 edits = append(edits, protocol.TextDocumentEdit{
228                         TextDocument: protocol.VersionedTextDocumentIdentifier{
229                                 Version: fh.Version(),
230                                 TextDocumentIdentifier: protocol.TextDocumentIdentifier{
231                                         URI: protocol.URIFromSpanURI(fh.URI()),
232                                 },
233                         },
234                         Edits: []protocol.TextEdit{
235                                 {
236                                         Range:   clRng,
237                                         NewText: string(edit.NewText),
238                                 },
239                         },
240                 })
241         }
242         return edits, nil
243 }
244
245 // getAllSuggestedFixInputs is a helper function to collect all possible needed
246 // inputs for an AppliesFunc or SuggestedFixFunc.
247 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) {
248         pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
249         if err != nil {
250                 return nil, span.Range{}, nil, nil, nil, nil, nil, errors.Errorf("getting file for Identifier: %w", err)
251         }
252         spn, err := pgf.Mapper.RangeSpan(pRng)
253         if err != nil {
254                 return nil, span.Range{}, nil, nil, nil, nil, nil, err
255         }
256         rng, err := spn.Range(pgf.Mapper.Converter)
257         if err != nil {
258                 return nil, span.Range{}, nil, nil, nil, nil, nil, err
259         }
260         src, err := fh.Read()
261         if err != nil {
262                 return nil, span.Range{}, nil, nil, nil, nil, nil, err
263         }
264         return snapshot.FileSet(), rng, src, pgf.File, pgf.Mapper, pkg.GetTypes(), pkg.GetTypesInfo(), nil
265 }