1 // Copyright 2020 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
10 "golang.org/x/tools/internal/lsp/protocol"
11 "golang.org/x/tools/internal/lsp/source"
23 FALLTHROUGH = "fallthrough"
30 INTERFACE = "interface"
42 // addKeywordCompletions offers keyword candidates appropriate at the position.
43 func (c *completer) addKeywordCompletions() {
44 seen := make(map[string]bool)
46 if c.wantTypeName() && c.inference.objType == nil {
47 // If we want a type name but don't have an expected obj type,
48 // include "interface", "struct", "func", "chan", and "map".
50 // "interface" and "struct" are more common declaring named types.
51 // Give them a higher score if we are in a type declaration.
52 structIntf, funcChanMap := stdScore, highScore
54 if _, namedDecl := c.path[1].(*ast.TypeSpec); namedDecl {
55 structIntf, funcChanMap = highScore, stdScore
59 c.addKeywordItems(seen, structIntf, STRUCT, INTERFACE)
60 c.addKeywordItems(seen, funcChanMap, FUNC, CHAN, MAP)
63 // If we are at the file scope, only offer decl keywords. We don't
64 // get *ast.Idents at the file scope because non-keyword identifiers
65 // turn into *ast.BadDecl, not *ast.Ident.
66 if len(c.path) == 1 || isASTFile(c.path[1]) {
67 c.addKeywordItems(seen, stdScore, TYPE, CONST, VAR, FUNC, IMPORT)
69 } else if _, ok := c.path[0].(*ast.Ident); !ok {
70 // Otherwise only offer keywords if the client is completing an identifier.
75 // Offer "range" if we are in ast.ForStmt.Init. This is what the
76 // AST looks like before "range" is typed, e.g. "for i := r<>".
77 if loop, ok := c.path[2].(*ast.ForStmt); ok && source.NodeContains(loop.Init, c.pos) {
78 c.addKeywordItems(seen, stdScore, RANGE)
82 // Only suggest keywords if we are beginning a statement.
83 switch n := c.path[1].(type) {
84 case *ast.BlockStmt, *ast.ExprStmt:
85 // OK - our ident must be at beginning of statement.
87 // Make sure we aren't in the Comm statement.
88 if !n.Colon.IsValid() || c.pos <= n.Colon {
92 // Make sure we aren't in the case List.
93 if !n.Colon.IsValid() || c.pos <= n.Colon {
100 // Filter out keywords depending on scope
101 // Skip the first one because we want to look at the enclosing scopes
103 for i, n := range path {
104 switch node := n.(type) {
105 case *ast.CaseClause:
106 // only recommend "fallthrough" and "break" within the bodies of a case clause
107 if c.pos > node.Colon {
108 c.addKeywordItems(seen, stdScore, BREAK)
109 // "fallthrough" is only valid in switch statements.
110 // A case clause is always nested within a block statement in a switch statement,
111 // that block statement is nested within either a TypeSwitchStmt or a SwitchStmt.
112 if i+2 >= len(path) {
115 if _, ok := path[i+2].(*ast.SwitchStmt); ok {
116 c.addKeywordItems(seen, stdScore, FALLTHROUGH)
119 case *ast.CommClause:
120 if c.pos > node.Colon {
121 c.addKeywordItems(seen, stdScore, BREAK)
123 case *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.SwitchStmt:
124 c.addKeywordItems(seen, stdScore, CASE, DEFAULT)
125 case *ast.ForStmt, *ast.RangeStmt:
126 c.addKeywordItems(seen, stdScore, BREAK, CONTINUE)
127 // This is a bit weak, functions allow for many keywords
129 if node.Body != nil && c.pos > node.Body.Lbrace {
130 c.addKeywordItems(seen, stdScore, DEFER, RETURN, FOR, GO, SWITCH, SELECT, IF, ELSE, VAR, CONST, GOTO, TYPE)
136 // addKeywordItems dedupes and adds completion items for the specified
137 // keywords with the specified score.
138 func (c *completer) addKeywordItems(seen map[string]bool, score float64, kws ...string) {
139 for _, kw := range kws {
145 if matchScore := c.matcher.Score(kw); matchScore > 0 {
146 c.items = append(c.items, CompletionItem{
148 Kind: protocol.KeywordCompletion,
150 Score: score * float64(matchScore),