internal/lsp: refactor completion and move into source directory

The completion function belongs in internal/lsp/source, so move it
there. Some small refactoring of completion, by moving each type of
completion into helper functions that append to the list of results.

Change-Id: I8599092906609591d499183657fe2d21d1f74df1
Reviewed-on: https://go-review.googlesource.com/c/148397
Reviewed-by: Ian Cottrell <iancottrell@google.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go
index c068ecd..878793b 100644
--- a/internal/lsp/completion.go
+++ b/internal/lsp/completion.go
@@ -1,723 +1,49 @@
 package lsp
 
 import (
-	"bytes"
-	"fmt"
-	"go/ast"
-	"go/format"
-	"go/token"
-	"go/types"
-	"strings"
+	"sort"
 
-	"golang.org/x/tools/go/ast/astutil"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
 )
 
-func completion(v *source.View, uri protocol.DocumentURI, pos protocol.Position) (items []protocol.CompletionItem, err error) {
-	f := v.GetFile(source.URI(uri))
-	if err != nil {
-		return nil, err
-	}
-	tok, err := f.GetToken()
-	if err != nil {
-		return nil, err
-	}
-	p := fromProtocolPosition(tok, pos)
-	file, err := f.GetAST() // Use p to prune the AST?
-	if err != nil {
-		return nil, err
-	}
-	pkg, err := f.GetPackage()
-	if err != nil {
-		return nil, err
-	}
-	items, _, err = completions(v.Config.Fset, file, p, pkg.Types, pkg.TypesInfo)
-	return items, err
-}
-
-// Completions returns the map of possible candidates for completion,
-// given a position, a file AST, and type information. 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 completions(fset *token.FileSet, file *ast.File, pos token.Pos, pkg *types.Package, info *types.Info) (completions []protocol.CompletionItem, prefix string, err error) {
-	path, _ := astutil.PathEnclosingInterval(file, pos, pos)
-	if path == nil {
-		return nil, "", fmt.Errorf("cannot find node enclosing position")
-	}
-	// If the position is not an identifier but immediately follows
-	// an identifier or selector period (as is common when
-	// requesting a completion), use the path to the preceding node.
-	if _, ok := path[0].(*ast.Ident); !ok {
-		if p, _ := astutil.PathEnclosingInterval(file, pos-1, pos-1); p != nil {
-			switch p[0].(type) {
-			case *ast.Ident, *ast.SelectorExpr:
-				path = p // use preceding ident/selector
-			}
-		}
-	}
-
-	expectedTyp := expectedType(path, pos, info)
-	enclosing := enclosingFunc(path, pos, info)
-	pkgStringer := qualifier(file, pkg, info)
-
-	seen := make(map[types.Object]bool)
-	const stdWeight = 1 // default rank for a completion result
-
-	// found adds a candidate completion.
-	// Only the first candidate of a given name is considered.
-	found := func(obj types.Object, weight float32) {
-		if obj.Pkg() != nil && obj.Pkg() != pkg && !obj.Exported() {
-			return // inaccessible
-		}
-		if !seen[obj] {
-			seen[obj] = true
-			if expectedTyp != nil && matchingTypes(expectedTyp, obj.Type()) {
-				weight *= 10
-			}
-			item := formatCompletion(obj, pkgStringer, weight, func(v *types.Var) bool {
-				return isParam(enclosing, v)
-			})
-			completions = append(completions, item)
-		}
-	}
-
-	// selector finds completions for
-	// the specified selector expression.
-	// TODO(rstambler): Set the prefix filter correctly for selectors.
-	selector := func(sel *ast.SelectorExpr) error {
-		// Is sel a qualified identifier?
-		if id, ok := sel.X.(*ast.Ident); ok {
-			if pkgname, ok := info.Uses[id].(*types.PkgName); ok {
-				// Enumerate package members.
-				// TODO(adonovan): can Imported() be nil?
-				scope := pkgname.Imported().Scope()
-				// TODO testcase: bad import
-				for _, name := range scope.Names() {
-					found(scope.Lookup(name), stdWeight)
-				}
-				return nil
-			}
-		}
-
-		// Inv: sel is a true selector.
-		tv, ok := info.Types[sel.X]
-		if !ok {
-			var buf bytes.Buffer
-			format.Node(&buf, fset, sel.X) // TODO check for error
-			return fmt.Errorf("cannot resolve %s", &buf)
-		}
-
-		// methods of T
-		mset := types.NewMethodSet(tv.Type)
-		for i := 0; i < mset.Len(); i++ {
-			found(mset.At(i).Obj(), stdWeight)
-		}
-
-		// methods of *T
-		if tv.Addressable() && !types.IsInterface(tv.Type) && !isPointer(tv.Type) {
-			mset := types.NewMethodSet(types.NewPointer(tv.Type))
-			for i := 0; i < mset.Len(); i++ {
-				found(mset.At(i).Obj(), stdWeight)
-			}
-		}
-
-		// fields of T
-		for _, f := range fieldSelections(tv.Type) {
-			found(f, stdWeight)
-		}
-
-		return nil
-	}
-
-	// lexical finds completions in the lexical environment.
-	lexical := func(path []ast.Node) {
-		var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
-		for _, n := range path {
-			switch node := n.(type) {
-			case *ast.FuncDecl:
-				n = node.Type
-			case *ast.FuncLit:
-				n = node.Type
-			}
-			scopes = append(scopes, info.Scopes[n])
-		}
-		scopes = append(scopes, pkg.Scope(), types.Universe)
-
-		// Process scopes innermost first.
-		for i, scope := range scopes {
-			if scope == nil {
-				continue
-			}
-			for _, name := range scope.Names() {
-				declScope, obj := scope.LookupParent(name, pos)
-				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
-					if i < len(path) {
-						node = path[i]
-					} else if i == len(path) { // use the *ast.File for package scope
-						node = path[i-1]
-					}
-					if node != nil {
-						if resolved := resolveInvalid(obj, node, info); resolved != nil {
-							obj = resolved
-						}
-					}
-				}
-
-				score := float32(stdWeight)
-				// Rank builtins significantly lower than other results.
-				if scope == types.Universe {
-					score *= 0.1
-				}
-				found(obj, score)
-			}
-		}
-	}
-
-	// complit finds completions for field names inside a composite literal.
-	// It reports whether the node was handled as part of a composite literal.
-	complit := func(node ast.Node) bool {
-		var lit *ast.CompositeLit
-
-		switch n := node.(type) {
-		case *ast.CompositeLit:
-			// 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, <>})
-			//
-			// 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.
-			var expr ast.Expr
-			for _, e := range n.Elts {
-				if e.Pos() <= pos && pos < e.End() {
-					expr = e
-					break
-				}
-			}
-			lit = n
-			// If the position belongs to a key-value expression and is after the colon,
-			// don't show composite literal completions.
-			if kv, ok := expr.(*ast.KeyValueExpr); ok && pos > kv.Colon {
-				lit = nil
-			}
-		case *ast.KeyValueExpr:
-			// If the enclosing node is a key-value expression (e.g. &x{foo: <>}),
-			// we show composite literal completions if the cursor position is before the colon.
-			if len(path) > 1 && pos < n.Colon {
-				if l, ok := path[1].(*ast.CompositeLit); ok {
-					lit = l
-				}
-			}
-		case *ast.Ident:
-			// 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<>).
-			// We handle both of these cases, showing composite literal completions only if
-			// the cursor position for the key-value expression is before the colon.
-			if len(path) > 1 {
-				if l, ok := path[1].(*ast.CompositeLit); ok {
-					lit = l
-				} else if len(path) > 2 {
-					if l, ok := path[2].(*ast.CompositeLit); ok {
-						// Confirm that cursor position is inside curly braces.
-						if l.Lbrace <= pos && pos <= l.Rbrace {
-							lit = l
-							if kv, ok := path[1].(*ast.KeyValueExpr); ok {
-								if pos > kv.Colon {
-									lit = nil
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-		if lit == nil {
-			return false
-		}
-		// Mark fields that have already been set, apart from the current field.
-		hasKeys := false // true if the composite literal already has key-value pairs
-		addedFields := make(map[*types.Var]bool)
-		for _, el := range lit.Elts {
-			if kv, ok := el.(*ast.KeyValueExpr); ok {
-				hasKeys = true
-				if kv.Pos() <= pos && pos <= kv.End() {
-					continue
-				}
-				if key, ok := kv.Key.(*ast.Ident); ok {
-					if used, ok := info.Uses[key]; ok {
-						if usedVar, ok := used.(*types.Var); ok {
-							addedFields[usedVar] = true
-						}
-					}
-				}
-			}
-		}
-		// If the underlying type of the composite literal is a struct,
-		// we show completions for the fields of this struct.
-		if tv, ok := info.Types[lit]; ok {
-			var structPkg *types.Package // package containing the struct type declaration
-			if s, ok := tv.Type.Underlying().(*types.Struct); ok {
-				for i := 0; i < s.NumFields(); i++ {
-					field := s.Field(i)
-					if i == 0 {
-						structPkg = field.Pkg()
-					}
-					if !addedFields[field] {
-						found(field, stdWeight*10)
-					}
-				}
-				// 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.
-				if !hasKeys && structPkg == pkg {
-					lexical(path)
-				}
-				return true
-			}
-		}
-		return false
-	}
-
-	if complit(path[0]) {
-		return completions, prefix, nil
-	}
-
-	switch n := path[0].(type) {
-	case *ast.Ident:
-		// Set the filter prefix.
-		prefix = n.Name[:pos-n.Pos()]
-
-		// Is this the Sel part of a selector?
-		if sel, ok := path[1].(*ast.SelectorExpr); ok && sel.Sel == n {
-			if err := selector(sel); err != nil {
-				return nil, prefix, err
-			}
-		} else {
-			// reject defining identifiers
-			if obj, ok := info.Defs[n]; ok {
-				if v, ok := obj.(*types.Var); ok && v.IsField() {
-					// An anonymous field is also a reference to a type.
-				} else {
-					of := ""
-					if obj != nil {
-						qual := types.RelativeTo(pkg)
-						of += ", of " + types.ObjectString(obj, qual)
-					}
-					return nil, "", fmt.Errorf("this is a definition%s", of)
-				}
-			}
-
-			lexical(path)
-		}
-
-	// Support completions when no letters of the function name have been
-	// typed yet, but the parens are there:
-	//   recv.‸(arg)
-	case *ast.TypeAssertExpr:
-		// Create a fake selector expression.
-		if err := selector(&ast.SelectorExpr{X: n.X}); err != nil {
-			return nil, prefix, err
-		}
-
-	case *ast.SelectorExpr:
-		if err := selector(n); err != nil {
-			return nil, prefix, err
-		}
-
-	default:
-		// TODO(adonovan): a lexical query may not be what the
-		// user expects when completing after the period of a
-		// type assertion.
-
-		lexical(path)
-	}
-
-	return completions, prefix, nil
-}
-
-// qualifier returns a function that appropriately formats a types.PkgName appearing in q.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(pkg *types.Package) string {
-		if pkg == pkg {
-			return ""
-		}
-		if name, ok := imports[pkg]; ok {
-			return name
-		}
-		return pkg.Name()
-	}
-}
-
-// enclosingFunc returns the signature of the function enclosing the position.
-func enclosingFunc(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
-}
-
-// formatCompletion returns the label, details, and kind for a types.Object,
-// fitting the format of a LSP completion item.
-func formatCompletion(obj types.Object, qualifier types.Qualifier, score float32, isParam func(*types.Var) bool) protocol.CompletionItem {
-	label := obj.Name()
-	detail := types.TypeString(obj.Type(), qualifier)
-
-	var kind protocol.CompletionItemKind
-
-	switch o := obj.(type) {
-	case *types.TypeName:
-		detail, kind = formatType(o.Type(), qualifier)
-		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 = protocol.ConstantCompletion
-	case *types.Var:
-		if _, ok := o.Type().(*types.Struct); ok {
-			detail = "struct{...}" // for anonymous structs
-		}
-		if o.IsField() {
-			kind = protocol.FieldCompletion
-		} else if isParam(o) {
-			kind = protocol.TypeParameterCompletion
-		} else {
-			kind = protocol.VariableCompletion
-		}
-	case *types.Func:
-		if sig, ok := o.Type().(*types.Signature); ok {
-			label += formatParams(sig.Params(), sig.Variadic(), qualifier)
-			detail = strings.Trim(types.TypeString(sig.Results(), qualifier), "()")
-			kind = protocol.FunctionCompletion
-			if sig.Recv() != nil {
-				kind = protocol.MethodCompletion
-			}
-		}
-	case *types.Builtin:
-		item, ok := builtinDetails[obj.Name()]
-		if !ok {
-			break
-		}
-		label, detail = item.label, item.detail
-		kind = protocol.FunctionCompletion
-	case *types.PkgName:
-		kind = protocol.ModuleCompletion // package??
-		detail = fmt.Sprintf("\"%s\"", o.Imported().Path())
-	case *types.Nil:
-		kind = protocol.VariableCompletion
-		detail = ""
-	}
-
-	detail = strings.TrimPrefix(detail, "untyped ")
-
-	return protocol.CompletionItem{
-		Label:  label,
-		Detail: detail,
-		Kind:   float64(kind),
-	}
-}
-
-// formatType returns the detail and kind for an object of type *types.TypeName.
-func formatType(typ types.Type, qualifier types.Qualifier) (detail string, kind protocol.CompletionItemKind) {
-	if types.IsInterface(typ) {
-		detail = "interface{...}"
-		kind = protocol.InterfaceCompletion
-	} else if _, ok := typ.(*types.Struct); ok {
-		detail = "struct{...}"
-		kind = protocol.StructCompletion
-	} else if typ != typ.Underlying() {
-		detail, kind = formatType(typ.Underlying(), qualifier)
-	} else {
-		detail = types.TypeString(typ, qualifier)
-		kind = protocol.TypeParameterCompletion // ???
-	}
-	return detail, kind
-}
-
-func formatParams(t *types.Tuple, variadic bool, qualifier types.Qualifier) string {
-	var b strings.Builder
-	b.WriteByte('(')
-	for i := 0; i < t.Len(); i++ {
-		if i > 0 {
-			b.WriteString(", ")
-		}
-		el := t.At(i)
-		typ := types.TypeString(el.Type(), qualifier)
-		// Handle a variadic parameter (can only be the final parameter).
-		if variadic && i == t.Len()-1 {
-			typ = strings.Replace(typ, "[]", "...", 1)
-		}
-		fmt.Fprintf(&b, "%v %v", el.Name(), typ)
-	}
-	b.WriteByte(')')
-	return b.String()
-}
-
-func isParam(sig *types.Signature, v *types.Var) bool {
-	if sig == nil {
-		return false
-	}
-	for i := 0; i < sig.Params().Len(); i++ {
-		if sig.Params().At(i) == v {
-			return true
-		}
-	}
-	return false
-}
-
-// 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
-			}
-			i := exprAtPos(pos, expr.Rhs)
-			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
-					}
-					i := exprAtPos(pos, expr.Args)
-					// 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
-}
-
-// matchingTypes reports whether actual is a good candidate type
-// for a completion in a context of the expected type.
-func matchingTypes(expected, actual types.Type) bool {
-	// 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()
-		}
-	}
-	return types.Identical(types.Default(expected), types.Default(actual))
-}
-
-// exprAtPos returns the index of the expression containing pos.
-func exprAtPos(pos token.Pos, args []ast.Expr) int {
-	for i, expr := range args {
-		if expr.Pos() <= pos && pos <= expr.End() {
-			return i
-		}
-	}
-	return len(args)
-}
-
-// fieldSelections returns the set of fields that can
-// be selected from a value of type T.
-func fieldSelections(T types.Type) (fields []*types.Var) {
-	// TODO(adonovan): this algorithm doesn't exclude ambiguous
-	// selections that match more than one field/method.
-	// types.NewSelectionSet should do that for us.
-
-	seen := make(map[types.Type]bool) // for termination on recursive types
-	var visit func(T types.Type)
-	visit = func(T types.Type) {
-		if !seen[T] {
-			seen[T] = true
-			if T, ok := deref(T).Underlying().(*types.Struct); ok {
-				for i := 0; i < T.NumFields(); i++ {
-					f := T.Field(i)
-					fields = append(fields, f)
-					if f.Anonymous() {
-						visit(f.Type())
-					}
-				}
-			}
-		}
-	}
-	visit(T)
-
-	return fields
-}
-
-func isPointer(T types.Type) bool {
-	_, ok := T.(*types.Pointer)
-	return ok
-}
-
-// deref returns a pointer's element type; otherwise it returns typ.
-func deref(typ types.Type) types.Type {
-	if p, ok := typ.Underlying().(*types.Pointer); ok {
-		return p.Elem()
-	}
-	return typ
-}
-
-// resolveInvalid traverses the node of the AST that defines the scope
-// containing the declaration of obj, and attempts to find a user-friendly
-// name for its invalid type. The resulting Object and its Type are fake.
-func resolveInvalid(obj types.Object, node ast.Node, info *types.Info) types.Object {
-	// Construct a fake type for the object and return a fake object with this type.
-	formatResult := func(expr ast.Expr) types.Object {
-		var typename string
-		switch t := expr.(type) {
-		case *ast.SelectorExpr:
-			typename = fmt.Sprintf("%s.%s", t.X, t.Sel)
-		case *ast.Ident:
-			typename = t.String()
-		default:
-			return nil
-		}
-		typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), nil, nil)
-		return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ)
-	}
-	var resultExpr ast.Expr
-	ast.Inspect(node, func(node ast.Node) bool {
-		switch n := node.(type) {
-		case *ast.ValueSpec:
-			for _, name := range n.Names {
-				if info.Defs[name] == obj {
-					resultExpr = n.Type
-				}
-			}
-			return false
-		case *ast.Field: // This case handles parameters and results of a FuncDecl or FuncLit.
-			for _, name := range n.Names {
-				if info.Defs[name] == obj {
-					resultExpr = n.Type
-				}
-			}
-			return false
-		// TODO(rstambler): Handle range statements.
-		default:
-			return true
-		}
+func toProtocolCompletionItems(items []source.CompletionItem) []protocol.CompletionItem {
+	var results []protocol.CompletionItem
+	sort.Slice(items, func(i, j int) bool {
+		return items[i].Score > items[j].Score
 	})
-	return formatResult(resultExpr)
+	for _, item := range items {
+		results = append(results, protocol.CompletionItem{
+			Label:  item.Label,
+			Detail: item.Detail,
+			Kind:   float64(toProtocolCompletionItemKind(item.Kind)),
+		})
+	}
+	return results
 }
 
-type itemDetails struct {
-	label, detail string
-}
+func toProtocolCompletionItemKind(kind source.CompletionItemKind) protocol.CompletionItemKind {
+	switch kind {
+	case source.InterfaceCompletionItem:
+		return protocol.InterfaceCompletion
+	case source.StructCompletionItem:
+		return protocol.StructCompletion
+	case source.TypeCompletionItem:
+		return protocol.TypeParameterCompletion // ??
+	case source.ConstantCompletionItem:
+		return protocol.ConstantCompletion
+	case source.FieldCompletionItem:
+		return protocol.FieldCompletion
+	case source.ParameterCompletionItem, source.VariableCompletionItem:
+		return protocol.VariableCompletion
+	case source.FunctionCompletionItem:
+		return protocol.FunctionCompletion
+	case source.MethodCompletionItem:
+		return protocol.MethodCompletion
+	case source.PackageCompletionItem:
+		return protocol.ModuleCompletion // ??
+	default:
+		return protocol.TextCompletion
+	}
 
-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, imag float64)",
-		detail: "complex128",
-	},
-	"copy": { // copy(dst, src []T) int
-		label:  "copy(dst, 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{}",
-	},
 }
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index 9ee806a..0818195 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -149,13 +149,19 @@
 }
 
 func (s *server) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
-	items, err := completion(s.view, params.TextDocument.URI, params.Position)
+	f := s.view.GetFile(source.URI(params.TextDocument.URI))
+	tok, err := f.GetToken()
+	if err != nil {
+		return nil, err
+	}
+	pos := fromProtocolPosition(tok, params.Position)
+	items, err := source.Completion(ctx, f, pos)
 	if err != nil {
 		return nil, err
 	}
 	return &protocol.CompletionList{
 		IsIncomplete: false,
-		Items:        items,
+		Items:        toProtocolCompletionItems(items),
 	}, nil
 }
 
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
new file mode 100644
index 0000000..5d1e29a
--- /dev/null
+++ b/internal/lsp/source/completion.go
@@ -0,0 +1,731 @@
+package source
+
+import (
+	"context"
+	"fmt"
+	"go/ast"
+	"go/token"
+	"go/types"
+	"strings"
+
+	"golang.org/x/tools/go/ast/astutil"
+)
+
+type CompletionItem struct {
+	Label, Detail string
+	Kind          CompletionItemKind
+	Score         int
+}
+
+type CompletionItemKind int
+
+const (
+	Unknown CompletionItemKind = iota
+	InterfaceCompletionItem
+	StructCompletionItem
+	TypeCompletionItem
+	ConstantCompletionItem
+	FieldCompletionItem
+	ParameterCompletionItem
+	VariableCompletionItem
+	FunctionCompletionItem
+	MethodCompletionItem
+	PackageCompletionItem
+)
+
+func Completion(ctx context.Context, f *File, pos token.Pos) ([]CompletionItem, error) {
+	file, err := f.GetAST()
+	if err != nil {
+		return nil, err
+	}
+	pkg, err := f.GetPackage()
+	if err != nil {
+		return nil, err
+	}
+	items, _, err := completions(file, pos, pkg.Fset, pkg.Types, pkg.TypesInfo)
+	return items, err
+}
+
+type finder func(types.Object, float64, []CompletionItem) []CompletionItem
+
+// completions returns the map of possible candidates for completion, given a
+// position, a file AST, and type information. 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 completions(file *ast.File, pos token.Pos, fset *token.FileSet, pkg *types.Package, info *types.Info) (items []CompletionItem, prefix string, err error) {
+	path, _ := astutil.PathEnclosingInterval(file, pos, pos)
+	if path == nil {
+		return nil, "", fmt.Errorf("cannot find node enclosing position")
+	}
+	// If the position is not an identifier but immediately follows
+	// an identifier or selector period (as is common when
+	// requesting a completion), use the path to the preceding node.
+	if _, ok := path[0].(*ast.Ident); !ok {
+		if p, _ := astutil.PathEnclosingInterval(file, pos-1, pos-1); p != nil {
+			switch p[0].(type) {
+			case *ast.Ident, *ast.SelectorExpr:
+				path = p // use preceding ident/selector
+			}
+		}
+	}
+
+	// Save certain facts about the query position, including the expected type
+	// of the completion result, the signature of the function enclosing the
+	// position.
+	typ := expectedType(path, pos, info)
+	sig := enclosingFunction(path, pos, info)
+	pkgStringer := qualifier(file, pkg, info)
+
+	seen := make(map[types.Object]bool)
+
+	// found adds a candidate completion.
+	// Only the first candidate of a given name is considered.
+	found := func(obj types.Object, weight float64, items []CompletionItem) []CompletionItem {
+		if obj.Pkg() != nil && obj.Pkg() != pkg && !obj.Exported() {
+			return items // inaccessible
+		}
+		if !seen[obj] {
+			seen[obj] = true
+			if typ != nil && matchingTypes(typ, obj.Type()) {
+				weight *= 10
+			}
+			item := formatCompletion(obj, pkgStringer, weight, func(v *types.Var) bool {
+				return isParameter(sig, v)
+			})
+			items = append(items, item)
+		}
+		return items
+	}
+
+	// The position is within a composite literal.
+	if items, ok := complit(path, pos, pkg, info, found); ok {
+		return items, "", nil
+	}
+
+	switch n := path[0].(type) {
+	case *ast.Ident:
+		// Set the filter prefix.
+		prefix = n.Name[:pos-n.Pos()]
+
+		// Is this the Sel part of a selector?
+		if sel, ok := path[1].(*ast.SelectorExpr); ok && sel.Sel == n {
+			return selector(sel, info, found)
+		}
+		// reject defining identifiers
+		if obj, ok := info.Defs[n]; ok {
+			if v, ok := obj.(*types.Var); ok && v.IsField() {
+				// An anonymous field is also a reference to a type.
+			} else {
+				of := ""
+				if obj != nil {
+					qual := types.RelativeTo(pkg)
+					of += ", of " + types.ObjectString(obj, qual)
+				}
+				return nil, "", fmt.Errorf("this is a definition%s", of)
+			}
+		}
+
+		items = append(items, lexical(path, pos, pkg, info, found)...)
+
+	// The function name hasn't been typed yet, but the parens are there:
+	//   recv.‸(arg)
+	case *ast.TypeAssertExpr:
+		// Create a fake selector expression.
+		return selector(&ast.SelectorExpr{X: n.X}, info, found)
+
+	case *ast.SelectorExpr:
+		return selector(n, info, found)
+
+	default:
+		// fallback to lexical completions
+		return lexical(path, pos, pkg, info, found), "", nil
+	}
+
+	return items, prefix, nil
+}
+
+// selector finds completions for
+// the specified selector expression.
+// TODO(rstambler): Set the prefix filter correctly for selectors.
+func selector(sel *ast.SelectorExpr, info *types.Info, found finder) (items []CompletionItem, prefix string, err error) {
+	// Is sel a qualified identifier?
+	if id, ok := sel.X.(*ast.Ident); ok {
+		if pkgname, ok := info.Uses[id].(*types.PkgName); ok {
+			// Enumerate package members.
+			// TODO(adonovan): can Imported() be nil?
+			scope := pkgname.Imported().Scope()
+			// TODO testcase: bad import
+			for _, name := range scope.Names() {
+				items = found(scope.Lookup(name), 1, items)
+			}
+			return items, prefix, nil
+		}
+	}
+
+	// Inv: sel is a true selector.
+	tv, ok := info.Types[sel.X]
+	if !ok {
+		return nil, "", fmt.Errorf("cannot resolve %s", sel.X)
+	}
+
+	// methods of T
+	mset := types.NewMethodSet(tv.Type)
+	for i := 0; i < mset.Len(); i++ {
+		items = found(mset.At(i).Obj(), 1, items)
+	}
+
+	// methods of *T
+	if tv.Addressable() && !types.IsInterface(tv.Type) && !isPointer(tv.Type) {
+		mset := types.NewMethodSet(types.NewPointer(tv.Type))
+		for i := 0; i < mset.Len(); i++ {
+			items = found(mset.At(i).Obj(), 1, items)
+		}
+	}
+
+	// fields of T
+	for _, f := range fieldSelections(tv.Type) {
+		items = found(f, 1, items)
+	}
+
+	return items, prefix, nil
+}
+
+// lexical finds completions in the lexical environment.
+func lexical(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Info, found finder) (items []CompletionItem) {
+	var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
+	for _, n := range path {
+		switch node := n.(type) {
+		case *ast.FuncDecl:
+			n = node.Type
+		case *ast.FuncLit:
+			n = node.Type
+		}
+		scopes = append(scopes, info.Scopes[n])
+	}
+	scopes = append(scopes, pkg.Scope(), types.Universe)
+
+	// Process scopes innermost first.
+	for i, scope := range scopes {
+		if scope == nil {
+			continue
+		}
+		for _, name := range scope.Names() {
+			declScope, obj := scope.LookupParent(name, pos)
+			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
+				if i < len(path) {
+					node = path[i]
+				} else if i == len(path) { // use the *ast.File for package scope
+					node = path[i-1]
+				}
+				if node != nil {
+					if resolved := resolveInvalid(obj, node, info); resolved != nil {
+						obj = resolved
+					}
+				}
+			}
+
+			score := 1.0
+			// Rank builtins significantly lower than other results.
+			if scope == types.Universe {
+				score *= 0.1
+			}
+			items = found(obj, score, items)
+		}
+	}
+	return items
+}
+
+// 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) {
+	var lit *ast.CompositeLit
+
+	// First, determine if the pos is within a composite literal.
+	switch n := path[0].(type) {
+	case *ast.CompositeLit:
+		// 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, <>})
+		//
+		// 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.
+		var expr ast.Expr
+		for _, e := range n.Elts {
+			if e.Pos() <= pos && pos < e.End() {
+				expr = e
+				break
+			}
+		}
+		lit = n
+		// If the position belongs to a key-value expression and is after the colon,
+		// don't show composite literal completions.
+		if kv, ok := expr.(*ast.KeyValueExpr); ok && pos > kv.Colon {
+			lit = nil
+		}
+	case *ast.KeyValueExpr:
+		// If the enclosing node is a key-value expression (e.g. &x{foo: <>}),
+		// we show composite literal completions if the cursor position is before the colon.
+		if len(path) > 1 && pos < n.Colon {
+			if l, ok := path[1].(*ast.CompositeLit); ok {
+				lit = l
+			}
+		}
+	case *ast.Ident:
+		// 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<>).
+		// We handle both of these cases, showing composite literal completions only if
+		// the cursor position for the key-value expression is before the colon.
+		if len(path) > 1 {
+			if l, ok := path[1].(*ast.CompositeLit); ok {
+				lit = l
+			} else if len(path) > 2 {
+				if l, ok := path[2].(*ast.CompositeLit); ok {
+					// Confirm that cursor position is inside curly braces.
+					if l.Lbrace <= pos && pos <= l.Rbrace {
+						lit = l
+						if kv, ok := path[1].(*ast.KeyValueExpr); ok {
+							if pos > kv.Colon {
+								lit = nil
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	// We are not in a composite literal.
+	if lit == nil {
+		return nil, false
+	}
+	// Mark fields of the composite literal that have already been set,
+	// except for the current field.
+	hasKeys := false // true if the composite literal already has key-value pairs
+	addedFields := make(map[*types.Var]bool)
+	for _, el := range lit.Elts {
+		if kv, ok := el.(*ast.KeyValueExpr); ok {
+			hasKeys = true
+			if kv.Pos() <= pos && pos <= kv.End() {
+				continue
+			}
+			if key, ok := kv.Key.(*ast.Ident); ok {
+				if used, ok := info.Uses[key]; ok {
+					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.
+	if tv, ok := info.Types[lit]; ok {
+		var structPkg *types.Package // package containing the struct type declaration
+		if s, ok := tv.Type.Underlying().(*types.Struct); ok {
+			for i := 0; i < s.NumFields(); i++ {
+				field := s.Field(i)
+				if i == 0 {
+					structPkg = field.Pkg()
+				}
+				if !addedFields[field] {
+					items = found(field, 10, items)
+				}
+			}
+			// 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.
+			if !hasKeys && structPkg == pkg {
+				items = append(items, lexical(path, pos, pkg, info, found)...)
+			}
+			return items, true
+		}
+	}
+	return items, false
+}
+
+// formatCompletion creates a completion item for a given types.Object.
+func formatCompletion(obj types.Object, qualifier types.Qualifier, score float64, isParam func(*types.Var) bool) CompletionItem {
+	label := obj.Name()
+	detail := types.TypeString(obj.Type(), qualifier)
+	var kind CompletionItemKind
+
+	switch o := obj.(type) {
+	case *types.TypeName:
+		detail, kind = formatType(o.Type(), qualifier)
+		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
+		} else if isParam(o) {
+			kind = ParameterCompletionItem
+		} else {
+			kind = VariableCompletionItem
+		}
+	case *types.Func:
+		if sig, ok := o.Type().(*types.Signature); ok {
+			label += formatParams(sig.Params(), sig.Variadic(), qualifier)
+			detail = strings.Trim(types.TypeString(sig.Results(), qualifier), "()")
+			kind = FunctionCompletionItem
+			if sig.Recv() != nil {
+				kind = MethodCompletionItem
+			}
+		}
+	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,
+		Detail: detail,
+		Kind:   kind,
+	}
+}
+
+// formatType returns the detail and kind for an object of type *types.TypeName.
+func formatType(typ types.Type, qualifier 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(), qualifier)
+	} else {
+		detail = types.TypeString(typ, qualifier)
+		kind = TypeCompletionItem
+	}
+	return detail, kind
+}
+
+// formatParams correctly format the parameters of a function.
+func formatParams(t *types.Tuple, variadic bool, qualifier types.Qualifier) string {
+	var b strings.Builder
+	b.WriteByte('(')
+	for i := 0; i < t.Len(); i++ {
+		if i > 0 {
+			b.WriteString(", ")
+		}
+		el := t.At(i)
+		typ := types.TypeString(el.Type(), qualifier)
+		// Handle a variadic parameter (can only be the final parameter).
+		if variadic && i == t.Len()-1 {
+			typ = strings.Replace(typ, "[]", "...", 1)
+		}
+		fmt.Fprintf(&b, "%v %v", el.Name(), typ)
+	}
+	b.WriteByte(')')
+	return b.String()
+}
+
+// isParameter returns true if the given *types.Var is a parameter to the given
+// *types.Signature.
+func isParameter(sig *types.Signature, v *types.Var) bool {
+	if sig == nil {
+		return false
+	}
+	for i := 0; i < sig.Params().Len(); i++ {
+		if sig.Params().At(i) == v {
+			return true
+		}
+	}
+	return false
+}
+
+// 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(pkg *types.Package) string {
+		if pkg == pkg {
+			return ""
+		}
+		if name, ok := imports[pkg]; ok {
+			return name
+		}
+		return pkg.Name()
+	}
+}
+
+// enclosingFunction returns the signature of the function enclosing the given
+// position.
+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
+}
+
+// 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
+			}
+			i := exprAtPos(pos, expr.Rhs)
+			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
+					}
+					i := exprAtPos(pos, expr.Args)
+					// 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
+}
+
+// matchingTypes reports whether actual is a good candidate type
+// for a completion in a context of the expected type.
+func matchingTypes(expected, actual types.Type) bool {
+	// 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()
+		}
+	}
+	return types.Identical(types.Default(expected), types.Default(actual))
+}
+
+// exprAtPos returns the index of the expression containing pos.
+func exprAtPos(pos token.Pos, args []ast.Expr) int {
+	for i, expr := range args {
+		if expr.Pos() <= pos && pos <= expr.End() {
+			return i
+		}
+	}
+	return len(args)
+}
+
+// fieldSelections returns the set of fields that can
+// be selected from a value of type T.
+func fieldSelections(T types.Type) (fields []*types.Var) {
+	// TODO(adonovan): this algorithm doesn't exclude ambiguous
+	// selections that match more than one field/method.
+	// types.NewSelectionSet should do that for us.
+
+	seen := make(map[types.Type]bool) // for termination on recursive types
+	var visit func(T types.Type)
+	visit = func(T types.Type) {
+		if !seen[T] {
+			seen[T] = true
+			if T, ok := deref(T).Underlying().(*types.Struct); ok {
+				for i := 0; i < T.NumFields(); i++ {
+					f := T.Field(i)
+					fields = append(fields, f)
+					if f.Anonymous() {
+						visit(f.Type())
+					}
+				}
+			}
+		}
+	}
+	visit(T)
+
+	return fields
+}
+
+func isPointer(T types.Type) bool {
+	_, ok := T.(*types.Pointer)
+	return ok
+}
+
+// deref returns a pointer's element type; otherwise it returns typ.
+func deref(typ types.Type) types.Type {
+	if p, ok := typ.Underlying().(*types.Pointer); ok {
+		return p.Elem()
+	}
+	return typ
+}
+
+// resolveInvalid traverses the node of the AST that defines the scope
+// containing the declaration of obj, and attempts to find a user-friendly
+// name for its invalid type. The resulting Object and its Type are fake.
+func resolveInvalid(obj types.Object, node ast.Node, info *types.Info) types.Object {
+	// Construct a fake type for the object and return a fake object with this type.
+	formatResult := func(expr ast.Expr) types.Object {
+		var typename string
+		switch t := expr.(type) {
+		case *ast.SelectorExpr:
+			typename = fmt.Sprintf("%s.%s", t.X, t.Sel)
+		case *ast.Ident:
+			typename = t.String()
+		default:
+			return nil
+		}
+		typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), nil, nil)
+		return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ)
+	}
+	var resultExpr ast.Expr
+	ast.Inspect(node, func(node ast.Node) bool {
+		switch n := node.(type) {
+		case *ast.ValueSpec:
+			for _, name := range n.Names {
+				if info.Defs[name] == obj {
+					resultExpr = n.Type
+				}
+			}
+			return false
+		case *ast.Field: // This case handles parameters and results of a FuncDecl or FuncLit.
+			for _, name := range n.Names {
+				if info.Defs[name] == obj {
+					resultExpr = n.Type
+				}
+			}
+			return false
+		// TODO(rstambler): Handle range statements.
+		default:
+			return true
+		}
+	})
+	return formatResult(resultExpr)
+}
+
+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, imag float64)",
+		detail: "complex128",
+	},
+	"copy": { // copy(dst, src []T) int
+		label:  "copy(dst, 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{}",
+	},
+}