2018-11-12 14:53:10 -07:00
|
|
|
// Copyright 2018 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 source
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-12-04 11:45:53 -07:00
|
|
|
"fmt"
|
2018-11-12 14:53:10 -07:00
|
|
|
"go/ast"
|
2019-08-12 14:59:23 -06:00
|
|
|
"go/doc"
|
2018-11-12 14:53:10 -07:00
|
|
|
"go/token"
|
|
|
|
"go/types"
|
|
|
|
|
|
|
|
"golang.org/x/tools/go/ast/astutil"
|
2020-04-17 07:32:56 -06:00
|
|
|
"golang.org/x/tools/internal/event"
|
2019-08-26 22:26:45 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
2019-08-06 13:13:11 -06:00
|
|
|
errors "golang.org/x/xerrors"
|
2018-11-12 14:53:10 -07:00
|
|
|
)
|
|
|
|
|
2020-02-04 20:44:33 -07:00
|
|
|
func SignatureHelp(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) (*protocol.SignatureInformation, int, error) {
|
2020-04-20 10:14:12 -06:00
|
|
|
ctx, done := event.Start(ctx, "source.SignatureHelp")
|
2019-06-26 20:46:12 -06:00
|
|
|
defer done()
|
2019-07-11 19:05:55 -06:00
|
|
|
|
2020-07-22 09:32:32 -06:00
|
|
|
pkg, pgf, err := getParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
2019-09-09 18:22:42 -06:00
|
|
|
if err != nil {
|
2020-05-01 08:05:39 -06:00
|
|
|
return nil, 0, fmt.Errorf("getting file for SignatureHelp: %w", err)
|
2019-09-16 16:17:51 -06:00
|
|
|
}
|
2020-07-21 13:15:06 -06:00
|
|
|
spn, err := pgf.Mapper.PointSpan(pos)
|
2019-09-17 09:19:11 -06:00
|
|
|
if err != nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, err
|
2019-09-16 16:17:51 -06:00
|
|
|
}
|
2020-07-21 13:15:06 -06:00
|
|
|
rng, err := spn.Range(pgf.Mapper.Converter)
|
2019-07-09 15:52:23 -06:00
|
|
|
if err != nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, err
|
2019-03-11 15:14:55 -06:00
|
|
|
}
|
2018-11-12 14:53:10 -07:00
|
|
|
// Find a call expression surrounding the query position.
|
|
|
|
var callExpr *ast.CallExpr
|
2020-07-21 13:15:06 -06:00
|
|
|
path, _ := astutil.PathEnclosingInterval(pgf.File, rng.Start, rng.Start)
|
2018-11-12 14:53:10 -07:00
|
|
|
if path == nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, errors.Errorf("cannot find node enclosing position")
|
2018-11-12 14:53:10 -07:00
|
|
|
}
|
2019-06-28 19:27:41 -06:00
|
|
|
FindCall:
|
2018-11-12 14:53:10 -07:00
|
|
|
for _, node := range path {
|
2019-06-28 19:27:41 -06:00
|
|
|
switch node := node.(type) {
|
|
|
|
case *ast.CallExpr:
|
2019-08-26 22:26:45 -06:00
|
|
|
if rng.Start >= node.Lparen && rng.Start <= node.Rparen {
|
2019-06-28 19:27:41 -06:00
|
|
|
callExpr = node
|
|
|
|
break FindCall
|
|
|
|
}
|
|
|
|
case *ast.FuncLit, *ast.FuncType:
|
|
|
|
// The user is within an anonymous function,
|
|
|
|
// which may be the parameter to the *ast.CallExpr.
|
|
|
|
// Don't show signature help in this case.
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, errors.Errorf("no signature help within a function declaration")
|
2018-11-12 14:53:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if callExpr == nil || callExpr.Fun == nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, errors.Errorf("cannot find an enclosing function")
|
2018-11-12 14:53:10 -07:00
|
|
|
}
|
|
|
|
|
2020-07-21 13:15:06 -06:00
|
|
|
qf := qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo())
|
2020-04-21 19:28:44 -06:00
|
|
|
|
2019-05-14 19:20:41 -06:00
|
|
|
// Get the object representing the function, if available.
|
|
|
|
// There is no object in certain cases such as calling a function returned by
|
|
|
|
// a function (e.g. "foo()()").
|
|
|
|
var obj types.Object
|
|
|
|
switch t := callExpr.Fun.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
obj = pkg.GetTypesInfo().ObjectOf(t)
|
|
|
|
case *ast.SelectorExpr:
|
|
|
|
obj = pkg.GetTypesInfo().ObjectOf(t.Sel)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle builtin functions separately.
|
|
|
|
if obj, ok := obj.(*types.Builtin); ok {
|
2020-07-24 15:41:50 -06:00
|
|
|
return builtinSignature(ctx, snapshot, callExpr, obj.Name(), rng.Start)
|
2019-05-14 19:20:41 -06:00
|
|
|
}
|
|
|
|
|
internal/lsp: improve signatureHelp in various cases
- show signature for function calls whose function expression is not
an object (e.g. the second call in foo()()). since the function name
is not available, we use the generic "func"
- only provide signature help when the position is on or within the
call expression parens. this is consistent with the one other lsp
server i tried (java). this improves the gopls experience in emacs
where lsp-mode is constantly calling "hover" and
"signatureHelp" ("hover" should be preferred unless you are inside
the function params list)
- use the entire signature type string as the label since that includes
the return values, which are useful to see
- don't qualify the function name with its package. it looks funny to
see "bytes.Cap()" as the help when you are in a call
to (*bytes.Buffer).Cap(). it could be useful to include invocant
type info, but leave it out for now since signature help is meant to
focus on the function parameters.
- don't turn variadic args "foo ...int" into "foo []int" for the
parameter information (i.e. maintain it as "foo ...int")
- when determining active parameter, count the space before a
parameter name as being part of that parameter (e.g. the space
before "b" in "func(a int, b int)")
- handle variadic params when determining the active param (i.e.
highlight "foo(a int, *b ...string*)" on signature help for final
param in `foo(123, "a", "b", "c")`
- don't generate an extra space in formatParams() for unnamed
arguments
I also tweaked the signatureHelp server log message to include the
error message itself, and populated the server's logger in lsp_test.go
to aid in development.
Fixes golang/go#31448
Change-Id: Iefe0e1e3c531d17197c0fa997b949174475a276c
GitHub-Last-Rev: 5c0b8ebd87a8c05d5d8f519ea096f94e89c77e2c
GitHub-Pull-Request: golang/tools#82
Reviewed-on: https://go-review.googlesource.com/c/tools/+/172439
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-04-16 22:54:13 -06:00
|
|
|
// Get the type information for the function being called.
|
|
|
|
sigType := pkg.GetTypesInfo().TypeOf(callExpr.Fun)
|
|
|
|
if sigType == nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, errors.Errorf("cannot get type for Fun %[1]T (%[1]v)", callExpr.Fun)
|
2018-11-12 14:53:10 -07:00
|
|
|
}
|
internal/lsp: improve signatureHelp in various cases
- show signature for function calls whose function expression is not
an object (e.g. the second call in foo()()). since the function name
is not available, we use the generic "func"
- only provide signature help when the position is on or within the
call expression parens. this is consistent with the one other lsp
server i tried (java). this improves the gopls experience in emacs
where lsp-mode is constantly calling "hover" and
"signatureHelp" ("hover" should be preferred unless you are inside
the function params list)
- use the entire signature type string as the label since that includes
the return values, which are useful to see
- don't qualify the function name with its package. it looks funny to
see "bytes.Cap()" as the help when you are in a call
to (*bytes.Buffer).Cap(). it could be useful to include invocant
type info, but leave it out for now since signature help is meant to
focus on the function parameters.
- don't turn variadic args "foo ...int" into "foo []int" for the
parameter information (i.e. maintain it as "foo ...int")
- when determining active parameter, count the space before a
parameter name as being part of that parameter (e.g. the space
before "b" in "func(a int, b int)")
- handle variadic params when determining the active param (i.e.
highlight "foo(a int, *b ...string*)" on signature help for final
param in `foo(123, "a", "b", "c")`
- don't generate an extra space in formatParams() for unnamed
arguments
I also tweaked the signatureHelp server log message to include the
error message itself, and populated the server's logger in lsp_test.go
to aid in development.
Fixes golang/go#31448
Change-Id: Iefe0e1e3c531d17197c0fa997b949174475a276c
GitHub-Last-Rev: 5c0b8ebd87a8c05d5d8f519ea096f94e89c77e2c
GitHub-Pull-Request: golang/tools#82
Reviewed-on: https://go-review.googlesource.com/c/tools/+/172439
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-04-16 22:54:13 -06:00
|
|
|
|
|
|
|
sig, _ := sigType.Underlying().(*types.Signature)
|
2018-11-12 14:53:10 -07:00
|
|
|
if sig == nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, errors.Errorf("cannot find signature for Fun %[1]T (%[1]v)", callExpr.Fun)
|
2018-11-12 14:53:10 -07:00
|
|
|
}
|
internal/lsp: improve signatureHelp in various cases
- show signature for function calls whose function expression is not
an object (e.g. the second call in foo()()). since the function name
is not available, we use the generic "func"
- only provide signature help when the position is on or within the
call expression parens. this is consistent with the one other lsp
server i tried (java). this improves the gopls experience in emacs
where lsp-mode is constantly calling "hover" and
"signatureHelp" ("hover" should be preferred unless you are inside
the function params list)
- use the entire signature type string as the label since that includes
the return values, which are useful to see
- don't qualify the function name with its package. it looks funny to
see "bytes.Cap()" as the help when you are in a call
to (*bytes.Buffer).Cap(). it could be useful to include invocant
type info, but leave it out for now since signature help is meant to
focus on the function parameters.
- don't turn variadic args "foo ...int" into "foo []int" for the
parameter information (i.e. maintain it as "foo ...int")
- when determining active parameter, count the space before a
parameter name as being part of that parameter (e.g. the space
before "b" in "func(a int, b int)")
- handle variadic params when determining the active param (i.e.
highlight "foo(a int, *b ...string*)" on signature help for final
param in `foo(123, "a", "b", "c")`
- don't generate an extra space in formatParams() for unnamed
arguments
I also tweaked the signatureHelp server log message to include the
error message itself, and populated the server's logger in lsp_test.go
to aid in development.
Fixes golang/go#31448
Change-Id: Iefe0e1e3c531d17197c0fa997b949174475a276c
GitHub-Last-Rev: 5c0b8ebd87a8c05d5d8f519ea096f94e89c77e2c
GitHub-Pull-Request: golang/tools#82
Reviewed-on: https://go-review.googlesource.com/c/tools/+/172439
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-04-16 22:54:13 -06:00
|
|
|
|
2020-03-02 07:51:12 -07:00
|
|
|
activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), rng.Start)
|
2019-05-14 19:20:41 -06:00
|
|
|
|
2019-06-04 23:16:23 -06:00
|
|
|
var (
|
|
|
|
name string
|
|
|
|
comment *ast.CommentGroup
|
|
|
|
)
|
2019-05-14 19:20:41 -06:00
|
|
|
if obj != nil {
|
2020-07-21 13:15:06 -06:00
|
|
|
node, err := objToDecl(ctx, snapshot, pkg, obj)
|
2019-06-04 23:16:23 -06:00
|
|
|
if err != nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, err
|
2019-06-04 23:16:23 -06:00
|
|
|
}
|
2020-07-28 15:00:10 -06:00
|
|
|
rng, err := objToMappedRange(snapshot, pkg, obj)
|
2019-06-04 23:16:23 -06:00
|
|
|
if err != nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, err
|
2019-06-04 23:16:23 -06:00
|
|
|
}
|
2020-04-27 21:26:00 -06:00
|
|
|
decl := Declaration{
|
2019-12-08 21:19:55 -07:00
|
|
|
obj: obj,
|
|
|
|
node: node,
|
2019-06-04 23:16:23 -06:00
|
|
|
}
|
2019-12-08 21:19:55 -07:00
|
|
|
decl.MappedRange = append(decl.MappedRange, rng)
|
2020-07-28 15:00:10 -06:00
|
|
|
d, err := hover(ctx, snapshot.FileSet(), pkg, decl)
|
2019-06-04 23:16:23 -06:00
|
|
|
if err != nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, err
|
2019-06-04 23:16:23 -06:00
|
|
|
}
|
2019-05-14 19:20:41 -06:00
|
|
|
name = obj.Name()
|
2019-06-04 23:16:23 -06:00
|
|
|
comment = d.comment
|
2019-05-14 19:20:41 -06:00
|
|
|
} else {
|
|
|
|
name = "func"
|
|
|
|
}
|
2020-07-21 13:15:06 -06:00
|
|
|
s, err := newSignature(ctx, snapshot, pkg, pgf.File, name, sig, comment, qf)
|
2020-01-11 18:29:13 -07:00
|
|
|
if err != nil {
|
2020-02-04 20:44:33 -07:00
|
|
|
return nil, 0, err
|
2019-09-16 15:17:59 -06:00
|
|
|
}
|
2020-04-21 19:28:44 -06:00
|
|
|
paramInfo := make([]protocol.ParameterInformation, 0, len(s.params))
|
|
|
|
for _, p := range s.params {
|
|
|
|
paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
|
2018-11-12 14:53:10 -07:00
|
|
|
}
|
2020-04-21 19:28:44 -06:00
|
|
|
return &protocol.SignatureInformation{
|
|
|
|
Label: name + s.format(),
|
|
|
|
Documentation: doc.Synopsis(s.doc),
|
|
|
|
Parameters: paramInfo,
|
|
|
|
}, activeParam, nil
|
2019-05-14 19:20:41 -06:00
|
|
|
}
|
internal/lsp: improve signatureHelp in various cases
- show signature for function calls whose function expression is not
an object (e.g. the second call in foo()()). since the function name
is not available, we use the generic "func"
- only provide signature help when the position is on or within the
call expression parens. this is consistent with the one other lsp
server i tried (java). this improves the gopls experience in emacs
where lsp-mode is constantly calling "hover" and
"signatureHelp" ("hover" should be preferred unless you are inside
the function params list)
- use the entire signature type string as the label since that includes
the return values, which are useful to see
- don't qualify the function name with its package. it looks funny to
see "bytes.Cap()" as the help when you are in a call
to (*bytes.Buffer).Cap(). it could be useful to include invocant
type info, but leave it out for now since signature help is meant to
focus on the function parameters.
- don't turn variadic args "foo ...int" into "foo []int" for the
parameter information (i.e. maintain it as "foo ...int")
- when determining active parameter, count the space before a
parameter name as being part of that parameter (e.g. the space
before "b" in "func(a int, b int)")
- handle variadic params when determining the active param (i.e.
highlight "foo(a int, *b ...string*)" on signature help for final
param in `foo(123, "a", "b", "c")`
- don't generate an extra space in formatParams() for unnamed
arguments
I also tweaked the signatureHelp server log message to include the
error message itself, and populated the server's logger in lsp_test.go
to aid in development.
Fixes golang/go#31448
Change-Id: Iefe0e1e3c531d17197c0fa997b949174475a276c
GitHub-Last-Rev: 5c0b8ebd87a8c05d5d8f519ea096f94e89c77e2c
GitHub-Pull-Request: golang/tools#82
Reviewed-on: https://go-review.googlesource.com/c/tools/+/172439
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-04-16 22:54:13 -06:00
|
|
|
|
2020-07-24 15:41:50 -06:00
|
|
|
func builtinSignature(ctx context.Context, snapshot Snapshot, callExpr *ast.CallExpr, name string, pos token.Pos) (*protocol.SignatureInformation, int, error) {
|
|
|
|
sig, err := newBuiltinSignature(ctx, snapshot, name)
|
2020-04-21 19:28:44 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
2019-05-14 19:20:41 -06:00
|
|
|
}
|
2020-04-21 19:28:44 -06:00
|
|
|
paramInfo := make([]protocol.ParameterInformation, 0, len(sig.params))
|
|
|
|
for _, p := range sig.params {
|
|
|
|
paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
|
2019-08-12 14:59:23 -06:00
|
|
|
}
|
2020-04-21 19:28:44 -06:00
|
|
|
activeParam := activeParameter(callExpr, len(sig.params), sig.variadic, pos)
|
2020-02-04 20:44:33 -07:00
|
|
|
return &protocol.SignatureInformation{
|
2020-04-21 19:28:44 -06:00
|
|
|
Label: sig.name + sig.format(),
|
|
|
|
Documentation: doc.Synopsis(sig.doc),
|
2020-02-04 20:44:33 -07:00
|
|
|
Parameters: paramInfo,
|
2020-04-21 19:28:44 -06:00
|
|
|
}, activeParam, nil
|
|
|
|
|
2019-05-14 19:20:41 -06:00
|
|
|
}
|
|
|
|
|
2020-03-02 07:51:12 -07:00
|
|
|
func activeParameter(callExpr *ast.CallExpr, numParams int, variadic bool, pos token.Pos) (activeParam int) {
|
|
|
|
if len(callExpr.Args) == 0 {
|
2020-01-25 16:22:03 -07:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
// First, check if the position is even in the range of the arguments.
|
2020-03-02 07:51:12 -07:00
|
|
|
start, end := callExpr.Lparen, callExpr.Rparen
|
2020-01-25 16:22:03 -07:00
|
|
|
if !(start <= pos && pos <= end) {
|
|
|
|
return 0
|
|
|
|
}
|
2020-03-02 07:51:12 -07:00
|
|
|
for _, expr := range callExpr.Args {
|
2018-11-12 14:53:10 -07:00
|
|
|
if start == token.NoPos {
|
|
|
|
start = expr.Pos()
|
|
|
|
}
|
|
|
|
end = expr.End()
|
|
|
|
if start <= pos && pos <= end {
|
|
|
|
break
|
|
|
|
}
|
internal/lsp: improve signatureHelp in various cases
- show signature for function calls whose function expression is not
an object (e.g. the second call in foo()()). since the function name
is not available, we use the generic "func"
- only provide signature help when the position is on or within the
call expression parens. this is consistent with the one other lsp
server i tried (java). this improves the gopls experience in emacs
where lsp-mode is constantly calling "hover" and
"signatureHelp" ("hover" should be preferred unless you are inside
the function params list)
- use the entire signature type string as the label since that includes
the return values, which are useful to see
- don't qualify the function name with its package. it looks funny to
see "bytes.Cap()" as the help when you are in a call
to (*bytes.Buffer).Cap(). it could be useful to include invocant
type info, but leave it out for now since signature help is meant to
focus on the function parameters.
- don't turn variadic args "foo ...int" into "foo []int" for the
parameter information (i.e. maintain it as "foo ...int")
- when determining active parameter, count the space before a
parameter name as being part of that parameter (e.g. the space
before "b" in "func(a int, b int)")
- handle variadic params when determining the active param (i.e.
highlight "foo(a int, *b ...string*)" on signature help for final
param in `foo(123, "a", "b", "c")`
- don't generate an extra space in formatParams() for unnamed
arguments
I also tweaked the signatureHelp server log message to include the
error message itself, and populated the server's logger in lsp_test.go
to aid in development.
Fixes golang/go#31448
Change-Id: Iefe0e1e3c531d17197c0fa997b949174475a276c
GitHub-Last-Rev: 5c0b8ebd87a8c05d5d8f519ea096f94e89c77e2c
GitHub-Pull-Request: golang/tools#82
Reviewed-on: https://go-review.googlesource.com/c/tools/+/172439
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-04-16 22:54:13 -06:00
|
|
|
// Don't advance the active parameter for the last parameter of a variadic function.
|
2019-05-14 19:20:41 -06:00
|
|
|
if !variadic || activeParam < numParams-1 {
|
internal/lsp: improve signatureHelp in various cases
- show signature for function calls whose function expression is not
an object (e.g. the second call in foo()()). since the function name
is not available, we use the generic "func"
- only provide signature help when the position is on or within the
call expression parens. this is consistent with the one other lsp
server i tried (java). this improves the gopls experience in emacs
where lsp-mode is constantly calling "hover" and
"signatureHelp" ("hover" should be preferred unless you are inside
the function params list)
- use the entire signature type string as the label since that includes
the return values, which are useful to see
- don't qualify the function name with its package. it looks funny to
see "bytes.Cap()" as the help when you are in a call
to (*bytes.Buffer).Cap(). it could be useful to include invocant
type info, but leave it out for now since signature help is meant to
focus on the function parameters.
- don't turn variadic args "foo ...int" into "foo []int" for the
parameter information (i.e. maintain it as "foo ...int")
- when determining active parameter, count the space before a
parameter name as being part of that parameter (e.g. the space
before "b" in "func(a int, b int)")
- handle variadic params when determining the active param (i.e.
highlight "foo(a int, *b ...string*)" on signature help for final
param in `foo(123, "a", "b", "c")`
- don't generate an extra space in formatParams() for unnamed
arguments
I also tweaked the signatureHelp server log message to include the
error message itself, and populated the server's logger in lsp_test.go
to aid in development.
Fixes golang/go#31448
Change-Id: Iefe0e1e3c531d17197c0fa997b949174475a276c
GitHub-Last-Rev: 5c0b8ebd87a8c05d5d8f519ea096f94e89c77e2c
GitHub-Pull-Request: golang/tools#82
Reviewed-on: https://go-review.googlesource.com/c/tools/+/172439
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-04-16 22:54:13 -06:00
|
|
|
activeParam++
|
|
|
|
}
|
2018-11-12 14:53:10 -07:00
|
|
|
start = expr.Pos() + 1 // to account for commas
|
|
|
|
}
|
2019-05-14 19:20:41 -06:00
|
|
|
return activeParam
|
2018-11-12 14:53:10 -07:00
|
|
|
}
|