mirror of
https://github.com/golang/go
synced 2024-11-18 16:14:46 -07:00
internal/lsp: implement signature help
Add SignatureHelp functionality to source package. Tests will be added in a subsequent change. Change-Id: Ia43280946d96a984c5741273a00c12255d637b2a Reviewed-on: https://go-review.googlesource.com/c/149177 Reviewed-by: Ian Cottrell <iancottrell@google.com> Run-TryBot: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
0a8e63141f
commit
7e59e591a2
@ -43,14 +43,15 @@ func (s *server) Initialize(ctx context.Context, params *protocol.InitializePara
|
||||
s.initialized = true
|
||||
return &protocol.InitializeResult{
|
||||
Capabilities: protocol.ServerCapabilities{
|
||||
CompletionProvider: protocol.CompletionOptions{},
|
||||
DefinitionProvider: true,
|
||||
DocumentFormattingProvider: true,
|
||||
DocumentRangeFormattingProvider: true,
|
||||
SignatureHelpProvider: protocol.SignatureHelpOptions{},
|
||||
TextDocumentSync: protocol.TextDocumentSyncOptions{
|
||||
Change: float64(protocol.Full), // full contents of file sent on each update
|
||||
OpenClose: true,
|
||||
},
|
||||
DocumentFormattingProvider: true,
|
||||
DocumentRangeFormattingProvider: true,
|
||||
CompletionProvider: protocol.CompletionOptions{},
|
||||
DefinitionProvider: true,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@ -174,8 +175,18 @@ func (s *server) Hover(context.Context, *protocol.TextDocumentPositionParams) (*
|
||||
return nil, notImplemented("Hover")
|
||||
}
|
||||
|
||||
func (s *server) SignatureHelp(context.Context, *protocol.TextDocumentPositionParams) (*protocol.SignatureHelp, error) {
|
||||
return nil, notImplemented("SignatureHelp")
|
||||
func (s *server) SignatureHelp(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.SignatureHelp, error) {
|
||||
f := s.view.GetFile(source.URI(params.TextDocument.URI))
|
||||
tok, err := f.GetToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pos := fromProtocolPosition(tok, params.Position)
|
||||
info, err := source.SignatureHelp(ctx, f, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toProtocolSignatureHelp(info), nil
|
||||
}
|
||||
|
||||
func (s *server) Definition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
|
||||
|
33
internal/lsp/signature_help.go
Normal file
33
internal/lsp/signature_help.go
Normal file
@ -0,0 +1,33 @@
|
||||
// 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 lsp
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
)
|
||||
|
||||
func toProtocolSignatureHelp(info *source.SignatureInformation) *protocol.SignatureHelp {
|
||||
return &protocol.SignatureHelp{
|
||||
ActiveParameter: float64(info.ActiveParameter),
|
||||
ActiveSignature: 0, // there is only ever one possible signature
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: info.Label,
|
||||
Parameters: toProtocolParameterInformation(info.Parameters),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func toProtocolParameterInformation(info []source.ParameterInformation) []protocol.ParameterInformation {
|
||||
var result []protocol.ParameterInformation
|
||||
for _, p := range info {
|
||||
result = append(result, protocol.ParameterInformation{
|
||||
Label: p.Label,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
118
internal/lsp/source/signature_help.go
Normal file
118
internal/lsp/source/signature_help.go
Normal file
@ -0,0 +1,118 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
type SignatureInformation struct {
|
||||
Label string
|
||||
Parameters []ParameterInformation
|
||||
ActiveParameter int
|
||||
}
|
||||
|
||||
type ParameterInformation struct {
|
||||
Label string
|
||||
}
|
||||
|
||||
func SignatureHelp(ctx context.Context, f *File, pos token.Pos) (*SignatureInformation, error) {
|
||||
fAST, err := f.GetAST()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg, err := f.GetPackage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find a call expression surrounding the query position.
|
||||
var callExpr *ast.CallExpr
|
||||
path, _ := astutil.PathEnclosingInterval(fAST, pos, pos)
|
||||
if path == nil {
|
||||
return nil, fmt.Errorf("cannot find node enclosing position")
|
||||
}
|
||||
for _, node := range path {
|
||||
if c, ok := node.(*ast.CallExpr); ok {
|
||||
callExpr = c
|
||||
break
|
||||
}
|
||||
}
|
||||
if callExpr == nil || callExpr.Fun == nil {
|
||||
return nil, fmt.Errorf("cannot find an enclosing function")
|
||||
}
|
||||
|
||||
// Get the type information for the function corresponding to the call expression.
|
||||
var obj types.Object
|
||||
switch t := callExpr.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
obj = pkg.TypesInfo.ObjectOf(t)
|
||||
case *ast.SelectorExpr:
|
||||
obj = pkg.TypesInfo.ObjectOf(t.Sel)
|
||||
default:
|
||||
return nil, fmt.Errorf("the enclosing function is malformed")
|
||||
}
|
||||
if obj == nil {
|
||||
return nil, fmt.Errorf("cannot resolve %s", callExpr.Fun)
|
||||
}
|
||||
// Find the signature corresponding to the object.
|
||||
var sig *types.Signature
|
||||
switch obj.(type) {
|
||||
case *types.Var:
|
||||
if underlying, ok := obj.Type().Underlying().(*types.Signature); ok {
|
||||
sig = underlying
|
||||
}
|
||||
case *types.Func:
|
||||
sig = obj.Type().(*types.Signature)
|
||||
}
|
||||
if sig == nil {
|
||||
return nil, fmt.Errorf("no function signatures found for %s", obj.Name())
|
||||
}
|
||||
pkgStringer := qualifier(fAST, pkg.Types, pkg.TypesInfo)
|
||||
var paramInfo []ParameterInformation
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
param := sig.Params().At(i)
|
||||
label := types.TypeString(param.Type(), pkgStringer)
|
||||
if param.Name() != "" {
|
||||
label = fmt.Sprintf("%s %s", param.Name(), label)
|
||||
}
|
||||
paramInfo = append(paramInfo, ParameterInformation{
|
||||
Label: label,
|
||||
})
|
||||
}
|
||||
// Determine the query position relative to the number of parameters in the function.
|
||||
var activeParam int
|
||||
var start, end token.Pos
|
||||
for i, expr := range callExpr.Args {
|
||||
if start == token.NoPos {
|
||||
start = expr.Pos()
|
||||
}
|
||||
end = expr.End()
|
||||
if i < len(callExpr.Args)-1 {
|
||||
end = callExpr.Args[i+1].Pos() - 1 // comma
|
||||
}
|
||||
if start <= pos && pos <= end {
|
||||
break
|
||||
}
|
||||
activeParam++
|
||||
start = expr.Pos() + 1 // to account for commas
|
||||
}
|
||||
// Label for function, qualified by package name.
|
||||
label := obj.Name()
|
||||
if pkg := pkgStringer(obj.Pkg()); pkg != "" {
|
||||
label = pkg + "." + label
|
||||
}
|
||||
return &SignatureInformation{
|
||||
Label: label + formatParams(sig.Params(), sig.Variadic(), pkgStringer),
|
||||
Parameters: paramInfo,
|
||||
ActiveParameter: activeParam,
|
||||
}, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user