| // 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" |
| |
| "golang.org/x/tools/internal/lsp/snippet" |
| ) |
| |
| // formatCompletion creates a completion item for a given types.Object. |
| func (c *completer) item(obj types.Object, score float64) CompletionItem { |
| var ( |
| label = obj.Name() |
| detail = types.TypeString(obj.Type(), c.qf) |
| insert = label |
| kind CompletionItemKind |
| plainSnippet *snippet.Builder |
| placeholderSnippet *snippet.Builder |
| ) |
| |
| 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 |
| plainSnippet, placeholderSnippet = c.structFieldSnippet(label, detail) |
| } else if c.isParameter(o) { |
| kind = ParameterCompletionItem |
| } else { |
| kind = VariableCompletionItem |
| } |
| case *types.Func: |
| if sig, ok := o.Type().(*types.Signature); ok { |
| params := formatEachParam(sig, c.qf) |
| label += formatParamParts(params) |
| detail = strings.Trim(types.TypeString(sig.Results(), c.qf), "()") |
| kind = FunctionCompletionItem |
| if sig.Recv() != nil { |
| kind = MethodCompletionItem |
| } |
| |
| plainSnippet, placeholderSnippet = c.funcCallSnippet(obj.Name(), params) |
| } |
| 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{ |
| Label: label, |
| Insert: insert, |
| Detail: detail, |
| Kind: kind, |
| Score: score, |
| PlainSnippet: plainSnippet, |
| PlaceholderSnippet: placeholderSnippet, |
| } |
| } |
| |
| // 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 |
| } |
| |
| // 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) |
| |
| b.WriteByte('(') |
| for i, p := range params { |
| if i > 0 { |
| b.WriteString(", ") |
| } |
| 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++ { |
| el := sig.Params().At(i) |
| typ := types.TypeString(el.Type(), qualifier) |
| // 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() == "" { |
| params = append(params, typ) |
| } else { |
| params = append(params, el.Name()+" "+typ) |
| } |
| } |
| return params |
| } |
| |
| // 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{}", |
| }, |
| } |