This repository has been archived by the owner on Feb 17, 2021. It is now read-only.
forked from mdempsky/gocode
-
Notifications
You must be signed in to change notification settings - Fork 28
/
cursorcontext.go
311 lines (279 loc) · 7.72 KB
/
cursorcontext.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
package main
import (
"go/ast"
"go/parser"
"go/scanner"
"go/token"
"log"
)
type cursor_context struct {
decl *decl
partial string
}
type token_iterator struct {
tokens []token_item
token_index int
}
type token_item struct {
off int
tok token.Token
lit string
}
func (i token_item) Literal() string {
if i.tok.IsLiteral() {
return i.lit
} else {
return i.tok.String()
}
return ""
}
func (this *token_iterator) token() token_item {
return this.tokens[this.token_index]
}
func (this *token_iterator) previous_token() bool {
if this.token_index <= 0 {
return false
}
this.token_index--
return true
}
var g_bracket_pairs = map[token.Token]token.Token{
token.RPAREN: token.LPAREN,
token.RBRACK: token.LBRACK,
}
// when the cursor is at the ')' or ']', move the cursor to an opposite bracket
// pair, this functions takes inner bracker pairs into account
func (this *token_iterator) skip_to_bracket_pair() bool {
right := this.token().tok
left := g_bracket_pairs[right]
return this.skip_to_left_bracket(left, right)
}
func (this *token_iterator) skip_to_left_bracket(left, right token.Token) bool {
// TODO: Make this functin recursive.
if this.token().tok == left {
return true
}
balance := 1
for balance != 0 {
this.previous_token()
if this.token_index == 0 {
return false
}
switch this.token().tok {
case right:
balance++
case left:
balance--
}
}
return true
}
// Move the cursor to the open brace of the current block, taking inner blocks
// into account.
func (this *token_iterator) skip_to_open_brace() bool {
return this.skip_to_left_bracket(token.LBRACE, token.RBRACE)
}
// try_extract_struct_init_expr tries to match the current cursor position as being inside a struct
// initialization expression of the form:
// &X{
// Xa: 1,
// Xb: 2,
// }
// Nested struct initialization expressions are handled correctly.
func (this *token_iterator) try_extract_struct_init_expr() []byte {
for this.token_index >= 0 {
if !this.skip_to_open_brace() {
return nil
}
if !this.previous_token() {
return nil
}
return []byte(this.token().Literal())
}
return nil
}
// starting from the end of the 'file', move backwards and return a slice of a
// valid Go expression
func (this *token_iterator) extract_go_expr() []byte {
// TODO: Make this function recursive.
orig := this.token_index
// prev always contains the type of the previously scanned token (initialized with the token
// right under the cursor). This is the token to the *right* of the current one.
prev := this.token().tok
loop:
for {
this.previous_token()
if this.token_index == 0 {
return make_expr(this.tokens[:orig])
}
t := this.token().tok
switch t {
case token.PERIOD:
if prev != token.IDENT {
// Not ".ident".
break loop
}
case token.IDENT:
if prev == token.IDENT {
// "ident ident".
break loop
}
case token.RPAREN, token.RBRACK:
if prev == token.IDENT {
// ")ident" or "]ident".
break loop
}
this.skip_to_bracket_pair()
default:
break loop
}
prev = t
}
exprT := this.tokens[this.token_index+1 : orig]
if *g_debug {
log.Printf("extracted expression tokens: %#v", exprT)
}
return make_expr(exprT)
}
// Given a slice of token_item, reassembles them into the original literal expression.
func make_expr(tokens []token_item) []byte {
e := ""
for _, t := range tokens {
e += t.Literal()
}
return []byte(e)
}
// this function is called when the cursor is at the '.' and you need to get the
// declaration before that dot
func (c *auto_complete_context) deduce_cursor_decl(iter *token_iterator) *decl {
e := string(iter.extract_go_expr())
expr, err := parser.ParseExpr(e)
if err != nil {
return nil
}
return expr_to_decl(expr, c.current.scope)
}
func new_token_iterator(src []byte, cursor int) token_iterator {
tokens := make([]token_item, 0, 1000)
var s scanner.Scanner
fset := token.NewFileSet()
file := fset.AddFile("", fset.Base(), len(src))
s.Init(file, src, nil, 0)
token_index := 0
for {
pos, tok, lit := s.Scan()
if tok == token.EOF {
break
}
off := fset.Position(pos).Offset
tokens = append(tokens, token_item{
off: off,
tok: tok,
lit: lit,
})
if cursor > off {
token_index++
}
}
return token_iterator{
tokens: tokens,
token_index: token_index,
}
}
// deduce cursor context, it includes the declaration under the cursor and partial identifier
// (usually a part of the name of the child declaration)
func (c *auto_complete_context) deduce_cursor_context(file []byte, cursor int) (cursor_context, bool) {
if cursor <= 0 {
return cursor_context{nil, ""}, true
}
iter := new_token_iterator(file, cursor)
// figure out what is just before the cursor
iter.previous_token()
switch r := iter.token().tok; r {
case token.PERIOD:
// we're '<whatever>.'
// figure out decl, Partial is ""
decl := c.deduce_cursor_decl(&iter)
return cursor_context{decl, ""}, decl != nil
case token.IDENT, token.TYPE, token.CONST, token.VAR, token.FUNC, token.PACKAGE:
// we're '<whatever>.<ident>'
// parse <ident> as Partial and figure out decl
tok := iter.token()
var partial string
if r == token.IDENT {
// Calculate the offset of the cursor position within the identifier.
// For instance, if we are 'ab#c', we want partial_len = 2 and partial = ab.
partial_len := cursor - tok.off
// Cursor may be past the end of the literal if there are whitespaces after
// the identifier, so we bring it back inside the appropriate limits if
// needed.
if partial_len > len(tok.Literal()) {
partial_len = len(tok.Literal())
}
partial = tok.Literal()[0:partial_len]
} else {
// Do not try to truncate if it is not an identifier.
partial = tok.Literal()
}
iter.previous_token()
if iter.token().tok == token.PERIOD {
decl := c.deduce_cursor_decl(&iter)
return cursor_context{decl, partial}, decl != nil
} else {
return cursor_context{nil, partial}, true
}
case token.COMMA, token.LBRACE:
// Try to parse the current expression as a structure initialization.
data := iter.try_extract_struct_init_expr()
if data == nil {
return cursor_context{nil, ""}, true
}
expr, err := parser.ParseExpr(string(data))
if err != nil {
return cursor_context{nil, ""}, true
}
decl := expr_to_decl(expr, c.current.scope)
if decl == nil {
return cursor_context{nil, ""}, true
}
// Make sure whatever is before the opening brace is a struct.
switch decl.typ.(type) {
case *ast.StructType:
// TODO: Return partial.
return cursor_context{struct_members_only(decl), ""}, true
}
}
return cursor_context{nil, ""}, true
}
// struct_members_only returns a copy of decl with all its children of type function stripped out.
// This is used when returning matches for struct initialization expressions, for which it does not
// make sense to suggest a function name associated with the struct.
func struct_members_only(decl *decl) *decl {
new_decl := *decl
for k, d := range new_decl.children {
switch d.typ.(type) {
case *ast.FuncType:
// Strip functions from the list.
delete(new_decl.children, k)
}
}
return &new_decl
}
// deduce the type of the expression under the cursor, a bit of copy & paste from the method
// above, returns true if deduction was successful (even if the result of it is nil)
func (c *auto_complete_context) deduce_cursor_type_pkg(file []byte, cursor int) (ast.Expr, string, bool) {
if cursor <= 0 {
return nil, "", true
}
iter := new_token_iterator(file, cursor)
// read backwards to extract expression
e := string(iter.extract_go_expr())
expr, err := parser.ParseExpr(e)
if err != nil {
return nil, "", false
} else {
t, scope, _ := infer_type(expr, c.current.scope, -1)
return t, lookup_pkg(get_type_path(t), scope), t != nil
}
return nil, "", false
}