internal/lsp/mod: speed up and simplify go.mod code lenses

The tidy and upgrade code lenses first run the go command they
represent, then show the lens if there are changes to be made. While
this makes for a cleaner user experience, it also means that we're
running a go command that hits the network in a synchronous code lens
call. That's risky if the user is on a slow network connection.

Removing the call does represent a change, possibly a regression, in UX.
However, we already felt that the presentation of the upgrade option on
each dependency line was too noisy. Therefore the only remaining benefit
is that the lenses only appear when they have something to do. We've
decided that's not sufficient justification. Remove the calls.

We may recreate them as diagnostics in the future, so I've left
ModUpgrade around. (ModTidy is still used by diagnostics.)

Change-Id: I16c5eaebd1a50e5358c2addde9c1bb3f1182aac2
Reviewed-on: https://go-review.googlesource.com/c/tools/+/271297
Trust: Heschi Kreinick <heschi@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/mod/code_lens.go b/internal/lsp/mod/code_lens.go
index db2aea2..e3aa2f6 100644
--- a/internal/lsp/mod/code_lens.go
+++ b/internal/lsp/mod/code_lens.go
@@ -6,7 +6,6 @@
 	"os"
 	"path/filepath"
 
-	"golang.org/x/mod/modfile"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/span"
@@ -23,93 +22,46 @@
 
 func upgradeLens(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
 	pm, err := snapshot.ParseMod(ctx, fh)
-	if err != nil {
+	if err != nil || pm.File == nil {
 		return nil, err
 	}
-	module := pm.File.Module
-	if module == nil || module.Syntax == nil {
+	if len(pm.File.Require) == 0 {
+		// Nothing to upgrade.
 		return nil, nil
 	}
-	upgrades, err := snapshot.ModUpgrade(ctx, fh)
+	upgradeDepArgs, err := source.MarshalArgs(fh.URI(), false, []string{"-u", "all"})
 	if err != nil {
 		return nil, err
 	}
-	var (
-		codelenses  []protocol.CodeLens
-		allUpgrades []string
-	)
-	for _, req := range pm.File.Require {
-		dep := req.Mod.Path
-		latest, ok := upgrades[dep]
-		if !ok {
-			continue
-		}
-		if req.Syntax == nil {
-			continue
-		}
-		// Get the range of the require directive.
-		rng, err := positionsToRange(fh.URI(), pm.Mapper, req.Syntax.Start, req.Syntax.End)
-		if err != nil {
-			return nil, err
-		}
-		upgradeDepArgs, err := source.MarshalArgs(fh.URI(), false, []string{dep})
-		if err != nil {
-			return nil, err
-		}
-		codelenses = append(codelenses, protocol.CodeLens{
-			Range: rng,
-			Command: protocol.Command{
-				Title:     fmt.Sprintf("Upgrade dependency to %s", latest),
-				Command:   source.CommandUpgradeDependency.ID(),
-				Arguments: upgradeDepArgs,
-			},
-		})
-		allUpgrades = append(allUpgrades, dep)
+	rng, err := moduleStmtRange(fh, pm)
+	if err != nil {
+		return nil, err
 	}
-	// If there is at least 1 upgrade, add "Upgrade all dependencies" to
-	// the module statement.
-	if len(allUpgrades) > 0 {
-		upgradeDepArgs, err := source.MarshalArgs(fh.URI(), false, append([]string{"-u"}, allUpgrades...))
-		if err != nil {
-			return nil, err
-		}
-		// Get the range of the module directive.
-		moduleRng, err := positionsToRange(pm.Mapper.URI, pm.Mapper, module.Syntax.Start, module.Syntax.End)
-		if err != nil {
-			return nil, err
-		}
-		codelenses = append(codelenses, protocol.CodeLens{
-			Range: moduleRng,
-			Command: protocol.Command{
-				Title:     "Upgrade all dependencies",
-				Command:   source.CommandUpgradeDependency.ID(),
-				Arguments: upgradeDepArgs,
-			},
-		})
-	}
-	return codelenses, err
+	return []protocol.CodeLens{{
+		Range: rng,
+		Command: protocol.Command{
+			Title:     "Upgrade all dependencies",
+			Command:   source.CommandUpgradeDependency.ID(),
+			Arguments: upgradeDepArgs,
+		},
+	}}, nil
+
 }
 
 func tidyLens(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
+	pm, err := snapshot.ParseMod(ctx, fh)
+	if err != nil || pm.File == nil {
+		return nil, err
+	}
+	if len(pm.File.Require) == 0 {
+		// Nothing to vendor.
+		return nil, nil
+	}
 	goModArgs, err := source.MarshalArgs(fh.URI())
 	if err != nil {
 		return nil, err
 	}
-	tidied, err := snapshot.ModTidy(ctx, fh)
-	if err != nil {
-		return nil, err
-	}
-	if len(tidied.Errors) == 0 {
-		return nil, nil
-	}
-	pm, err := snapshot.ParseMod(ctx, fh)
-	if err != nil {
-		return nil, err
-	}
-	if pm.File == nil || pm.File.Module == nil || pm.File.Module.Syntax == nil {
-		return nil, fmt.Errorf("no parsed go.mod for %s", fh.URI())
-	}
-	rng, err := positionsToRange(pm.Mapper.URI, pm.Mapper, pm.File.Module.Syntax.Start, pm.File.Module.Syntax.End)
+	rng, err := moduleStmtRange(fh, pm)
 	if err != nil {
 		return nil, err
 	}
@@ -120,22 +72,19 @@
 			Command:   source.CommandTidy.ID(),
 			Arguments: goModArgs,
 		},
-	}}, err
+	}}, nil
 }
 
 func vendorLens(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
-	goModArgs, err := source.MarshalArgs(fh.URI())
-	if err != nil {
-		return nil, err
-	}
 	pm, err := snapshot.ParseMod(ctx, fh)
+	if err != nil || pm.File == nil {
+		return nil, err
+	}
+	rng, err := moduleStmtRange(fh, pm)
 	if err != nil {
 		return nil, err
 	}
-	if pm.File == nil || pm.File.Module == nil || pm.File.Module.Syntax == nil {
-		return nil, fmt.Errorf("no parsed go.mod for %s", fh.URI())
-	}
-	rng, err := positionsToRange(pm.Mapper.URI, pm.Mapper, pm.File.Module.Syntax.Start, pm.File.Module.Syntax.End)
+	goModArgs, err := source.MarshalArgs(fh.URI())
 	if err != nil {
 		return nil, err
 	}
@@ -156,18 +105,22 @@
 	}}, nil
 }
 
-func positionsToRange(uri span.URI, m *protocol.ColumnMapper, s, e modfile.Position) (protocol.Range, error) {
-	line, col, err := m.Converter.ToPosition(s.Byte)
+func moduleStmtRange(fh source.FileHandle, pm *source.ParsedModule) (protocol.Range, error) {
+	if pm.File == nil || pm.File.Module == nil || pm.File.Module.Syntax == nil {
+		return protocol.Range{}, fmt.Errorf("no module statement in %s", fh.URI())
+	}
+	syntax := pm.File.Module.Syntax
+	line, col, err := pm.Mapper.Converter.ToPosition(syntax.Start.Byte)
 	if err != nil {
 		return protocol.Range{}, err
 	}
-	start := span.NewPoint(line, col, s.Byte)
-	line, col, err = m.Converter.ToPosition(e.Byte)
+	start := span.NewPoint(line, col, syntax.Start.Byte)
+	line, col, err = pm.Mapper.Converter.ToPosition(syntax.End.Byte)
 	if err != nil {
 		return protocol.Range{}, err
 	}
-	end := span.NewPoint(line, col, e.Byte)
-	rng, err := m.Range(span.New(uri, start, end))
+	end := span.NewPoint(line, col, syntax.End.Byte)
+	rng, err := pm.Mapper.Range(span.New(fh.URI(), start, end))
 	if err != nil {
 		return protocol.Range{}, err
 	}