2019-04-24 17:26:34 -06: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.
|
|
|
|
|
2018-11-07 18:57:08 -07:00
|
|
|
package source
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
|
|
|
"go/types"
|
|
|
|
|
|
|
|
"golang.org/x/tools/go/ast/astutil"
|
2019-04-28 21:19:54 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/snippet"
|
2018-11-07 18:57:08 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
type CompletionItem struct {
|
2019-04-28 21:19:54 -06:00
|
|
|
// Label is the primary text the user sees for this completion item.
|
|
|
|
Label string
|
|
|
|
|
2019-04-29 17:47:54 -06:00
|
|
|
// Detail is supplemental information to present to the user.
|
|
|
|
// This often contains the type or return type of the completion item.
|
2019-04-28 21:19:54 -06:00
|
|
|
Detail string
|
|
|
|
|
2019-04-29 17:47:54 -06:00
|
|
|
// InsertText is the text to insert if this item is selected.
|
|
|
|
// Any of the prefix that has already been typed is not trimmed.
|
|
|
|
// The insert text does not contain snippets.
|
|
|
|
InsertText string
|
2019-04-28 21:19:54 -06:00
|
|
|
|
|
|
|
Kind CompletionItemKind
|
|
|
|
|
2019-04-29 17:47:54 -06:00
|
|
|
// Score is the internal relevance score.
|
|
|
|
// A higher score indicates that this completion item is more relevant.
|
2019-04-28 21:19:54 -06:00
|
|
|
Score float64
|
|
|
|
|
2019-04-29 17:47:54 -06:00
|
|
|
// Snippet is the LSP snippet for the completion item, without placeholders.
|
|
|
|
// The LSP specification contains details about LSP snippets.
|
|
|
|
// For example, a snippet for a function with the following signature:
|
|
|
|
//
|
|
|
|
// func foo(a, b, c int)
|
|
|
|
//
|
|
|
|
// would be:
|
|
|
|
//
|
|
|
|
// foo(${1:})
|
|
|
|
//
|
|
|
|
Snippet *snippet.Builder
|
|
|
|
|
|
|
|
// PlaceholderSnippet is the LSP snippet for the completion ite, containing
|
|
|
|
// placeholders. The LSP specification contains details about LSP snippets.
|
|
|
|
// For example, a placeholder snippet for a function with the following signature:
|
|
|
|
//
|
|
|
|
// func foo(a, b, c int)
|
|
|
|
//
|
|
|
|
// would be:
|
|
|
|
//
|
|
|
|
// foo(${1:a int}, ${2: b int}, ${3: c int})
|
|
|
|
//
|
2019-04-28 21:19:54 -06:00
|
|
|
PlaceholderSnippet *snippet.Builder
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type CompletionItemKind int
|
|
|
|
|
|
|
|
const (
|
|
|
|
Unknown CompletionItemKind = iota
|
|
|
|
InterfaceCompletionItem
|
|
|
|
StructCompletionItem
|
|
|
|
TypeCompletionItem
|
|
|
|
ConstantCompletionItem
|
|
|
|
FieldCompletionItem
|
|
|
|
ParameterCompletionItem
|
|
|
|
VariableCompletionItem
|
|
|
|
FunctionCompletionItem
|
|
|
|
MethodCompletionItem
|
|
|
|
PackageCompletionItem
|
|
|
|
)
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// Scoring constants are used for weighting the relevance of different candidates.
|
|
|
|
const (
|
|
|
|
// stdScore is the base score for all completion items.
|
|
|
|
stdScore float64 = 1.0
|
|
|
|
|
|
|
|
// highScore indicates a very relevant completion item.
|
|
|
|
highScore float64 = 10.0
|
|
|
|
|
|
|
|
// lowScore indicates an irrelevant or not useful completion item.
|
|
|
|
lowScore float64 = 0.01
|
|
|
|
)
|
|
|
|
|
|
|
|
// completer contains the necessary information for a single completion request.
|
|
|
|
type completer struct {
|
|
|
|
// Package-specific fields.
|
|
|
|
types *types.Package
|
|
|
|
info *types.Info
|
|
|
|
qf types.Qualifier
|
2019-04-29 19:08:16 -06:00
|
|
|
|
|
|
|
// view is the View associated with this completion request.
|
|
|
|
view View
|
|
|
|
|
|
|
|
// ctx is the context associated with this completion request.
|
|
|
|
ctx context.Context
|
2019-04-24 17:26:34 -06:00
|
|
|
|
|
|
|
// pos is the position at which the request was triggered.
|
|
|
|
pos token.Pos
|
2018-12-05 15:00:36 -07:00
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// path is the path of AST nodes enclosing the position.
|
|
|
|
path []ast.Node
|
|
|
|
|
|
|
|
// seen is the map that ensures we do not return duplicate results.
|
|
|
|
seen map[types.Object]bool
|
|
|
|
|
|
|
|
// items is the list of completion items returned.
|
|
|
|
items []CompletionItem
|
|
|
|
|
|
|
|
// prefix is the already-typed portion of the completion candidates.
|
|
|
|
prefix string
|
|
|
|
|
|
|
|
// expectedType is the type we expect the completion candidate to be.
|
|
|
|
// It may not be set.
|
|
|
|
expectedType types.Type
|
|
|
|
|
|
|
|
// enclosingFunction is the function declaration enclosing the position.
|
|
|
|
enclosingFunction *types.Signature
|
|
|
|
|
|
|
|
// preferTypeNames is true if we are completing at a position that expects a type,
|
|
|
|
// not a value.
|
|
|
|
preferTypeNames bool
|
2019-04-28 21:19:54 -06:00
|
|
|
|
|
|
|
// enclosingCompositeLiteral is the composite literal enclosing the position.
|
|
|
|
enclosingCompositeLiteral *ast.CompositeLit
|
|
|
|
|
|
|
|
// enclosingKeyValue is the key value expression enclosing the position.
|
|
|
|
enclosingKeyValue *ast.KeyValueExpr
|
|
|
|
|
|
|
|
// inCompositeLiteralField is true if we are completing a composite literal field.
|
|
|
|
inCompositeLiteralField bool
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// found adds a candidate completion.
|
|
|
|
//
|
|
|
|
// Only the first candidate of a given name is considered.
|
|
|
|
func (c *completer) found(obj types.Object, weight float64) {
|
|
|
|
if obj.Pkg() != nil && obj.Pkg() != c.types && !obj.Exported() {
|
|
|
|
return // inaccessible
|
|
|
|
}
|
|
|
|
if c.seen[obj] {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.seen[obj] = true
|
|
|
|
if c.matchingType(obj.Type()) {
|
|
|
|
weight *= highScore
|
|
|
|
}
|
|
|
|
if _, ok := obj.(*types.TypeName); !ok && c.preferTypeNames {
|
|
|
|
weight *= lowScore
|
|
|
|
}
|
|
|
|
c.items = append(c.items, c.item(obj, weight))
|
|
|
|
}
|
2018-12-05 15:00:36 -07:00
|
|
|
|
|
|
|
// Completion returns a list of possible candidates for completion, given a
|
2019-04-24 17:26:34 -06:00
|
|
|
// a file and a position.
|
|
|
|
//
|
|
|
|
// The prefix is computed based on the preceding identifier and can be used by
|
|
|
|
// the client to score the quality of the completion. For instance, some clients
|
|
|
|
// may tolerate imperfect matches as valid completion results, since users may make typos.
|
|
|
|
func Completion(ctx context.Context, f File, pos token.Pos) ([]CompletionItem, string, error) {
|
2019-03-05 15:30:44 -07:00
|
|
|
file := f.GetAST(ctx)
|
|
|
|
pkg := f.GetPackage(ctx)
|
2019-04-29 17:47:54 -06:00
|
|
|
if pkg == nil || pkg.IsIllTyped() {
|
2019-03-11 15:14:55 -06:00
|
|
|
return nil, "", fmt.Errorf("package for %s is ill typed", f.URI())
|
|
|
|
}
|
2019-04-23 16:00:40 -06:00
|
|
|
|
|
|
|
// Completion is based on what precedes the cursor.
|
2019-04-24 17:26:34 -06:00
|
|
|
// Find the path to the position before pos.
|
2019-04-23 16:00:40 -06:00
|
|
|
path, _ := astutil.PathEnclosingInterval(file, pos-1, pos-1)
|
2018-11-07 18:57:08 -07:00
|
|
|
if path == nil {
|
|
|
|
return nil, "", fmt.Errorf("cannot find node enclosing position")
|
|
|
|
}
|
2019-04-23 16:34:23 -06:00
|
|
|
// Skip completion inside comments.
|
2019-04-24 17:26:34 -06:00
|
|
|
for _, g := range file.Comments {
|
|
|
|
if g.Pos() <= pos && pos <= g.End() {
|
|
|
|
return nil, "", nil
|
|
|
|
}
|
2019-04-23 16:34:23 -06:00
|
|
|
}
|
|
|
|
// Skip completion inside any kind of literal.
|
|
|
|
if _, ok := path[0].(*ast.BasicLit); ok {
|
2019-04-24 17:26:34 -06:00
|
|
|
return nil, "", nil
|
2019-01-15 23:36:31 -07:00
|
|
|
}
|
|
|
|
|
2019-04-29 17:47:54 -06:00
|
|
|
lit, kv, inCompositeLiteralField := enclosingCompositeLiteral(path, pos)
|
2019-04-24 17:26:34 -06:00
|
|
|
c := &completer{
|
2019-04-28 21:19:54 -06:00
|
|
|
types: pkg.GetTypes(),
|
|
|
|
info: pkg.GetTypesInfo(),
|
|
|
|
qf: qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
|
2019-04-29 19:08:16 -06:00
|
|
|
view: f.View(),
|
|
|
|
ctx: ctx,
|
2019-04-28 21:19:54 -06:00
|
|
|
path: path,
|
|
|
|
pos: pos,
|
|
|
|
seen: make(map[types.Object]bool),
|
|
|
|
expectedType: expectedType(path, pos, pkg.GetTypesInfo()),
|
|
|
|
enclosingFunction: enclosingFunction(path, pos, pkg.GetTypesInfo()),
|
|
|
|
preferTypeNames: preferTypeNames(path, pos),
|
2019-04-29 17:47:54 -06:00
|
|
|
enclosingCompositeLiteral: lit,
|
2019-04-28 21:19:54 -06:00
|
|
|
enclosingKeyValue: kv,
|
2019-04-29 17:47:54 -06:00
|
|
|
inCompositeLiteralField: inCompositeLiteralField,
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
2019-01-31 23:40:03 -07:00
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// Composite literals are handled entirely separately.
|
2019-04-28 21:19:54 -06:00
|
|
|
if c.enclosingCompositeLiteral != nil {
|
|
|
|
c.expectedType = c.expectedCompositeLiteralType(c.enclosingCompositeLiteral, c.enclosingKeyValue)
|
2019-04-24 17:26:34 -06:00
|
|
|
|
2019-04-28 21:19:54 -06:00
|
|
|
if c.inCompositeLiteralField {
|
|
|
|
if err := c.compositeLiteral(c.enclosingCompositeLiteral, c.enclosingKeyValue); err != nil {
|
2019-04-24 17:26:34 -06:00
|
|
|
return nil, "", err
|
2019-01-31 23:40:03 -07:00
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return c.items, c.prefix, nil
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
// Set the filter prefix.
|
2019-04-24 17:26:34 -06:00
|
|
|
c.prefix = n.Name[:pos-n.Pos()]
|
2018-11-07 18:57:08 -07:00
|
|
|
|
|
|
|
// Is this the Sel part of a selector?
|
|
|
|
if sel, ok := path[1].(*ast.SelectorExpr); ok && sel.Sel == n {
|
2019-04-24 17:26:34 -06:00
|
|
|
if err := c.selector(sel); err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
return c.items, c.prefix, nil
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
// reject defining identifiers
|
2019-03-06 14:33:47 -07:00
|
|
|
if obj, ok := pkg.GetTypesInfo().Defs[n]; ok {
|
2018-11-07 18:57:08 -07:00
|
|
|
if v, ok := obj.(*types.Var); ok && v.IsField() {
|
|
|
|
// An anonymous field is also a reference to a type.
|
|
|
|
} else {
|
|
|
|
of := ""
|
|
|
|
if obj != nil {
|
2019-03-06 14:33:47 -07:00
|
|
|
qual := types.RelativeTo(pkg.GetTypes())
|
2018-11-07 18:57:08 -07:00
|
|
|
of += ", of " + types.ObjectString(obj, qual)
|
|
|
|
}
|
|
|
|
return nil, "", fmt.Errorf("this is a definition%s", of)
|
|
|
|
}
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
if err := c.lexical(); err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
2018-11-07 18:57:08 -07:00
|
|
|
|
|
|
|
// The function name hasn't been typed yet, but the parens are there:
|
|
|
|
// recv.‸(arg)
|
|
|
|
case *ast.TypeAssertExpr:
|
|
|
|
// Create a fake selector expression.
|
2019-04-24 17:26:34 -06:00
|
|
|
if err := c.selector(&ast.SelectorExpr{X: n.X}); err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
2018-11-07 18:57:08 -07:00
|
|
|
|
|
|
|
case *ast.SelectorExpr:
|
2019-04-24 17:26:34 -06:00
|
|
|
if err := c.selector(n); err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
2018-11-07 18:57:08 -07:00
|
|
|
|
|
|
|
default:
|
|
|
|
// fallback to lexical completions
|
2019-04-24 17:26:34 -06:00
|
|
|
if err := c.lexical(); err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return c.items, c.prefix, nil
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// selector finds completions for the specified selector expression.
|
|
|
|
func (c *completer) selector(sel *ast.SelectorExpr) error {
|
2018-11-07 18:57:08 -07:00
|
|
|
// Is sel a qualified identifier?
|
|
|
|
if id, ok := sel.X.(*ast.Ident); ok {
|
2019-04-24 17:26:34 -06:00
|
|
|
if pkgname, ok := c.info.Uses[id].(*types.PkgName); ok {
|
2018-11-07 18:57:08 -07:00
|
|
|
// Enumerate package members.
|
|
|
|
scope := pkgname.Imported().Scope()
|
|
|
|
for _, name := range scope.Names() {
|
2019-04-24 17:26:34 -06:00
|
|
|
c.found(scope.Lookup(name), stdScore)
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return nil
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// Invariant: sel is a true selector.
|
|
|
|
tv, ok := c.info.Types[sel.X]
|
2018-11-07 18:57:08 -07:00
|
|
|
if !ok {
|
2019-04-24 17:26:34 -06:00
|
|
|
return fmt.Errorf("cannot resolve %s", sel.X)
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// Add methods of T.
|
2018-11-07 18:57:08 -07:00
|
|
|
mset := types.NewMethodSet(tv.Type)
|
|
|
|
for i := 0; i < mset.Len(); i++ {
|
2019-04-24 17:26:34 -06:00
|
|
|
c.found(mset.At(i).Obj(), stdScore)
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// Add methods of *T.
|
2018-11-07 18:57:08 -07:00
|
|
|
if tv.Addressable() && !types.IsInterface(tv.Type) && !isPointer(tv.Type) {
|
|
|
|
mset := types.NewMethodSet(types.NewPointer(tv.Type))
|
|
|
|
for i := 0; i < mset.Len(); i++ {
|
2019-04-24 17:26:34 -06:00
|
|
|
c.found(mset.At(i).Obj(), stdScore)
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// Add fields of T.
|
2018-11-07 18:57:08 -07:00
|
|
|
for _, f := range fieldSelections(tv.Type) {
|
2019-04-24 17:26:34 -06:00
|
|
|
c.found(f, stdScore)
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return nil
|
2019-01-31 23:40:03 -07:00
|
|
|
}
|
|
|
|
|
2018-11-07 18:57:08 -07:00
|
|
|
// lexical finds completions in the lexical environment.
|
2019-04-24 17:26:34 -06:00
|
|
|
func (c *completer) lexical() error {
|
2018-11-07 18:57:08 -07:00
|
|
|
var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
|
2019-04-24 17:26:34 -06:00
|
|
|
for _, n := range c.path {
|
2018-11-07 18:57:08 -07:00
|
|
|
switch node := n.(type) {
|
|
|
|
case *ast.FuncDecl:
|
|
|
|
n = node.Type
|
|
|
|
case *ast.FuncLit:
|
|
|
|
n = node.Type
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
scopes = append(scopes, c.info.Scopes[n])
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
scopes = append(scopes, c.types.Scope(), types.Universe)
|
2018-11-07 18:57:08 -07:00
|
|
|
|
2019-04-25 15:19:24 -06:00
|
|
|
// Track seen variables to avoid showing completions for shadowed variables.
|
|
|
|
// This works since we look at scopes from innermost to outermost.
|
|
|
|
seen := make(map[string]struct{})
|
|
|
|
|
2018-11-07 18:57:08 -07:00
|
|
|
// Process scopes innermost first.
|
|
|
|
for i, scope := range scopes {
|
|
|
|
if scope == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, name := range scope.Names() {
|
2019-04-24 17:26:34 -06:00
|
|
|
declScope, obj := scope.LookupParent(name, c.pos)
|
2018-11-07 18:57:08 -07:00
|
|
|
if declScope != scope {
|
|
|
|
continue // Name was declared in some enclosing scope, or not at all.
|
|
|
|
}
|
|
|
|
// If obj's type is invalid, find the AST node that defines the lexical block
|
|
|
|
// containing the declaration of obj. Don't resolve types for packages.
|
|
|
|
if _, ok := obj.(*types.PkgName); !ok && obj.Type() == types.Typ[types.Invalid] {
|
|
|
|
// Match the scope to its ast.Node. If the scope is the package scope,
|
|
|
|
// use the *ast.File as the starting node.
|
|
|
|
var node ast.Node
|
2019-04-24 17:26:34 -06:00
|
|
|
if i < len(c.path) {
|
|
|
|
node = c.path[i]
|
|
|
|
} else if i == len(c.path) { // use the *ast.File for package scope
|
|
|
|
node = c.path[i-1]
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
if node != nil {
|
2019-04-24 17:26:34 -06:00
|
|
|
if resolved := resolveInvalid(obj, node, c.info); resolved != nil {
|
2018-11-07 18:57:08 -07:00
|
|
|
obj = resolved
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-13 09:13:53 -07:00
|
|
|
score := stdScore
|
2018-11-07 18:57:08 -07:00
|
|
|
// Rank builtins significantly lower than other results.
|
|
|
|
if scope == types.Universe {
|
|
|
|
score *= 0.1
|
|
|
|
}
|
2019-04-25 15:19:24 -06:00
|
|
|
// If we haven't already added a candidate for an object with this name.
|
|
|
|
if _, ok := seen[obj.Name()]; !ok {
|
|
|
|
seen[obj.Name()] = struct{}{}
|
2019-04-24 17:26:34 -06:00
|
|
|
c.found(obj, score)
|
2019-04-25 15:19:24 -06:00
|
|
|
}
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return nil
|
2019-01-15 23:36:31 -07:00
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// compositeLiteral finds completions for field names inside a composite literal.
|
|
|
|
func (c *completer) compositeLiteral(lit *ast.CompositeLit, kv *ast.KeyValueExpr) error {
|
|
|
|
switch n := c.path[0].(type) {
|
2018-11-07 18:57:08 -07:00
|
|
|
case *ast.Ident:
|
2019-04-24 17:26:34 -06:00
|
|
|
c.prefix = n.Name[:c.pos-n.Pos()]
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
// Mark fields of the composite literal that have already been set,
|
|
|
|
// except for the current field.
|
2019-04-24 17:26:34 -06:00
|
|
|
hasKeys := kv != nil // true if the composite literal already has key-value pairs
|
2018-11-07 18:57:08 -07:00
|
|
|
addedFields := make(map[*types.Var]bool)
|
|
|
|
for _, el := range lit.Elts {
|
2019-04-24 17:26:34 -06:00
|
|
|
if kvExpr, ok := el.(*ast.KeyValueExpr); ok {
|
|
|
|
if kv == kvExpr {
|
2018-11-07 18:57:08 -07:00
|
|
|
continue
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
|
|
|
|
hasKeys = true
|
|
|
|
if key, ok := kvExpr.Key.(*ast.Ident); ok {
|
|
|
|
if used, ok := c.info.Uses[key]; ok {
|
2018-11-07 18:57:08 -07:00
|
|
|
if usedVar, ok := used.(*types.Var); ok {
|
|
|
|
addedFields[usedVar] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If the underlying type of the composite literal is a struct,
|
|
|
|
// collect completions for the fields of this struct.
|
2019-04-24 17:26:34 -06:00
|
|
|
if tv, ok := c.info.Types[lit]; ok {
|
|
|
|
switch t := tv.Type.Underlying().(type) {
|
|
|
|
case *types.Struct:
|
|
|
|
var structPkg *types.Package // package that struct is declared in
|
|
|
|
for i := 0; i < t.NumFields(); i++ {
|
|
|
|
field := t.Field(i)
|
2018-11-07 18:57:08 -07:00
|
|
|
if i == 0 {
|
|
|
|
structPkg = field.Pkg()
|
|
|
|
}
|
|
|
|
if !addedFields[field] {
|
2019-04-24 17:26:34 -06:00
|
|
|
c.found(field, highScore)
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Add lexical completions if the user hasn't typed a key value expression
|
|
|
|
// and if the struct fields are defined in the same package as the user is in.
|
2019-04-24 17:26:34 -06:00
|
|
|
if !hasKeys && structPkg == c.types {
|
|
|
|
return c.lexical()
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
default:
|
|
|
|
return c.lexical()
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return nil
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
|
2019-04-28 21:19:54 -06:00
|
|
|
func enclosingCompositeLiteral(path []ast.Node, pos token.Pos) (lit *ast.CompositeLit, kv *ast.KeyValueExpr, ok bool) {
|
|
|
|
for _, n := range path {
|
2019-04-23 16:00:40 -06:00
|
|
|
switch n := n.(type) {
|
|
|
|
case *ast.CompositeLit:
|
2019-04-24 17:26:34 -06:00
|
|
|
// The enclosing node will be a composite literal if the user has just
|
|
|
|
// opened the curly brace (e.g. &x{<>) or the completion request is triggered
|
|
|
|
// from an already completed composite literal expression (e.g. &x{foo: 1, <>})
|
|
|
|
//
|
|
|
|
// The position is not part of the composite literal unless it falls within the
|
|
|
|
// curly braces (e.g. "foo.Foo<>Struct{}").
|
2019-04-28 21:19:54 -06:00
|
|
|
if n.Lbrace <= pos && pos <= n.Rbrace {
|
2019-04-24 17:26:34 -06:00
|
|
|
lit = n
|
|
|
|
|
|
|
|
// If the cursor position is within a key-value expression inside the composite
|
|
|
|
// literal, we try to determine if it is before or after the colon. If it is before
|
|
|
|
// the colon, we return field completions. If the cursor does not belong to any
|
|
|
|
// expression within the composite literal, we show composite literal completions.
|
2019-04-28 21:19:54 -06:00
|
|
|
if expr, isKeyValue := exprAtPos(pos, n.Elts).(*ast.KeyValueExpr); kv == nil && isKeyValue {
|
2019-04-24 17:26:34 -06:00
|
|
|
kv = expr
|
|
|
|
|
|
|
|
// If the position belongs to a key-value expression and is after the colon,
|
|
|
|
// don't show composite literal completions.
|
2019-04-28 21:19:54 -06:00
|
|
|
ok = pos <= kv.Colon
|
2019-04-24 17:26:34 -06:00
|
|
|
} else if kv == nil {
|
|
|
|
ok = true
|
2019-04-23 16:00:40 -06:00
|
|
|
}
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return lit, kv, ok
|
2019-04-23 16:00:40 -06:00
|
|
|
case *ast.KeyValueExpr:
|
2019-04-24 17:26:34 -06:00
|
|
|
if kv == nil {
|
|
|
|
kv = n
|
|
|
|
|
|
|
|
// If the position belongs to a key-value expression and is after the colon,
|
|
|
|
// don't show composite literal completions.
|
2019-04-28 21:19:54 -06:00
|
|
|
ok = pos <= kv.Colon
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
2019-04-23 16:00:40 -06:00
|
|
|
case *ast.FuncType, *ast.CallExpr, *ast.TypeAssertExpr:
|
|
|
|
// These node types break the type link between the leaf node and
|
|
|
|
// the composite literal. The type of the leaf node becomes unrelated
|
|
|
|
// to the type of the composite literal, so we return nil to avoid
|
|
|
|
// inappropriate completions. For example, "Foo{Bar: x.Baz(<>)}"
|
|
|
|
// should complete as a function argument to Baz, not part of the Foo
|
|
|
|
// composite literal.
|
2019-04-24 17:26:34 -06:00
|
|
|
return nil, nil, false
|
2019-04-23 16:00:40 -06:00
|
|
|
}
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return lit, kv, ok
|
2019-04-23 16:00:40 -06:00
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// enclosingFunction returns the signature of the function enclosing the given position.
|
2018-11-07 18:57:08 -07:00
|
|
|
func enclosingFunction(path []ast.Node, pos token.Pos, info *types.Info) *types.Signature {
|
|
|
|
for _, node := range path {
|
|
|
|
switch t := node.(type) {
|
|
|
|
case *ast.FuncDecl:
|
|
|
|
if obj, ok := info.Defs[t.Name]; ok {
|
|
|
|
return obj.Type().(*types.Signature)
|
|
|
|
}
|
|
|
|
case *ast.FuncLit:
|
|
|
|
if typ, ok := info.Types[t]; ok {
|
|
|
|
return typ.Type.(*types.Signature)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-29 17:47:54 -06:00
|
|
|
func (c *completer) expectedCompositeLiteralType(lit *ast.CompositeLit, kv *ast.KeyValueExpr) types.Type {
|
|
|
|
litType, ok := c.info.Types[lit]
|
2019-04-23 16:00:40 -06:00
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
2019-04-29 17:47:54 -06:00
|
|
|
switch t := litType.Type.Underlying().(type) {
|
2019-04-23 16:00:40 -06:00
|
|
|
case *types.Slice:
|
|
|
|
return t.Elem()
|
|
|
|
case *types.Array:
|
|
|
|
return t.Elem()
|
|
|
|
case *types.Map:
|
2019-04-24 17:26:34 -06:00
|
|
|
if kv == nil || c.pos <= kv.Colon {
|
2019-04-23 16:00:40 -06:00
|
|
|
return t.Key()
|
|
|
|
}
|
|
|
|
return t.Elem()
|
|
|
|
case *types.Struct:
|
2019-04-24 17:26:34 -06:00
|
|
|
// If we are in a key-value expression.
|
2019-04-23 16:00:40 -06:00
|
|
|
if kv != nil {
|
2019-04-24 17:26:34 -06:00
|
|
|
// There is no expected type for a struct field name.
|
|
|
|
if c.pos <= kv.Colon {
|
2019-04-23 16:00:40 -06:00
|
|
|
return nil
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
// Find the type of the struct field whose name matches the key.
|
|
|
|
if key, ok := kv.Key.(*ast.Ident); ok {
|
2019-04-23 16:00:40 -06:00
|
|
|
for i := 0; i < t.NumFields(); i++ {
|
2019-04-24 17:26:34 -06:00
|
|
|
if field := t.Field(i); field.Name() == key.Name {
|
2019-04-23 16:00:40 -06:00
|
|
|
return field.Type()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
// We are in a struct literal, but not a specific key-value pair.
|
|
|
|
// If the struct literal doesn't have explicit field names,
|
|
|
|
// we may still be able to suggest an expected type.
|
2019-04-29 17:47:54 -06:00
|
|
|
for _, el := range lit.Elts {
|
2019-04-23 16:00:40 -06:00
|
|
|
if _, ok := el.(*ast.KeyValueExpr); ok {
|
2019-04-24 17:26:34 -06:00
|
|
|
return nil
|
2019-04-23 16:00:40 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// The order of the literal fields must match the order in the struct definition.
|
2019-04-24 17:26:34 -06:00
|
|
|
// Find the element that the position belongs to and suggest that field's type.
|
2019-04-29 17:47:54 -06:00
|
|
|
if i := indexExprAtPos(c.pos, lit.Elts); i < t.NumFields() {
|
2019-04-23 16:00:40 -06:00
|
|
|
return t.Field(i).Type()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-11-07 18:57:08 -07:00
|
|
|
// expectedType returns the expected type for an expression at the query position.
|
|
|
|
func expectedType(path []ast.Node, pos token.Pos, info *types.Info) types.Type {
|
|
|
|
for i, node := range path {
|
|
|
|
if i == 2 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
switch expr := node.(type) {
|
|
|
|
case *ast.BinaryExpr:
|
|
|
|
// Determine if query position comes from left or right of op.
|
|
|
|
e := expr.X
|
|
|
|
if pos < expr.OpPos {
|
|
|
|
e = expr.Y
|
|
|
|
}
|
|
|
|
if tv, ok := info.Types[e]; ok {
|
|
|
|
return tv.Type
|
|
|
|
}
|
|
|
|
case *ast.AssignStmt:
|
|
|
|
// Only rank completions if you are on the right side of the token.
|
|
|
|
if pos <= expr.TokPos {
|
|
|
|
break
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
i := indexExprAtPos(pos, expr.Rhs)
|
2018-11-07 18:57:08 -07:00
|
|
|
if i >= len(expr.Lhs) {
|
|
|
|
i = len(expr.Lhs) - 1
|
|
|
|
}
|
|
|
|
if tv, ok := info.Types[expr.Lhs[i]]; ok {
|
|
|
|
return tv.Type
|
|
|
|
}
|
|
|
|
case *ast.CallExpr:
|
|
|
|
if tv, ok := info.Types[expr.Fun]; ok {
|
|
|
|
if sig, ok := tv.Type.(*types.Signature); ok {
|
|
|
|
if sig.Params().Len() == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
i := indexExprAtPos(pos, expr.Args)
|
2018-11-07 18:57:08 -07:00
|
|
|
// Make sure not to run past the end of expected parameters.
|
|
|
|
if i >= sig.Params().Len() {
|
|
|
|
i = sig.Params().Len() - 1
|
|
|
|
}
|
|
|
|
return sig.Params().At(i).Type()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// preferTypeNames checks if given token position is inside func receiver,
|
|
|
|
// type params, or type results. For example:
|
|
|
|
//
|
|
|
|
// func (<>) foo(<>) (<>) {}
|
|
|
|
//
|
|
|
|
func preferTypeNames(path []ast.Node, pos token.Pos) bool {
|
|
|
|
for _, p := range path {
|
|
|
|
switch n := p.(type) {
|
|
|
|
case *ast.FuncDecl:
|
|
|
|
if r := n.Recv; r != nil && r.Pos() <= pos && pos <= r.End() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if t := n.Type; t != nil {
|
|
|
|
if p := t.Params; p != nil && p.Pos() <= pos && pos <= p.End() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if r := t.Results; r != nil && r.Pos() <= pos && pos <= r.End() {
|
|
|
|
return true
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return false
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return false
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
// matchingTypes reports whether actual is a good candidate type
|
|
|
|
// for a completion in a context of the expected type.
|
|
|
|
func (c *completer) matchingType(actual types.Type) bool {
|
|
|
|
if c.expectedType == nil {
|
|
|
|
return false
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
// Use a function's return type as its type.
|
|
|
|
if sig, ok := actual.(*types.Signature); ok {
|
|
|
|
if sig.Results().Len() == 1 {
|
|
|
|
actual = sig.Results().At(0).Type()
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
return types.Identical(types.Default(c.expectedType), types.Default(actual))
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|