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