зеркало из https://github.com/golang/tools.git
internal/lsp: fix unimported completions with -mod=readonly
When -mod=readonly and GOPROXY=off are set, the newly imported package is not type-checked with the new import until it is reflected in the go.mod file. In such cases, we can continue treating the package as unimported and get symbols through unimported completions. Fixes golang/go#43339 Change-Id: I864c2c6738b537093c0670a266e9030af33f2d36 Reviewed-on: https://go-review.googlesource.com/c/tools/+/280095 Trust: Rebecca Stambler <rstambler@golang.org> Run-TryBot: Rebecca Stambler <rstambler@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> gopls-CI: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Родитель
0661ca7ea1
Коммит
84d76fe320
|
@ -213,3 +213,60 @@ func compareCompletionResults(want []string, gotItems []protocol.CompletionItem)
|
|||
|
||||
return ""
|
||||
}
|
||||
|
||||
func TestUnimportedCompletion(t *testing.T) {
|
||||
testenv.NeedsGo1Point(t, 14)
|
||||
|
||||
const mod = `
|
||||
-- go.mod --
|
||||
module mod.com
|
||||
|
||||
go 1.12
|
||||
-- main.go --
|
||||
package main
|
||||
|
||||
func main() {
|
||||
_ = blah
|
||||
}
|
||||
`
|
||||
withOptions(
|
||||
ProxyFiles(proxy),
|
||||
).run(t, mod, func(t *testing.T, env *Env) {
|
||||
// Explicitly download example.com so it's added to the module cache
|
||||
// and offered as an unimported completion.
|
||||
env.RunGoCommand("get", "example.com@v1.2.3")
|
||||
env.RunGoCommand("mod", "tidy")
|
||||
|
||||
// Trigger unimported completions for the example.com/blah package.
|
||||
env.OpenFile("main.go")
|
||||
pos := env.RegexpSearch("main.go", "ah")
|
||||
completions := env.Completion("main.go", pos)
|
||||
if len(completions.Items) == 0 {
|
||||
t.Fatalf("no completion items")
|
||||
}
|
||||
env.AcceptCompletion("main.go", pos, completions.Items[0])
|
||||
|
||||
// Trigger completions once again for the blah.<> selector.
|
||||
env.RegexpReplace("main.go", "_ = blah", "_ = blah.")
|
||||
env.Await(
|
||||
CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 2),
|
||||
)
|
||||
pos = env.RegexpSearch("main.go", "\n}")
|
||||
completions = env.Completion("main.go", pos)
|
||||
if len(completions.Items) != 1 {
|
||||
t.Fatalf("expected 1 completion item, got %v", len(completions.Items))
|
||||
}
|
||||
item := completions.Items[0]
|
||||
if item.Label != "Name" {
|
||||
t.Fatalf("expected completion item blah.Name, got %v", item.Label)
|
||||
}
|
||||
env.AcceptCompletion("main.go", pos, item)
|
||||
|
||||
// Await the diagnostics to add example.com/blah to the go.mod file.
|
||||
env.SaveBufferWithoutActions("main.go")
|
||||
env.Await(
|
||||
env.DiagnosticAtRegexp("go.mod", "module mod.com"),
|
||||
env.DiagnosticAtRegexp("main.go", `"example.com/blah"`),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -331,6 +331,15 @@ func (e *Env) Completion(path string, pos fake.Pos) *protocol.CompletionList {
|
|||
return completions
|
||||
}
|
||||
|
||||
// AcceptCompletion accepts a completion for the given item at the given
|
||||
// position.
|
||||
func (e *Env) AcceptCompletion(path string, pos fake.Pos, item protocol.CompletionItem) {
|
||||
e.T.Helper()
|
||||
if err := e.Editor.AcceptCompletion(e.Ctx, path, pos, item); err != nil {
|
||||
e.T.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// CodeAction calls testDocument/codeAction for the given path, and calls
|
||||
// t.Fatal if there are errors.
|
||||
func (e *Env) CodeAction(path string) []protocol.CodeAction {
|
||||
|
|
|
@ -907,6 +907,23 @@ func (e *Editor) Completion(ctx context.Context, path string, pos Pos) (*protoco
|
|||
return completions, nil
|
||||
}
|
||||
|
||||
// AcceptCompletion accepts a completion for the given item at the given
|
||||
// position.
|
||||
func (e *Editor) AcceptCompletion(ctx context.Context, path string, pos Pos, item protocol.CompletionItem) error {
|
||||
if e.Server == nil {
|
||||
return nil
|
||||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
_, ok := e.buffers[path]
|
||||
if !ok {
|
||||
return fmt.Errorf("buffer %q is not open", path)
|
||||
}
|
||||
return e.editBufferLocked(ctx, path, convertEdits(append([]protocol.TextEdit{
|
||||
*item.TextEdit,
|
||||
}, item.AdditionalTextEdits...)))
|
||||
}
|
||||
|
||||
// References executes a reference request on the server.
|
||||
func (e *Editor) References(ctx context.Context, path string, pos Pos) ([]protocol.Location, error) {
|
||||
if e.Server == nil {
|
||||
|
|
|
@ -1070,6 +1070,19 @@ func (c *completer) selector(ctx context.Context, sel *ast.SelectorExpr) error {
|
|||
// Is sel a qualified identifier?
|
||||
if id, ok := sel.X.(*ast.Ident); ok {
|
||||
if pkgName, ok := c.pkg.GetTypesInfo().Uses[id].(*types.PkgName); ok {
|
||||
var pkg source.Package
|
||||
for _, imp := range c.pkg.Imports() {
|
||||
if imp.PkgPath() == pkgName.Imported().Path() {
|
||||
pkg = imp
|
||||
}
|
||||
}
|
||||
// If the package is not imported, try searching for unimported
|
||||
// completions.
|
||||
if pkg == nil && c.opts.unimported {
|
||||
if err := c.unimportedMembers(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
candidates := c.packageMembers(pkgName.Imported(), stdScore, nil)
|
||||
for _, cand := range candidates {
|
||||
c.deepState.enqueue(cand)
|
||||
|
|
Загрузка…
Ссылка в новой задаче