Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / source / completion / completion_keywords.go
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.
4
5 package completion
6
7 import (
8         "go/ast"
9
10         "golang.org/x/tools/internal/lsp/protocol"
11         "golang.org/x/tools/internal/lsp/source"
12 )
13
14 const (
15         BREAK       = "break"
16         CASE        = "case"
17         CHAN        = "chan"
18         CONST       = "const"
19         CONTINUE    = "continue"
20         DEFAULT     = "default"
21         DEFER       = "defer"
22         ELSE        = "else"
23         FALLTHROUGH = "fallthrough"
24         FOR         = "for"
25         FUNC        = "func"
26         GO          = "go"
27         GOTO        = "goto"
28         IF          = "if"
29         IMPORT      = "import"
30         INTERFACE   = "interface"
31         MAP         = "map"
32         PACKAGE     = "package"
33         RANGE       = "range"
34         RETURN      = "return"
35         SELECT      = "select"
36         STRUCT      = "struct"
37         SWITCH      = "switch"
38         TYPE        = "type"
39         VAR         = "var"
40 )
41
42 // addKeywordCompletions offers keyword candidates appropriate at the position.
43 func (c *completer) addKeywordCompletions() {
44         seen := make(map[string]bool)
45
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".
49
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
53                 if len(c.path) > 1 {
54                         if _, namedDecl := c.path[1].(*ast.TypeSpec); namedDecl {
55                                 structIntf, funcChanMap = highScore, stdScore
56                         }
57                 }
58
59                 c.addKeywordItems(seen, structIntf, STRUCT, INTERFACE)
60                 c.addKeywordItems(seen, funcChanMap, FUNC, CHAN, MAP)
61         }
62
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)
68                 return
69         } else if _, ok := c.path[0].(*ast.Ident); !ok {
70                 // Otherwise only offer keywords if the client is completing an identifier.
71                 return
72         }
73
74         if len(c.path) > 2 {
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)
79                 }
80         }
81
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.
86         case *ast.CommClause:
87                 // Make sure we aren't in the Comm statement.
88                 if !n.Colon.IsValid() || c.pos <= n.Colon {
89                         return
90                 }
91         case *ast.CaseClause:
92                 // Make sure we aren't in the case List.
93                 if !n.Colon.IsValid() || c.pos <= n.Colon {
94                         return
95                 }
96         default:
97                 return
98         }
99
100         // Filter out keywords depending on scope
101         // Skip the first one because we want to look at the enclosing scopes
102         path := c.path[1:]
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) {
113                                         continue
114                                 }
115                                 if _, ok := path[i+2].(*ast.SwitchStmt); ok {
116                                         c.addKeywordItems(seen, stdScore, FALLTHROUGH)
117                                 }
118                         }
119                 case *ast.CommClause:
120                         if c.pos > node.Colon {
121                                 c.addKeywordItems(seen, stdScore, BREAK)
122                         }
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
128                 case *ast.FuncDecl:
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)
131                         }
132                 }
133         }
134 }
135
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 {
140                 if seen[kw] {
141                         continue
142                 }
143                 seen[kw] = true
144
145                 if matchScore := c.matcher.Score(kw); matchScore > 0 {
146                         c.items = append(c.items, CompletionItem{
147                                 Label:      kw,
148                                 Kind:       protocol.KeywordCompletion,
149                                 InsertText: kw,
150                                 Score:      score * float64(matchScore),
151                         })
152                 }
153         }
154 }