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
5package cmd
6
7import (
8	"context"
9	"flag"
10	"fmt"
11
12	"golang.org/x/tools/internal/lsp/protocol"
13	"golang.org/x/tools/internal/span"
14	"golang.org/x/tools/internal/tool"
15)
16
17// signature implements the signature verb for gopls
18type signature struct {
19	app *Application
20}
21
22func (r *signature) Name() string      { return "signature" }
23func (r *signature) Usage() string     { return "<position>" }
24func (r *signature) ShortHelp() string { return "display selected identifier's signature" }
25func (r *signature) DetailedHelp(f *flag.FlagSet) {
26	fmt.Fprint(f.Output(), `
27Example:
28
29  $ # 1-indexed location (:line:column or :#offset) of the target identifier
30  $ gopls signature helper/helper.go:8:6
31  $ gopls signature helper/helper.go:#53
32
33  gopls signature flags are:
34`)
35	f.PrintDefaults()
36}
37
38func (r *signature) Run(ctx context.Context, args ...string) error {
39	if len(args) != 1 {
40		return tool.CommandLineErrorf("signature expects 1 argument (position)")
41	}
42
43	conn, err := r.app.connect(ctx)
44	if err != nil {
45		return err
46	}
47	defer conn.terminate(ctx)
48
49	from := span.Parse(args[0])
50	file := conn.AddFile(ctx, from.URI())
51	if file.err != nil {
52		return file.err
53	}
54
55	loc, err := file.mapper.Location(from)
56	if err != nil {
57		return err
58	}
59
60	tdpp := protocol.TextDocumentPositionParams{
61		TextDocument: protocol.TextDocumentIdentifier{
62			URI: protocol.NewURI(from.URI()),
63		},
64		Position: loc.Range.Start,
65	}
66	p := protocol.SignatureHelpParams{
67		TextDocumentPositionParams: tdpp,
68	}
69
70	s, err := conn.SignatureHelp(ctx, &p)
71	if err != nil {
72		return err
73	}
74
75	if len(s.Signatures) == 0 {
76		return tool.CommandLineErrorf("%v: not a function", from)
77	}
78
79	// there is only ever one possible signature,
80	// see toProtocolSignatureHelp in lsp/signature_help.go
81	signature := s.Signatures[0]
82	fmt.Printf("%s\n", signature.Label)
83	if signature.Documentation != "" {
84		fmt.Printf("\n%s\n", signature.Documentation)
85	}
86
87	return nil
88}
89