From 6fbec87eedc6ef7a7088b7bc1fd1010f591f52d8 Mon Sep 17 00:00:00 2001 From: Rebecca Stambler Date: Mon, 31 Aug 2020 15:33:05 -0400 Subject: [PATCH] internal/lsp: use -json for module upgrades This is better than parsing the default output. Also, change the start progress message, since it ends up duplicating the title in the message that the user sees. Change-Id: I3540d30c7976c6be0722531b2e258341081e0b72 Reviewed-on: https://go-review.googlesource.com/c/tools/+/251920 Run-TryBot: Rebecca Stambler TryBot-Result: Gobot Gobot Reviewed-by: Robert Findley --- internal/lsp/cache/mod.go | 47 +++++++++++++++++++++++---------------- internal/lsp/command.go | 8 ++++--- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/internal/lsp/cache/mod.go b/internal/lsp/cache/mod.go index dfa1f4f8c6..c2e80ff5b9 100644 --- a/internal/lsp/cache/mod.go +++ b/internal/lsp/cache/mod.go @@ -6,7 +6,9 @@ package cache import ( "context" + "encoding/json" "fmt" + "io" "os" "path/filepath" "regexp" @@ -273,6 +275,15 @@ func (muh *modUpgradeHandle) upgrades(ctx context.Context, snapshot *snapshot) ( return data.upgrades, data.err } +// moduleUpgrade describes a module that can be upgraded to a particular +// version. +type moduleUpgrade struct { + Path string + Update struct { + Version string + } +} + func (s *snapshot) ModUpgrade(ctx context.Context, fh source.FileHandle) (map[string]string, error) { if err := s.awaitLoaded(ctx); err != nil { return nil, err @@ -306,7 +317,7 @@ func (s *snapshot) ModUpgrade(ctx context.Context, fh source.FileHandle) (map[st } // Run "go list -mod readonly -u -m all" to be able to see which deps can be // upgraded without modifying mod file. - args := []string{"-u", "-m", "all"} + args := []string{"-u", "-m", "-json", "all"} if !snapshot.view.tmpMod || containsVendor(fh.URI()) { // Use -mod=readonly if the module contains a vendor directory // (see golang/go#38711). @@ -316,28 +327,26 @@ func (s *snapshot) ModUpgrade(ctx context.Context, fh source.FileHandle) (map[st if err != nil { return &modUpgradeData{err: err} } - upgradesList := strings.Split(stdout.String(), "\n") - if len(upgradesList) <= 1 { - return nil + var upgradeList []moduleUpgrade + dec := json.NewDecoder(stdout) + for { + var m moduleUpgrade + if err := dec.Decode(&m); err == io.EOF { + break + } else if err != nil { + return &modUpgradeData{err: err} + } + upgradeList = append(upgradeList, m) + } + if len(upgradeList) <= 1 { + return &modUpgradeData{} } upgrades := make(map[string]string) - for _, upgrade := range upgradesList[1:] { - // Example: "github.com/x/tools v1.1.0 [v1.2.0]" - info := strings.Split(upgrade, " ") - if len(info) != 3 { + for _, upgrade := range upgradeList[1:] { + if upgrade.Update.Version == "" { continue } - dep, version := info[0], info[2] - - // Make sure that the format matches our expectation. - if len(version) < 2 { - continue - } - if version[0] != '[' || version[len(version)-1] != ']' { - continue - } - latest := version[1 : len(version)-1] // remove the "[" and "]" - upgrades[dep] = latest + upgrades[upgrade.Path] = upgrade.Update.Version } return &modUpgradeData{ upgrades: upgrades, diff --git a/internal/lsp/command.go b/internal/lsp/command.go index 191af7321a..c53d4dc59a 100644 --- a/internal/lsp/command.go +++ b/internal/lsp/command.go @@ -105,7 +105,7 @@ func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCom // clients are aware of the work item before the command completes. This // matters for regtests, where having a continuous thread of work is // convenient for assertions. - work := s.progress.start(ctx, title, title+": running...", params.WorkDoneToken, cancel) + work := s.progress.start(ctx, title, "Running...", params.WorkDoneToken, cancel) go func() { defer cancel() err := s.runCommand(ctx, work, command, params.Arguments) @@ -117,10 +117,12 @@ func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCom work.end(title + ": failed") // Show a message when work completes with error, because the progress end // message is typically dismissed immediately by LSP clients. - s.client.ShowMessage(ctx, &protocol.ShowMessageParams{ + if err := s.client.ShowMessage(ctx, &protocol.ShowMessageParams{ Type: protocol.Error, Message: fmt.Sprintf("%s: An error occurred: %v", title, err), - }) + }); err != nil { + event.Error(ctx, title+": failed to show message", err) + } default: work.end(command.Name + ": completed") }