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