зеркало из https://github.com/golang/tools.git
internal/lsp: add basic support for hover
This change adds a very simple implementation of hovering. It doesn't show any documentation, just the object string for the given object. Also, this change sets the prefix for composite literals, making sure we don't insert duplicate text. Change-Id: Ib706ec821a9e459a6c61c10f5dd28d1798944fa3 Reviewed-on: https://go-review.googlesource.com/c/152599 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Родитель
22934f0fdb
Коммит
62e1d13d53
|
@ -57,6 +57,7 @@ func (s *server) Initialize(ctx context.Context, params *protocol.InitializePara
|
|||
DefinitionProvider: true,
|
||||
DocumentFormattingProvider: true,
|
||||
DocumentRangeFormattingProvider: true,
|
||||
HoverProvider: true,
|
||||
SignatureHelpProvider: protocol.SignatureHelpOptions{
|
||||
TriggerCharacters: []string{"(", ","},
|
||||
},
|
||||
|
@ -184,8 +185,24 @@ func (s *server) CompletionResolve(context.Context, *protocol.CompletionItem) (*
|
|||
return nil, notImplemented("CompletionResolve")
|
||||
}
|
||||
|
||||
func (s *server) Hover(context.Context, *protocol.TextDocumentPositionParams) (*protocol.Hover, error) {
|
||||
return nil, notImplemented("Hover")
|
||||
func (s *server) Hover(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.Hover, 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)
|
||||
contents, rng, err := source.Hover(ctx, f, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &protocol.Hover{
|
||||
Contents: protocol.MarkupContent{
|
||||
Kind: protocol.Markdown,
|
||||
Value: contents,
|
||||
},
|
||||
Range: toProtocolRange(tok, rng),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) SignatureHelp(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.SignatureHelp, error) {
|
||||
|
|
|
@ -101,8 +101,8 @@ func completions(file *ast.File, pos token.Pos, fset *token.FileSet, pkg *types.
|
|||
}
|
||||
|
||||
// The position is within a composite literal.
|
||||
if items, ok := complit(path, pos, pkg, info, found); ok {
|
||||
return items, "", nil
|
||||
if items, prefix, ok := complit(path, pos, pkg, info, found); ok {
|
||||
return items, prefix, nil
|
||||
}
|
||||
switch n := path[0].(type) {
|
||||
case *ast.Ident:
|
||||
|
@ -250,7 +250,7 @@ func lexical(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Inf
|
|||
|
||||
// complit finds completions for field names inside a composite literal.
|
||||
// It reports whether the node was handled as part of a composite literal.
|
||||
func complit(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Info, found finder) (items []CompletionItem, ok bool) {
|
||||
func complit(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Info, found finder) (items []CompletionItem, prefix string, ok bool) {
|
||||
var lit *ast.CompositeLit
|
||||
|
||||
// First, determine if the pos is within a composite literal.
|
||||
|
@ -286,6 +286,8 @@ func complit(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Inf
|
|||
}
|
||||
}
|
||||
case *ast.Ident:
|
||||
prefix = n.Name[:pos-n.Pos()]
|
||||
|
||||
// If the enclosing node is an identifier, it can either be an identifier that is
|
||||
// part of a composite literal (e.g. &x{fo<>}), or it can be an identifier that is
|
||||
// part of a key-value expression, which is part of a composite literal (e.g. &x{foo: ba<>).
|
||||
|
@ -311,7 +313,7 @@ func complit(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Inf
|
|||
}
|
||||
// We are not in a composite literal.
|
||||
if lit == nil {
|
||||
return nil, false
|
||||
return nil, prefix, false
|
||||
}
|
||||
// Mark fields of the composite literal that have already been set,
|
||||
// except for the current field.
|
||||
|
@ -351,10 +353,10 @@ func complit(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Inf
|
|||
if !hasKeys && structPkg == pkg {
|
||||
items = append(items, lexical(path, pos, pkg, info, found)...)
|
||||
}
|
||||
return items, true
|
||||
return items, prefix, true
|
||||
}
|
||||
}
|
||||
return items, false
|
||||
return items, prefix, false
|
||||
}
|
||||
|
||||
// formatCompletion creates a completion item for a given types.Object.
|
||||
|
|
|
@ -30,7 +30,7 @@ func Definition(ctx context.Context, f *File, pos token.Pos) (Range, error) {
|
|||
return Range{}, err
|
||||
}
|
||||
if i.ident == nil {
|
||||
return Range{}, fmt.Errorf("definition was not a valid identifier")
|
||||
return Range{}, fmt.Errorf("not a valid identifier")
|
||||
}
|
||||
obj := pkg.TypesInfo.ObjectOf(i.ident)
|
||||
if obj == nil {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// 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/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func Hover(ctx context.Context, f *File, pos token.Pos) (string, Range, error) {
|
||||
fAST, err := f.GetAST()
|
||||
if err != nil {
|
||||
return "", Range{}, err
|
||||
}
|
||||
pkg, err := f.GetPackage()
|
||||
if err != nil {
|
||||
return "", Range{}, err
|
||||
}
|
||||
i, err := findIdentifier(fAST, pos)
|
||||
if err != nil {
|
||||
return "", Range{}, err
|
||||
}
|
||||
if i.ident == nil {
|
||||
return "", Range{}, fmt.Errorf("not a valid identifier")
|
||||
}
|
||||
obj := pkg.TypesInfo.ObjectOf(i.ident)
|
||||
if obj == nil {
|
||||
return "", Range{}, fmt.Errorf("no object")
|
||||
}
|
||||
if i.wasEmbeddedField {
|
||||
// the original position was on the embedded field declaration
|
||||
// so we try to dig out the type and jump to that instead
|
||||
if v, ok := obj.(*types.Var); ok {
|
||||
if n, ok := v.Type().(*types.Named); ok {
|
||||
obj = n.Obj()
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO(rstambler): Add documentation and improve quality of object string.
|
||||
content := types.ObjectString(obj, qualifier(fAST, pkg.Types, pkg.TypesInfo))
|
||||
markdown := "```go\n" + content + "\n```"
|
||||
return markdown, Range{
|
||||
Start: i.ident.Pos(),
|
||||
End: i.ident.End(),
|
||||
}, nil
|
||||
}
|
Загрузка…
Ссылка в новой задаче