Skip to content

Commit b16c73b

Browse files
authored
checker: fix vls autocomplete (#25488)
1 parent 69a3e1e commit b16c73b

2 files changed

Lines changed: 195 additions & 43 deletions

File tree

‎vlib/v/checker/autocomplete.v‎

Lines changed: 194 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,22 @@ struct ACFieldMethod {
1111
typ string
1212
}
1313

14-
fn abs(a int) int {
15-
if a < 0 {
16-
return -a
17-
}
18-
return a
19-
}
20-
2114
pub fn (mut c Checker) run_ac(ast_file &ast.File) {
2215
}
2316

2417
// Autocomplete for function parameters `os.write_bytes(**path string, bytes []u8***)` etc
2518
pub fn (mut c Checker) autocomplete_for_fn_call_expr() {
2619
// println(c.pref.linfo.expr)
27-
fn_name := c.pref.linfo.expr.replace('()', '').trim_space()
20+
mut fn_name := if c.pref.linfo.expr.ends_with('(') {
21+
c.pref.linfo.expr[..c.pref.linfo.expr.len - 1].trim_space()
22+
} else {
23+
c.pref.linfo.expr.replace('()', '').trim_space()
24+
}
25+
mod_name := fn_name.all_before_last('.')
26+
resolved_mod_name := c.try_resolve_to_import_mod_name(mod_name)
27+
if resolved_mod_name.len > 0 {
28+
fn_name = resolved_mod_name + '.' + fn_name.all_after_last('.')
29+
}
2830
f := c.table.find_fn(fn_name) or {
2931
println('failed to find fn "${fn_name}"')
3032
return
@@ -34,12 +36,39 @@ pub fn (mut c Checker) autocomplete_for_fn_call_expr() {
3436
}
3537

3638
fn (mut c Checker) ident_gotodef() {
37-
name := c.pref.linfo.expr.after('gd^').trim_space()
38-
f := c.table.find_fn(name) or {
39-
println('failed to find fn "${name}"')
39+
mut ident_name := c.pref.linfo.expr.after('gd^').trim_space()
40+
mod_name := ident_name.all_before_last('.')
41+
resolved_mod_name := c.try_resolve_to_import_mod_name(mod_name)
42+
if resolved_mod_name.len > 0 {
43+
ident_name = resolved_mod_name + '.' + ident_name.all_after_last('.')
44+
}
45+
if f := c.table.find_fn(ident_name) {
46+
println('${f.file}:${f.pos.line_nr}:${f.pos.col}')
4047
return
4148
}
42-
println('${f.file}:${f.pos.line_nr}:${f.pos.col}')
49+
// TODO: As EnumDecl has no `file` field, we can't goto enum right now
50+
// for name,val in c.table.enum_decls {
51+
// if val.is_pub && name == ident_name {
52+
// println('${val.file}:${val.pos.line_nr}:${val.pos.col}')
53+
// return
54+
// }
55+
//}
56+
57+
// TODO: we need other TypeDecl information here
58+
// for mut sym in c.table.type_symbols {
59+
// if sym.is_pub && sym.name == ident_name {
60+
// match mut sym.info {
61+
// ast.Alias {
62+
// }
63+
// ast.Interface {
64+
// }
65+
// ast.Struct {
66+
// }
67+
// else {
68+
// }
69+
// }
70+
// }
71+
//}
4372
}
4473

4574
// Autocomplete for `myvar. ...`, `os. ...`
@@ -53,11 +82,11 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) {
5382
' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" node.mod="${node.mod}" col="${c.pref.linfo.col}"')
5483
}
5584
// Make sure this ident is on the same line as requeste, in the same file, and has the same name
56-
same_line := node.pos.line_nr in [c.pref.linfo.line_nr - 1, c.pref.linfo.line_nr + 1, c.pref.linfo.line_nr]
85+
same_line := c.pref.linfo.line_nr == node.pos.line_nr
5786
if !same_line {
5887
return
5988
}
60-
same_col := abs(c.pref.linfo.col - node.pos.col) < 3
89+
same_col := c.pref.linfo.col == node.pos.col + node.pos.len
6190
if !same_col {
6291
return
6392
}
@@ -67,19 +96,22 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) {
6796
}
6897
// Module autocomplete
6998
// `os. ...`
70-
// println(node)
71-
if node.name == '' && node.mod != 'builtin' {
72-
c.module_autocomplete(node)
73-
return
74-
} else if node.name == '' && node.mod == 'builtin' {
75-
return
99+
mod_name := c.try_resolve_to_import_mod_name(node.name)
100+
if mod_name.len > 0 {
101+
if node.mod == c.file.mod.name {
102+
c.module_autocomplete(mod_name)
103+
}
104+
exit(0)
76105
}
77106
mut sb := strings.new_builder(10)
78107
if node.kind == .unresolved {
79108
// println(node)
80109
eprintln('unresolved type, maybe "${node.name}" was not defined. otherwise this is a bug, should never happen; please report')
81110
exit(1)
82111
}
112+
if node.obj.typ == ast.no_type {
113+
exit(0)
114+
}
83115
sym := c.table.sym(c.unwrap_generic(node.obj.typ))
84116
// sb.writeln('VAR ${node.name}:${sym.name} ${node.pos.line_nr}')
85117
nt := '${node.name}:${sym.name}'
@@ -151,28 +183,15 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) {
151183
}
152184
}
153185

154-
fn (mut c Checker) module_autocomplete(node ast.Ident) {
155-
mut sb := strings.new_builder(10)
156-
// println(c.table.fns)
157-
sb.writeln('{"methods":[')
158-
prefix := node.mod + '.'
159-
mut empty := true
160-
for _, f in c.table.fns {
161-
mut name := f.name
162-
if name.starts_with(prefix) {
163-
empty = false
164-
if name.contains('__static__') {
165-
name = name.replace('__static__', '.')
166-
}
167-
name = name.after('.') // The user already typed `mod.`, so suggest the name without module
168-
sb.writeln('"${name}:int" ,')
169-
}
170-
}
171-
if !empty {
172-
sb.go_back(2) // remove final ,
173-
}
174-
sb.writeln(']}')
175-
println(sb.str().trim_space())
186+
fn (mut c Checker) module_autocomplete(mod string) {
187+
println('{')
188+
c.write_mod_funcs(mod)
189+
c.write_mod_type_alias(mod)
190+
c.write_mod_interfaces(mod)
191+
c.write_mod_enums(mod)
192+
c.write_mod_consts(mod)
193+
c.write_mod_structs(mod)
194+
println('}')
176195
}
177196

178197
fn build_method_summary(method ast.Fn) string {
@@ -199,3 +218,136 @@ fn (c &Checker) build_fn_summary(method ast.Fn) string {
199218
}
200219
return s + ')'
201220
}
221+
222+
fn (c &Checker) try_resolve_to_import_mod_name(name string) string {
223+
if name in c.file.used_imports {
224+
// resolve alias to mod name
225+
mod_name := c.file.imports.filter(it.alias == name)[0].mod
226+
return mod_name
227+
}
228+
return ''
229+
}
230+
231+
fn (c &Checker) write_mod_funcs(mod string) {
232+
mut sb := strings.new_builder(128)
233+
sb.writeln('"functions":[')
234+
mut empty := true
235+
for _, f in c.table.fns {
236+
mut name := f.name
237+
if f.is_pub && name.all_before_last('.') == mod {
238+
empty = false
239+
if name.contains('__static__') {
240+
name = name.replace('__static__', '.')
241+
}
242+
name = name.all_after_last('.') // The user already typed `mod.`, so suggest the name without module
243+
type_string := if f.return_type != ast.no_type {
244+
c.table.type_to_str(f.return_type)
245+
} else {
246+
'int'
247+
}
248+
sb.writeln('"${name}:${type_string}" ,')
249+
}
250+
}
251+
if !empty {
252+
sb.go_back(2) // remove final ,
253+
}
254+
sb.writeln('],')
255+
println(sb.str().trim_space())
256+
}
257+
258+
fn (c &Checker) write_mod_type_alias(mod string) {
259+
mut sb := strings.new_builder(128)
260+
sb.writeln('"type_alias":[')
261+
mut empty := true
262+
for sym in c.table.type_symbols {
263+
if sym.is_pub && sym.info is ast.Alias {
264+
if sym.name.all_before_last('.') == mod {
265+
empty = false
266+
sb.writeln('"${sym.name.all_after_last('.')}" ,')
267+
}
268+
}
269+
}
270+
if !empty {
271+
sb.go_back(2) // remove final ,
272+
}
273+
sb.writeln('],')
274+
println(sb.str().trim_space())
275+
}
276+
277+
fn (c &Checker) write_mod_interfaces(mod string) {
278+
mut sb := strings.new_builder(128)
279+
sb.writeln('"interfaces":[')
280+
mut empty := true
281+
for sym in c.table.type_symbols {
282+
if sym.is_pub && sym.info is ast.Interface {
283+
if sym.name.all_before_last('.') == mod {
284+
empty = false
285+
sb.writeln('"${sym.name.all_after_last('.')}" ,')
286+
}
287+
}
288+
}
289+
if !empty {
290+
sb.go_back(2) // remove final ,
291+
}
292+
sb.writeln('],')
293+
println(sb.str().trim_space())
294+
}
295+
296+
fn (c &Checker) write_mod_enums(mod string) {
297+
mut sb := strings.new_builder(128)
298+
sb.writeln('"enums":[')
299+
mut empty := true
300+
for name, val in c.table.enum_decls {
301+
if val.is_pub && name.all_before_last('.') == mod {
302+
empty = false
303+
sb.writeln('"${name.all_after_last('.')}" ,')
304+
}
305+
}
306+
if !empty {
307+
sb.go_back(2) // remove final ,
308+
}
309+
sb.writeln('],')
310+
println(sb.str().trim_space())
311+
}
312+
313+
fn (c &Checker) write_mod_consts(mod string) {
314+
mut sb := strings.new_builder(128)
315+
sb.writeln('"constants":[')
316+
mut empty := true
317+
for _, obj in c.table.global_scope.objects {
318+
if obj is ast.ConstField && obj.is_pub {
319+
if obj.name.all_before_last('.') == mod {
320+
empty = false
321+
if obj.typ != ast.no_type {
322+
sb.writeln('"${obj.name.all_after_last('.')}:${c.table.type_to_str(obj.typ)}" ,')
323+
} else {
324+
sb.writeln('"${obj.name.all_after_last('.')}:int" ,')
325+
}
326+
}
327+
}
328+
}
329+
if !empty {
330+
sb.go_back(2) // remove final ,
331+
}
332+
sb.writeln('],')
333+
println(sb.str().trim_space())
334+
}
335+
336+
fn (c &Checker) write_mod_structs(mod string) {
337+
mut sb := strings.new_builder(128)
338+
sb.writeln('"structs":[')
339+
mut empty := true
340+
for sym in c.table.type_symbols {
341+
if sym.is_pub && sym.info is ast.Struct {
342+
if sym.name.all_before_last('.') == mod {
343+
empty = false
344+
sb.writeln('"${sym.name.all_after_last('.')}" ,')
345+
}
346+
}
347+
}
348+
if !empty {
349+
sb.go_back(2) // remove final ,
350+
}
351+
sb.writeln(']')
352+
println(sb.str().trim_space())
353+
}

‎vlib/v/checker/checker.v‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
449449
c.ident_gotodef()
450450
exit(0)
451451
}
452-
if c.pref.linfo.expr.contains('()') {
452+
if c.pref.linfo.expr.contains('()') || c.pref.linfo.expr.ends_with('(') {
453453
c.autocomplete_for_fn_call_expr()
454454
exit(0)
455455
}

0 commit comments

Comments
 (0)