2019-04-24 17:26:34 -06:00
|
|
|
// Copyright 2019 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 (
|
2019-05-14 19:20:41 -06:00
|
|
|
"context"
|
2019-04-24 17:26:34 -06:00
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/types"
|
|
|
|
"strings"
|
2019-04-28 21:19:54 -06:00
|
|
|
|
2020-04-17 07:32:56 -06:00
|
|
|
"golang.org/x/tools/internal/event"
|
2019-11-12 14:58:00 -07:00
|
|
|
"golang.org/x/tools/internal/imports"
|
2020-03-10 21:09:39 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/debug/tag"
|
2019-08-16 15:05:40 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
2019-04-28 21:19:54 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/snippet"
|
2019-07-02 15:31:31 -06:00
|
|
|
"golang.org/x/tools/internal/span"
|
2019-04-24 17:26:34 -06:00
|
|
|
)
|
|
|
|
|
2019-06-19 16:24:05 -06:00
|
|
|
// formatCompletion creates a completion item for a given candidate.
|
2020-04-05 23:18:15 -06:00
|
|
|
func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, error) {
|
2019-06-19 16:24:05 -06:00
|
|
|
obj := cand.obj
|
|
|
|
|
2019-04-29 19:08:16 -06:00
|
|
|
// Handle builtin types separately.
|
|
|
|
if obj.Parent() == types.Universe {
|
2020-04-21 19:28:44 -06:00
|
|
|
return c.formatBuiltin(ctx, cand)
|
2019-04-29 19:08:16 -06:00
|
|
|
}
|
|
|
|
|
2019-04-28 21:19:54 -06:00
|
|
|
var (
|
2019-09-04 11:23:14 -06:00
|
|
|
label = cand.name
|
|
|
|
detail = types.TypeString(obj.Type(), c.qf)
|
|
|
|
insert = label
|
2019-09-24 22:46:57 -06:00
|
|
|
kind = protocol.TextCompletion
|
2019-09-04 11:23:14 -06:00
|
|
|
snip *snippet.Builder
|
|
|
|
protocolEdits []protocol.TextEdit
|
2019-04-28 21:19:54 -06:00
|
|
|
)
|
2019-11-01 11:50:21 -06:00
|
|
|
if obj.Type() == nil {
|
|
|
|
detail = ""
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
|
2019-09-04 11:23:14 -06:00
|
|
|
// expandFuncCall mutates the completion label, detail, and snippet
|
2019-06-19 16:24:05 -06:00
|
|
|
// to that of an invocation of sig.
|
2020-04-21 20:41:25 -06:00
|
|
|
expandFuncCall := func(sig *types.Signature) error {
|
2020-04-24 15:30:13 -06:00
|
|
|
s, err := newSignature(ctx, c.snapshot, c.pkg, c.file, "", sig, nil, c.qf)
|
2020-04-21 19:28:44 -06:00
|
|
|
if err != nil {
|
2020-04-21 20:41:25 -06:00
|
|
|
return err
|
2020-04-21 19:28:44 -06:00
|
|
|
}
|
|
|
|
snip = c.functionCallSnippet(label, s.params)
|
|
|
|
detail = "func" + s.format()
|
2019-11-03 16:57:27 -07:00
|
|
|
|
|
|
|
// Add variadic "..." if we are using a function result to fill in a variadic parameter.
|
2020-01-17 14:53:10 -07:00
|
|
|
if sig.Results().Len() == 1 && c.inference.matchesVariadic(sig.Results().At(0).Type()) {
|
2019-11-03 16:57:27 -07:00
|
|
|
snip.WriteText("...")
|
|
|
|
}
|
2020-04-21 20:41:25 -06:00
|
|
|
return nil
|
2019-06-19 16:24:05 -06:00
|
|
|
}
|
|
|
|
|
2019-05-15 12:16:16 -06:00
|
|
|
switch obj := obj.(type) {
|
2019-04-24 17:26:34 -06:00
|
|
|
case *types.TypeName:
|
2019-05-15 12:16:16 -06:00
|
|
|
detail, kind = formatType(obj.Type(), c.qf)
|
2019-04-24 17:26:34 -06:00
|
|
|
case *types.Const:
|
2019-09-24 22:46:57 -06:00
|
|
|
kind = protocol.ConstantCompletion
|
2019-04-24 17:26:34 -06:00
|
|
|
case *types.Var:
|
2019-05-15 12:16:16 -06:00
|
|
|
if _, ok := obj.Type().(*types.Struct); ok {
|
2019-04-24 17:26:34 -06:00
|
|
|
detail = "struct{...}" // for anonymous structs
|
2019-11-22 12:49:12 -07:00
|
|
|
} else if obj.IsField() {
|
2020-04-24 15:30:13 -06:00
|
|
|
detail = formatVarType(ctx, c.snapshot, c.pkg, c.file, obj, c.qf)
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
2019-05-15 12:16:16 -06:00
|
|
|
if obj.IsField() {
|
2019-09-24 22:46:57 -06:00
|
|
|
kind = protocol.FieldCompletion
|
2019-09-04 11:23:14 -06:00
|
|
|
snip = c.structFieldSnippet(label, detail)
|
2019-04-24 17:26:34 -06:00
|
|
|
} else {
|
2019-09-24 22:46:57 -06:00
|
|
|
kind = protocol.VariableCompletion
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
2019-11-01 11:50:21 -06:00
|
|
|
if obj.Type() == nil {
|
|
|
|
break
|
|
|
|
}
|
2019-06-19 16:24:05 -06:00
|
|
|
|
|
|
|
if sig, ok := obj.Type().Underlying().(*types.Signature); ok && cand.expandFuncCall {
|
2020-04-21 20:41:25 -06:00
|
|
|
if err := expandFuncCall(sig); err != nil {
|
|
|
|
return CompletionItem{}, err
|
|
|
|
}
|
2019-06-19 16:24:05 -06:00
|
|
|
}
|
2019-11-03 16:57:27 -07:00
|
|
|
|
|
|
|
// Add variadic "..." if we are using a variable to fill in a variadic parameter.
|
2020-01-17 14:53:10 -07:00
|
|
|
if c.inference.matchesVariadic(obj.Type()) {
|
2019-11-03 16:57:27 -07:00
|
|
|
snip = &snippet.Builder{}
|
|
|
|
snip.WriteText(insert + "...")
|
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
case *types.Func:
|
2019-06-19 16:24:05 -06:00
|
|
|
sig, ok := obj.Type().Underlying().(*types.Signature)
|
2019-04-29 17:47:54 -06:00
|
|
|
if !ok {
|
|
|
|
break
|
|
|
|
}
|
2019-09-24 22:46:57 -06:00
|
|
|
kind = protocol.FunctionCompletion
|
2019-06-19 16:24:05 -06:00
|
|
|
if sig != nil && sig.Recv() != nil {
|
2019-09-24 22:46:57 -06:00
|
|
|
kind = protocol.MethodCompletion
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
2019-06-19 16:24:05 -06:00
|
|
|
|
|
|
|
if cand.expandFuncCall {
|
2020-04-21 20:41:25 -06:00
|
|
|
if err := expandFuncCall(sig); err != nil {
|
|
|
|
return CompletionItem{}, err
|
|
|
|
}
|
2019-06-19 16:24:05 -06:00
|
|
|
}
|
2019-04-24 17:26:34 -06:00
|
|
|
case *types.PkgName:
|
2019-09-24 22:46:57 -06:00
|
|
|
kind = protocol.ModuleCompletion
|
2019-06-19 16:24:05 -06:00
|
|
|
detail = fmt.Sprintf("%q", obj.Imported().Path())
|
2019-09-18 13:26:39 -06:00
|
|
|
case *types.Label:
|
|
|
|
kind = protocol.ConstantCompletion
|
|
|
|
detail = "label"
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
2019-06-19 16:24:05 -06:00
|
|
|
|
2019-08-14 15:25:47 -06:00
|
|
|
// If this candidate needs an additional import statement,
|
|
|
|
// add the additional text edits needed.
|
2019-11-01 11:50:21 -06:00
|
|
|
if cand.imp != nil {
|
2020-04-05 23:18:15 -06:00
|
|
|
addlEdits, err := c.importEdits(ctx, cand.imp)
|
2019-11-01 11:50:21 -06:00
|
|
|
if err != nil {
|
|
|
|
return CompletionItem{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
protocolEdits = append(protocolEdits, addlEdits...)
|
|
|
|
if kind != protocol.ModuleCompletion {
|
|
|
|
if detail != "" {
|
|
|
|
detail += " "
|
|
|
|
}
|
2019-11-05 12:53:55 -07:00
|
|
|
detail += fmt.Sprintf("(from %q)", cand.imp.importPath)
|
2019-11-01 11:50:21 -06:00
|
|
|
}
|
2019-08-14 15:25:47 -06:00
|
|
|
}
|
|
|
|
|
2019-12-27 09:57:52 -07:00
|
|
|
// Prepend "&" or "*" operator as appropriate.
|
|
|
|
var prefixOp string
|
2019-12-22 10:58:14 -07:00
|
|
|
if cand.takeAddress {
|
2019-12-27 09:57:52 -07:00
|
|
|
prefixOp = "&"
|
2020-02-07 14:55:25 -07:00
|
|
|
} else if cand.makePointer {
|
2019-12-27 09:57:52 -07:00
|
|
|
prefixOp = "*"
|
2020-02-07 14:55:25 -07:00
|
|
|
} else if cand.dereference > 0 {
|
|
|
|
prefixOp = strings.Repeat("*", cand.dereference)
|
2019-12-27 09:57:52 -07:00
|
|
|
}
|
2019-12-22 10:58:14 -07:00
|
|
|
|
2019-12-27 09:57:52 -07:00
|
|
|
if prefixOp != "" {
|
|
|
|
// If we are in a selector, add an edit to place prefix before selector.
|
|
|
|
if sel := enclosingSelector(c.path, c.pos); sel != nil {
|
|
|
|
edits, err := prependEdit(c.snapshot.View().Session().Cache().FileSet(), c.mapper, sel, prefixOp)
|
2019-12-22 10:58:14 -07:00
|
|
|
if err != nil {
|
2020-04-05 23:18:15 -06:00
|
|
|
return CompletionItem{}, err
|
2019-12-22 10:58:14 -07:00
|
|
|
}
|
2020-04-05 23:18:15 -06:00
|
|
|
protocolEdits = append(protocolEdits, edits...)
|
2019-12-22 10:58:14 -07:00
|
|
|
} else {
|
2019-12-27 09:57:52 -07:00
|
|
|
// If there is no selector, just stick the prefix at the start.
|
|
|
|
insert = prefixOp + insert
|
2019-12-22 10:58:14 -07:00
|
|
|
}
|
2019-12-23 21:06:34 -07:00
|
|
|
|
2019-12-27 09:57:52 -07:00
|
|
|
label = prefixOp + label
|
2019-12-22 10:58:14 -07:00
|
|
|
}
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
detail = strings.TrimPrefix(detail, "untyped ")
|
2019-07-02 15:31:31 -06:00
|
|
|
item := CompletionItem{
|
2019-08-14 15:25:47 -06:00
|
|
|
Label: label,
|
|
|
|
InsertText: insert,
|
2019-08-16 15:05:40 -06:00
|
|
|
AdditionalTextEdits: protocolEdits,
|
2019-08-14 15:25:47 -06:00
|
|
|
Detail: detail,
|
|
|
|
Kind: kind,
|
|
|
|
Score: cand.score,
|
|
|
|
Depth: len(c.deepState.chain),
|
2019-09-04 11:23:14 -06:00
|
|
|
snippet: snip,
|
2020-03-01 15:33:21 -07:00
|
|
|
obj: obj,
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
2019-08-29 00:43:58 -06:00
|
|
|
// If the user doesn't want documentation for completion items.
|
2019-12-29 00:22:12 -07:00
|
|
|
if !c.opts.documentation {
|
2019-08-29 00:43:58 -06:00
|
|
|
return item, nil
|
|
|
|
}
|
2019-11-20 14:38:43 -07:00
|
|
|
pos := c.snapshot.View().Session().Cache().FileSet().Position(obj.Pos())
|
2019-09-10 11:26:49 -06:00
|
|
|
|
|
|
|
// We ignore errors here, because some types, like "unsafe" or "error",
|
|
|
|
// may not have valid positions that we can use to get documentation.
|
2019-08-29 00:43:58 -06:00
|
|
|
if !pos.IsValid() {
|
|
|
|
return item, nil
|
|
|
|
}
|
2020-02-12 14:36:46 -07:00
|
|
|
uri := span.URIFromPath(pos.Filename)
|
2019-11-05 12:53:55 -07:00
|
|
|
|
|
|
|
// Find the source file of the candidate, starting from a package
|
|
|
|
// that should have it in its dependencies.
|
|
|
|
searchPkg := c.pkg
|
|
|
|
if cand.imp != nil && cand.imp.pkg != nil {
|
|
|
|
searchPkg = cand.imp.pkg
|
|
|
|
}
|
2020-02-25 21:28:00 -07:00
|
|
|
|
2020-07-21 13:15:06 -06:00
|
|
|
pgf, pkg, err := findPosInPackage(c.snapshot.View(), searchPkg, obj.Pos())
|
2019-09-10 11:26:49 -06:00
|
|
|
if err != nil {
|
2019-11-07 16:02:47 -07:00
|
|
|
return item, nil
|
2019-08-29 00:43:58 -06:00
|
|
|
}
|
2020-02-25 21:28:00 -07:00
|
|
|
|
2020-07-21 13:15:06 -06:00
|
|
|
posToDecl, err := c.snapshot.PosToDecl(ctx, pgf)
|
2019-08-29 00:43:58 -06:00
|
|
|
if err != nil {
|
2020-02-25 21:28:00 -07:00
|
|
|
return CompletionItem{}, err
|
|
|
|
}
|
|
|
|
decl := posToDecl[obj.Pos()]
|
|
|
|
if decl == nil {
|
2019-11-07 16:02:47 -07:00
|
|
|
return item, nil
|
2019-08-29 00:43:58 -06:00
|
|
|
}
|
2020-02-25 21:28:00 -07:00
|
|
|
|
|
|
|
hover, err := hoverInfo(pkg, obj, decl)
|
2019-08-29 00:43:58 -06:00
|
|
|
if err != nil {
|
2020-04-05 23:18:15 -06:00
|
|
|
event.Error(ctx, "failed to find Hover", err, tag.URI.Of(uri))
|
2019-11-07 16:02:47 -07:00
|
|
|
return item, nil
|
2019-08-29 00:43:58 -06:00
|
|
|
}
|
|
|
|
item.Documentation = hover.Synopsis
|
2019-12-29 00:22:12 -07:00
|
|
|
if c.opts.fullDocumentation {
|
2019-08-29 00:43:58 -06:00
|
|
|
item.Documentation = hover.FullDocumentation
|
|
|
|
}
|
2019-07-02 15:31:31 -06:00
|
|
|
return item, nil
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
|
|
|
|
2019-11-01 11:50:21 -06:00
|
|
|
// importEdits produces the text edits necessary to add the given import to the current file.
|
2020-04-05 23:18:15 -06:00
|
|
|
func (c *completer) importEdits(ctx context.Context, imp *importInfo) ([]protocol.TextEdit, error) {
|
2019-10-15 15:42:30 -06:00
|
|
|
if imp == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2020-07-21 13:15:06 -06:00
|
|
|
pgf, err := c.pkg.File(span.URIFromPath(c.filename))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-10-15 15:42:30 -06:00
|
|
|
}
|
|
|
|
|
2020-07-21 13:15:06 -06:00
|
|
|
return computeOneImportFixEdits(ctx, c.snapshot.View(), pgf, &imports.ImportFix{
|
2019-11-12 14:58:00 -07:00
|
|
|
StmtInfo: imports.ImportInfo{
|
|
|
|
ImportPath: imp.importPath,
|
|
|
|
Name: imp.name,
|
|
|
|
},
|
|
|
|
// IdentName is unused on this path and is difficult to get.
|
|
|
|
FixType: imports.AddImport,
|
|
|
|
})
|
2019-10-15 15:42:30 -06:00
|
|
|
}
|
|
|
|
|
2020-04-21 19:28:44 -06:00
|
|
|
func (c *completer) formatBuiltin(ctx context.Context, cand candidate) (CompletionItem, error) {
|
2019-06-19 16:24:05 -06:00
|
|
|
obj := cand.obj
|
2019-04-29 19:08:16 -06:00
|
|
|
item := CompletionItem{
|
|
|
|
Label: obj.Name(),
|
|
|
|
InsertText: obj.Name(),
|
2019-06-19 16:24:05 -06:00
|
|
|
Score: cand.score,
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
2019-04-29 19:08:16 -06:00
|
|
|
switch obj.(type) {
|
|
|
|
case *types.Const:
|
2019-09-24 22:46:57 -06:00
|
|
|
item.Kind = protocol.ConstantCompletion
|
2019-04-29 19:08:16 -06:00
|
|
|
case *types.Builtin:
|
2019-09-24 22:46:57 -06:00
|
|
|
item.Kind = protocol.FunctionCompletion
|
2020-04-21 19:28:44 -06:00
|
|
|
sig, err := newBuiltinSignature(ctx, c.snapshot.View(), obj.Name())
|
2020-01-11 18:29:13 -07:00
|
|
|
if err != nil {
|
2020-04-21 19:28:44 -06:00
|
|
|
return CompletionItem{}, err
|
2019-05-08 01:25:54 -06:00
|
|
|
}
|
2020-04-21 19:28:44 -06:00
|
|
|
item.Detail = "func" + sig.format()
|
|
|
|
item.snippet = c.functionCallSnippet(obj.Name(), sig.params)
|
2019-04-29 19:08:16 -06:00
|
|
|
case *types.TypeName:
|
|
|
|
if types.IsInterface(obj.Type()) {
|
2019-09-24 22:46:57 -06:00
|
|
|
item.Kind = protocol.InterfaceCompletion
|
2019-04-29 19:08:16 -06:00
|
|
|
} else {
|
2019-09-24 22:46:57 -06:00
|
|
|
item.Kind = protocol.ClassCompletion
|
2019-04-29 19:08:16 -06:00
|
|
|
}
|
|
|
|
case *types.Nil:
|
2019-09-24 22:46:57 -06:00
|
|
|
item.Kind = protocol.VariableCompletion
|
2019-04-29 19:08:16 -06:00
|
|
|
}
|
2020-04-21 19:28:44 -06:00
|
|
|
return item, nil
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// qualifier returns a function that appropriately formats a types.PkgName
|
|
|
|
// appearing in a *ast.File.
|
|
|
|
func qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifier {
|
|
|
|
// Construct mapping of import paths to their defined or implicit names.
|
|
|
|
imports := make(map[*types.Package]string)
|
|
|
|
for _, imp := range f.Imports {
|
|
|
|
var obj types.Object
|
|
|
|
if imp.Name != nil {
|
|
|
|
obj = info.Defs[imp.Name]
|
|
|
|
} else {
|
|
|
|
obj = info.Implicits[imp]
|
|
|
|
}
|
|
|
|
if pkgname, ok := obj.(*types.PkgName); ok {
|
|
|
|
imports[pkgname.Imported()] = pkgname.Name()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Define qualifier to replace full package paths with names of the imports.
|
|
|
|
return func(p *types.Package) string {
|
|
|
|
if p == pkg {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
if name, ok := imports[p]; ok {
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
return p.Name()
|
|
|
|
}
|
|
|
|
}
|