| // 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 ( |
| "go/types" |
| "strings" |
| |
| "golang.org/x/tools/internal/lsp/snippet" |
| ) |
| |
| // literal generates composite literal and make() completion items. |
| func (c *completer) literal(literalType types.Type) { |
| if c.expectedType.objType == nil { |
| return |
| } |
| |
| // Don't provide literal candidates for variadic function arguments. |
| // For example, don't provide "[]interface{}{}" in "fmt.Print(<>)". |
| if c.expectedType.variadic { |
| 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 isEmptyInterface(c.expectedType.objType) { |
| 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 { |
| if _, named := deref(c.expectedType.objType).(*types.Named); !named { |
| return |
| } |
| } |
| |
| // Check if an object of type literalType or *literalType would |
| // match our expected type. |
| if !c.matchingType(literalType) { |
| literalType = types.NewPointer(literalType) |
| |
| if !c.matchingType(literalType) { |
| return |
| } |
| } |
| |
| ptr, isPointer := literalType.(*types.Pointer) |
| if isPointer { |
| literalType = ptr.Elem() |
| } |
| |
| typeName := types.TypeString(literalType, c.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(), c.qf) |
| case *types.Array: |
| matchName = types.TypeString(t.Elem(), c.qf) |
| } |
| |
| // If prefix matches the type name, client may want a composite literal. |
| if score := c.matcher.Score(matchName); score >= 0 { |
| if isPointer { |
| typeName = "&" + typeName |
| } |
| |
| switch t := literalType.Underlying().(type) { |
| case *types.Struct, *types.Array, *types.Slice, *types.Map: |
| c.compositeLiteral(t, typeName, float64(score)) |
| } |
| } |
| |
| // 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); !isPointer && 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)) |
| 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)) |
| } |
| } |
| } |
| |
| // 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 |
| |
| // compositeLiteral adds a composite literal completion item for the given typeName. |
| func (c *completer) compositeLiteral(T types.Type, typeName string, matchScore float64) { |
| snip := &snippet.Builder{} |
| snip.WriteText(typeName + "{") |
| // Don't put the tab stop inside the composite literal curlies "{}" |
| // for structs that have no fields. |
| if strct, ok := T.(*types.Struct); !ok || strct.NumFields() > 0 { |
| snip.WriteFinalTabstop() |
| } |
| snip.WriteText("}") |
| |
| nonSnippet := typeName + "{}" |
| |
| c.items = append(c.items, CompletionItem{ |
| Label: nonSnippet, |
| InsertText: nonSnippet, |
| Score: matchScore * literalCandidateScore, |
| Kind: VariableCompletionItem, |
| 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) { |
| // 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: FunctionCompletionItem, |
| snippet: snip, |
| }) |
| } |