Skip to content

Commit 940f2c5

Browse files
authored
parser,checker: vls fix goto definition (#25595)
1 parent 13efcad commit 940f2c5

File tree

10 files changed

+227
-127
lines changed

10 files changed

+227
-127
lines changed

vlib/v/ast/types.v

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ pub mut:
188188
generic_types []Type
189189
concrete_types []Type
190190
parent_type Type
191+
name_pos token.Pos
191192
}
192193

193194
// instantiation of a generic struct
@@ -212,6 +213,7 @@ pub mut:
212213
generic_types []Type
213214
concrete_types []Type
214215
parent_type Type
216+
name_pos token.Pos
215217
}
216218

217219
pub struct Enum {
@@ -222,6 +224,7 @@ pub:
222224
uses_exprs bool
223225
typ Type
224226
attrs map[string][]Attr
227+
name_pos token.Pos
225228
}
226229

227230
@[minify]
@@ -231,6 +234,7 @@ pub mut:
231234
pub:
232235
language Language
233236
is_import bool
237+
name_pos token.Pos
234238
}
235239

236240
pub struct Aggregate {
@@ -273,6 +277,7 @@ pub struct Map {
273277
pub mut:
274278
key_type Type
275279
value_type Type
280+
name_pos token.Pos
276281
}
277282

278283
@[minify]
@@ -287,6 +292,7 @@ pub mut:
287292
generic_types []Type
288293
concrete_types []Type
289294
parent_type Type
295+
name_pos token.Pos
290296
}
291297

292298
// <atomic.h> defines special typenames

vlib/v/builder/builder.v

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,10 +549,11 @@ pub fn (mut b Builder) print_warnings_and_errors() {
549549
}
550550
}
551551
if b.pref.json_errors {
552-
util.print_json_errors(json_errors)
552+
if !b.pref.is_vls || b.pref.linfo.method !in [.definition, .completion, .signature_help] {
553+
util.print_json_errors(json_errors)
554+
}
553555
// eprintln(json2.encode_pretty(json_errors))
554556
}
555-
556557
if !b.pref.skip_warnings {
557558
for file in b.parsed_files {
558559
for err in file.warnings {

vlib/v/checker/autocomplete.v

Lines changed: 117 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module checker
55
import strings
66
import v.ast
77
import os
8+
import token
89

910
enum DetailKind {
1011
text = 1
@@ -43,77 +44,130 @@ struct Detail {
4344
insert_text_format ?int // 1 for PlainText, 2 for Snippet
4445
}
4546

47+
fn (mut c Checker) get_fn_from_call_expr(node ast.CallExpr) !ast.Fn {
48+
fn_name := node.name
49+
return if node.is_method {
50+
left_sym := c.table.sym(c.unwrap_generic(node.left_type))
51+
c.table.find_method(left_sym, fn_name) or {
52+
return error('failed to find method "${fn_name}"')
53+
}
54+
} else {
55+
c.table.find_fn(fn_name) or { return error('failed to find fn "${fn_name}"') }
56+
}
57+
}
58+
4659
// Autocomplete for function parameters `os.write_bytes(**path string, bytes []u8***)` etc
4760
pub fn (mut c Checker) autocomplete_for_fn_call_expr(node ast.CallExpr) {
48-
// Make sure this ident is on the same line and same file as request
49-
same_line := c.pref.linfo.line_nr + 1 == node.pos.line_nr
50-
if !same_line {
51-
return
52-
}
53-
if node.pos.file_idx < 0 {
61+
if c.pref.linfo.method != .signature_help {
5462
return
5563
}
56-
if c.pref.linfo.path != c.table.filelist[node.pos.file_idx] {
64+
if !c.vls_is_the_node(node.name_pos) {
5765
return
5866
}
59-
col := c.pref.linfo.expr.all_after_last('^').int()
60-
if node.name_pos.col + node.name_pos.len + 1 != col {
61-
return
62-
}
63-
fn_name := node.name
64-
f := if node.is_method {
65-
left_sym := c.table.sym(c.unwrap_generic(node.left_type))
66-
c.table.find_method(left_sym, fn_name) or {
67-
println('failed to find method "${fn_name}"')
68-
return
69-
}
70-
} else {
71-
c.table.find_fn(fn_name) or {
72-
println('failed to find fn "${fn_name}"')
73-
return
74-
}
67+
f := c.get_fn_from_call_expr(node) or {
68+
println(err)
69+
exit(1)
7570
}
7671
res := c.build_fn_summary(f)
7772
println(res)
7873
exit(0)
7974
}
8075

81-
fn (mut c Checker) ident_gotodef() {
82-
mut ident_name := c.pref.linfo.expr.after('gd^').trim_space()
83-
mod_name := ident_name.all_before_last('.')
84-
resolved_mod_name := c.try_resolve_to_import_mod_name(mod_name)
85-
if resolved_mod_name.len > 0 {
86-
ident_name = resolved_mod_name + '.' + ident_name.all_after_last('.')
87-
}
88-
if f := c.table.find_fn(ident_name) {
89-
println('${f.file}:${f.pos.line_nr}:${f.pos.col}')
76+
fn (mut c Checker) ident_gotodef(node_ ast.Expr) {
77+
if c.pref.linfo.method != .definition {
78+
return
79+
}
80+
if !c.vls_is_the_node(node_.pos()) {
9081
return
9182
}
83+
mut node := unsafe { node_ }
84+
mut pos := token.Pos{}
85+
match mut node {
86+
ast.CallExpr {
87+
if !c.vls_is_the_node(node.name_pos) {
88+
return
89+
}
90+
f := c.get_fn_from_call_expr(node) or {
91+
println(err)
92+
exit(1)
93+
}
94+
pos = f.name_pos
95+
}
96+
ast.Ident {
97+
// global objects
98+
for _, obj in c.table.global_scope.objects {
99+
if obj is ast.ConstField && obj.name == node.name {
100+
pos = obj.pos
101+
break
102+
} else if obj is ast.GlobalField && obj.name == node.name {
103+
pos = obj.pos
104+
break
105+
} else if obj is ast.Var && obj.name == node.name {
106+
pos = obj.pos
107+
break
108+
}
109+
}
110+
// local objects
111+
if pos == token.Pos{} && !isnil(c.fn_scope) {
112+
if obj := c.fn_scope.find_var(node.name) {
113+
pos = obj.pos
114+
}
115+
}
116+
//
117+
}
118+
ast.StructInit {
119+
if !c.vls_is_the_node(node.name_pos) {
120+
return
121+
}
122+
mut info := c.table.sym(node.typ).info
123+
pos = match mut info {
124+
ast.Struct {
125+
info.name_pos
126+
}
127+
ast.Alias {
128+
info.name_pos
129+
}
130+
ast.SumType {
131+
info.name_pos
132+
}
133+
else {
134+
pos
135+
}
136+
}
137+
}
138+
ast.SelectorExpr {
139+
sym := c.table.sym(node.expr_type)
140+
f := c.table.find_field(sym, node.field_name) or {
141+
println('failed to find field "${node.field_name}"')
142+
exit(1)
143+
}
144+
pos = f.pos
145+
}
146+
else {}
147+
}
148+
if pos.file_idx != -1 {
149+
println('${c.table.filelist[pos.file_idx]}:${pos.line_nr + 1}:${pos.col}')
150+
}
151+
exit(0)
92152
}
93153

94154
// Autocomplete for `myvar. ...`, `os. ...`
95155
fn (mut c Checker) ident_autocomplete(node ast.Ident) {
156+
if c.pref.linfo.method != .completion {
157+
return
158+
}
96159
// Mini LS hack (v -line-info "a.v:16")
97160
if c.pref.is_verbose {
98161
println(
99162
'checker.ident_autocomplete() info.line_nr=${c.pref.linfo.line_nr} node.line_nr=${node.pos.line_nr} ' +
100163
' node.col=${node.pos.col} pwd="${os.getwd()}" file="${c.file.path}", ' +
101-
//' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" expr="${c.pref.linfo.expr}"')
102-
' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" node.mod="${node.mod}" col="${c.pref.linfo.col}"')
164+
' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" node.mod="${node.mod}" col="${c.pref.linfo.col}"')
103165
}
104166
if node.mod == 'builtin' {
105167
// User can't type in `builtin.func(` at all
106168
return
107169
}
108-
// Make sure this ident is on the same line and same file as request
109-
same_line := c.pref.linfo.line_nr == node.pos.line_nr
110-
if !same_line {
111-
return
112-
}
113-
if node.pos.file_idx < 0 {
114-
return
115-
}
116-
if c.pref.linfo.path != c.table.filelist[node.pos.file_idx] {
170+
if !c.vls_is_the_node(node.pos) {
117171
return
118172
}
119173
check_name := if c.pref.linfo.col == node.pos.col {
@@ -142,7 +196,6 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) {
142196
c.module_autocomplete(mod_name)
143197
exit(0)
144198
}
145-
146199
if node.kind == .unresolved {
147200
eprintln('unresolved type, maybe "${node.name}" was not defined. otherwise this is a bug, should never happen; please report')
148201
exit(1)
@@ -196,8 +249,7 @@ fn (c &Checker) build_fn_summary(func ast.Fn) string {
196249
sb.writeln('\t}]')
197250
sb.writeln('}],')
198251
sb.writeln('"activeSignature":0,')
199-
sb.writeln('"activeParameter":0,')
200-
sb.writeln('"_type":"SignatureHelp"')
252+
sb.writeln('"activeParameter":0')
201253
sb.writeln('}')
202254
return sb.str()
203255
}
@@ -341,7 +393,7 @@ fn (c &Checker) vls_gen_type_details(mut details []Detail, sym ast.TypeSymbol) {
341393

342394
fn (c &Checker) vls_write_details(details []Detail) {
343395
mut sb := strings.new_builder(details.len * 32)
344-
sb.writeln('{"details" : [')
396+
sb.writeln('{"details": [')
345397
for detail in details {
346398
sb.write_string('{"kind":${int(detail.kind)},')
347399
sb.write_string('"label":"${detail.label}",')
@@ -386,3 +438,21 @@ fn (c &Checker) vls_map_v_kind_to_lsp_kind(kind ast.Kind) DetailKind {
386438
}
387439
return .text
388440
}
441+
442+
fn (c &Checker) vls_is_the_node(pos token.Pos) bool {
443+
// Make sure this ident is on the same line and same file as request
444+
same_line := c.pref.linfo.line_nr == pos.line_nr
445+
if !same_line {
446+
return false
447+
}
448+
if pos.file_idx < 0 {
449+
return false
450+
}
451+
if c.pref.linfo.path != c.table.filelist[pos.file_idx] {
452+
return false
453+
}
454+
if c.pref.linfo.col > pos.col + pos.len || c.pref.linfo.col < pos.col {
455+
return false
456+
}
457+
return true
458+
}

vlib/v/checker/checker.v

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -442,35 +442,6 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
442442
c.error('a _test.v file should have *at least* one `test_` function', token.Pos{})
443443
}
444444
}
445-
// After the main checker run, run the line info check, print line info, and exit (if it's present)
446-
if !c.pref.linfo.is_running && c.pref.line_info != '' { //'' && c.pref.linfo.line_nr == 0 {
447-
// c.do_line_info(c.pref.line_info, ast_files)
448-
// println('setting is_running=true, pref.path=${c.pref.linfo.path} curdir' + os.getwd())
449-
c.pref.linfo.is_running = true
450-
// println('linfo path=${c.pref.linfo.path}')
451-
// Go to definition
452-
if c.pref.linfo.expr.starts_with('gd^') {
453-
c.ident_gotodef()
454-
exit(0)
455-
}
456-
for i, file in ast_files {
457-
// println(file.path)
458-
if file.path == c.pref.linfo.path {
459-
// println('running c.check_files')
460-
c.check_files([ast_files[i]])
461-
exit(0)
462-
} else if file.path.starts_with('./') {
463-
// Maybe it's a "./foo.v", linfo.path has an absolute path
464-
abs_path := os.join_path(os.getwd(), file.path).replace('/./', '/') // TODO: join_path shouldn't have /./
465-
if abs_path == c.pref.linfo.path {
466-
c.check_files([ast_files[i]])
467-
exit(0)
468-
}
469-
}
470-
}
471-
println('failed to find file "${c.pref.linfo.path}"')
472-
exit(0)
473-
}
474445
// Make sure fn main is defined in non lib builds
475446
if c.pref.build_mode == .build_module || c.pref.is_test {
476447
return
@@ -3017,6 +2988,11 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type {
30172988
c.error('checker: too many expr levels: ${c.expr_level} ', node.pos())
30182989
return ast.void_type
30192990
}
2991+
defer {
2992+
if c.pref.is_vls {
2993+
c.ident_gotodef(node)
2994+
}
2995+
}
30202996
match mut node {
30212997
ast.IfExpr {
30222998
return c.if_expr(mut node)
@@ -4144,10 +4120,10 @@ fn (mut c Checker) resolve_var_fn(func &ast.Fn, mut node ast.Ident, name string)
41444120
}
41454121

41464122
fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
4147-
if c.pref.linfo.is_running {
4148-
// LS hack (v -line-info "a.v:16")
4149-
// TODO perf $if
4150-
c.ident_autocomplete(node)
4123+
defer {
4124+
if c.pref.is_vls {
4125+
c.ident_autocomplete(node)
4126+
}
41514127
}
41524128
// TODO: move this
41534129
if c.const_deps.len > 0 {

vlib/v/checker/fn.v

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -760,9 +760,7 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
760760
c.fn_call(mut node, mut continue_check)
761761
}
762762
if c.pref.is_vls {
763-
if c.pref.linfo.expr.starts_with('fn^') {
764-
c.autocomplete_for_fn_call_expr(node)
765-
}
763+
c.autocomplete_for_fn_call_expr(node)
766764
}
767765
if !continue_check {
768766
return ast.void_type

vlib/v/parser/parser.v

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,11 +1596,6 @@ fn (mut p Parser) name_expr() ast.Expr {
15961596
// prepend the full import
15971597
mod = p.imports[p.tok.lit]
15981598
}
1599-
if p.pref.linfo.is_running {
1600-
// VLS autocomplete for module fns: `os...`
1601-
// TODO perf $if
1602-
// p.module_autocomplete(node)
1603-
}
16041599
line_nr := p.tok.line_nr
16051600
p.next()
16061601
p.check(.dot)
@@ -2930,6 +2925,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
29302925
variants: variant_types
29312926
is_generic: generic_types.len > 0
29322927
generic_types: generic_types
2928+
name_pos: name_pos
29332929
}
29342930
is_pub: is_pub
29352931
})
@@ -2986,6 +2982,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
29862982
info: ast.Alias{
29872983
parent_type: parent_type
29882984
language: parent_language
2985+
name_pos: name_pos
29892986
}
29902987
is_pub: is_pub
29912988
})

0 commit comments

Comments
 (0)