Skip to content

Commit 08a6323

Browse files
committed
gopls/completion: unimported completions with explicit package names
If a user types foo., and foo is not a package already imported in the current file, the unimported completion code searches for the package foo in a number of places. If it fails to find it, the new code looks in the current module for an import like import foo "package-path-for-bar" and tries to satisfy the completion from package bar. If it can, it will also add a suitable import statement. Related: golang/go#40278 golang/go#74918 Change-Id: I5345a7d1a23fa240c468b9b21b2e9bcddf27c770 Reviewed-on: https://go-review.googlesource.com/c/tools/+/698578 Reviewed-by: Madeline Kalil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 14e7f8a commit 08a6323

File tree

3 files changed

+76
-5
lines changed

3 files changed

+76
-5
lines changed

gopls/internal/golang/completion/unimported.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ package completion
1818
// 1. the imports of some other file of the current package,
1919
// 2. the standard library,
2020
// 3. the imports of some other file in the current workspace,
21-
// 4. the module cache.
21+
// 4. imports in the current module with 'foo' as the explicit package name,
22+
// 5. the module cache,
2223
// It stops at the first success.
2324

2425
import (
@@ -63,11 +64,47 @@ func (c *completer) unimported(ctx context.Context, pkgname metadata.PackageName
6364
return
6465
}
6566

66-
// look in the module cache, for the last chance
67+
// before looking in the module cache, maybe it is an explicit
68+
// package name used in this module
69+
if c.explicitPkgName(ctx, pkgname, prefix) {
70+
return
71+
}
72+
73+
// look in the module cache
6774
items, err := c.modcacheMatches(pkgname, prefix)
68-
if err == nil {
69-
c.scoreList(items)
75+
if err == nil && c.scoreList(items) {
76+
return
77+
}
78+
79+
// out of things to do
80+
}
81+
82+
// see if some file in the current package satisfied a foo. import
83+
// because foo is an explicit package name (import foo "a.b.c")
84+
func (c *completer) explicitPkgName(ctx context.Context, pkgname metadata.PackageName, prefix string) bool {
85+
for _, pgf := range c.pkg.CompiledGoFiles() {
86+
imports := pgf.File.Imports
87+
for _, imp := range imports {
88+
if imp.Name != nil && imp.Name.Name == string(pkgname) {
89+
path := strings.Trim(imp.Path.Value, `"`)
90+
if c.tryPath(ctx, metadata.PackagePath(path), string(pkgname), prefix) {
91+
return true // one is enough
92+
}
93+
}
94+
}
95+
}
96+
return false
97+
}
98+
99+
// see if this path contains a usable import with explict package name
100+
func (c *completer) tryPath(ctx context.Context, path metadata.PackagePath, pkgname, prefix string) bool {
101+
packages := c.snapshot.MetadataGraph().ForPackagePath
102+
ids := []metadata.PackageID{}
103+
for _, pkg := range packages[path] { // could there ever be more than one?
104+
ids = append(ids, pkg.ID) // pkg.ID. ID: "math/rand" but Name: "rand"
70105
}
106+
items := c.pkgIDmatches(ctx, ids, metadata.PackageName(pkgname), prefix)
107+
return c.scoreList(items)
71108
}
72109

73110
// find all the packageIDs for packages in the workspace that have the desired name

gopls/internal/test/integration/completion/completion_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,3 +1461,37 @@ func main() {
14611461
}
14621462
})
14631463
}
1464+
1465+
// find import foo "bar" for foo.xxx
1466+
func TestImportAlias(t *testing.T) {
1467+
testenv.NeedsGoCommand1Point(t, 22) // we will find math/rand/v2
1468+
const files = `
1469+
-- a.go --
1470+
package x
1471+
var _ = xrand.
1472+
-- b.go --
1473+
package x
1474+
1475+
import xrand "math/rand"
1476+
1477+
var _ = xrand.Int()
1478+
1479+
-- go.mod --
1480+
module foo.com
1481+
1482+
go 1.24.2
1483+
`
1484+
Run(t, files, func(t *testing.T, env *Env) {
1485+
env.OpenFile("a.go")
1486+
env.Await(env.DoneWithOpen())
1487+
loc := env.RegexpSearch("a.go", "xrand.()")
1488+
compls := env.Completion(loc)
1489+
if len(compls.Items) == 0 {
1490+
t.Fatal("no completions")
1491+
}
1492+
one := compls.Items[0].AdditionalTextEdits[0].NewText
1493+
if one != "\nimport xrand \"math/rand\"\n" {
1494+
t.Errorf("wrong import %q", one)
1495+
}
1496+
})
1497+
}

gopls/internal/test/integration/completion/fixedbugs_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ module example.com
2121
2222
go 1.18
2323
-- playdos/play.go --
24-
package
24+
package // comment (to preserve spaces)
2525
`
2626

2727
Run(t, files, func(t *testing.T, env *Env) {

0 commit comments

Comments
 (0)