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 (
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/types"
|
|
|
|
"strings"
|
2019-04-28 21:19:54 -06:00
|
|
|
|
|
|
|
"golang.org/x/tools/internal/lsp/snippet"
|
2019-04-24 17:26:34 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// formatCompletion creates a completion item for a given types.Object.
|
|
|
|
func (c *completer) item(obj types.Object, score float64) CompletionItem {
|
2019-04-28 21:19:54 -06:00
|
|
|
var (
|
|
|
|
label = obj.Name()
|
|
|
|
detail = types.TypeString(obj.Type(), c.qf)
|
|
|
|
insert = label
|
|
|
|
kind CompletionItemKind
|
|
|
|
plainSnippet *snippet.Builder
|
|
|
|
placeholderSnippet *snippet.Builder
|
|
|
|
)
|
2019-04-24 17:26:34 -06:00
|
|
|
|
|
|
|
switch o := obj.(type) {
|
|
|
|
case *types.TypeName:
|
|
|
|
detail, kind = formatType(o.Type(), c.qf)
|
|
|
|
if obj.Parent() == types.Universe {
|
|
|
|
detail = ""
|
|
|
|
}
|
|
|
|
case *types.Const:
|
|
|
|
if obj.Parent() == types.Universe {
|
|
|
|
detail = ""
|
|
|
|
} else {
|
|
|
|
val := o.Val().ExactString()
|
|
|
|
if !strings.Contains(val, "\\n") { // skip any multiline constants
|
|
|
|
label += " = " + o.Val().ExactString()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kind = ConstantCompletionItem
|
|
|
|
case *types.Var:
|
|
|
|
if _, ok := o.Type().(*types.Struct); ok {
|
|
|
|
detail = "struct{...}" // for anonymous structs
|
|
|
|
}
|
|
|
|
if o.IsField() {
|
|
|
|
kind = FieldCompletionItem
|
2019-04-28 21:19:54 -06:00
|
|
|
plainSnippet, placeholderSnippet = c.structFieldSnippet(label, detail)
|
2019-04-24 17:26:34 -06:00
|
|
|
} else if c.isParameter(o) {
|
|
|
|
kind = ParameterCompletionItem
|
|
|
|
} else {
|
|
|
|
kind = VariableCompletionItem
|
|
|
|
}
|
|
|
|
case *types.Func:
|
|
|
|
if sig, ok := o.Type().(*types.Signature); ok {
|
2019-04-28 21:19:54 -06:00
|
|
|
params := formatEachParam(sig, c.qf)
|
|
|
|
label += formatParamParts(params)
|
2019-04-24 17:26:34 -06:00
|
|
|
detail = strings.Trim(types.TypeString(sig.Results(), c.qf), "()")
|
|
|
|
kind = FunctionCompletionItem
|
|
|
|
if sig.Recv() != nil {
|
|
|
|
kind = MethodCompletionItem
|
|
|
|
}
|
2019-04-28 21:19:54 -06:00
|
|
|
|
|
|
|
plainSnippet, placeholderSnippet = c.funcCallSnippet(obj.Name(), params)
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
|
|
|
case *types.Builtin:
|
|
|
|
item, ok := builtinDetails[obj.Name()]
|
|
|
|
if !ok {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
label, detail = item.label, item.detail
|
|
|
|
kind = FunctionCompletionItem
|
|
|
|
case *types.PkgName:
|
|
|
|
kind = PackageCompletionItem
|
|
|
|
detail = fmt.Sprintf("\"%s\"", o.Imported().Path())
|
|
|
|
case *types.Nil:
|
|
|
|
kind = VariableCompletionItem
|
|
|
|
detail = ""
|
|
|
|
}
|
|
|
|
detail = strings.TrimPrefix(detail, "untyped ")
|
|
|
|
|
|
|
|
return CompletionItem{
|
2019-04-28 21:19:54 -06:00
|
|
|
Label: label,
|
|
|
|
Insert: insert,
|
|
|
|
Detail: detail,
|
|
|
|
Kind: kind,
|
|
|
|
Score: score,
|
|
|
|
PlainSnippet: plainSnippet,
|
|
|
|
PlaceholderSnippet: placeholderSnippet,
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// isParameter returns true if the given *types.Var is a parameter
|
|
|
|
// of the enclosingFunction.
|
|
|
|
func (c *completer) isParameter(v *types.Var) bool {
|
|
|
|
if c.enclosingFunction == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := 0; i < c.enclosingFunction.Params().Len(); i++ {
|
|
|
|
if c.enclosingFunction.Params().At(i) == v {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// formatType returns the detail and kind for an object of type *types.TypeName.
|
|
|
|
func formatType(typ types.Type, qf types.Qualifier) (detail string, kind CompletionItemKind) {
|
|
|
|
if types.IsInterface(typ) {
|
|
|
|
detail = "interface{...}"
|
|
|
|
kind = InterfaceCompletionItem
|
|
|
|
} else if _, ok := typ.(*types.Struct); ok {
|
|
|
|
detail = "struct{...}"
|
|
|
|
kind = StructCompletionItem
|
|
|
|
} else if typ != typ.Underlying() {
|
|
|
|
detail, kind = formatType(typ.Underlying(), qf)
|
|
|
|
} else {
|
|
|
|
detail = types.TypeString(typ, qf)
|
|
|
|
kind = TypeCompletionItem
|
|
|
|
}
|
|
|
|
return detail, kind
|
|
|
|
}
|
|
|
|
|
2019-04-28 21:19:54 -06:00
|
|
|
// formatParams correctly formats the parameters of a function.
|
|
|
|
func formatParams(sig *types.Signature, qualifier types.Qualifier) string {
|
|
|
|
return formatParamParts(formatEachParam(sig, qualifier))
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatParamParts(params []string) string {
|
|
|
|
totalLen := 2 // parens
|
|
|
|
|
|
|
|
// length of each param itself
|
|
|
|
for _, p := range params {
|
|
|
|
totalLen += len(p)
|
|
|
|
}
|
|
|
|
// length of ", " separator
|
|
|
|
if len(params) > 1 {
|
|
|
|
totalLen += 2 * (len(params) - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
var b strings.Builder
|
|
|
|
b.Grow(totalLen)
|
|
|
|
|
2019-04-24 17:26:34 -06:00
|
|
|
b.WriteByte('(')
|
2019-04-28 21:19:54 -06:00
|
|
|
for i, p := range params {
|
2019-04-24 17:26:34 -06:00
|
|
|
if i > 0 {
|
|
|
|
b.WriteString(", ")
|
|
|
|
}
|
2019-04-28 21:19:54 -06:00
|
|
|
b.WriteString(p)
|
|
|
|
}
|
|
|
|
b.WriteByte(')')
|
|
|
|
|
|
|
|
return b.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatEachParam(sig *types.Signature, qualifier types.Qualifier) []string {
|
|
|
|
params := make([]string, 0, sig.Params().Len())
|
|
|
|
for i := 0; i < sig.Params().Len(); i++ {
|
2019-04-24 17:26:34 -06:00
|
|
|
el := sig.Params().At(i)
|
2019-04-28 21:19:54 -06:00
|
|
|
typ := types.TypeString(el.Type(), qualifier)
|
2019-04-24 17:26:34 -06:00
|
|
|
// Handle a variadic parameter (can only be the final parameter).
|
|
|
|
if sig.Variadic() && i == sig.Params().Len()-1 {
|
|
|
|
typ = strings.Replace(typ, "[]", "...", 1)
|
|
|
|
}
|
|
|
|
if el.Name() == "" {
|
2019-04-28 21:19:54 -06:00
|
|
|
params = append(params, typ)
|
2019-04-24 17:26:34 -06:00
|
|
|
} else {
|
2019-04-28 21:19:54 -06:00
|
|
|
params = append(params, el.Name()+" "+typ)
|
2019-04-24 17:26:34 -06:00
|
|
|
}
|
|
|
|
}
|
2019-04-28 21:19:54 -06:00
|
|
|
return params
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type itemDetails struct {
|
|
|
|
label, detail string
|
|
|
|
}
|
|
|
|
|
|
|
|
var builtinDetails = map[string]itemDetails{
|
|
|
|
"append": { // append(slice []T, elems ...T)
|
|
|
|
label: "append(slice []T, elems ...T)",
|
|
|
|
detail: "[]T",
|
|
|
|
},
|
|
|
|
"cap": { // cap(v []T) int
|
|
|
|
label: "cap(v []T)",
|
|
|
|
detail: "int",
|
|
|
|
},
|
|
|
|
"close": { // close(c chan<- T)
|
|
|
|
label: "close(c chan<- T)",
|
|
|
|
},
|
|
|
|
"complex": { // complex(r, i float64) complex128
|
|
|
|
label: "complex(real float64, imag float64)",
|
|
|
|
detail: "complex128",
|
|
|
|
},
|
|
|
|
"copy": { // copy(dst, src []T) int
|
|
|
|
label: "copy(dst []T, src []T)",
|
|
|
|
detail: "int",
|
|
|
|
},
|
|
|
|
"delete": { // delete(m map[T]T1, key T)
|
|
|
|
label: "delete(m map[K]V, key K)",
|
|
|
|
},
|
|
|
|
"imag": { // imag(c complex128) float64
|
|
|
|
label: "imag(complex128)",
|
|
|
|
detail: "float64",
|
|
|
|
},
|
|
|
|
"len": { // len(v T) int
|
|
|
|
label: "len(T)",
|
|
|
|
detail: "int",
|
|
|
|
},
|
|
|
|
"make": { // make(t T, size ...int) T
|
|
|
|
label: "make(t T, size ...int)",
|
|
|
|
detail: "T",
|
|
|
|
},
|
|
|
|
"new": { // new(T) *T
|
|
|
|
label: "new(T)",
|
|
|
|
detail: "*T",
|
|
|
|
},
|
|
|
|
"panic": { // panic(v interface{})
|
|
|
|
label: "panic(interface{})",
|
|
|
|
},
|
|
|
|
"print": { // print(args ...T)
|
|
|
|
label: "print(args ...T)",
|
|
|
|
},
|
|
|
|
"println": { // println(args ...T)
|
|
|
|
label: "println(args ...T)",
|
|
|
|
},
|
|
|
|
"real": { // real(c complex128) float64
|
|
|
|
label: "real(complex128)",
|
|
|
|
detail: "float64",
|
|
|
|
},
|
|
|
|
"recover": { // recover() interface{}
|
|
|
|
label: "recover()",
|
|
|
|
detail: "interface{}",
|
|
|
|
},
|
|
|
|
}
|