Skip to content

Commit edbfdbe

Browse files
committed
gopls/internal/lsp/source/completion: (unimported) add placeholders
The logic added in CL 472183 to provide completions of functions from unimported packages, which lack type information, didn't bother with placeholders. This change causes it to generate placeholders ("name type") from the raw syntax. Fixes golang/go#60269 Change-Id: I66340e18de90bdee471a0dfbb1e3fd5c77fec75f Reviewed-on: https://go-review.googlesource.com/c/tools/+/496596 gopls-CI: kokoro <[email protected]> Run-TryBot: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 3a5dbf3 commit edbfdbe

File tree

2 files changed

+68
-9
lines changed

2 files changed

+68
-9
lines changed

gopls/internal/lsp/source/completion/completion.go

+20-8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"go/ast"
1313
"go/constant"
1414
"go/parser"
15+
"go/printer"
1516
"go/scanner"
1617
"go/token"
1718
"go/types"
@@ -1268,19 +1269,30 @@ func (c *completer) selector(ctx context.Context, sel *ast.SelectorExpr) error {
12681269
var sn snippet.Builder
12691270
sn.WriteText(id.Name)
12701271
sn.WriteText("(")
1272+
1273+
var cfg printer.Config // slight overkill
12711274
var nparams int
1275+
param := func(name string, typ ast.Expr) {
1276+
if nparams > 0 {
1277+
sn.WriteText(", ")
1278+
}
1279+
nparams++
1280+
sn.WritePlaceholder(func(b *snippet.Builder) {
1281+
var buf strings.Builder
1282+
buf.WriteString(name)
1283+
buf.WriteByte(' ')
1284+
cfg.Fprint(&buf, token.NewFileSet(), typ)
1285+
b.WriteText(buf.String())
1286+
})
1287+
}
12721288
for _, field := range fn.Type.Params.List {
12731289
if field.Names != nil {
1274-
nparams += len(field.Names)
1290+
for _, name := range field.Names {
1291+
param(name.Name, field.Type)
1292+
}
12751293
} else {
1276-
nparams++
1277-
}
1278-
}
1279-
for i := 0; i < nparams; i++ {
1280-
if i > 0 {
1281-
sn.WriteText(", ")
1294+
param("_", field.Type)
12821295
}
1283-
sn.WritePlaceholder(nil)
12841296
}
12851297
sn.WriteText(")")
12861298
item.snippet = &sn

gopls/internal/regtest/completion/completion_test.go

+48-1
Original file line numberDiff line numberDiff line change
@@ -527,13 +527,60 @@ func main() {
527527
env.AcceptCompletion(loc, completions.Items[0])
528528
env.Await(env.DoneWithChange())
529529
got := env.BufferText("main.go")
530-
want := "package main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"math\"\r\n)\r\n\r\nfunc main() {\r\n\tfmt.Println(\"a\")\r\n\tmath.Sqrt(${1:})\r\n}\r\n"
530+
want := "package main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"math\"\r\n)\r\n\r\nfunc main() {\r\n\tfmt.Println(\"a\")\r\n\tmath.Sqrt(${1:x float64})\r\n}\r\n"
531531
if diff := cmp.Diff(want, got); diff != "" {
532532
t.Errorf("unimported completion (-want +got):\n%s", diff)
533533
}
534534
})
535535
}
536536

537+
func TestUnimportedCompletionHasPlaceholders60269(t *testing.T) {
538+
// We can't express this as a marker test because it doesn't support AcceptCompletion.
539+
const src = `
540+
-- go.mod --
541+
module example.com
542+
go 1.12
543+
544+
-- a/a.go --
545+
package a
546+
547+
var _ = b.F
548+
549+
-- b/b.go --
550+
package b
551+
552+
func F0(a, b int, c float64) {}
553+
func F1(int, chan *string) {}
554+
`
555+
WithOptions(
556+
WindowsLineEndings(),
557+
).Run(t, src, func(t *testing.T, env *Env) {
558+
env.OpenFile("a/a.go")
559+
env.Await(env.DoneWithOpen())
560+
561+
// The table lists the expected completions as they appear in Items.
562+
const common = "package a\r\n\r\nimport \"example.com/b\"\r\n\r\nvar _ = "
563+
for i, want := range []string{
564+
common + "b.F0(${1:a int}, ${2:b int}, ${3:c float64})\r\n",
565+
common + "b.F1(${1:_ int}, ${2:_ chan *string})\r\n",
566+
} {
567+
loc := env.RegexpSearch("a/a.go", "b.F()")
568+
completions := env.Completion(loc)
569+
if len(completions.Items) == 0 {
570+
t.Fatalf("no completion items")
571+
}
572+
saved := env.BufferText("a/a.go")
573+
env.AcceptCompletion(loc, completions.Items[i])
574+
env.Await(env.DoneWithChange())
575+
got := env.BufferText("a/a.go")
576+
if diff := cmp.Diff(want, got); diff != "" {
577+
t.Errorf("%d: unimported completion (-want +got):\n%s", i, diff)
578+
}
579+
env.SetBufferContent("a/a.go", saved) // restore
580+
}
581+
})
582+
}
583+
537584
func TestPackageMemberCompletionAfterSyntaxError(t *testing.T) {
538585
// This test documents the current broken behavior due to golang/go#58833.
539586
const src = `

0 commit comments

Comments
 (0)