internal/lsp: search for deep completions across function calls

We now continue deep completion search across function calls. The
function must take no arguments and return a single argument. For
example, when completing "fo<>" you might get candidates such as
"foo.bar().baz()".

Previously we would stop searching for deep completions when we hit a
function call. For example, we would stop at "foo.bar()", never
finding "foo.bar().baz()". At the time I was worried about the search
scope growing too large, but now that we dynamically limit the search
scope there isn't much left to worry about.

Change-Id: I48772c154400662876682503c1f58ef6e3dca688
Reviewed-on: https://go-review.googlesource.com/c/tools/+/201222
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Muir Manders 2019-10-01 13:36:34 -07:00 committed by Rebecca Stambler
parent 02335f11d5
commit 0abb09c987
3 changed files with 59 additions and 5 deletions

View File

@ -39,10 +39,16 @@ type deepCompletionState struct {
candidateCount int
}
// push pushes obj onto our search stack.
func (s *deepCompletionState) push(obj types.Object) {
// push pushes obj onto our search stack. If invoke is true then
// invocation parens "()" will be appended to the object name.
func (s *deepCompletionState) push(obj types.Object, invoke bool) {
s.chain = append(s.chain, obj)
s.chainNames = append(s.chainNames, obj.Name())
name := obj.Name()
if invoke {
name += "()"
}
s.chainNames = append(s.chainNames, name)
}
// pop pops the last object off our search stack.
@ -156,8 +162,21 @@ func (c *completer) deepSearch(obj types.Object) {
return
}
if sig, ok := obj.Type().Underlying().(*types.Signature); ok {
// If obj is a function that takes no arguments and returns one
// value, keep searching across the function call.
if sig.Params().Len() == 0 && sig.Results().Len() == 1 {
// Pass invoke=true since the function needs to be invoked in
// the deep chain.
c.deepState.push(obj, true)
// The result of a function call is not addressable.
c.methodsAndFields(sig.Results().At(0).Type(), false)
c.deepState.pop()
}
}
// Push this object onto our search stack.
c.deepState.push(obj)
c.deepState.push(obj, false)
switch obj := obj.(type) {
case *types.PkgName:

View File

@ -88,3 +88,38 @@ func _() {
var i int
i = a //@deep(" //", deepAD, deepABC, deepA, deepAB)
}
type foo struct {
b bar
}
func (f foo) bar() bar {
return f.b
}
func (f foo) barPtr() *bar {
return &f.b
}
type bar struct{}
func (b bar) valueReceiver() int {
return 0
}
func (b *bar) ptrReceiver() int {
return 0
}
func _() {
var (
i int
f foo
)
f.bar().valueReceiver //@item(deepBarValue, "f.bar().valueReceiver", "func() int", "method")
f.barPtr().valueReceiver //@item(deepBarPtrValue, "f.barPtr().valueReceiver", "func() int", "method")
f.barPtr().ptrReceiver //@item(deepBarPtrPtr, "f.barPtr().ptrReceiver", "func() int", "method")
i = fb //@fuzzy(" //", deepBarValue, deepBarPtrPtr, deepBarPtrValue)
}

View File

@ -3,7 +3,7 @@ CompletionsCount = 178
CompletionSnippetCount = 39
UnimportedCompletionsCount = 1
DeepCompletionsCount = 5
FuzzyCompletionsCount = 6
FuzzyCompletionsCount = 7
RankedCompletionsCount = 1
CaseSensitiveCompletionsCount = 4
DiagnosticsCount = 21