1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:54:43 -07:00

internal/lsp: add an upgrade all dependencies codelens

This change adds an upgrade all dependencies codelens on the go.mod file if there are available upgrades.

Updates golang/go#36501

Change-Id: I86c1ae7e7a6dc01b7f5cd7eb18e5a11d96a3acc1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/221108
Run-TryBot: Rohan Challa <rohan@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Rohan Challa 2020-02-26 14:11:30 -05:00
parent 8d77031968
commit c4f5635f10
10 changed files with 62 additions and 37 deletions

View File

@ -71,7 +71,7 @@ func NewRunner(exporter packagestest.Exporter, data *tests.Data, ctx context.Con
return r return r
} }
func (r *runner) CodeLens(t *testing.T, spn span.Span, want []protocol.CodeLens) { func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) {
//TODO: add command line completions tests when it works //TODO: add command line completions tests when it works
} }

View File

@ -2,6 +2,7 @@ package lsp
import ( import (
"context" "context"
"strings"
"golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/protocol"
@ -35,15 +36,15 @@ func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCom
return nil, errors.Errorf("expected one file URI and one dependency for call to `go get`, got %v", params.Arguments) return nil, errors.Errorf("expected one file URI and one dependency for call to `go get`, got %v", params.Arguments)
} }
uri := protocol.DocumentURI(params.Arguments[0].(string)) uri := protocol.DocumentURI(params.Arguments[0].(string))
deps := params.Arguments[1].(string)
snapshot, _, ok, err := s.beginFileRequest(uri, source.UnknownKind) snapshot, _, ok, err := s.beginFileRequest(uri, source.UnknownKind)
if !ok { if !ok {
return nil, err return nil, err
} }
dep := params.Arguments[1].(string)
// Run "go get" on the dependency to upgrade it to the latest version. // Run "go get" on the dependency to upgrade it to the latest version.
inv := gocommand.Invocation{ inv := gocommand.Invocation{
Verb: "get", Verb: "get",
Args: []string{dep}, Args: strings.Split(deps, " "),
Env: snapshot.Config(ctx).Env, Env: snapshot.Config(ctx).Env,
WorkingDir: snapshot.View().Folder().Filename(), WorkingDir: snapshot.View().Folder().Filename(),
} }

View File

@ -96,19 +96,19 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
} }
} }
func (r *runner) CodeLens(t *testing.T, spn span.Span, want []protocol.CodeLens) { func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) {
if source.DetectLanguage("", spn.URI().Filename()) != source.Mod { if source.DetectLanguage("", uri.Filename()) != source.Mod {
return return
} }
v, err := r.server.session.ViewOf(spn.URI()) v, err := r.server.session.ViewOf(uri)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
got, err := mod.CodeLens(r.ctx, v.Snapshot(), spn.URI()) got, err := mod.CodeLens(r.ctx, v.Snapshot(), uri)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if diff := tests.DiffCodeLens(spn.URI(), want, got); diff != "" { if diff := tests.DiffCodeLens(uri, want, got); diff != "" {
t.Error(diff) t.Error(diff)
} }
} }

View File

@ -3,7 +3,9 @@ package mod
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"golang.org/x/mod/modfile"
"golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/telemetry" "golang.org/x/tools/internal/lsp/telemetry"
@ -32,6 +34,7 @@ func CodeLens(ctx context.Context, snapshot source.Snapshot, uri span.URI) ([]pr
return nil, err return nil, err
} }
var codelens []protocol.CodeLens var codelens []protocol.CodeLens
var allUpgrades []string
for _, req := range f.Require { for _, req := range f.Require {
dep := req.Mod.Path dep := req.Mod.Path
latest, ok := upgrades[dep] latest, ok := upgrades[dep]
@ -39,18 +42,7 @@ func CodeLens(ctx context.Context, snapshot source.Snapshot, uri span.URI) ([]pr
continue continue
} }
// Get the range of the require directive. // Get the range of the require directive.
s, e := req.Syntax.Start, req.Syntax.End rng, err := positionsToRange(uri, m, req.Syntax.Start, req.Syntax.End)
line, col, err := m.Converter.ToPosition(s.Byte)
if err != nil {
return nil, err
}
start := span.NewPoint(line, col, s.Byte)
line, col, err = m.Converter.ToPosition(e.Byte)
if err != nil {
return nil, err
}
end := span.NewPoint(line, col, e.Byte)
rng, err := m.Range(span.New(uri, start, end))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,6 +54,41 @@ func CodeLens(ctx context.Context, snapshot source.Snapshot, uri span.URI) ([]pr
Arguments: []interface{}{uri, dep}, 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 := f.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: "upgrade.dependency",
Arguments: []interface{}{uri, strings.Join(append([]string{"-u"}, allUpgrades...), " ")},
},
})
} }
return codelens, err 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
}

View File

@ -83,9 +83,6 @@ func (s *Server) codeLens(ctx context.Context, params *protocol.CodeLensParams)
if !ok { if !ok {
return nil, err return nil, err
} }
if !snapshot.IsSaved(fh.Identity().URI) {
return nil, nil
}
return mod.CodeLens(ctx, snapshot, fh.Identity().URI) return mod.CodeLens(ctx, snapshot, fh.Identity().URI)
} }

View File

@ -894,7 +894,7 @@ func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {
// This is a pure LSP feature, no source level functionality to be tested. // This is a pure LSP feature, no source level functionality to be tested.
} }
func (r *runner) CodeLens(t *testing.T, spn span.Span, want []protocol.CodeLens) { func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) {
// This is a pure LSP feature, no source level functionality to be tested. // This is a pure LSP feature, no source level functionality to be tested.
} }

View File

@ -1,4 +1,4 @@
module upgradedep module upgradedep //@codelens("module upgradedep", "Upgrade all dependencies", "upgrade.dependency")
// TODO(microsoft/vscode-go#12): Another issue. //@link(`microsoft/vscode-go#12`, `https://github.com/microsoft/vscode-go/issues/12`) // TODO(microsoft/vscode-go#12): Another issue. //@link(`microsoft/vscode-go#12`, `https://github.com/microsoft/vscode-go/issues/12`)

View File

@ -1,5 +1,5 @@
-- summary -- -- summary --
CodeLensCount = 1 CodeLensCount = 2
CompletionsCount = 0 CompletionsCount = 0
CompletionSnippetCount = 0 CompletionSnippetCount = 0
UnimportedCompletionsCount = 0 UnimportedCompletionsCount = 0

View File

@ -43,7 +43,7 @@ const (
var UpdateGolden = flag.Bool("golden", false, "Update golden files") var UpdateGolden = flag.Bool("golden", false, "Update golden files")
type CodeLens map[span.Span][]protocol.CodeLens type CodeLens map[span.URI][]protocol.CodeLens
type Diagnostics map[span.URI][]source.Diagnostic type Diagnostics map[span.URI][]source.Diagnostic
type CompletionItems map[token.Pos]*source.CompletionItem type CompletionItems map[token.Pos]*source.CompletionItem
type Completions map[span.Span][]Completion type Completions map[span.Span][]Completion
@ -115,7 +115,7 @@ type Data struct {
} }
type Tests interface { type Tests interface {
CodeLens(*testing.T, span.Span, []protocol.CodeLens) CodeLens(*testing.T, span.URI, []protocol.CodeLens)
Diagnostics(*testing.T, span.URI, []source.Diagnostic) Diagnostics(*testing.T, span.URI, []source.Diagnostic)
Completion(*testing.T, span.Span, Completion, CompletionItems) Completion(*testing.T, span.Span, Completion, CompletionItems)
CompletionSnippet(*testing.T, span.Span, CompletionSnippet, bool, CompletionItems) CompletionSnippet(*testing.T, span.Span, CompletionSnippet, bool, CompletionItems)
@ -530,14 +530,14 @@ func Run(t *testing.T, tests Tests, data *Data) {
t.Run("CodeLens", func(t *testing.T) { t.Run("CodeLens", func(t *testing.T) {
t.Helper() t.Helper()
for spn, want := range data.CodeLens { for uri, want := range data.CodeLens {
// Check if we should skip this URI if the -modfile flag is not available. // Check if we should skip this URI if the -modfile flag is not available.
if shouldSkip(data, spn.URI()) { if shouldSkip(data, uri) {
continue continue
} }
t.Run(SpanName(spn), func(t *testing.T) { t.Run(uriName(uri), func(t *testing.T) {
t.Helper() t.Helper()
tests.CodeLens(t, spn, want) tests.CodeLens(t, uri, want)
}) })
} }
}) })
@ -767,7 +767,7 @@ func checkData(t *testing.T, data *Data) {
return count return count
} }
countCodeLens := func(c map[span.Span][]protocol.CodeLens) (count int) { countCodeLens := func(c map[span.URI][]protocol.CodeLens) (count int) {
for _, want := range c { for _, want := range c {
count += len(want) count += len(want)
} }
@ -883,8 +883,8 @@ func (data *Data) Golden(tag string, target string, update func() ([]byte, error
} }
func (data *Data) collectCodeLens(spn span.Span, title, cmd string) { func (data *Data) collectCodeLens(spn span.Span, title, cmd string) {
if _, ok := data.CodeLens[spn]; !ok { if _, ok := data.CodeLens[spn.URI()]; !ok {
data.CodeLens[spn] = []protocol.CodeLens{} data.CodeLens[spn.URI()] = []protocol.CodeLens{}
} }
m, err := data.Mapper(spn.URI()) m, err := data.Mapper(spn.URI())
if err != nil { if err != nil {
@ -894,7 +894,7 @@ func (data *Data) collectCodeLens(spn span.Span, title, cmd string) {
if err != nil { if err != nil {
return return
} }
data.CodeLens[spn] = append(data.CodeLens[spn], protocol.CodeLens{ data.CodeLens[spn.URI()] = append(data.CodeLens[spn.URI()], protocol.CodeLens{
Range: rng, Range: rng,
Command: protocol.Command{ Command: protocol.Command{
Title: title, Title: title,

View File

@ -197,7 +197,7 @@ func DiffDiagnostics(uri span.URI, want, got []source.Diagnostic) string {
return "" return ""
} }
func summarizeDiagnostics(i int, uri span.URI, want []source.Diagnostic, got []source.Diagnostic, reason string, args ...interface{}) string { func summarizeDiagnostics(i int, uri span.URI, want, got []source.Diagnostic, reason string, args ...interface{}) string {
msg := &bytes.Buffer{} msg := &bytes.Buffer{}
fmt.Fprint(msg, "diagnostics failed") fmt.Fprint(msg, "diagnostics failed")
if i >= 0 { if i >= 0 {