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 }