github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/mod/code_lens.go (about)

     1  package mod
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"golang.org/x/mod/modfile"
    10  	"github.com/april1989/origin-go-tools/internal/lsp/protocol"
    11  	"github.com/april1989/origin-go-tools/internal/lsp/source"
    12  	"github.com/april1989/origin-go-tools/internal/span"
    13  )
    14  
    15  // LensFuncs returns the supported lensFuncs for go.mod files.
    16  func LensFuncs() map[string]source.LensFunc {
    17  	return map[string]source.LensFunc{
    18  		source.CommandUpgradeDependency.Name: upgradeLens,
    19  		source.CommandTidy.Name:              tidyLens,
    20  		source.CommandVendor.Name:            vendorLens,
    21  	}
    22  }
    23  
    24  func upgradeLens(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
    25  	pm, err := snapshot.ParseMod(ctx, fh)
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  	module := pm.File.Module
    30  	if module == nil || module.Syntax == nil {
    31  		return nil, nil
    32  	}
    33  	upgrades, err := snapshot.ModUpgrade(ctx, fh)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	var (
    38  		codelenses  []protocol.CodeLens
    39  		allUpgrades []string
    40  	)
    41  	for _, req := range pm.File.Require {
    42  		dep := req.Mod.Path
    43  		latest, ok := upgrades[dep]
    44  		if !ok {
    45  			continue
    46  		}
    47  		// Get the range of the require directive.
    48  		rng, err := positionsToRange(fh.URI(), pm.Mapper, req.Syntax.Start, req.Syntax.End)
    49  		if err != nil {
    50  			return nil, err
    51  		}
    52  		upgradeDepArgs, err := source.MarshalArgs(fh.URI(), []string{dep})
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  		codelenses = append(codelenses, protocol.CodeLens{
    57  			Range: rng,
    58  			Command: protocol.Command{
    59  				Title:     fmt.Sprintf("Upgrade dependency to %s", latest),
    60  				Command:   source.CommandUpgradeDependency.Name,
    61  				Arguments: upgradeDepArgs,
    62  			},
    63  		})
    64  		allUpgrades = append(allUpgrades, dep)
    65  	}
    66  	// If there is at least 1 upgrade, add "Upgrade all dependencies" to
    67  	// the module statement.
    68  	if len(allUpgrades) > 0 {
    69  		upgradeDepArgs, err := source.MarshalArgs(fh.URI(), append([]string{"-u"}, allUpgrades...))
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		// Get the range of the module directive.
    74  		moduleRng, err := positionsToRange(pm.Mapper.URI, pm.Mapper, module.Syntax.Start, module.Syntax.End)
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  		codelenses = append(codelenses, protocol.CodeLens{
    79  			Range: moduleRng,
    80  			Command: protocol.Command{
    81  				Title:     "Upgrade all dependencies",
    82  				Command:   source.CommandUpgradeDependency.Name,
    83  				Arguments: upgradeDepArgs,
    84  			},
    85  		})
    86  	}
    87  	return codelenses, err
    88  }
    89  
    90  func tidyLens(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
    91  	goModArgs, err := source.MarshalArgs(fh.URI())
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	tidied, err := snapshot.ModTidy(ctx, fh)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	if len(tidied.Errors) == 0 {
   100  		return nil, nil
   101  	}
   102  	pm, err := snapshot.ParseMod(ctx, fh)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	rng, err := positionsToRange(pm.Mapper.URI, pm.Mapper, pm.File.Module.Syntax.Start, pm.File.Module.Syntax.End)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	return []protocol.CodeLens{{
   111  		Range: rng,
   112  		Command: protocol.Command{
   113  			Title:     "Tidy module",
   114  			Command:   source.CommandTidy.Name,
   115  			Arguments: goModArgs,
   116  		},
   117  	}}, err
   118  }
   119  
   120  func vendorLens(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.CodeLens, error) {
   121  	goModArgs, err := source.MarshalArgs(fh.URI())
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	pm, err := snapshot.ParseMod(ctx, fh)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	rng, err := positionsToRange(pm.Mapper.URI, pm.Mapper, pm.File.Module.Syntax.Start, pm.File.Module.Syntax.End)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	// Change the message depending on whether or not the module already has a
   134  	// vendor directory.
   135  	title := "Create vendor directory"
   136  	vendorDir := filepath.Join(filepath.Dir(fh.URI().Filename()), "vendor")
   137  	if info, _ := os.Stat(vendorDir); info != nil && info.IsDir() {
   138  		title = "Sync vendor directory"
   139  	}
   140  	return []protocol.CodeLens{{
   141  		Range: rng,
   142  		Command: protocol.Command{
   143  			Title:     title,
   144  			Command:   source.CommandVendor.Name,
   145  			Arguments: goModArgs,
   146  		},
   147  	}}, nil
   148  }
   149  
   150  func positionsToRange(uri span.URI, m *protocol.ColumnMapper, s, e modfile.Position) (protocol.Range, error) {
   151  	line, col, err := m.Converter.ToPosition(s.Byte)
   152  	if err != nil {
   153  		return protocol.Range{}, err
   154  	}
   155  	start := span.NewPoint(line, col, s.Byte)
   156  	line, col, err = m.Converter.ToPosition(e.Byte)
   157  	if err != nil {
   158  		return protocol.Range{}, err
   159  	}
   160  	end := span.NewPoint(line, col, e.Byte)
   161  	rng, err := m.Range(span.New(uri, start, end))
   162  	if err != nil {
   163  		return protocol.Range{}, err
   164  	}
   165  	return rng, err
   166  }