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 / code_lens.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         "go/ast"
10         "go/token"
11         "go/types"
12         "path/filepath"
13         "regexp"
14         "strings"
15
16         "golang.org/x/tools/internal/lsp/protocol"
17         "golang.org/x/tools/internal/span"
18 )
19
20 type LensFunc func(context.Context, Snapshot, FileHandle) ([]protocol.CodeLens, error)
21
22 // LensFuncs returns the supported lensFuncs for Go files.
23 func LensFuncs() map[string]LensFunc {
24         return map[string]LensFunc{
25                 CommandGenerate.Name:      goGenerateCodeLens,
26                 CommandTest.Name:          runTestCodeLens,
27                 CommandRegenerateCgo.Name: regenerateCgoLens,
28                 CommandToggleDetails.Name: toggleDetailsCodeLens,
29         }
30 }
31
32 var (
33         testRe      = regexp.MustCompile("^Test[^a-z]")
34         benchmarkRe = regexp.MustCompile("^Benchmark[^a-z]")
35 )
36
37 func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) {
38         codeLens := make([]protocol.CodeLens, 0)
39
40         fns, err := TestsAndBenchmarks(ctx, snapshot, fh)
41         if err != nil {
42                 return nil, err
43         }
44         for _, fn := range fns.Tests {
45                 jsonArgs, err := MarshalArgs(fh.URI(), []string{fn.Name}, nil)
46                 if err != nil {
47                         return nil, err
48                 }
49                 codeLens = append(codeLens, protocol.CodeLens{
50                         Range: protocol.Range{Start: fn.Rng.Start, End: fn.Rng.Start},
51                         Command: protocol.Command{
52                                 Title:     "run test",
53                                 Command:   CommandTest.ID(),
54                                 Arguments: jsonArgs,
55                         },
56                 })
57         }
58
59         for _, fn := range fns.Benchmarks {
60                 jsonArgs, err := MarshalArgs(fh.URI(), nil, []string{fn.Name})
61                 if err != nil {
62                         return nil, err
63                 }
64                 codeLens = append(codeLens, protocol.CodeLens{
65                         Range: protocol.Range{Start: fn.Rng.Start, End: fn.Rng.Start},
66                         Command: protocol.Command{
67                                 Title:     "run benchmark",
68                                 Command:   CommandTest.ID(),
69                                 Arguments: jsonArgs,
70                         },
71                 })
72         }
73
74         _, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage)
75         if err != nil {
76                 return nil, err
77         }
78         // add a code lens to the top of the file which runs all benchmarks in the file
79         rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, pgf.File.Package, pgf.File.Package).Range()
80         if err != nil {
81                 return nil, err
82         }
83         args, err := MarshalArgs(fh.URI(), []string{}, fns.Benchmarks)
84         if err != nil {
85                 return nil, err
86         }
87         codeLens = append(codeLens, protocol.CodeLens{
88                 Range: rng,
89                 Command: protocol.Command{
90                         Title:     "run file benchmarks",
91                         Command:   CommandTest.ID(),
92                         Arguments: args,
93                 },
94         })
95         return codeLens, nil
96 }
97
98 type testFn struct {
99         Name string
100         Rng  protocol.Range
101 }
102
103 type testFns struct {
104         Tests      []testFn
105         Benchmarks []testFn
106 }
107
108 func TestsAndBenchmarks(ctx context.Context, snapshot Snapshot, fh FileHandle) (testFns, error) {
109         var out testFns
110
111         if !strings.HasSuffix(fh.URI().Filename(), "_test.go") {
112                 return out, nil
113         }
114         pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage)
115         if err != nil {
116                 return out, err
117         }
118
119         for _, d := range pgf.File.Decls {
120                 fn, ok := d.(*ast.FuncDecl)
121                 if !ok {
122                         continue
123                 }
124
125                 rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, d.Pos(), fn.End()).Range()
126                 if err != nil {
127                         return out, err
128                 }
129
130                 if matchTestFunc(fn, pkg, testRe, "T") {
131                         out.Tests = append(out.Tests, testFn{fn.Name.Name, rng})
132                 }
133
134                 if matchTestFunc(fn, pkg, benchmarkRe, "B") {
135                         out.Benchmarks = append(out.Benchmarks, testFn{fn.Name.Name, rng})
136                 }
137         }
138
139         return out, nil
140 }
141
142 func matchTestFunc(fn *ast.FuncDecl, pkg Package, nameRe *regexp.Regexp, paramID string) bool {
143         // Make sure that the function name matches a test function.
144         if !nameRe.MatchString(fn.Name.Name) {
145                 return false
146         }
147         info := pkg.GetTypesInfo()
148         if info == nil {
149                 return false
150         }
151         obj := info.ObjectOf(fn.Name)
152         if obj == nil {
153                 return false
154         }
155         sig, ok := obj.Type().(*types.Signature)
156         if !ok {
157                 return false
158         }
159         // Test functions should have only one parameter.
160         if sig.Params().Len() != 1 {
161                 return false
162         }
163
164         // Check the type of the only parameter
165         paramTyp, ok := sig.Params().At(0).Type().(*types.Pointer)
166         if !ok {
167                 return false
168         }
169         named, ok := paramTyp.Elem().(*types.Named)
170         if !ok {
171                 return false
172         }
173         namedObj := named.Obj()
174         if namedObj.Pkg().Path() != "testing" {
175                 return false
176         }
177         return namedObj.Id() == paramID
178 }
179
180 func goGenerateCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) {
181         pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
182         if err != nil {
183                 return nil, err
184         }
185         const ggDirective = "//go:generate"
186         for _, c := range pgf.File.Comments {
187                 for _, l := range c.List {
188                         if !strings.HasPrefix(l.Text, ggDirective) {
189                                 continue
190                         }
191                         rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, l.Pos(), l.Pos()+token.Pos(len(ggDirective))).Range()
192                         if err != nil {
193                                 return nil, err
194                         }
195                         dir := span.URIFromPath(filepath.Dir(fh.URI().Filename()))
196                         nonRecursiveArgs, err := MarshalArgs(dir, false)
197                         if err != nil {
198                                 return nil, err
199                         }
200                         recursiveArgs, err := MarshalArgs(dir, true)
201                         if err != nil {
202                                 return nil, err
203                         }
204                         return []protocol.CodeLens{
205                                 {
206                                         Range: rng,
207                                         Command: protocol.Command{
208                                                 Title:     "run go generate",
209                                                 Command:   CommandGenerate.ID(),
210                                                 Arguments: nonRecursiveArgs,
211                                         },
212                                 },
213                                 {
214                                         Range: rng,
215                                         Command: protocol.Command{
216                                                 Title:     "run go generate ./...",
217                                                 Command:   CommandGenerate.ID(),
218                                                 Arguments: recursiveArgs,
219                                         },
220                                 },
221                         }, nil
222
223                 }
224         }
225         return nil, nil
226 }
227
228 func regenerateCgoLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) {
229         pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
230         if err != nil {
231                 return nil, err
232         }
233         var c *ast.ImportSpec
234         for _, imp := range pgf.File.Imports {
235                 if imp.Path.Value == `"C"` {
236                         c = imp
237                 }
238         }
239         if c == nil {
240                 return nil, nil
241         }
242         rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, c.Pos(), c.EndPos).Range()
243         if err != nil {
244                 return nil, err
245         }
246         jsonArgs, err := MarshalArgs(fh.URI())
247         if err != nil {
248                 return nil, err
249         }
250         return []protocol.CodeLens{
251                 {
252                         Range: rng,
253                         Command: protocol.Command{
254                                 Title:     "regenerate cgo definitions",
255                                 Command:   CommandRegenerateCgo.ID(),
256                                 Arguments: jsonArgs,
257                         },
258                 },
259         }, nil
260 }
261
262 func toggleDetailsCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) {
263         _, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage)
264         if err != nil {
265                 return nil, err
266         }
267         rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, pgf.File.Package, pgf.File.Package).Range()
268         if err != nil {
269                 return nil, err
270         }
271         jsonArgs, err := MarshalArgs(fh.URI())
272         if err != nil {
273                 return nil, err
274         }
275         return []protocol.CodeLens{{
276                 Range: rng,
277                 Command: protocol.Command{
278                         Title:     "Toggle gc annotation details",
279                         Command:   CommandToggleDetails.ID(),
280                         Arguments: jsonArgs,
281                 },
282         }}, nil
283 }