1
0
mirror of https://github.com/golang/go synced 2024-11-06 04:36:15 -07:00
go/internal/lsp/mod/code_lens.go
Rebecca Stambler 1745ac5bc6 internal/lsp: refactor various module-specific handles in cache
This change separates out different functions of mod handles.
Previously, we had ModHandle and ModTidyHandle. ModHandle was used to
parse go.mod files and get the results of `go mod why` and possible
dependency upgrades.

Now, we factor this out into 4 handles: ParseModHandle, ModWhyHandle,
ModUpgradeHandle, and ModTidyHandle. This allows each handle to be
specific to its own functionality. It also simplifies the code a bit,
as the handles can be written in terms of ParseModHandles instead of
FileHandles.

I may have some follow-up CLs to refactor the `go mod tidy` logic out of
the cache package, though I'm no longer certain that that's a good
choice.

Change-Id: I8e12299dfdda7bb61b05903d9aa474461d7f4836
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239117
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-06-23 18:31:46 +00:00

109 lines
2.9 KiB
Go

package mod
import (
"context"
"fmt"
"strings"
"golang.org/x/mod/modfile"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/lsp/debug/tag"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span"
)
// CodeLens computes code lens for a go.mod file.
func CodeLens(ctx context.Context, snapshot source.Snapshot, uri span.URI) ([]protocol.CodeLens, error) {
if !snapshot.View().Options().EnabledCodeLens[source.CommandUpgradeDependency] {
return nil, nil
}
ctx, done := event.Start(ctx, "mod.CodeLens", tag.URI.Of(uri))
defer done()
// Only show go.mod code lenses in module mode, for the view's go.mod.
if modURI := snapshot.View().ModFile(); modURI == "" || modURI != uri {
return nil, nil
}
fh, err := snapshot.GetFile(ctx, uri)
if err != nil {
return nil, err
}
pmh, err := snapshot.ParseModHandle(ctx, fh)
if err != nil {
return nil, err
}
file, m, _, err := pmh.Parse(ctx)
if err != nil {
return nil, err
}
muh, err := snapshot.ModUpgradeHandle(ctx)
if err != nil {
return nil, err
}
upgrades, err := muh.Upgrades(ctx)
if err != nil {
return nil, err
}
var (
codelens []protocol.CodeLens
allUpgrades []string
)
for _, req := range file.Require {
dep := req.Mod.Path
latest, ok := upgrades[dep]
if !ok {
continue
}
// Get the range of the require directive.
rng, err := positionsToRange(uri, m, req.Syntax.Start, req.Syntax.End)
if err != nil {
return nil, err
}
codelens = append(codelens, protocol.CodeLens{
Range: rng,
Command: protocol.Command{
Title: fmt.Sprintf("Upgrade dependency to %s", latest),
Command: source.CommandUpgradeDependency,
Arguments: []interface{}{uri, dep},
},
})
allUpgrades = append(allUpgrades, dep)
}
// If there is at least 1 upgrade, add an "Upgrade all dependencies" to the module statement.
if module := file.Module; len(allUpgrades) > 0 && module != nil && module.Syntax != nil {
// Get the range of the module directive.
rng, err := positionsToRange(uri, m, module.Syntax.Start, module.Syntax.End)
if err != nil {
return nil, err
}
codelens = append(codelens, protocol.CodeLens{
Range: rng,
Command: protocol.Command{
Title: "Upgrade all dependencies",
Command: source.CommandUpgradeDependency,
Arguments: []interface{}{uri, strings.Join(append([]string{"-u"}, allUpgrades...), " ")},
},
})
}
return codelens, err
}
func positionsToRange(uri span.URI, m *protocol.ColumnMapper, s, e modfile.Position) (protocol.Range, error) {
line, col, err := m.Converter.ToPosition(s.Byte)
if err != nil {
return protocol.Range{}, err
}
start := span.NewPoint(line, col, s.Byte)
line, col, err = m.Converter.ToPosition(e.Byte)
if err != nil {
return protocol.Range{}, err
}
end := span.NewPoint(line, col, e.Byte)
rng, err := m.Range(span.New(uri, start, end))
if err != nil {
return protocol.Range{}, err
}
return rng, err
}