--- /dev/null
+// 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 completion
+
+import (
+ "context"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "strings"
+ "unicode"
+
+ "golang.org/x/tools/internal/event"
+ "golang.org/x/tools/internal/lsp/diff"
+ "golang.org/x/tools/internal/lsp/protocol"
+ "golang.org/x/tools/internal/lsp/snippet"
+ "golang.org/x/tools/internal/lsp/source"
+)
+
+// literal generates composite literal, function literal, and make()
+// completion items.
+func (c *completer) literal(ctx context.Context, literalType types.Type, imp *importInfo) {
+ if !c.opts.literal {
+ return
+ }
+
+ expType := c.inference.objType
+
+ if c.inference.matchesVariadic(literalType) {
+ // Don't offer literal slice candidates for variadic arguments.
+ // For example, don't offer "[]interface{}{}" in "fmt.Print(<>)".
+ return
+ }
+
+ // Avoid literal candidates if the expected type is an empty
+ // interface. It isn't very useful to suggest a literal candidate of
+ // every possible type.
+ if expType != nil && isEmptyInterface(expType) {
+ return
+ }
+
+ // We handle unnamed literal completions explicitly before searching
+ // for candidates. Avoid named-type literal completions for
+ // unnamed-type expected type since that results in duplicate
+ // candidates. For example, in
+ //
+ // type mySlice []int
+ // var []int = <>
+ //
+ // don't offer "mySlice{}" since we have already added a candidate
+ // of "[]int{}".
+ if _, named := literalType.(*types.Named); named && expType != nil {
+ if _, named := source.Deref(expType).(*types.Named); !named {
+ return
+ }
+ }
+
+ // Check if an object of type literalType would match our expected type.
+ cand := candidate{
+ obj: c.fakeObj(literalType),
+ }
+
+ switch literalType.Underlying().(type) {
+ // These literal types are addressable (e.g. "&[]int{}"), others are
+ // not (e.g. can't do "&(func(){})").
+ case *types.Struct, *types.Array, *types.Slice, *types.Map:
+ cand.addressable = true
+ }
+
+ if !c.matchingCandidate(&cand) {
+ return
+ }
+
+ var (
+ qf = c.qf
+ sel = enclosingSelector(c.path, c.pos)
+ )
+
+ // Don't qualify the type name if we are in a selector expression
+ // since the package name is already present.
+ if sel != nil {
+ qf = func(_ *types.Package) string { return "" }
+ }
+
+ typeName := types.TypeString(literalType, qf)
+
+ // A type name of "[]int" doesn't work very will with the matcher
+ // since "[" isn't a valid identifier prefix. Here we strip off the
+ // slice (and array) prefix yielding just "int".
+ matchName := typeName
+ switch t := literalType.(type) {
+ case *types.Slice:
+ matchName = types.TypeString(t.Elem(), qf)
+ case *types.Array:
+ matchName = types.TypeString(t.Elem(), qf)
+ }
+
+ addlEdits, err := c.importEdits(imp)
+ if err != nil {
+ event.Error(ctx, "error adding import for literal candidate", err)
+ return
+ }
+
+ // If prefix matches the type name, client may want a composite literal.
+ if score := c.matcher.Score(matchName); score > 0 {
+ if cand.takeAddress {
+ if sel != nil {
+ // If we are in a selector we must place the "&" before the selector.
+ // For example, "foo.B<>" must complete to "&foo.Bar{}", not
+ // "foo.&Bar{}".
+ edits, err := prependEdit(c.snapshot.FileSet(), c.mapper, sel, "&")
+ if err != nil {
+ event.Error(ctx, "error making edit for literal pointer completion", err)
+ return
+ }
+ addlEdits = append(addlEdits, edits...)
+ } else {
+ // Otherwise we can stick the "&" directly before the type name.
+ typeName = "&" + typeName
+ }
+ }
+
+ switch t := literalType.Underlying().(type) {
+ case *types.Struct, *types.Array, *types.Slice, *types.Map:
+ c.compositeLiteral(t, typeName, float64(score), addlEdits)
+ case *types.Signature:
+ // Add a literal completion for a signature type that implements
+ // an interface. For example, offer "http.HandlerFunc()" when
+ // expected type is "http.Handler".
+ if source.IsInterface(expType) {
+ c.basicLiteral(t, typeName, float64(score), addlEdits)
+ }
+ case *types.Basic:
+ // Add a literal completion for basic types that implement our
+ // expected interface (e.g. named string type http.Dir
+ // implements http.FileSystem), or are identical to our expected
+ // type (i.e. yielding a type conversion such as "float64()").
+ if source.IsInterface(expType) || types.Identical(expType, literalType) {
+ c.basicLiteral(t, typeName, float64(score), addlEdits)
+ }
+ }
+ }
+
+ // If prefix matches "make", client may want a "make()"
+ // invocation. We also include the type name to allow for more
+ // flexible fuzzy matching.
+ if score := c.matcher.Score("make." + matchName); !cand.takeAddress && score > 0 {
+ switch literalType.Underlying().(type) {
+ case *types.Slice:
+ // The second argument to "make()" for slices is required, so default to "0".
+ c.makeCall(typeName, "0", float64(score), addlEdits)
+ case *types.Map, *types.Chan:
+ // Maps and channels don't require the second argument, so omit
+ // to keep things simple for now.
+ c.makeCall(typeName, "", float64(score), addlEdits)
+ }
+ }
+
+ // If prefix matches "func", client may want a function literal.
+ if score := c.matcher.Score("func"); !cand.takeAddress && score > 0 && !source.IsInterface(expType) {
+ switch t := literalType.Underlying().(type) {
+ case *types.Signature:
+ c.functionLiteral(ctx, t, float64(score))
+ }
+ }
+}
+
+// prependEdit produces text edits that preprend the specified prefix
+// to the specified node.
+func prependEdit(fset *token.FileSet, m *protocol.ColumnMapper, node ast.Node, prefix string) ([]protocol.TextEdit, error) {
+ rng := source.NewMappedRange(fset, m, node.Pos(), node.Pos())
+ spn, err := rng.Span()
+ if err != nil {
+ return nil, err
+ }
+ return source.ToProtocolEdits(m, []diff.TextEdit{{
+ Span: spn,
+ NewText: prefix,
+ }})
+}
+
+// literalCandidateScore is the base score for literal candidates.
+// Literal candidates match the expected type so they should be high
+// scoring, but we want them ranked below lexical objects of the
+// correct type, so scale down highScore.
+const literalCandidateScore = highScore / 2
+
+// functionLiteral adds a function literal completion item for the
+// given signature.
+func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, matchScore float64) {
+ snip := &snippet.Builder{}
+ snip.WriteText("func(")
+
+ // First we generate names for each param and keep a seen count so
+ // we know if we need to uniquify param names. For example,
+ // "func(int)" will become "func(i int)", but "func(int, int64)"
+ // will become "func(i1 int, i2 int64)".
+ var (
+ paramNames = make([]string, sig.Params().Len())
+ paramNameCount = make(map[string]int)
+ )
+ for i := 0; i < sig.Params().Len(); i++ {
+ var (
+ p = sig.Params().At(i)
+ name = p.Name()
+ )
+ if name == "" {
+ // If the param has no name in the signature, guess a name based
+ // on the type. Use an empty qualifier to ignore the package.
+ // For example, we want to name "http.Request" "r", not "hr".
+ name = source.FormatVarType(ctx, c.snapshot, c.pkg, p, func(p *types.Package) string {
+ return ""
+ })
+ name = abbreviateTypeName(name)
+ }
+ paramNames[i] = name
+ if name != "_" {
+ paramNameCount[name]++
+ }
+ }
+
+ for n, c := range paramNameCount {
+ // Any names we saw more than once will need a unique suffix added
+ // on. Reset the count to 1 to act as the suffix for the first
+ // name.
+ if c >= 2 {
+ paramNameCount[n] = 1
+ } else {
+ delete(paramNameCount, n)
+ }
+ }
+
+ for i := 0; i < sig.Params().Len(); i++ {
+ if i > 0 {
+ snip.WriteText(", ")
+ }
+
+ var (
+ p = sig.Params().At(i)
+ name = paramNames[i]
+ )
+
+ // Uniquify names by adding on an incrementing numeric suffix.
+ if idx, found := paramNameCount[name]; found {
+ paramNameCount[name]++
+ name = fmt.Sprintf("%s%d", name, idx)
+ }
+
+ if name != p.Name() && c.opts.placeholders {
+ // If we didn't use the signature's param name verbatim then we
+ // may have chosen a poor name. Give the user a placeholder so
+ // they can easily fix the name.
+ snip.WritePlaceholder(func(b *snippet.Builder) {
+ b.WriteText(name)
+ })
+ } else {
+ snip.WriteText(name)
+ }
+
+ // If the following param's type is identical to this one, omit
+ // this param's type string. For example, emit "i, j int" instead
+ // of "i int, j int".
+ if i == sig.Params().Len()-1 || !types.Identical(p.Type(), sig.Params().At(i+1).Type()) {
+ snip.WriteText(" ")
+ typeStr := source.FormatVarType(ctx, c.snapshot, c.pkg, p, c.qf)
+ if sig.Variadic() && i == sig.Params().Len()-1 {
+ typeStr = strings.Replace(typeStr, "[]", "...", 1)
+ }
+ snip.WriteText(typeStr)
+ }
+ }
+ snip.WriteText(")")
+
+ results := sig.Results()
+ if results.Len() > 0 {
+ snip.WriteText(" ")
+ }
+
+ resultsNeedParens := results.Len() > 1 ||
+ results.Len() == 1 && results.At(0).Name() != ""
+
+ if resultsNeedParens {
+ snip.WriteText("(")
+ }
+ for i := 0; i < results.Len(); i++ {
+ if i > 0 {
+ snip.WriteText(", ")
+ }
+ r := results.At(i)
+ if name := r.Name(); name != "" {
+ snip.WriteText(name + " ")
+ }
+ snip.WriteText(source.FormatVarType(ctx, c.snapshot, c.pkg, r, c.qf))
+ }
+ if resultsNeedParens {
+ snip.WriteText(")")
+ }
+
+ snip.WriteText(" {")
+ snip.WriteFinalTabstop()
+ snip.WriteText("}")
+
+ c.items = append(c.items, CompletionItem{
+ Label: "func(...) {}",
+ Score: matchScore * literalCandidateScore,
+ Kind: protocol.VariableCompletion,
+ snippet: snip,
+ })
+}
+
+// abbreviateTypeName abbreviates type names into acronyms. For
+// example, "fooBar" is abbreviated "fb". Care is taken to ignore
+// non-identifier runes. For example, "[]int" becomes "i", and
+// "struct { i int }" becomes "s".
+func abbreviateTypeName(s string) string {
+ var (
+ b strings.Builder
+ useNextUpper bool
+ )
+
+ // Trim off leading non-letters. We trim everything between "[" and
+ // "]" to handle array types like "[someConst]int".
+ var inBracket bool
+ s = strings.TrimFunc(s, func(r rune) bool {
+ if inBracket {
+ inBracket = r != ']'
+ return true
+ }
+
+ if r == '[' {
+ inBracket = true
+ }
+
+ return !unicode.IsLetter(r)
+ })
+
+ for i, r := range s {
+ // Stop if we encounter a non-identifier rune.
+ if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
+ break
+ }
+
+ if i == 0 {
+ b.WriteRune(unicode.ToLower(r))
+ }
+
+ if unicode.IsUpper(r) {
+ if useNextUpper {
+ b.WriteRune(unicode.ToLower(r))
+ useNextUpper = false
+ }
+ } else {
+ useNextUpper = true
+ }
+ }
+
+ return b.String()
+}
+
+// compositeLiteral adds a composite literal completion item for the given typeName.
+func (c *completer) compositeLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) {
+ snip := &snippet.Builder{}
+ snip.WriteText(typeName + "{")
+ // Don't put the tab stop inside the composite literal curlies "{}"
+ // for structs that have no accessible fields.
+ if strct, ok := T.(*types.Struct); !ok || fieldsAccessible(strct, c.pkg.GetTypes()) {
+ snip.WriteFinalTabstop()
+ }
+ snip.WriteText("}")
+
+ nonSnippet := typeName + "{}"
+
+ c.items = append(c.items, CompletionItem{
+ Label: nonSnippet,
+ InsertText: nonSnippet,
+ Score: matchScore * literalCandidateScore,
+ Kind: protocol.VariableCompletion,
+ AdditionalTextEdits: edits,
+ snippet: snip,
+ })
+}
+
+// basicLiteral adds a literal completion item for the given basic
+// type name typeName.
+func (c *completer) basicLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) {
+ snip := &snippet.Builder{}
+ snip.WriteText(typeName + "(")
+ snip.WriteFinalTabstop()
+ snip.WriteText(")")
+
+ nonSnippet := typeName + "()"
+
+ c.items = append(c.items, CompletionItem{
+ Label: nonSnippet,
+ InsertText: nonSnippet,
+ Detail: T.String(),
+ Score: matchScore * literalCandidateScore,
+ Kind: protocol.VariableCompletion,
+ AdditionalTextEdits: edits,
+ snippet: snip,
+ })
+}
+
+// makeCall adds a completion item for a "make()" call given a specific type.
+func (c *completer) makeCall(typeName string, secondArg string, matchScore float64, edits []protocol.TextEdit) {
+ // Keep it simple and don't add any placeholders for optional "make()" arguments.
+
+ snip := &snippet.Builder{}
+ snip.WriteText("make(" + typeName)
+ if secondArg != "" {
+ snip.WriteText(", ")
+ snip.WritePlaceholder(func(b *snippet.Builder) {
+ if c.opts.placeholders {
+ b.WriteText(secondArg)
+ }
+ })
+ }
+ snip.WriteText(")")
+
+ var nonSnippet strings.Builder
+ nonSnippet.WriteString("make(" + typeName)
+ if secondArg != "" {
+ nonSnippet.WriteString(", ")
+ nonSnippet.WriteString(secondArg)
+ }
+ nonSnippet.WriteByte(')')
+
+ c.items = append(c.items, CompletionItem{
+ Label: nonSnippet.String(),
+ InsertText: nonSnippet.String(),
+ Score: matchScore * literalCandidateScore,
+ Kind: protocol.FunctionCompletion,
+ AdditionalTextEdits: edits,
+ snippet: snip,
+ })
+}