Skip to content

Commit d462c83

Browse files
vikblomfindleyr
authored andcommitted
gopls/internal/lsp: Replace input text when completing a definition
Completing a definition is based on a parsed identifier and should replace the range of that identifier with the completion. Fixes golang/go#56852 Change-Id: I44c751d1db6d55f90113fb918dda344bb7d62f87 Reviewed-on: https://go-review.googlesource.com/c/tools/+/453335 TryBot-Result: Gopher Robot <[email protected]> gopls-CI: kokoro <[email protected]> Reviewed-by: Peter Weinberger <[email protected]> Reviewed-by: Robert Findley <[email protected]> Run-TryBot: Peter Weinberger <[email protected]>
1 parent 7efffe1 commit d462c83

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ func definition(path []ast.Node, obj types.Object, tokFile *token.File, fh sourc
3636
// can't happen
3737
return nil, nil
3838
}
39-
pos := path[0].Pos()
39+
start := path[0].Pos()
40+
end := path[0].End()
4041
sel := &Selection{
4142
content: "",
42-
cursor: pos,
43-
rng: span.NewRange(tokFile, pos, pos),
43+
cursor: start,
44+
rng: span.NewRange(tokFile, start, end),
4445
}
4546
var ans []CompletionItem
4647

gopls/internal/regtest/completion/completion_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"golang.org/x/tools/gopls/internal/hooks"
1313
. "golang.org/x/tools/gopls/internal/lsp/regtest"
1414
"golang.org/x/tools/internal/bug"
15+
"golang.org/x/tools/internal/testenv"
1516

1617
"golang.org/x/tools/gopls/internal/lsp/fake"
1718
"golang.org/x/tools/gopls/internal/lsp/protocol"
@@ -596,6 +597,90 @@ func BenchmarkFoo()
596597
})
597598
}
598599

600+
// Test that completing a definition replaces source text when applied, golang/go#56852.
601+
// Note: With go <= 1.16 the completions does not add parameters and fails these tests.
602+
func TestDefinitionReplaceRange(t *testing.T) {
603+
testenv.NeedsGo1Point(t, 17)
604+
605+
const mod = `
606+
-- go.mod --
607+
module mod.com
608+
609+
go 1.17
610+
`
611+
612+
tests := []struct {
613+
name string
614+
before, after string
615+
}{
616+
{
617+
name: "func TestMa",
618+
before: `
619+
package foo_test
620+
621+
func TestMa
622+
`,
623+
after: `
624+
package foo_test
625+
626+
func TestMain(m *testing.M)
627+
`,
628+
},
629+
{
630+
name: "func TestSome",
631+
before: `
632+
package foo_test
633+
634+
func TestSome
635+
`,
636+
after: `
637+
package foo_test
638+
639+
func TestSome(t *testing.T)
640+
`,
641+
},
642+
{
643+
name: "func Bench",
644+
before: `
645+
package foo_test
646+
647+
func Bench
648+
`,
649+
// Note: Snippet with escaped }.
650+
after: `
651+
package foo_test
652+
653+
func Benchmark${1:Xxx}(b *testing.B) {
654+
$0
655+
\}
656+
`,
657+
},
658+
}
659+
660+
Run(t, mod, func(t *testing.T, env *Env) {
661+
env.CreateBuffer("foo_test.go", "")
662+
663+
for _, tst := range tests {
664+
tst.before = strings.Trim(tst.before, "\n")
665+
tst.after = strings.Trim(tst.after, "\n")
666+
env.SetBufferContent("foo_test.go", tst.before)
667+
668+
pos := env.RegexpSearch("foo_test.go", tst.name)
669+
pos.Column = len(tst.name)
670+
completions := env.Completion("foo_test.go", pos)
671+
if len(completions.Items) == 0 {
672+
t.Fatalf("no completion items")
673+
}
674+
675+
env.AcceptCompletion("foo_test.go", pos, completions.Items[0])
676+
env.Await(env.DoneWithChange())
677+
if buf := env.Editor.BufferText("foo_test.go"); buf != tst.after {
678+
t.Errorf("incorrect completion: got %q, want %q", buf, tst.after)
679+
}
680+
}
681+
})
682+
}
683+
599684
func TestGoWorkCompletion(t *testing.T) {
600685
const files = `
601686
-- go.work --

0 commit comments

Comments
 (0)