.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / internal / lsp / mod / 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 mod
6
7 import (
8         "context"
9         "fmt"
10         "os"
11         "path/filepath"
12
13         "golang.org/x/mod/modfile"
14         "golang.org/x/tools/internal/lsp/command"
15         "golang.org/x/tools/internal/lsp/protocol"
16         "golang.org/x/tools/internal/lsp/source"
17         "golang.org/x/tools/internal/span"
18 )
19
20 // LensFuncs returns the supported lensFuncs for go.mod files.
21 func LensFuncs() map[command.Command]source.LensFunc {
22         return map[command.Command]source.LensFunc{
23                 command.UpgradeDependency: upgradeLenses,
24                 command.Tidy:              tidyLens,
25                 command.Vendor:            vendorLens,
26         }
27 }
28
29 func upgradeLenses(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
30         pm, err := snapshot.ParseMod(ctx, fh)
31         if err != nil || pm.File == nil {
32                 return nil, err
33         }
34         if len(pm.File.Require) == 0 {
35                 // Nothing to upgrade.
36                 return nil, nil
37         }
38         var requires []string
39         for _, req := range pm.File.Require {
40                 requires = append(requires, req.Mod.Path)
41         }
42         uri := protocol.URIFromSpanURI(fh.URI())
43         checkUpgrade, err := command.NewCheckUpgradesCommand("Check for upgrades", command.CheckUpgradesArgs{
44                 URI:     uri,
45                 Modules: requires,
46         })
47         if err != nil {
48                 return nil, err
49         }
50         upgradeTransitive, err := command.NewUpgradeDependencyCommand("Upgrade transitive dependencies", command.DependencyArgs{
51                 URI:        uri,
52                 AddRequire: false,
53                 GoCmdArgs:  []string{"-u", "all"},
54         })
55         if err != nil {
56                 return nil, err
57         }
58         upgradeDirect, err := command.NewUpgradeDependencyCommand("Upgrade direct dependencies", command.DependencyArgs{
59                 URI:        uri,
60                 AddRequire: false,
61                 GoCmdArgs:  requires,
62         })
63         if err != nil {
64                 return nil, err
65         }
66         // Put the upgrade code lenses above the first require block or statement.
67         rng, err := firstRequireRange(fh, pm)
68         if err != nil {
69                 return nil, err
70         }
71
72         return []protocol.CodeLens{
73                 {Range: rng, Command: checkUpgrade},
74                 {Range: rng, Command: upgradeTransitive},
75                 {Range: rng, Command: upgradeDirect},
76         }, nil
77 }
78
79 func tidyLens(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
80         pm, err := snapshot.ParseMod(ctx, fh)
81         if err != nil || pm.File == nil {
82                 return nil, err
83         }
84         if len(pm.File.Require) == 0 {
85                 // Nothing to vendor.
86                 return nil, nil
87         }
88         uri := protocol.URIFromSpanURI(fh.URI())
89         cmd, err := command.NewTidyCommand("Run go mod tidy", command.URIArgs{URIs: []protocol.DocumentURI{uri}})
90         if err != nil {
91                 return nil, err
92         }
93         rng, err := moduleStmtRange(fh, pm)
94         if err != nil {
95                 return nil, err
96         }
97         return []protocol.CodeLens{{
98                 Range:   rng,
99                 Command: cmd,
100         }}, nil
101 }
102
103 func vendorLens(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
104         pm, err := snapshot.ParseMod(ctx, fh)
105         if err != nil || pm.File == nil {
106                 return nil, err
107         }
108         rng, err := moduleStmtRange(fh, pm)
109         if err != nil {
110                 return nil, err
111         }
112         title := "Create vendor directory"
113         uri := protocol.URIFromSpanURI(fh.URI())
114         cmd, err := command.NewVendorCommand(title, command.URIArg{URI: uri})
115         if err != nil {
116                 return nil, err
117         }
118         // Change the message depending on whether or not the module already has a
119         // vendor directory.
120         vendorDir := filepath.Join(filepath.Dir(fh.URI().Filename()), "vendor")
121         if info, _ := os.Stat(vendorDir); info != nil && info.IsDir() {
122                 title = "Sync vendor directory"
123         }
124         return []protocol.CodeLens{{Range: rng, Command: cmd}}, nil
125 }
126
127 func moduleStmtRange(fh source.FileHandle, pm *source.ParsedModule) (protocol.Range, error) {
128         if pm.File == nil || pm.File.Module == nil || pm.File.Module.Syntax == nil {
129                 return protocol.Range{}, fmt.Errorf("no module statement in %s", fh.URI())
130         }
131         syntax := pm.File.Module.Syntax
132         return lineToRange(pm.Mapper, fh.URI(), syntax.Start, syntax.End)
133 }
134
135 // firstRequireRange returns the range for the first "require" in the given
136 // go.mod file. This is either a require block or an individual require line.
137 func firstRequireRange(fh source.FileHandle, pm *source.ParsedModule) (protocol.Range, error) {
138         if len(pm.File.Require) == 0 {
139                 return protocol.Range{}, fmt.Errorf("no requires in the file %s", fh.URI())
140         }
141         var start, end modfile.Position
142         for _, stmt := range pm.File.Syntax.Stmt {
143                 if b, ok := stmt.(*modfile.LineBlock); ok && len(b.Token) == 1 && b.Token[0] == "require" {
144                         start, end = b.Span()
145                         break
146                 }
147         }
148
149         firstRequire := pm.File.Require[0].Syntax
150         if start.Byte == 0 || firstRequire.Start.Byte < start.Byte {
151                 start, end = firstRequire.Start, firstRequire.End
152         }
153         return lineToRange(pm.Mapper, fh.URI(), start, end)
154 }
155
156 func lineToRange(m *protocol.ColumnMapper, uri span.URI, start, end modfile.Position) (protocol.Range, error) {
157         line, col, err := m.Converter.ToPosition(start.Byte)
158         if err != nil {
159                 return protocol.Range{}, err
160         }
161         s := span.NewPoint(line, col, start.Byte)
162         line, col, err = m.Converter.ToPosition(end.Byte)
163         if err != nil {
164                 return protocol.Range{}, err
165         }
166         e := span.NewPoint(line, col, end.Byte)
167         return m.Range(span.New(uri, s, e))
168 }