Skip to content

Commit 6989dc1

Browse files
authored
parser,checker: add more support for mod autocomplete, allow for .vv files too (#25562)
1 parent d9a24ba commit 6989dc1

5 files changed

Lines changed: 166 additions & 90 deletions

File tree

vlib/v/checker/autocomplete.v

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,28 +86,45 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) {
8686
//' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" expr="${c.pref.linfo.expr}"')
8787
' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" node.mod="${node.mod}" col="${c.pref.linfo.col}"')
8888
}
89+
if node.mod == 'builtin' {
90+
// User can't type in `builtin.func(` at all
91+
return
92+
}
8993
// Make sure this ident is on the same line and same file as request
9094
same_line := c.pref.linfo.line_nr == node.pos.line_nr
9195
if !same_line {
9296
return
9397
}
94-
same_col := c.pref.linfo.col == node.pos.col + node.pos.len
95-
if !same_col {
96-
return
97-
}
9898
if node.pos.file_idx < 0 {
9999
return
100100
}
101101
if c.pref.linfo.path != c.table.filelist[node.pos.file_idx] {
102102
return
103103
}
104+
check_name := if c.pref.linfo.col == node.pos.col {
105+
if node.name == '' {
106+
// for `os` in middle of text, followed by something else in next line:
107+
// `os.
108+
// something`
109+
node.mod
110+
} else {
111+
''
112+
}
113+
} else if c.pref.linfo.col == node.pos.col + node.pos.len {
114+
// for `os` at end of scope :
115+
// `os. }`
116+
node.name
117+
} else {
118+
''
119+
}
120+
if check_name.len == 0 {
121+
return
122+
}
104123
// Module autocomplete
105124
// `os. ...`
106-
mod_name := c.try_resolve_to_import_mod_name(node.name)
125+
mod_name := c.try_resolve_to_import_mod_name(check_name)
107126
if mod_name.len > 0 {
108-
if node.mod == c.file.mod.name {
109-
c.module_autocomplete(mod_name)
110-
}
127+
c.module_autocomplete(mod_name)
111128
exit(0)
112129
}
113130

@@ -150,14 +167,17 @@ fn (c &Checker) build_fn_summary(method ast.Fn) string {
150167
}
151168
}
152169
sb.write_string(')')
170+
if method.return_type != ast.void_type {
171+
sb.write_string(c.table.type_to_str(method.return_type))
172+
}
153173
return sb.str()
154174
}
155175

156176
fn (c &Checker) try_resolve_to_import_mod_name(name string) string {
157-
if name in c.file.used_imports {
158-
// resolve alias to mod name
159-
mod_name := c.file.imports.filter(it.alias == name)[0].mod
160-
return mod_name
177+
for imp in c.file.imports {
178+
if (imp.alias == name || imp.mod == name) && imp.alias in c.file.used_imports {
179+
return imp.mod
180+
}
161181
}
162182
return ''
163183
}

vlib/v/parser/parser.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2126,7 +2126,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
21262126
mut field_name := ''
21272127
// check if the name is on the same line as the dot
21282128
if p.prev_tok.pos().line_nr == name_pos.line_nr || p.tok.kind != .name {
2129-
if p.is_vls {
2129+
if p.is_vls && p.tok.kind != .name {
21302130
if p.tok.kind in [.rpar, .rcbr] {
21312131
// Simplify the dot expression for VLS, so that the parser doesn't error
21322132
// `println(x.)` => `println(x)`

vlib/v/pref/line_info.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn (mut p Preferences) parse_line_info(line string) {
2424
file_name := vals[..vals.len - 2].join(':')
2525
line_nr := vals[vals.len - 2].int() - 1
2626

27-
if !file_name.ends_with('.v') || line_nr == -1 {
27+
if (!file_name.ends_with('.v') && !file_name.ends_with('.vv')) || line_nr == -1 {
2828
eprintln(format_err)
2929
return
3030
}

vlib/v/tests/vls/autocomplete_module_test.v

Lines changed: 119 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,32 @@ import v.util.diff
44

55
const vroot = os.real_path(@VMODROOT)
66
const tmp_dir = os.real_path(os.temp_dir())
7+
const text_file = os.join_path(vroot, 'vlib', 'v', 'tests', 'vls', 'sample_text.vv')
78

8-
const text_file_orig = os.join_path(vroot, 'vlib', 'v', 'tests', 'vls', 'sample_text.vv')
9-
const text_file = os.join_path(tmp_dir, 'sample_text.v')
10-
const text_file_result = $if windows { text_file.replace('\\', '/') } $else { text_file }
9+
const autocomplete_info_for_mod_sample_mod1 = '{"details" : [
10+
{"kind":3,"label":"public_fn1","detail":"string","documentation":""},
11+
{"kind":22,"label":"PublicStruct1","detail":"","documentation":""},
12+
{"kind":13,"label":"PublicEnum1","detail":"","documentation":""},
13+
{"kind":8,"label":"PublicInterface1","detail":"","documentation":""},
14+
{"kind":7,"label":"PublicAlias1_1","detail":"","documentation":""},
15+
{"kind":7,"label":"PublicAlias1_2","detail":"","documentation":""},
16+
{"kind":21,"label":"public_const1","detail":"","documentation":""}
17+
]}'
1118

12-
fn testsuite_begin() {
13-
eprintln('testsuite_begin, text_file = ${text_file}')
14-
os.cp(text_file_orig, text_file) or { panic(err) }
15-
}
19+
const autocomplete_info_for_mod_sample_mod2 = '{"details" : [
20+
{"kind":3,"label":"public_fn2","detail":"string","documentation":""},
21+
{"kind":22,"label":"PublicStruct2","detail":"","documentation":""},
22+
{"kind":13,"label":"PublicEnum2","detail":"","documentation":""},
23+
{"kind":8,"label":"PublicInterface2","detail":"","documentation":""},
24+
{"kind":7,"label":"PublicAlias2","detail":"","documentation":""},
25+
{"kind":21,"label":"public_const2","detail":"","documentation":""}
26+
]}'
1627

17-
fn testsuite_end() {
18-
}
28+
const autocomplete_info_for_mod_struct = '{"details" : [
29+
{"kind":5,"label":"a","detail":"int","documentation":""},
30+
{"kind":5,"label":"b","detail":"string","documentation":""},
31+
{"kind":2,"label":"add","detail":"void","documentation":""}
32+
]}'
1933

2034
struct TestData {
2135
cmd string
@@ -24,110 +38,144 @@ struct TestData {
2438

2539
const test_data = [
2640
TestData{
27-
cmd: 'v -check -json-errors -nocolor -vls-mode -line-info "${text_file}:18:3" ${os.quoted_path(text_file)}'
28-
output: '{"details" : [
29-
{"kind":3,"label":"public_fn1","detail":"string","documentation":""},
30-
{"kind":22,"label":"PublicStruct1","detail":"","documentation":""},
31-
{"kind":13,"label":"PublicEnum1","detail":"","documentation":""},
32-
{"kind":8,"label":"PublicInterface1","detail":"","documentation":""},
33-
{"kind":7,"label":"PublicAlias1_1","detail":"","documentation":""},
34-
{"kind":7,"label":"PublicAlias1_2","detail":"","documentation":""},
35-
{"kind":21,"label":"public_const1","detail":"","documentation":""}
36-
]}'
41+
cmd: 'v -check -json-errors -nocolor -vls-mode -line-info "${text_file}:19:3" ${os.quoted_path(text_file)}'
42+
output: autocomplete_info_for_mod_sample_mod1
43+
},
44+
TestData{
45+
cmd: 'v -check -json-errors -nocolor -vls-mode -line-info "${text_file}:20:13" ${os.quoted_path(text_file)}'
46+
output: autocomplete_info_for_mod_sample_mod2
47+
},
48+
TestData{
49+
cmd: 'v -check -json-errors -nocolor -vls-mode -line-info "${text_file}:22:3" ${os.quoted_path(text_file)}'
50+
output: autocomplete_info_for_mod_struct
51+
},
52+
TestData{
53+
cmd: 'v -check -json-errors -nocolor -vls-mode -line-info "${text_file}:23:3" ${os.quoted_path(text_file)}'
54+
output: autocomplete_info_for_mod_sample_mod1
55+
},
56+
TestData{
57+
cmd: 'v -check -json-errors -nocolor -vls-mode -line-info "${text_file}:26:28" ${os.quoted_path(text_file)}'
58+
output: autocomplete_info_for_mod_sample_mod1
59+
},
60+
TestData{
61+
cmd: 'v -check -json-errors -nocolor -vls-mode -line-info "${text_file}:27:8" ${os.quoted_path(text_file)}'
62+
output: ''
63+
},
64+
TestData{
65+
cmd: 'v -check -json-errors -nocolor -vls-mode -line-info "${text_file}:28:9" ${os.quoted_path(text_file)}'
66+
output: 'unresolved type, maybe "builtin" was not defined. otherwise this is a bug, should never happen; please report'
3767
},
3868
TestData{
3969
cmd: 'v -w -vls-mode -check -json-errors ${os.quoted_path(text_file)}'
4070
output: '[
4171
{
4272
"path":"${text_file}",
43-
"message":"undefined ident: `a`",
44-
"line_nr":14,
73+
"message":"unexpected token `:=`, expecting `)`",
74+
"line_nr":26,
4575
"col":4,
4676
"len":0
4777
}
4878
,
4979
{
5080
"path":"${text_file}",
51-
"message":"operator `+=` not defined on left operand type `void`",
52-
"line_nr":14,
53-
"col":4,
81+
"message":"unexpected name `strings`, expecting `)`",
82+
"line_nr":27,
83+
"col":2,
84+
"len":0
85+
}
86+
,
87+
{
88+
"path":"${text_file}",
89+
"message":"undefined ident: ``",
90+
"line_nr":19,
91+
"col":3,
92+
"len":0
93+
}
94+
,
95+
{
96+
"path":"${text_file}",
97+
"message":"undefined ident: ``",
98+
"line_nr":20,
99+
"col":13,
54100
"len":0
55101
}
56102
,
57103
{
58104
"path":"${text_file}",
59-
"message":"cannot assign to `a`: expected `void`, not `int`",
60-
"line_nr":14,
61-
"col":9,
105+
"message":"undefined ident: ``",
106+
"line_nr":23,
107+
"col":3,
62108
"len":0
63109
}
64110
,
65111
{
66112
"path":"${text_file}",
67-
"message":"undefined ident: `s`",
68-
"line_nr":18,
113+
"message":"cannot use `main.MyS` as `string` in argument 1 to `string.all_before_last`",
114+
"line_nr":26,
115+
"col":2,
116+
"len":0
117+
}
118+
,
119+
{
120+
"path":"${text_file}",
121+
"message":"undefined ident: ``",
122+
"line_nr":26,
123+
"col":28,
124+
"len":0
125+
}
126+
,
127+
{
128+
"path":"${text_file}",
129+
"message":"`` (no value) used as value in argument 1 to `string.all_before_last`",
130+
"line_nr":26,
131+
"col":27,
132+
"len":0
133+
}
134+
,
135+
{
136+
"path":"${text_file}",
137+
"message":"`string` has no property ``",
138+
"line_nr":26,
139+
"col":11,
140+
"len":0
141+
}
142+
,
143+
{
144+
"path":"${text_file}",
145+
"message":"undefined ident: `builtin`",
146+
"line_nr":28,
147+
"col":2,
148+
"len":0
149+
}
150+
,
151+
{
152+
"path":"${text_file}",
153+
"message":"`builtin` does not return a value",
154+
"line_nr":28,
69155
"col":2,
70156
"len":0
71157
}
72158
]
73-
'
74-
},
75-
TestData{
76-
cmd: 'v -check -nocolor -vls-mode ${os.quoted_path(text_file)}'
77-
output: '${text_file_result}:14:4: error: undefined ident: `a`
78-
12 | // add add `val` to `a`
79-
13 | fn (mut m MyS) add(val int) {
80-
14 | m.a += val
81-
| ^
82-
15 | }
83-
16 |
84-
${text_file_result}:14:4: error: operator `+=` not defined on left operand type `void`
85-
12 | // add add `val` to `a`
86-
13 | fn (mut m MyS) add(val int) {
87-
14 | m.a += val
88-
| ^
89-
15 | }
90-
16 |
91-
${text_file_result}:14:9: error: cannot assign to `a`: expected `void`, not `int`
92-
12 | // add add `val` to `a`
93-
13 | fn (mut m MyS) add(val int) {
94-
14 | m.a += val
95-
| ~~~
96-
15 | }
97-
16 |
98-
${text_file_result}:18:2: error: undefined ident: `s`
99-
16 |
100-
17 | fn main() {
101-
18 | s.
102-
| ^
103-
19 | //sample_mod2.
104-
20 | //mut k := MyS{}
105-
${text_file_result}:5:8: warning: module \'sample_mod2 (v.tests.vls.sample_mod2)\' is imported but never used
106-
3 |
107-
4 | import v.tests.vls.sample_mod1 as s
108-
5 | import v.tests.vls.sample_mod2
109-
| ~~~~~~~~~~~~~~~~~~~~~~~
110-
6 |
111-
7 | struct MyS{
112159
'
113160
},
114161
]
115162

116163
fn test_main() {
117164
mut total_errors := 0
118165

166+
dump(text_file)
119167
for t in test_data {
120168
res := os.execute(t.cmd)
121169
if res.exit_code < 0 {
122170
println('fail execute ${t.cmd}')
123171
panic(res.output)
124172
}
125173
res_output := $if windows {
126-
res.output.replace('\r\n', '\n')
174+
res.output.replace('\r\n', '\n').trim_space()
127175
} $else {
128-
res.output
176+
res.output.trim_space()
129177
}
130-
if t.output != res_output {
178+
if t.output.trim_space() != res_output {
131179
println('${term.red('FAIL')} ${t.cmd}')
132180
if diff_ := diff.compare_text(t.output, res_output) {
133181
println(term.header('difference:', '-'))

vlib/v/tests/vls/sample_text.vv

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import v.tests.vls.sample_mod1 as s
55
import v.tests.vls.sample_mod2
66

77
struct MyS{
8-
b int
9-
a string
8+
mut:
9+
a int
10+
b string
1011
}
1112

1213
// add add `val` to `a`
@@ -16,7 +17,14 @@ fn (mut m MyS) add(val int) {
1617

1718
fn main() {
1819
s.
19-
//sample_mod2.
20-
//mut k := MyS{}
21-
//k.
20+
sample_mod2.
21+
mut k := MyS{}
22+
k.
23+
s.
24+
mut str := 'hello'
25+
x := str.all_before_last(
26+
k := str.all_before_last(s.
27+
strings.
28+
builtin.
29+
v := 1
2230
}

0 commit comments

Comments
 (0)