golang.org/x/tools/gopls@v0.15.3/internal/cmd/definition.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 "encoding/json" 10 "flag" 11 "fmt" 12 "os" 13 "strings" 14 15 "golang.org/x/tools/gopls/internal/protocol" 16 "golang.org/x/tools/gopls/internal/settings" 17 "golang.org/x/tools/internal/tool" 18 ) 19 20 // A Definition is the result of a 'definition' query. 21 type Definition struct { 22 Span span `json:"span"` // span of the definition 23 Description string `json:"description"` // description of the denoted object 24 } 25 26 // These constant is printed in the help, and then used in a test to verify the 27 // help is still valid. 28 // They refer to "Set" in "flag.FlagSet" from the DetailedHelp method below. 29 const ( 30 exampleLine = 44 31 exampleColumn = 47 32 exampleOffset = 1270 33 ) 34 35 // definition implements the definition verb for gopls. 36 type definition struct { 37 app *Application 38 39 JSON bool `flag:"json" help:"emit output in JSON format"` 40 MarkdownSupported bool `flag:"markdown" help:"support markdown in responses"` 41 } 42 43 func (d *definition) Name() string { return "definition" } 44 func (d *definition) Parent() string { return d.app.Name() } 45 func (d *definition) Usage() string { return "[definition-flags] <position>" } 46 func (d *definition) ShortHelp() string { return "show declaration of selected identifier" } 47 func (d *definition) DetailedHelp(f *flag.FlagSet) { 48 fmt.Fprintf(f.Output(), ` 49 Example: show the definition of the identifier at syntax at offset %[1]v in this file (flag.FlagSet): 50 51 $ gopls definition internal/cmd/definition.go:%[1]v:%[2]v 52 $ gopls definition internal/cmd/definition.go:#%[3]v 53 54 definition-flags: 55 `, exampleLine, exampleColumn, exampleOffset) 56 printFlagDefaults(f) 57 } 58 59 // Run performs the definition query as specified by args and prints the 60 // results to stdout. 61 func (d *definition) Run(ctx context.Context, args ...string) error { 62 if len(args) != 1 { 63 return tool.CommandLineErrorf("definition expects 1 argument") 64 } 65 // Plaintext makes more sense for the command line. 66 opts := d.app.options 67 d.app.options = func(o *settings.Options) { 68 if opts != nil { 69 opts(o) 70 } 71 o.PreferredContentFormat = protocol.PlainText 72 if d.MarkdownSupported { 73 o.PreferredContentFormat = protocol.Markdown 74 } 75 } 76 conn, err := d.app.connect(ctx, nil) 77 if err != nil { 78 return err 79 } 80 defer conn.terminate(ctx) 81 from := parseSpan(args[0]) 82 file, err := conn.openFile(ctx, from.URI()) 83 if err != nil { 84 return err 85 } 86 loc, err := file.spanLocation(from) 87 if err != nil { 88 return err 89 } 90 p := protocol.DefinitionParams{ 91 TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), 92 } 93 locs, err := conn.Definition(ctx, &p) 94 if err != nil { 95 return fmt.Errorf("%v: %v", from, err) 96 } 97 98 if len(locs) == 0 { 99 return fmt.Errorf("%v: not an identifier", from) 100 } 101 file, err = conn.openFile(ctx, locs[0].URI) 102 if err != nil { 103 return fmt.Errorf("%v: %v", from, err) 104 } 105 definition, err := file.locationSpan(locs[0]) 106 if err != nil { 107 return fmt.Errorf("%v: %v", from, err) 108 } 109 110 q := protocol.HoverParams{ 111 TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), 112 } 113 hover, err := conn.Hover(ctx, &q) 114 if err != nil { 115 return fmt.Errorf("%v: %v", from, err) 116 } 117 var description string 118 if hover != nil { 119 description = strings.TrimSpace(hover.Contents.Value) 120 } 121 122 result := &Definition{ 123 Span: definition, 124 Description: description, 125 } 126 if d.JSON { 127 enc := json.NewEncoder(os.Stdout) 128 enc.SetIndent("", "\t") 129 return enc.Encode(result) 130 } 131 fmt.Printf("%v", result.Span) 132 if len(result.Description) > 0 { 133 fmt.Printf(": defined here as %s", result.Description) 134 } 135 fmt.Printf("\n") 136 return nil 137 }