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 }