1
0
mirror of https://github.com/golang/go synced 2024-11-18 23:54:41 -07:00
go/internal/lsp/source/signature_help.go

199 lines
5.6 KiB
Go
Raw Normal View History

// 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"
"go/ast"
"go/doc"
"go/token"
"go/types"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors"
)
type SignatureInformation struct {
Label, Documentation string
Parameters []ParameterInformation
ActiveParameter int
}
type ParameterInformation struct {
Label string
}
func SignatureHelp(ctx context.Context, view View, f GoFile, pos protocol.Position) (*SignatureInformation, error) {
ctx, done := trace.StartSpan(ctx, "source.SignatureHelp")
defer done()
file, pkgs, m, err := fileToMapper(ctx, view, f.URI())
if err != nil {
return nil, err
}
pkg, err := bestPackage(f.URI(), pkgs)
if err != nil {
return nil, err
}
spn, err := m.PointSpan(pos)
if err != nil {
return nil, err
}
rng, err := spn.Range(m.Converter)
if err != nil {
return nil, err
}
// Find a call expression surrounding the query position.
var callExpr *ast.CallExpr
path, _ := astutil.PathEnclosingInterval(file, rng.Start, rng.Start)
if path == nil {
return nil, errors.Errorf("cannot find node enclosing position")
}
FindCall:
for _, node := range path {
switch node := node.(type) {
case *ast.CallExpr:
if rng.Start >= node.Lparen && rng.Start <= node.Rparen {
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.
return nil, errors.Errorf("no signature help within a function declaration")
}
}
if callExpr == nil || callExpr.Fun == nil {
return nil, errors.Errorf("cannot find an enclosing function")
}
// 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 {
return builtinSignature(ctx, f.View(), callExpr, obj.Name(), rng.Start)
}
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 {
return nil, errors.Errorf("cannot get type for Fun %[1]T (%[1]v)", callExpr.Fun)
}
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)
if sig == nil {
return nil, errors.Errorf("cannot find signature for Fun %[1]T (%[1]v)", callExpr.Fun)
}
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
qf := qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo())
params := formatParams(sig.Params(), sig.Variadic(), qf)
results, writeResultParens := formatResults(sig.Results(), qf)
activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), rng.Start)
var (
name string
comment *ast.CommentGroup
)
if obj != nil {
node, err := objToNode(ctx, f.View(), pkg, obj)
if err != nil {
return nil, err
}
rng, err := objToMappedRange(ctx, view, obj)
if err != nil {
return nil, err
}
decl := &Declaration{
obj: obj,
mappedRange: rng,
node: node,
}
d, err := decl.hover(ctx)
if err != nil {
return nil, err
}
name = obj.Name()
comment = d.comment
} else {
name = "func"
}
return signatureInformation(name, comment, params, results, writeResultParens, activeParam), nil
}
func builtinSignature(ctx context.Context, v View, callExpr *ast.CallExpr, name string, pos token.Pos) (*SignatureInformation, error) {
decl, ok := lookupBuiltinDecl(v, name).(*ast.FuncDecl)
if !ok {
return nil, errors.Errorf("no function declaration for builtin: %s", name)
}
params, _ := formatFieldList(ctx, v, decl.Type.Params)
results, writeResultParens := formatFieldList(ctx, v, decl.Type.Results)
var (
numParams int
variadic bool
)
if decl.Type.Params.List != nil {
numParams = len(decl.Type.Params.List)
lastParam := decl.Type.Params.List[numParams-1]
if _, ok := lastParam.Type.(*ast.Ellipsis); ok {
variadic = true
}
}
activeParam := activeParameter(callExpr, numParams, variadic, pos)
return signatureInformation(name, nil, params, results, writeResultParens, activeParam), nil
}
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
func signatureInformation(name string, comment *ast.CommentGroup, params, results []string, writeResultParens bool, activeParam int) *SignatureInformation {
paramInfo := make([]ParameterInformation, 0, len(params))
for _, p := range params {
paramInfo = append(paramInfo, ParameterInformation{Label: p})
}
label := name + formatFunction(params, results, writeResultParens)
var c string
if comment != nil {
c = doc.Synopsis(comment.Text())
}
return &SignatureInformation{
Label: label,
Documentation: c,
Parameters: paramInfo,
ActiveParameter: activeParam,
}
}
func activeParameter(callExpr *ast.CallExpr, numParams int, variadic bool, pos token.Pos) int {
// Determine the query position relative to the number of parameters in the function.
var activeParam int
var start, end token.Pos
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
for _, expr := range callExpr.Args {
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.
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++
}
start = expr.Pos() + 1 // to account for commas
}
return activeParam
}