github.com/v2fly/tools@v0.100.0/internal/lsp/source/completion/literal.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package completion
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"go/types"
    11  	"strings"
    12  	"unicode"
    13  
    14  	"github.com/v2fly/tools/internal/event"
    15  	"github.com/v2fly/tools/internal/lsp/protocol"
    16  	"github.com/v2fly/tools/internal/lsp/snippet"
    17  	"github.com/v2fly/tools/internal/lsp/source"
    18  )
    19  
    20  // literal generates composite literal, function literal, and make()
    21  // completion items.
    22  func (c *completer) literal(ctx context.Context, literalType types.Type, imp *importInfo) {
    23  	if !c.opts.literal {
    24  		return
    25  	}
    26  
    27  	expType := c.inference.objType
    28  
    29  	if c.inference.matchesVariadic(literalType) {
    30  		// Don't offer literal slice candidates for variadic arguments.
    31  		// For example, don't offer "[]interface{}{}" in "fmt.Print(<>)".
    32  		return
    33  	}
    34  
    35  	// Avoid literal candidates if the expected type is an empty
    36  	// interface. It isn't very useful to suggest a literal candidate of
    37  	// every possible type.
    38  	if expType != nil && isEmptyInterface(expType) {
    39  		return
    40  	}
    41  
    42  	// We handle unnamed literal completions explicitly before searching
    43  	// for candidates. Avoid named-type literal completions for
    44  	// unnamed-type expected type since that results in duplicate
    45  	// candidates. For example, in
    46  	//
    47  	// type mySlice []int
    48  	// var []int = <>
    49  	//
    50  	// don't offer "mySlice{}" since we have already added a candidate
    51  	// of "[]int{}".
    52  	if _, named := literalType.(*types.Named); named && expType != nil {
    53  		if _, named := source.Deref(expType).(*types.Named); !named {
    54  			return
    55  		}
    56  	}
    57  
    58  	// Check if an object of type literalType would match our expected type.
    59  	cand := candidate{
    60  		obj: c.fakeObj(literalType),
    61  	}
    62  
    63  	switch literalType.Underlying().(type) {
    64  	// These literal types are addressable (e.g. "&[]int{}"), others are
    65  	// not (e.g. can't do "&(func(){})").
    66  	case *types.Struct, *types.Array, *types.Slice, *types.Map:
    67  		cand.addressable = true
    68  	}
    69  
    70  	if !c.matchingCandidate(&cand) {
    71  		return
    72  	}
    73  
    74  	var (
    75  		qf  = c.qf
    76  		sel = enclosingSelector(c.path, c.pos)
    77  	)
    78  
    79  	// Don't qualify the type name if we are in a selector expression
    80  	// since the package name is already present.
    81  	if sel != nil {
    82  		qf = func(_ *types.Package) string { return "" }
    83  	}
    84  
    85  	typeName := types.TypeString(literalType, qf)
    86  
    87  	// A type name of "[]int" doesn't work very will with the matcher
    88  	// since "[" isn't a valid identifier prefix. Here we strip off the
    89  	// slice (and array) prefix yielding just "int".
    90  	matchName := typeName
    91  	switch t := literalType.(type) {
    92  	case *types.Slice:
    93  		matchName = types.TypeString(t.Elem(), qf)
    94  	case *types.Array:
    95  		matchName = types.TypeString(t.Elem(), qf)
    96  	}
    97  
    98  	addlEdits, err := c.importEdits(imp)
    99  	if err != nil {
   100  		event.Error(ctx, "error adding import for literal candidate", err)
   101  		return
   102  	}
   103  
   104  	// If prefix matches the type name, client may want a composite literal.
   105  	if score := c.matcher.Score(matchName); score > 0 {
   106  		if cand.takeAddress {
   107  			if sel != nil {
   108  				// If we are in a selector we must place the "&" before the selector.
   109  				// For example, "foo.B<>" must complete to "&foo.Bar{}", not
   110  				// "foo.&Bar{}".
   111  				edits, err := c.editText(sel.Pos(), sel.Pos(), "&")
   112  				if err != nil {
   113  					event.Error(ctx, "error making edit for literal pointer completion", err)
   114  					return
   115  				}
   116  				addlEdits = append(addlEdits, edits...)
   117  			} else {
   118  				// Otherwise we can stick the "&" directly before the type name.
   119  				typeName = "&" + typeName
   120  			}
   121  		}
   122  
   123  		switch t := literalType.Underlying().(type) {
   124  		case *types.Struct, *types.Array, *types.Slice, *types.Map:
   125  			c.compositeLiteral(t, typeName, float64(score), addlEdits)
   126  		case *types.Signature:
   127  			// Add a literal completion for a signature type that implements
   128  			// an interface. For example, offer "http.HandlerFunc()" when
   129  			// expected type is "http.Handler".
   130  			if source.IsInterface(expType) {
   131  				c.basicLiteral(t, typeName, float64(score), addlEdits)
   132  			}
   133  		case *types.Basic:
   134  			// Add a literal completion for basic types that implement our
   135  			// expected interface (e.g. named string type http.Dir
   136  			// implements http.FileSystem), or are identical to our expected
   137  			// type (i.e. yielding a type conversion such as "float64()").
   138  			if source.IsInterface(expType) || types.Identical(expType, literalType) {
   139  				c.basicLiteral(t, typeName, float64(score), addlEdits)
   140  			}
   141  		}
   142  	}
   143  
   144  	// If prefix matches "make", client may want a "make()"
   145  	// invocation. We also include the type name to allow for more
   146  	// flexible fuzzy matching.
   147  	if score := c.matcher.Score("make." + matchName); !cand.takeAddress && score > 0 {
   148  		switch literalType.Underlying().(type) {
   149  		case *types.Slice:
   150  			// The second argument to "make()" for slices is required, so default to "0".
   151  			c.makeCall(typeName, "0", float64(score), addlEdits)
   152  		case *types.Map, *types.Chan:
   153  			// Maps and channels don't require the second argument, so omit
   154  			// to keep things simple for now.
   155  			c.makeCall(typeName, "", float64(score), addlEdits)
   156  		}
   157  	}
   158  
   159  	// If prefix matches "func", client may want a function literal.
   160  	if score := c.matcher.Score("func"); !cand.takeAddress && score > 0 && !source.IsInterface(expType) {
   161  		switch t := literalType.Underlying().(type) {
   162  		case *types.Signature:
   163  			c.functionLiteral(ctx, t, float64(score))
   164  		}
   165  	}
   166  }
   167  
   168  // literalCandidateScore is the base score for literal candidates.
   169  // Literal candidates match the expected type so they should be high
   170  // scoring, but we want them ranked below lexical objects of the
   171  // correct type, so scale down highScore.
   172  const literalCandidateScore = highScore / 2
   173  
   174  // functionLiteral adds a function literal completion item for the
   175  // given signature.
   176  func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, matchScore float64) {
   177  	snip := &snippet.Builder{}
   178  	snip.WriteText("func(")
   179  
   180  	// First we generate names for each param and keep a seen count so
   181  	// we know if we need to uniquify param names. For example,
   182  	// "func(int)" will become "func(i int)", but "func(int, int64)"
   183  	// will become "func(i1 int, i2 int64)".
   184  	var (
   185  		paramNames     = make([]string, sig.Params().Len())
   186  		paramNameCount = make(map[string]int)
   187  	)
   188  	for i := 0; i < sig.Params().Len(); i++ {
   189  		var (
   190  			p    = sig.Params().At(i)
   191  			name = p.Name()
   192  		)
   193  		if name == "" {
   194  			// If the param has no name in the signature, guess a name based
   195  			// on the type. Use an empty qualifier to ignore the package.
   196  			// For example, we want to name "http.Request" "r", not "hr".
   197  			name = source.FormatVarType(ctx, c.snapshot, c.pkg, p, func(p *types.Package) string {
   198  				return ""
   199  			})
   200  			name = abbreviateTypeName(name)
   201  		}
   202  		paramNames[i] = name
   203  		if name != "_" {
   204  			paramNameCount[name]++
   205  		}
   206  	}
   207  
   208  	for n, c := range paramNameCount {
   209  		// Any names we saw more than once will need a unique suffix added
   210  		// on. Reset the count to 1 to act as the suffix for the first
   211  		// name.
   212  		if c >= 2 {
   213  			paramNameCount[n] = 1
   214  		} else {
   215  			delete(paramNameCount, n)
   216  		}
   217  	}
   218  
   219  	for i := 0; i < sig.Params().Len(); i++ {
   220  		if i > 0 {
   221  			snip.WriteText(", ")
   222  		}
   223  
   224  		var (
   225  			p    = sig.Params().At(i)
   226  			name = paramNames[i]
   227  		)
   228  
   229  		// Uniquify names by adding on an incrementing numeric suffix.
   230  		if idx, found := paramNameCount[name]; found {
   231  			paramNameCount[name]++
   232  			name = fmt.Sprintf("%s%d", name, idx)
   233  		}
   234  
   235  		if name != p.Name() && c.opts.placeholders {
   236  			// If we didn't use the signature's param name verbatim then we
   237  			// may have chosen a poor name. Give the user a placeholder so
   238  			// they can easily fix the name.
   239  			snip.WritePlaceholder(func(b *snippet.Builder) {
   240  				b.WriteText(name)
   241  			})
   242  		} else {
   243  			snip.WriteText(name)
   244  		}
   245  
   246  		// If the following param's type is identical to this one, omit
   247  		// this param's type string. For example, emit "i, j int" instead
   248  		// of "i int, j int".
   249  		if i == sig.Params().Len()-1 || !types.Identical(p.Type(), sig.Params().At(i+1).Type()) {
   250  			snip.WriteText(" ")
   251  			typeStr := source.FormatVarType(ctx, c.snapshot, c.pkg, p, c.qf)
   252  			if sig.Variadic() && i == sig.Params().Len()-1 {
   253  				typeStr = strings.Replace(typeStr, "[]", "...", 1)
   254  			}
   255  			snip.WriteText(typeStr)
   256  		}
   257  	}
   258  	snip.WriteText(")")
   259  
   260  	results := sig.Results()
   261  	if results.Len() > 0 {
   262  		snip.WriteText(" ")
   263  	}
   264  
   265  	resultsNeedParens := results.Len() > 1 ||
   266  		results.Len() == 1 && results.At(0).Name() != ""
   267  
   268  	if resultsNeedParens {
   269  		snip.WriteText("(")
   270  	}
   271  	for i := 0; i < results.Len(); i++ {
   272  		if i > 0 {
   273  			snip.WriteText(", ")
   274  		}
   275  		r := results.At(i)
   276  		if name := r.Name(); name != "" {
   277  			snip.WriteText(name + " ")
   278  		}
   279  		snip.WriteText(source.FormatVarType(ctx, c.snapshot, c.pkg, r, c.qf))
   280  	}
   281  	if resultsNeedParens {
   282  		snip.WriteText(")")
   283  	}
   284  
   285  	snip.WriteText(" {")
   286  	snip.WriteFinalTabstop()
   287  	snip.WriteText("}")
   288  
   289  	c.items = append(c.items, CompletionItem{
   290  		Label:   "func(...) {}",
   291  		Score:   matchScore * literalCandidateScore,
   292  		Kind:    protocol.VariableCompletion,
   293  		snippet: snip,
   294  	})
   295  }
   296  
   297  // abbreviateTypeName abbreviates type names into acronyms. For
   298  // example, "fooBar" is abbreviated "fb". Care is taken to ignore
   299  // non-identifier runes. For example, "[]int" becomes "i", and
   300  // "struct { i int }" becomes "s".
   301  func abbreviateTypeName(s string) string {
   302  	var (
   303  		b            strings.Builder
   304  		useNextUpper bool
   305  	)
   306  
   307  	// Trim off leading non-letters. We trim everything between "[" and
   308  	// "]" to handle array types like "[someConst]int".
   309  	var inBracket bool
   310  	s = strings.TrimFunc(s, func(r rune) bool {
   311  		if inBracket {
   312  			inBracket = r != ']'
   313  			return true
   314  		}
   315  
   316  		if r == '[' {
   317  			inBracket = true
   318  		}
   319  
   320  		return !unicode.IsLetter(r)
   321  	})
   322  
   323  	for i, r := range s {
   324  		// Stop if we encounter a non-identifier rune.
   325  		if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
   326  			break
   327  		}
   328  
   329  		if i == 0 {
   330  			b.WriteRune(unicode.ToLower(r))
   331  		}
   332  
   333  		if unicode.IsUpper(r) {
   334  			if useNextUpper {
   335  				b.WriteRune(unicode.ToLower(r))
   336  				useNextUpper = false
   337  			}
   338  		} else {
   339  			useNextUpper = true
   340  		}
   341  	}
   342  
   343  	return b.String()
   344  }
   345  
   346  // compositeLiteral adds a composite literal completion item for the given typeName.
   347  func (c *completer) compositeLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) {
   348  	snip := &snippet.Builder{}
   349  	snip.WriteText(typeName + "{")
   350  	// Don't put the tab stop inside the composite literal curlies "{}"
   351  	// for structs that have no accessible fields.
   352  	if strct, ok := T.(*types.Struct); !ok || fieldsAccessible(strct, c.pkg.GetTypes()) {
   353  		snip.WriteFinalTabstop()
   354  	}
   355  	snip.WriteText("}")
   356  
   357  	nonSnippet := typeName + "{}"
   358  
   359  	c.items = append(c.items, CompletionItem{
   360  		Label:               nonSnippet,
   361  		InsertText:          nonSnippet,
   362  		Score:               matchScore * literalCandidateScore,
   363  		Kind:                protocol.VariableCompletion,
   364  		AdditionalTextEdits: edits,
   365  		snippet:             snip,
   366  	})
   367  }
   368  
   369  // basicLiteral adds a literal completion item for the given basic
   370  // type name typeName.
   371  func (c *completer) basicLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) {
   372  	snip := &snippet.Builder{}
   373  	snip.WriteText(typeName + "(")
   374  	snip.WriteFinalTabstop()
   375  	snip.WriteText(")")
   376  
   377  	nonSnippet := typeName + "()"
   378  
   379  	c.items = append(c.items, CompletionItem{
   380  		Label:               nonSnippet,
   381  		InsertText:          nonSnippet,
   382  		Detail:              T.String(),
   383  		Score:               matchScore * literalCandidateScore,
   384  		Kind:                protocol.VariableCompletion,
   385  		AdditionalTextEdits: edits,
   386  		snippet:             snip,
   387  	})
   388  }
   389  
   390  // makeCall adds a completion item for a "make()" call given a specific type.
   391  func (c *completer) makeCall(typeName string, secondArg string, matchScore float64, edits []protocol.TextEdit) {
   392  	// Keep it simple and don't add any placeholders for optional "make()" arguments.
   393  
   394  	snip := &snippet.Builder{}
   395  	snip.WriteText("make(" + typeName)
   396  	if secondArg != "" {
   397  		snip.WriteText(", ")
   398  		snip.WritePlaceholder(func(b *snippet.Builder) {
   399  			if c.opts.placeholders {
   400  				b.WriteText(secondArg)
   401  			}
   402  		})
   403  	}
   404  	snip.WriteText(")")
   405  
   406  	var nonSnippet strings.Builder
   407  	nonSnippet.WriteString("make(" + typeName)
   408  	if secondArg != "" {
   409  		nonSnippet.WriteString(", ")
   410  		nonSnippet.WriteString(secondArg)
   411  	}
   412  	nonSnippet.WriteByte(')')
   413  
   414  	c.items = append(c.items, CompletionItem{
   415  		Label:               nonSnippet.String(),
   416  		InsertText:          nonSnippet.String(),
   417  		Score:               matchScore * literalCandidateScore,
   418  		Kind:                protocol.FunctionCompletion,
   419  		AdditionalTextEdits: edits,
   420  		snippet:             snip,
   421  	})
   422  }