golang.org/x/tools/gopls@v0.15.3/internal/cmd/codelens.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cmd 6 7 import ( 8 "context" 9 "flag" 10 "fmt" 11 12 "golang.org/x/tools/gopls/internal/protocol" 13 "golang.org/x/tools/gopls/internal/settings" 14 "golang.org/x/tools/internal/tool" 15 ) 16 17 // codelens implements the codelens verb for gopls. 18 type codelens struct { 19 EditFlags 20 app *Application 21 22 Exec bool `flag:"exec" help:"execute the first matching code lens"` 23 } 24 25 func (r *codelens) Name() string { return "codelens" } 26 func (r *codelens) Parent() string { return r.app.Name() } 27 func (r *codelens) Usage() string { return "[codelens-flags] file[:line[:col]] [title]" } 28 func (r *codelens) ShortHelp() string { return "List or execute code lenses for a file" } 29 func (r *codelens) DetailedHelp(f *flag.FlagSet) { 30 fmt.Fprint(f.Output(), ` 31 The codelens command lists or executes code lenses for the specified 32 file, or line within a file. A code lens is a command associated with 33 a position in the code. 34 35 With an optional title argment, only code lenses matching that 36 title are considered. 37 38 By default, the codelens command lists the available lenses for the 39 specified file or line within a file, including the title and 40 title of the command. With the -exec flag, the first matching command 41 is executed, and its output is printed to stdout. 42 43 Example: 44 45 $ gopls codelens a_test.go # list code lenses in a file 46 $ gopls codelens a_test.go:10 # list code lenses on line 10 47 $ gopls codelens a_test.go gopls.test # list gopls.test commands 48 $ gopls codelens -run a_test.go:10 gopls.test # run a specific test 49 50 codelens-flags: 51 `) 52 printFlagDefaults(f) 53 } 54 55 func (r *codelens) Run(ctx context.Context, args ...string) error { 56 var filename, title string 57 switch len(args) { 58 case 0: 59 return tool.CommandLineErrorf("codelens requires a file name") 60 case 2: 61 title = args[1] 62 fallthrough 63 case 1: 64 filename = args[0] 65 default: 66 return tool.CommandLineErrorf("codelens expects at most two arguments") 67 } 68 69 r.app.editFlags = &r.EditFlags // in case a codelens perform an edit 70 71 // Override the default setting for codelenses[Test], which is 72 // off by default because VS Code has a superior client-side 73 // implementation. But this client is not VS Code. 74 // See golang.LensFuncs(). 75 origOptions := r.app.options 76 r.app.options = func(opts *settings.Options) { 77 origOptions(opts) 78 if opts.Codelenses == nil { 79 opts.Codelenses = make(map[string]bool) 80 } 81 opts.Codelenses["test"] = true 82 } 83 84 // TODO(adonovan): cleanup: factor progress with stats subcommand. 85 cmdDone, onProgress := commandProgress() 86 87 conn, err := r.app.connect(ctx, onProgress) 88 if err != nil { 89 return err 90 } 91 defer conn.terminate(ctx) 92 93 filespan := parseSpan(filename) 94 file, err := conn.openFile(ctx, filespan.URI()) 95 if err != nil { 96 return err 97 } 98 loc, err := file.spanLocation(filespan) 99 if err != nil { 100 return err 101 } 102 103 p := protocol.CodeLensParams{ 104 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI}, 105 } 106 lenses, err := conn.CodeLens(ctx, &p) 107 if err != nil { 108 return err 109 } 110 111 for _, lens := range lenses { 112 sp, err := file.rangeSpan(lens.Range) 113 if err != nil { 114 return nil 115 } 116 117 if title != "" && lens.Command.Title != title { 118 continue // title was specified but does not match 119 } 120 if filespan.HasPosition() && !protocol.Intersect(loc.Range, lens.Range) { 121 continue // position was specified but does not match 122 } 123 124 // -exec: run the first matching code lens. 125 if r.Exec { 126 _, err := conn.executeCommand(ctx, cmdDone, lens.Command) 127 return err 128 } 129 130 // No -exec: list matching code lenses. 131 fmt.Printf("%v: %q [%s]\n", sp, lens.Command.Title, lens.Command.Command) 132 } 133 134 if r.Exec { 135 return fmt.Errorf("no code lens at %s with title %q", filespan, title) 136 } 137 return nil 138 }