mirror of
https://github.com/golang/go
synced 2024-11-18 16:24:42 -07:00
internal/lsp/source: complete keywords as types
Offer "struct", "interface", "map", "chan", and "func" keywords when we expect a type. For example "var foo i<>" will offer "interface". Because "struct" and "interface" are more often used when declaring named types, they get a higher score in type declarations. Otherwise, "map", "chan" and "func" get a higher score. I also got rid of the special keyword scoring. Now keywords just use stdScore and highScore. This makes the interplay with other types of candidates more predictable. Keywords are offered in pretty limited contexts, so I don't think they will be annoying. Finally, keyword candidate score is now to be scaled properly based on how well they match the prefix. Previously they weren't penalized for not matching well, so there were probably some situations where keywords were ranked too high. Updates golang/go#34009. Change-Id: I0b659c00a8503cd72da28853dfe54fcb67f734ae Reviewed-on: https://go-review.googlesource.com/c/tools/+/220503 Run-TryBot: Muir Manders <muir@mnd.rs> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
51e69f7192
commit
2047c2d578
@ -1746,7 +1746,7 @@ Nodes:
|
||||
case *ast.MapType:
|
||||
wantTypeName = true
|
||||
if n.Key != nil {
|
||||
wantComparable = n.Key.Pos() <= c.pos && c.pos <= n.Key.End()
|
||||
wantComparable = nodeContains(n.Key, c.pos)
|
||||
} else {
|
||||
// If the key is empty, assume we are completing the key if
|
||||
// pos is directly after the "map[".
|
||||
@ -1754,10 +1754,10 @@ Nodes:
|
||||
}
|
||||
break Nodes
|
||||
case *ast.ValueSpec:
|
||||
if n.Type != nil && n.Type.Pos() <= c.pos && c.pos <= n.Type.End() {
|
||||
wantTypeName = true
|
||||
}
|
||||
wantTypeName = nodeContains(n.Type, c.pos)
|
||||
break Nodes
|
||||
case *ast.TypeSpec:
|
||||
wantTypeName = nodeContains(n.Type, c.pos)
|
||||
default:
|
||||
if breaksExpectedTypeInference(p) {
|
||||
return typeNameInference{}
|
||||
|
@ -36,8 +36,6 @@ const (
|
||||
|
||||
// addKeywordCompletions offers keyword candidates appropriate at the position.
|
||||
func (c *completer) addKeywordCompletions() {
|
||||
const keywordScore = 0.9
|
||||
|
||||
seen := make(map[string]bool)
|
||||
|
||||
// addKeywords dedupes and adds completion items for the specified
|
||||
@ -49,22 +47,39 @@ func (c *completer) addKeywordCompletions() {
|
||||
}
|
||||
seen[kw] = true
|
||||
|
||||
if c.matcher.Score(kw) > 0 {
|
||||
if matchScore := c.matcher.Score(kw); matchScore > 0 {
|
||||
c.items = append(c.items, CompletionItem{
|
||||
Label: kw,
|
||||
Kind: protocol.KeywordCompletion,
|
||||
InsertText: kw,
|
||||
Score: score,
|
||||
Score: score * float64(matchScore),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.wantTypeName() {
|
||||
// If we expect a type name, include "interface", "struct",
|
||||
// "func", "chan", and "map".
|
||||
|
||||
// "interface" and "struct" are more common declaring named types.
|
||||
// Give them a higher score if we are in a type declaration.
|
||||
structIntf, funcChanMap := stdScore, highScore
|
||||
if len(c.path) > 1 {
|
||||
if _, namedDecl := c.path[1].(*ast.TypeSpec); namedDecl {
|
||||
structIntf, funcChanMap = highScore, stdScore
|
||||
}
|
||||
}
|
||||
|
||||
addKeywords(structIntf, STRUCT, INTERFACE)
|
||||
addKeywords(funcChanMap, FUNC, CHAN, MAP)
|
||||
}
|
||||
|
||||
// If we are at the file scope, only offer decl keywords. We don't
|
||||
// get *ast.Idents at the file scope because non-keyword identifiers
|
||||
// turn into *ast.BadDecl, not *ast.Ident.
|
||||
if len(c.path) == 1 || isASTFile(c.path[1]) {
|
||||
addKeywords(keywordScore, TYPE, CONST, VAR, FUNC, IMPORT)
|
||||
addKeywords(stdScore, TYPE, CONST, VAR, FUNC, IMPORT)
|
||||
return
|
||||
} else if _, ok := c.path[0].(*ast.Ident); !ok {
|
||||
// Otherwise only offer keywords if the client is completing an identifier.
|
||||
@ -86,7 +101,7 @@ func (c *completer) addKeywordCompletions() {
|
||||
case *ast.CaseClause:
|
||||
// only recommend "fallthrough" and "break" within the bodies of a case clause
|
||||
if c.pos > node.Colon {
|
||||
addKeywords(keywordScore, BREAK)
|
||||
addKeywords(stdScore, BREAK)
|
||||
// "fallthrough" is only valid in switch statements.
|
||||
// A case clause is always nested within a block statement in a switch statement,
|
||||
// that block statement is nested within either a TypeSwitchStmt or a SwitchStmt.
|
||||
@ -94,21 +109,21 @@ func (c *completer) addKeywordCompletions() {
|
||||
continue
|
||||
}
|
||||
if _, ok := path[i+2].(*ast.SwitchStmt); ok {
|
||||
addKeywords(keywordScore, FALLTHROUGH)
|
||||
addKeywords(stdScore, FALLTHROUGH)
|
||||
}
|
||||
}
|
||||
case *ast.CommClause:
|
||||
if c.pos > node.Colon {
|
||||
addKeywords(keywordScore, BREAK)
|
||||
addKeywords(stdScore, BREAK)
|
||||
}
|
||||
case *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.SwitchStmt:
|
||||
addKeywords(keywordScore+lowScore, CASE, DEFAULT)
|
||||
addKeywords(stdScore, CASE, DEFAULT)
|
||||
case *ast.ForStmt:
|
||||
addKeywords(keywordScore, BREAK, CONTINUE)
|
||||
addKeywords(stdScore, BREAK, CONTINUE)
|
||||
// This is a bit weak, functions allow for many keywords
|
||||
case *ast.FuncDecl:
|
||||
if node.Body != nil && c.pos > node.Body.Lbrace {
|
||||
addKeywords(keywordScore-lowScore, DEFER, RETURN, FOR, GO, SWITCH, SELECT, IF, ELSE, VAR, CONST, GOTO, TYPE)
|
||||
addKeywords(stdScore, DEFER, RETURN, FOR, GO, SWITCH, SELECT, IF, ELSE, VAR, CONST, GOTO, TYPE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,18 @@ package keywords
|
||||
//@rank("", type),rank("", func),rank("", var),rank("", const),rank("", import)
|
||||
|
||||
func _() {
|
||||
var test int
|
||||
var test int //@rank(" //", int, interface)
|
||||
var tChan chan int
|
||||
var _ m //@complete(" //", map)
|
||||
var _ f //@complete(" //", func)
|
||||
var _ c //@complete(" //", chan)
|
||||
|
||||
var _ str //@rank(" //", string, struct)
|
||||
|
||||
type _ int //@rank(" //", interface, int)
|
||||
|
||||
type _ str //@rank(" //", struct, string)
|
||||
|
||||
switch test {
|
||||
case 1: // TODO: trying to complete case here will break because the parser wont return *ast.Ident
|
||||
b //@complete(" //", break)
|
||||
@ -32,7 +42,7 @@ func _() {
|
||||
}
|
||||
|
||||
for index := 0; index < test; index++ {
|
||||
c //@complete(" //", continue, const)
|
||||
c //@complete(" //", const, continue)
|
||||
b //@complete(" //", break)
|
||||
}
|
||||
|
||||
@ -75,3 +85,8 @@ func _() {
|
||||
/* var */ //@item(var, "var", "", "keyword")
|
||||
/* const */ //@item(const, "const", "", "keyword")
|
||||
/* goto */ //@item(goto, "goto", "", "keyword")
|
||||
/* struct */ //@item(struct, "struct", "", "keyword")
|
||||
/* interface */ //@item(interface, "interface", "", "keyword")
|
||||
/* map */ //@item(map, "map", "", "keyword")
|
||||
/* func */ //@item(func, "func", "", "keyword")
|
||||
/* chan */ //@item(chan, "chan", "", "keyword")
|
||||
|
4
internal/lsp/testdata/lsp/summary.txt.golden
vendored
4
internal/lsp/testdata/lsp/summary.txt.golden
vendored
@ -1,11 +1,11 @@
|
||||
-- summary --
|
||||
CodeLensCount = 0
|
||||
CompletionsCount = 226
|
||||
CompletionsCount = 229
|
||||
CompletionSnippetCount = 68
|
||||
UnimportedCompletionsCount = 11
|
||||
DeepCompletionsCount = 5
|
||||
FuzzyCompletionsCount = 8
|
||||
RankedCompletionsCount = 112
|
||||
RankedCompletionsCount = 116
|
||||
CaseSensitiveCompletionsCount = 4
|
||||
DiagnosticsCount = 39
|
||||
FoldingRangesCount = 2
|
||||
|
Loading…
Reference in New Issue
Block a user