Skip to content

Commit 03d333a

Browse files
committed
internal/lsp: add snippet completion for function type parameters
Fixes golang/go#51544 Change-Id: I29cf2a0fe878eed263b406ff3ebf1eaeffe10e4b Reviewed-on: https://go-review.googlesource.com/c/tools/+/390854 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Peter Weinberger <pjw@google.com>
1 parent 94322c4 commit 03d333a

File tree

5 files changed

+66
-13
lines changed

5 files changed

+66
-13
lines changed

internal/lsp/source/completion/format.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ Suffixes:
130130
case invoke:
131131
if sig, ok := funcType.Underlying().(*types.Signature); ok {
132132
s := source.NewSignature(ctx, c.snapshot, c.pkg, sig, nil, c.qf)
133-
c.functionCallSnippet("", s.Params(), &snip)
133+
c.functionCallSnippet("", s.TypeParams(), s.Params(), &snip)
134134
if sig.Results().Len() == 1 {
135135
funcType = sig.Results().At(0).Type()
136136
}
@@ -307,7 +307,7 @@ func (c *completer) formatBuiltin(ctx context.Context, cand candidate) (Completi
307307
}
308308
item.Detail = "func" + sig.Format()
309309
item.snippet = &snippet.Builder{}
310-
c.functionCallSnippet(obj.Name(), sig.Params(), item.snippet)
310+
c.functionCallSnippet(obj.Name(), sig.TypeParams(), sig.Params(), item.snippet)
311311
case *types.TypeName:
312312
if types.IsInterface(obj.Type()) {
313313
item.Kind = protocol.InterfaceCompletion

internal/lsp/source/completion/snippet.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (c *completer) structFieldSnippet(cand candidate, detail string, snip *snip
4949
}
5050

5151
// functionCallSnippets calculates the snippet for function calls.
52-
func (c *completer) functionCallSnippet(name string, params []string, snip *snippet.Builder) {
52+
func (c *completer) functionCallSnippet(name string, tparams, params []string, snip *snippet.Builder) {
5353
// If there is no suffix then we need to reuse existing call parens
5454
// "()" if present. If there is an identifier suffix then we always
5555
// need to include "()" since we don't overwrite the suffix.
@@ -73,7 +73,26 @@ func (c *completer) functionCallSnippet(name string, params []string, snip *snip
7373
}
7474
}
7575

76-
snip.WriteText(name + "(")
76+
snip.WriteText(name)
77+
78+
if len(tparams) > 0 {
79+
snip.WriteText("[")
80+
if c.opts.placeholders {
81+
for i, tp := range tparams {
82+
if i > 0 {
83+
snip.WriteText(", ")
84+
}
85+
snip.WritePlaceholder(func(b *snippet.Builder) {
86+
b.WriteText(tp)
87+
})
88+
}
89+
} else {
90+
snip.WritePlaceholder(nil)
91+
}
92+
snip.WriteText("]")
93+
}
94+
95+
snip.WriteText("(")
7796

7897
if c.opts.placeholders {
7998
// A placeholder snippet turns "someFun<>" into "someFunc(<*i int*>, *s string*)".

internal/lsp/source/types_format.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ func FormatType(typ types.Type, qf types.Qualifier) (detail string, kind protoco
3939
}
4040

4141
type signature struct {
42-
name, doc string
43-
params, results []string
44-
variadic bool
45-
needResultParens bool
42+
name, doc string
43+
typeParams, params, results []string
44+
variadic bool
45+
needResultParens bool
4646
}
4747

4848
func (s *signature) Format() string {
@@ -75,6 +75,10 @@ func (s *signature) Format() string {
7575
return b.String()
7676
}
7777

78+
func (s *signature) TypeParams() []string {
79+
return s.typeParams
80+
}
81+
7882
func (s *signature) Params() []string {
7983
return s.params
8084
}
@@ -171,24 +175,33 @@ func formatFieldList(ctx context.Context, snapshot Snapshot, list *ast.FieldList
171175
// FormatTypeParams turns TypeParamList into its Go representation, such as:
172176
// [T, Y]. Note that it does not print constraints as this is mainly used for
173177
// formatting type params in method receivers.
174-
func FormatTypeParams(tp *typeparams.TypeParamList) string {
175-
if tp == nil || tp.Len() == 0 {
178+
func FormatTypeParams(tparams *typeparams.TypeParamList) string {
179+
if tparams == nil || tparams.Len() == 0 {
176180
return ""
177181
}
178182
var buf bytes.Buffer
179183
buf.WriteByte('[')
180-
for i := 0; i < tp.Len(); i++ {
184+
for i := 0; i < tparams.Len(); i++ {
181185
if i > 0 {
182186
buf.WriteString(", ")
183187
}
184-
buf.WriteString(tp.At(i).Obj().Name())
188+
buf.WriteString(tparams.At(i).Obj().Name())
185189
}
186190
buf.WriteByte(']')
187191
return buf.String()
188192
}
189193

190194
// NewSignature returns formatted signature for a types.Signature struct.
191195
func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier) *signature {
196+
var tparams []string
197+
tpList := typeparams.ForSignature(sig)
198+
for i := 0; i < tpList.Len(); i++ {
199+
tparam := tpList.At(i)
200+
// TODO: is it possible to reuse the logic from FormatVarType here?
201+
s := tparam.Obj().Name() + " " + tparam.Constraint().String()
202+
tparams = append(tparams, s)
203+
}
204+
192205
params := make([]string, 0, sig.Params().Len())
193206
for i := 0; i < sig.Params().Len(); i++ {
194207
el := sig.Params().At(i)
@@ -199,6 +212,7 @@ func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signa
199212
}
200213
params = append(params, p)
201214
}
215+
202216
var needResultParens bool
203217
results := make([]string, 0, sig.Results().Len())
204218
for i := 0; i < sig.Results().Len(); i++ {
@@ -228,6 +242,7 @@ func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signa
228242
}
229243
return &signature{
230244
doc: d,
245+
typeParams: tparams,
231246
params: params,
232247
results: results,
233248
variadic: sig.Variadic(),
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// +build go1.18
2+
//go:build go1.18
3+
4+
package snippets
5+
6+
type SyncMap[K comparable, V any] struct{}
7+
8+
func NewSyncMap[K comparable, V any]() (result *SyncMap[K, V]) { //@item(NewSyncMap, "NewSyncMap", "", "")
9+
return
10+
}
11+
12+
func Identity[P ~int](p P) P { //@item(Identity, "Identity", "", "")
13+
return p
14+
}
15+
16+
func _() {
17+
_ = NewSyncM //@snippet(" //", NewSyncMap, "NewSyncMap[${1:}]()", "NewSyncMap[${1:K comparable}, ${2:V any}]()")
18+
_ = Identi //@snippet(" //", Identity, "Identity[${1:}](${2:})", "Identity[${1:P ~int}](${2:p P})")
19+
}

internal/lsp/testdata/summary_go1.18.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
CallHierarchyCount = 2
33
CodeLensCount = 5
44
CompletionsCount = 266
5-
CompletionSnippetCount = 107
5+
CompletionSnippetCount = 109
66
UnimportedCompletionsCount = 5
77
DeepCompletionsCount = 5
88
FuzzyCompletionsCount = 8

0 commit comments

Comments
 (0)