Skip to content

Commit b579874

Browse files
muirdmfindleyr
authored andcommitted
lsp/completion: reorganize how we track candidate type mods
"type mod" refers to agglutinative expressions such as dereference "*", invocation "()", and slicing "[:]". When considering an object as a completion candidate, we check whether applying a type mod would make it a better candidate. Previously we tracked the type mods we wanted to apply to a candidate by setting bool fields. Now instead we keep a slice of the type mods. This has two main advantages: - The mods are now ordered which will allow us to format candidates properly when the same mods can appear in different order (e.g. "<-*foo" or *<-foo"). - We can now record any mod multiple times allowing for "<-<-foo" or "foo()()". I changed the formatting code to always create a snippet object since that made things simpler. I had to tweak a few snippet helper methods to accept a snippet argument rather than creating a new snippet. This commit's only functional change is that we no longer show any type mods in candidate labels. For example, the user will now see "foo" in the completion popup instead of "*foo". Showing the operators adds noise to the candidate list, and we didn't display them consistently. Updates golang/go#46045. Change-Id: I3ea7baa1ee2fee80a1f8cfe88cbae1093ae269ba Reviewed-on: https://go-review.googlesource.com/c/tools/+/323449 Run-TryBot: Muir Manders <muir@mnd.rs> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> Trust: Rebecca Stambler <rstambler@golang.org>
1 parent 890984b commit b579874

File tree

14 files changed

+143
-159
lines changed

14 files changed

+143
-159
lines changed

gopls/internal/regtest/completion/completion_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,9 +443,9 @@ func _() {
443443
}{
444444
{`var _ a = aaaa()`, []string{"aaaa1", "aaaa2"}},
445445
{`var _ b = bbbb()`, []string{"bbbb1", "bbbb2"}},
446-
{`var _ c = xxxx()`, []string{"***xxxxd", "**xxxxe", "xxxxc"}},
447-
{`var _ d = xxxx()`, []string{"***xxxxe", "*xxxxc", "xxxxd"}},
448-
{`var _ e = xxxx()`, []string{"**xxxxc", "*xxxxd", "xxxxe"}},
446+
{`var _ c = xxxx()`, []string{"xxxxc", "xxxxd", "xxxxe"}},
447+
{`var _ d = xxxx()`, []string{"xxxxc", "xxxxd", "xxxxe"}},
448+
{`var _ e = xxxx()`, []string{"xxxxc", "xxxxd", "xxxxe"}},
449449
}
450450
for _, tt := range tests {
451451
completions := env.Completion("main.go", env.RegexpSearch("main.go", tt.re))

internal/lsp/source/completion/completion.go

Lines changed: 51 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -370,31 +370,14 @@ type candidate struct {
370370
// expanded calls for function invocations.
371371
names []string
372372

373-
// expandFuncCall is true if obj should be invoked in the completion.
374-
// For example, expandFuncCall=true yields "foo()", expandFuncCall=false yields "foo".
375-
expandFuncCall bool
376-
377-
// takeAddress is true if the completion should take a pointer to obj.
378-
// For example, takeAddress=true yields "&foo", takeAddress=false yields "foo".
379-
takeAddress bool
373+
// mods contains modifications that should be applied to the
374+
// candidate when inserted. For example, "foo" may be insterted as
375+
// "*foo" or "foo()".
376+
mods []typeModKind
380377

381378
// addressable is true if a pointer can be taken to the candidate.
382379
addressable bool
383380

384-
// makePointer is true if the candidate type name T should be made into *T.
385-
makePointer bool
386-
387-
// dereference is a count of how many times to dereference the candidate obj.
388-
// For example, dereference=2 turns "foo" into "**foo" when formatting.
389-
dereference int
390-
391-
// takeSlice is true if obj is an array that should be converted to a slice.
392-
takeSlice bool
393-
394-
// variadic is true if this candidate fills a variadic param and
395-
// needs "..." appended.
396-
variadic bool
397-
398381
// convertTo is a type that this candidate should be cast to. For
399382
// example, if convertTo is float64, "foo" should be formatted as
400383
// "float64(foo)".
@@ -405,6 +388,15 @@ type candidate struct {
405388
imp *importInfo
406389
}
407390

391+
func (c candidate) hasMod(mod typeModKind) bool {
392+
for _, m := range c.mods {
393+
if m == mod {
394+
return true
395+
}
396+
}
397+
return false
398+
}
399+
408400
// ErrIsDefinition is an error that informs the user they got no
409401
// completions because they tried to complete the name of a new object
410402
// being defined.
@@ -1768,20 +1760,23 @@ func (c *completer) expectedCompositeLiteralType() types.Type {
17681760
return nil
17691761
}
17701762

1771-
// typeModifier represents an operator that changes the expected type.
1772-
type typeModifier struct {
1773-
mod typeMod
1763+
// typeMod represents an operator that changes the expected type.
1764+
type typeMod struct {
1765+
mod typeModKind
17741766
arrayLen int64
17751767
}
17761768

1777-
type typeMod int
1769+
type typeModKind int
17781770

17791771
const (
1780-
dereference typeMod = iota // pointer indirection: "*"
1781-
reference // adds level of pointer: "&" for values, "*" for type names
1782-
chanRead // channel read operator ("<-")
1783-
slice // make a slice type ("[]" in "[]int")
1784-
array // make an array type ("[2]" in "[2]int")
1772+
dereference typeModKind = iota // pointer indirection: "*"
1773+
reference // adds level of pointer: "&" for values, "*" for type names
1774+
chanRead // channel read operator: "<-"
1775+
sliceType // make a slice type: "[]" in "[]int"
1776+
arrayType // make an array type: "[2]" in "[2]int"
1777+
invoke // make a function call: "()" in "foo()"
1778+
takeSlice // take slice of array: "[:]" in "foo[:]"
1779+
takeDotDotDot // turn slice into variadic args: "..." in "foo..."
17851780
)
17861781

17871782
type objKind int
@@ -1832,7 +1827,7 @@ type candidateInference struct {
18321827

18331828
// modifiers are prefixes such as "*", "&" or "<-" that influence how
18341829
// a candidate type relates to the expected type.
1835-
modifiers []typeModifier
1830+
modifiers []typeMod
18361831

18371832
// convertibleTo is a type our candidate type must be convertible to.
18381833
convertibleTo types.Type
@@ -1882,7 +1877,7 @@ type typeNameInference struct {
18821877

18831878
// modifiers are prefixes such as "*", "&" or "<-" that influence how
18841879
// a candidate type relates to the expected type.
1885-
modifiers []typeModifier
1880+
modifiers []typeMod
18861881

18871882
// assertableFrom is a type that must be assertable to our candidate type.
18881883
assertableFrom types.Type
@@ -2108,13 +2103,13 @@ Nodes:
21082103
}
21092104
return inf
21102105
case *ast.StarExpr:
2111-
inf.modifiers = append(inf.modifiers, typeModifier{mod: dereference})
2106+
inf.modifiers = append(inf.modifiers, typeMod{mod: dereference})
21122107
case *ast.UnaryExpr:
21132108
switch node.Op {
21142109
case token.AND:
2115-
inf.modifiers = append(inf.modifiers, typeModifier{mod: reference})
2110+
inf.modifiers = append(inf.modifiers, typeMod{mod: reference})
21162111
case token.ARROW:
2117-
inf.modifiers = append(inf.modifiers, typeModifier{mod: chanRead})
2112+
inf.modifiers = append(inf.modifiers, typeMod{mod: chanRead})
21182113
}
21192114
case *ast.DeferStmt, *ast.GoStmt:
21202115
inf.objKind |= kindFunc
@@ -2209,9 +2204,9 @@ func (ci candidateInference) applyTypeNameModifiers(typ types.Type) types.Type {
22092204
switch mod.mod {
22102205
case reference:
22112206
typ = types.NewPointer(typ)
2212-
case array:
2207+
case arrayType:
22132208
typ = types.NewArray(typ, mod.arrayLen)
2214-
case slice:
2209+
case sliceType:
22152210
typ = types.NewSlice(typ)
22162211
}
22172212
}
@@ -2325,7 +2320,7 @@ Nodes:
23252320
}
23262321
return typeNameInference{}
23272322
case *ast.StarExpr:
2328-
inf.modifiers = append(inf.modifiers, typeModifier{mod: reference})
2323+
inf.modifiers = append(inf.modifiers, typeMod{mod: reference})
23292324
case *ast.CompositeLit:
23302325
// We want a type name if position is in the "Type" part of a
23312326
// composite literal (e.g. "Foo<>{}").
@@ -2338,7 +2333,7 @@ Nodes:
23382333
// the composite literal and not the type name, but if
23392334
// affects our type completion nonetheless.
23402335
if u, ok := c.path[i+1].(*ast.UnaryExpr); ok && u.Op == token.AND {
2341-
inf.modifiers = append(inf.modifiers, typeModifier{mod: reference})
2336+
inf.modifiers = append(inf.modifiers, typeMod{mod: reference})
23422337
}
23432338
}
23442339
}
@@ -2349,13 +2344,13 @@ Nodes:
23492344
inf.wantTypeName = true
23502345
if n.Len == nil {
23512346
// No "Len" expression means a slice type.
2352-
inf.modifiers = append(inf.modifiers, typeModifier{mod: slice})
2347+
inf.modifiers = append(inf.modifiers, typeMod{mod: sliceType})
23532348
} else {
23542349
// Try to get the array type using the constant value of "Len".
23552350
tv, ok := c.pkg.GetTypesInfo().Types[n.Len]
23562351
if ok && tv.Value != nil && tv.Value.Kind() == constant.Int {
23572352
if arrayLen, ok := constant.Int64Val(tv.Value); ok {
2358-
inf.modifiers = append(inf.modifiers, typeModifier{mod: array, arrayLen: arrayLen})
2353+
inf.modifiers = append(inf.modifiers, typeMod{mod: arrayType, arrayLen: arrayLen})
23592354
}
23602355
}
23612356
}
@@ -2417,7 +2412,7 @@ func (c *candidate) anyCandType(f func(t types.Type, addressable bool) bool) boo
24172412
if sig, ok := objType.Underlying().(*types.Signature); ok {
24182413
if sig.Results().Len() == 1 && f(sig.Results().At(0).Type(), false) {
24192414
// Mark the candidate so we know to append "()" when formatting.
2420-
c.expandFuncCall = true
2415+
c.mods = append(c.mods, invoke)
24212416
return true
24222417
}
24232418
}
@@ -2454,8 +2449,10 @@ func (c *candidate) anyCandType(f func(t types.Type, addressable bool) bool) boo
24542449
}
24552450

24562451
if f(ptr.Elem(), false) {
2457-
// Mark the candidate so we know to prepend "*" when formatting.
2458-
c.dereference = ptrDepth
2452+
for i := 0; i < ptrDepth; i++ {
2453+
// Mark the candidate so we know to prepend "*" when formatting.
2454+
c.mods = append(c.mods, dereference)
2455+
}
24592456
return true
24602457
}
24612458

@@ -2465,13 +2462,13 @@ func (c *candidate) anyCandType(f func(t types.Type, addressable bool) bool) boo
24652462
// Check if c is addressable and a pointer to c matches our type inference.
24662463
if c.addressable && f(types.NewPointer(objType), false) {
24672464
// Mark the candidate so we know to prepend "&" when formatting.
2468-
c.takeAddress = true
2465+
c.mods = append(c.mods, reference)
24692466
return true
24702467
}
24712468

24722469
if array, ok := objType.Underlying().(*types.Array); ok {
24732470
if f(types.NewSlice(array.Elem()), false) {
2474-
c.takeSlice = true
2471+
c.mods = append(c.mods, takeSlice)
24752472
return true
24762473
}
24772474
}
@@ -2510,15 +2507,17 @@ func (c *completer) matchingCandidate(cand *candidate) bool {
25102507
if sig, ok := candType.Underlying().(*types.Signature); ok {
25112508
if c.inference.assigneesMatch(cand, sig) {
25122509
// Invoke the candidate if its results are multi-assignable.
2513-
cand.expandFuncCall = true
2510+
cand.mods = append(cand.mods, invoke)
25142511
return true
25152512
}
25162513
}
25172514

25182515
// Default to invoking *types.Func candidates. This is so function
25192516
// completions in an empty statement (or other cases with no expected type)
25202517
// are invoked by default.
2521-
cand.expandFuncCall = isFunc(cand.obj)
2518+
if isFunc(cand.obj) {
2519+
cand.mods = append(cand.mods, invoke)
2520+
}
25222521

25232522
return false
25242523
}
@@ -2572,7 +2571,7 @@ func (ci *candidateInference) candTypeMatches(cand *candidate) bool {
25722571
}
25732572

25742573
if expType == variadicType {
2575-
cand.variadic = true
2574+
cand.mods = append(cand.mods, takeDotDotDot)
25762575
}
25772576

25782577
// Lower candidate score for untyped conversions. This avoids
@@ -2611,7 +2610,7 @@ func (ci *candidateInference) candTypeMatches(cand *candidate) bool {
26112610
// matches.
26122611
if ci.kindMatches(candType) {
26132612
if ci.objKind == kindFunc {
2614-
cand.expandFuncCall = true
2613+
cand.mods = append(cand.mods, invoke)
26152614
}
26162615
return true
26172616
}
@@ -2814,11 +2813,11 @@ func (c *completer) matchingTypeName(cand *candidate) bool {
28142813
if c.inference.typeName.compLitType {
28152814
// If we are completing a composite literal type as in
28162815
// "foo<>{}", to make a pointer we must prepend "&".
2817-
cand.takeAddress = true
2816+
cand.mods = append(cand.mods, reference)
28182817
} else {
28192818
// If we are completing a normal type name such as "foo<>", to
28202819
// make a pointer we must prepend "*".
2821-
cand.makePointer = true
2820+
cand.mods = append(cand.mods, dereference)
28222821
}
28232822
return true
28242823
}

internal/lsp/source/completion/deep_completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ func (c *completer) addCandidate(ctx context.Context, cand *candidate) {
249249
}
250250

251251
// Lower score of method calls so we prefer fields and vars over calls.
252-
if cand.expandFuncCall {
252+
if cand.hasMod(invoke) {
253253
if sig, ok := obj.Type().Underlying().(*types.Signature); ok && sig.Recv() != nil {
254254
cand.score *= 0.9
255255
}

0 commit comments

Comments
 (0)