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