diff --git a/internal/lsp/source/completion/literal.go b/internal/lsp/source/completion/literal.go index 03c4401211..139ec17dc0 100644 --- a/internal/lsp/source/completion/literal.go +++ b/internal/lsp/source/completion/literal.go @@ -186,12 +186,18 @@ func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, m var ( paramNames = make([]string, sig.Params().Len()) paramNameCount = make(map[string]int) + hasTypeParams bool ) for i := 0; i < sig.Params().Len(); i++ { var ( p = sig.Params().At(i) name = p.Name() ) + + if tp, _ := p.Type().(*typeparams.TypeParam); tp != nil && !c.typeParamInScope(tp) { + hasTypeParams = true + } + 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. @@ -219,6 +225,14 @@ func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, m } for i := 0; i < sig.Params().Len(); i++ { + if hasTypeParams && !c.opts.placeholders { + // If there are type params in the args then the user must + // choose the concrete types. If placeholders are disabled just + // drop them between the parens and let them fill things in. + snip.WritePlaceholder(nil) + break + } + if i > 0 { snip.WriteText(", ") } @@ -254,7 +268,14 @@ func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, m if sig.Variadic() && i == sig.Params().Len()-1 { typeStr = strings.Replace(typeStr, "[]", "...", 1) } - snip.WriteText(typeStr) + + if tp, _ := p.Type().(*typeparams.TypeParam); tp != nil && !c.typeParamInScope(tp) { + snip.WritePlaceholder(func(snip *snippet.Builder) { + snip.WriteText(typeStr) + }) + } else { + snip.WriteText(typeStr) + } } } snip.WriteText(")") @@ -267,10 +288,24 @@ func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, m resultsNeedParens := results.Len() > 1 || results.Len() == 1 && results.At(0).Name() != "" + var resultHasTypeParams bool + for i := 0; i < results.Len(); i++ { + if tp, _ := results.At(i).Type().(*typeparams.TypeParam); tp != nil && !c.typeParamInScope(tp) { + resultHasTypeParams = true + } + } + if resultsNeedParens { snip.WriteText("(") } for i := 0; i < results.Len(); i++ { + if resultHasTypeParams && !c.opts.placeholders { + // Leave an empty tabstop if placeholders are disabled and there + // are type args that need specificying. + snip.WritePlaceholder(nil) + break + } + if i > 0 { snip.WriteText(", ") } @@ -278,7 +313,15 @@ func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, m if name := r.Name(); name != "" { snip.WriteText(name + " ") } - snip.WriteText(source.FormatVarType(ctx, c.snapshot, c.pkg, r, c.qf)) + + text := source.FormatVarType(ctx, c.snapshot, c.pkg, r, c.qf) + if tp, _ := r.Type().(*typeparams.TypeParam); tp != nil && !c.typeParamInScope(tp) { + snip.WritePlaceholder(func(snip *snippet.Builder) { + snip.WriteText(text) + }) + } else { + snip.WriteText(text) + } } if resultsNeedParens { snip.WriteText(")") diff --git a/internal/lsp/testdata/summary_go1.18.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden index e33bcec9e7..f2f0eaca74 100644 --- a/internal/lsp/testdata/summary_go1.18.txt.golden +++ b/internal/lsp/testdata/summary_go1.18.txt.golden @@ -2,7 +2,7 @@ CallHierarchyCount = 2 CodeLensCount = 5 CompletionsCount = 266 -CompletionSnippetCount = 113 +CompletionSnippetCount = 116 UnimportedCompletionsCount = 5 DeepCompletionsCount = 5 FuzzyCompletionsCount = 8 diff --git a/internal/lsp/testdata/typeparams/type_params.go b/internal/lsp/testdata/typeparams/type_params.go index 48b428ee94..715726b1a4 100644 --- a/internal/lsp/testdata/typeparams/type_params.go +++ b/internal/lsp/testdata/typeparams/type_params.go @@ -48,3 +48,13 @@ func _() { var ab float64 //@item(tpFloat, "ab", "float64", "var") returnTP[int](a) //@rank(")", tpInt, tpFloat) } + +func takesFunc[T any](func(T) T) { + var _ func(t T) T = f //@snippet(" //", tpLitFunc, "func(t T) T {$0\\}", "func(t T) T {$0\\}") +} + +func _() { + _ = "func(...) {}" //@item(tpLitFunc, "func(...) {}", "", "var") + takesFunc() //@snippet(")", tpLitFunc, "func(${1:}) ${2:} {$0\\}", "func(${1:t} ${2:T}) ${3:T} {$0\\}") + takesFunc[int]() //@snippet(")", tpLitFunc, "func(i int) int {$0\\}", "func(${1:i} int) int {$0\\}") +}