|  | // Copyright 2019 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package cmd | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "encoding/json" | 
|  | "flag" | 
|  | "fmt" | 
|  | "os" | 
|  | "strings" | 
|  |  | 
|  | "golang.org/x/tools/gopls/internal/protocol" | 
|  | "golang.org/x/tools/gopls/internal/settings" | 
|  | "golang.org/x/tools/internal/tool" | 
|  | ) | 
|  |  | 
|  | // A Definition is the result of a 'definition' query. | 
|  | type Definition struct { | 
|  | Span        span   `json:"span"`        // span of the definition | 
|  | Description string `json:"description"` // description of the denoted object | 
|  | } | 
|  |  | 
|  | // These constant is printed in the help, and then used in a test to verify the | 
|  | // help is still valid. | 
|  | // They refer to "Set" in "flag.FlagSet" from the DetailedHelp method below. | 
|  | const ( | 
|  | exampleLine   = 44 | 
|  | exampleColumn = 47 | 
|  | exampleOffset = 1270 | 
|  | ) | 
|  |  | 
|  | // definition implements the definition verb for gopls. | 
|  | type definition struct { | 
|  | app *Application | 
|  |  | 
|  | JSON              bool `flag:"json" help:"emit output in JSON format"` | 
|  | MarkdownSupported bool `flag:"markdown" help:"support markdown in responses"` | 
|  | } | 
|  |  | 
|  | func (d *definition) Name() string      { return "definition" } | 
|  | func (d *definition) Parent() string    { return d.app.Name() } | 
|  | func (d *definition) Usage() string     { return "[definition-flags] <position>" } | 
|  | func (d *definition) ShortHelp() string { return "show declaration of selected identifier" } | 
|  | func (d *definition) DetailedHelp(f *flag.FlagSet) { | 
|  | fmt.Fprintf(f.Output(), ` | 
|  | Example: show the definition of the identifier at syntax at offset %[1]v in this file (flag.FlagSet): | 
|  |  | 
|  | $ gopls definition internal/cmd/definition.go:%[1]v:%[2]v | 
|  | $ gopls definition internal/cmd/definition.go:#%[3]v | 
|  |  | 
|  | definition-flags: | 
|  | `, exampleLine, exampleColumn, exampleOffset) | 
|  | printFlagDefaults(f) | 
|  | } | 
|  |  | 
|  | // Run performs the definition query as specified by args and prints the | 
|  | // results to stdout. | 
|  | func (d *definition) Run(ctx context.Context, args ...string) error { | 
|  | if len(args) != 1 { | 
|  | return tool.CommandLineErrorf("definition expects 1 argument") | 
|  | } | 
|  | // Plaintext makes more sense for the command line. | 
|  | opts := d.app.options | 
|  | d.app.options = func(o *settings.Options) { | 
|  | if opts != nil { | 
|  | opts(o) | 
|  | } | 
|  | o.PreferredContentFormat = protocol.PlainText | 
|  | if d.MarkdownSupported { | 
|  | o.PreferredContentFormat = protocol.Markdown | 
|  | } | 
|  | } | 
|  | cli, _, err := d.app.connect(ctx) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | defer cli.terminate(ctx) | 
|  | from := parseSpan(args[0]) | 
|  | file, err := cli.openFile(ctx, from.URI()) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | loc, err := file.spanLocation(from) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | p := protocol.DefinitionParams{ | 
|  | TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), | 
|  | } | 
|  | locs, err := cli.server.Definition(ctx, &p) | 
|  | if err != nil { | 
|  | return fmt.Errorf("%v: %v", from, err) | 
|  | } | 
|  |  | 
|  | if len(locs) == 0 { | 
|  | return fmt.Errorf("%v: no definition location (not an identifier?)", from) | 
|  | } | 
|  | file, err = cli.openFile(ctx, locs[0].URI) | 
|  | if err != nil { | 
|  | return fmt.Errorf("%v: %v", from, err) | 
|  | } | 
|  | definition, err := file.locationSpan(locs[0]) | 
|  | if err != nil { | 
|  | return fmt.Errorf("%v: %v", from, err) | 
|  | } | 
|  |  | 
|  | q := protocol.HoverParams{ | 
|  | TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), | 
|  | } | 
|  | hover, err := cli.server.Hover(ctx, &q) | 
|  | if err != nil { | 
|  | return fmt.Errorf("%v: %v", from, err) | 
|  | } | 
|  | var description string | 
|  | if hover != nil { | 
|  | description = strings.TrimSpace(hover.Contents.Value) | 
|  | } | 
|  |  | 
|  | result := &Definition{ | 
|  | Span:        definition, | 
|  | Description: description, | 
|  | } | 
|  | if d.JSON { | 
|  | enc := json.NewEncoder(os.Stdout) | 
|  | enc.SetIndent("", "\t") | 
|  | return enc.Encode(result) | 
|  | } | 
|  | fmt.Printf("%v", result.Span) | 
|  | if len(result.Description) > 0 { | 
|  | fmt.Printf(": defined here as %s", result.Description) | 
|  | } | 
|  | fmt.Printf("\n") | 
|  | return nil | 
|  | } |