11import os
22import term
33import v.util.diff
4+ import json
45
56const vroot = os.real_path (@VMODROOT)
67const tmp_dir = os.real_path (os.temp_dir ())
78const text_file = os.join_path (vroot, 'vlib' , 'v' , 'tests' , 'vls' , 'sample_text.vv' )
9+ // note: windows path separator will cause json decode fail
10+ const json_errors_text_file = os.to_slash (text_file)
811const mod1_text_file = os.join_path (vroot, 'vlib' , 'v' , 'tests' , 'vls' , 'sample_mod1' ,
912 'sample.v' )
1013
@@ -45,157 +48,187 @@ const fn_signature_info_for_all_before_last = '{
4548}
4649'
4750
51+ enum Method {
52+ unknown @['unknown' ]
53+ initialize @['initialize' ]
54+ initialized @['initialized' ]
55+ did_open @['textDocument/didOpen' ]
56+ did_change @['textDocument/didChange' ]
57+ definition @['textDocument/definition' ]
58+ completion @['textDocument/completion' ]
59+ signature_help @['textDocument/signatureHelp' ]
60+ set_trace @['$/setTrace' ]
61+ cancel_request @['$/cancelRequest' ]
62+ shutdown @['shutdown' ]
63+ exit @['exit' ]
64+ }
65+
4866struct TestData {
67+ method Method
4968 cmd string
5069 output string
5170}
5271
5372const test_data = [
5473 TestData{
74+ method: .completion
5575 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :19:3" ${os.quoted_path(text_file)} '
5676 output: autocomplete_info_for_mod_sample_mod1
5777 },
5878 TestData{
79+ method: .completion
5980 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :20:13" ${os.quoted_path(text_file)} '
6081 output: autocomplete_info_for_mod_sample_mod2
6182 },
6283 TestData{
84+ method: .completion
6385 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :22:3" ${os.quoted_path(text_file)} '
6486 output: autocomplete_info_for_mod_struct
6587 },
6688 TestData{
89+ method: .completion
6790 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :23:3" ${os.quoted_path(text_file)} '
6891 output: autocomplete_info_for_mod_sample_mod1
6992 },
7093 TestData{
94+ method: .completion
7195 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :26:28" ${os.quoted_path(text_file)} '
7296 output: autocomplete_info_for_mod_sample_mod1
7397 },
7498 TestData{
99+ method: .signature_help
75100 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :25:fn^26" ${os.quoted_path(text_file)} '
76101 output: fn_signature_info_for_all_before_last
77102 },
78103 TestData{
104+ method: .completion
79105 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :27:9" ${os.quoted_path(text_file)} '
80106 output: ''
81107 },
82108 TestData{
109+ method: .completion
83110 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :28:9" ${os.quoted_path(text_file)} '
84111 output: 'unresolved type, maybe "builtin" was not defined. otherwise this is a bug, should never happen; please report'
85112 },
86113 TestData{
114+ method: .definition
87115 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :30:gd^10" ${os.quoted_path(text_file)} '
88116 output: '${mod1_text_file} :50:7'
89117 },
90118 TestData{
119+ method: .definition
91120 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :31:gd^12" ${os.quoted_path(text_file)} '
92121 output: '${mod1_text_file} :8:11'
93122 },
94123 TestData{
124+ method: .definition
95125 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :32:gd^11" ${os.quoted_path(text_file)} '
96126 output: '${mod1_text_file} :41:9'
97127 },
98128 TestData{
129+ method: .definition
99130 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :33:gd^15" ${os.quoted_path(text_file)} '
100131 output: '${mod1_text_file} :44:9'
101132 },
102133 TestData{
134+ method: .definition
103135 cmd: 'v -w -check -json-errors -nocolor -vls-mode -line-info "${text_file} :34:gd^13" ${os.quoted_path(text_file)} '
104136 output: '${mod1_text_file} :19:10'
105137 },
106138 TestData{
139+ method: .did_change
107140 cmd: 'v -w -vls-mode -check -json-errors ${os.quoted_path(text_file)} '
108141 output: '[
109142{
110- "path":"${text_file } ",
143+ "path":"${json_errors_text_file } ",
111144"message":"unexpected token `:=`, expecting `)`",
112145"line_nr":26,
113146"col":4,
114147"len":0
115148}
116149,
117150{
118- "path":"${text_file } ",
151+ "path":"${json_errors_text_file } ",
119152"message":"unexpected name `strings`, expecting `)`",
120153"line_nr":27,
121154"col":2,
122155"len":0
123156}
124157,
125158{
126- "path":"${text_file } ",
159+ "path":"${json_errors_text_file } ",
127160"message":"undefined ident: ``",
128161"line_nr":19,
129162"col":3,
130163"len":0
131164}
132165,
133166{
134- "path":"${text_file } ",
167+ "path":"${json_errors_text_file } ",
135168"message":"undefined ident: ``",
136169"line_nr":20,
137170"col":13,
138171"len":0
139172}
140173,
141174{
142- "path":"${text_file } ",
175+ "path":"${json_errors_text_file } ",
143176"message":"undefined ident: ``",
144177"line_nr":23,
145178"col":3,
146179"len":0
147180}
148181,
149182{
150- "path":"${text_file } ",
183+ "path":"${json_errors_text_file } ",
151184"message":"undefined ident: `j`",
152185"line_nr":26,
153186"col":2,
154187"len":0
155188}
156189,
157190{
158- "path":"${text_file } ",
191+ "path":"${json_errors_text_file } ",
159192"message":"`j` (no value) used as value in argument 1 to `string.all_before_last`",
160193"line_nr":26,
161194"col":2,
162195"len":0
163196}
164197,
165198{
166- "path":"${text_file } ",
199+ "path":"${json_errors_text_file } ",
167200"message":"undefined ident: ``",
168201"line_nr":26,
169202"col":28,
170203"len":0
171204}
172205,
173206{
174- "path":"${text_file } ",
207+ "path":"${json_errors_text_file } ",
175208"message":"`` (no value) used as value in argument 1 to `string.all_before_last`",
176209"line_nr":26,
177210"col":27,
178211"len":0
179212}
180213,
181214{
182- "path":"${text_file } ",
215+ "path":"${json_errors_text_file } ",
183216"message":"`string` has no property ``",
184217"line_nr":26,
185218"col":11,
186219"len":0
187220}
188221,
189222{
190- "path":"${text_file } ",
223+ "path":"${json_errors_text_file } ",
191224"message":"undefined ident: `builtin`",
192225"line_nr":28,
193226"col":2,
194227"len":0
195228}
196229,
197230{
198- "path":"${text_file } ",
231+ "path":"${json_errors_text_file } ",
199232"message":"`builtin` does not return a value",
200233"line_nr":28,
201234"col":2,
@@ -206,6 +239,43 @@ const test_data = [
206239 },
207240]
208241
242+ // copy from `vls`
243+ struct JsonError {
244+ path string
245+ message string
246+ line_nr int
247+ col int
248+ len int
249+ }
250+
251+ struct Detail {
252+ kind int // The type of item (e.g., Method, Function, Field)
253+ label string // The name of the completion item
254+ detail string // Additional info like the function signature or return type
255+ documentation string // The documentation for the item
256+ insert_text ? string @[json: 'insertText' ]
257+ insert_text_format ? int @[json: 'insertTextFormat' ] // 1 for PlainText, 2 for Snippet
258+ }
259+
260+ struct JsonVarAC {
261+ details []Detail
262+ }
263+
264+ struct SignatureHelp {
265+ signatures []SignatureInformation
266+ active_signature int @[json: 'activeSignature' ]
267+ active_parameter int @[json: 'activeParameter' ]
268+ }
269+
270+ struct SignatureInformation {
271+ label string
272+ parameters []ParameterInformation
273+ }
274+
275+ struct ParameterInformation {
276+ label string
277+ }
278+
209279fn test_main () {
210280 mut total_errors := 0
211281
@@ -236,6 +306,90 @@ fn test_main() {
236306 } else {
237307 println ('${term.green('OK ')} ${t.cmd} ' )
238308 }
309+
310+ // Try to decode the response message and verify
311+ // TODO: remove `unresolved type, maybe`
312+ if t.output.trim_space ().len > 0 && ! t.output.starts_with ('unresolved type, maybe' ) {
313+ dump (t.output)
314+ match t.method {
315+ .definition {
316+ check_valid_goto_definition (t.output)!
317+ }
318+ .completion {
319+ check_valid_auto_completion (t.output)!
320+ }
321+ .did_change {
322+ check_valid_json_errors (t.output)!
323+ }
324+ .signature_help {
325+ check_valid_fn_signature (t.output)!
326+ }
327+ else {}
328+ }
329+ }
239330 }
240331 assert total_errors == 0
241332}
333+
334+ fn check_valid_goto_definition (message string ) ! {
335+ // `/home/path/aaa.v:19:10`
336+ fields := message.split (':' )
337+ if fields.len > = 3 {
338+ path := fields[..fields.len - 2 ].join (':' )
339+ line_nr := fields[fields.len - 2 ].int ()
340+ col := fields[fields.len - 1 ].int ()
341+ if line_nr < = 0 {
342+ return error ('goto_definition: line_nr should > 0: ${line_nr} ' )
343+ }
344+ if col < = 0 {
345+ return error ('goto_definition: col should > 0: ${col} ' )
346+ }
347+ if path.len == 0 {
348+ return error ('goto_definition: file.len should > 0: ${path} ' )
349+ }
350+ } else {
351+ return error ('goto_definition: goto_definition format error' )
352+ }
353+ }
354+
355+ fn check_valid_auto_completion (message string ) ! {
356+ // {"kind":5,"label":"a","detail":"int","documentation":""},
357+ result := json.decode (JsonVarAC, message) or { return error ('completion: fail to json decode' ) }
358+ for detail in result.details {
359+ if detail.kind < = 0 || detail.kind > 25 {
360+ return error ('completion: kind should in 1-25 : ${detail.kind} ' )
361+ }
362+ }
363+ }
364+
365+ fn check_valid_json_errors (message string ) ! {
366+ results := json.decode ([]JsonError, message) or {
367+ return error ('json_errors: fail to json decode' )
368+ }
369+ for result in results {
370+ if result.path.len == 0 {
371+ return error ('json_errors: path.len should > 0' )
372+ }
373+ if result.message.len == 0 {
374+ return error ('json_errors: message.len should > 0' )
375+ }
376+ if result.line_nr < = 0 {
377+ return error ('json_errors: line_nr should > 0' )
378+ }
379+ if result.col < = 0 {
380+ return error ('json_errors: col should > 0' )
381+ }
382+ }
383+ }
384+
385+ fn check_valid_fn_signature (message string ) ! {
386+ result := json.decode (SignatureHelp, message) or {
387+ return error ('fn_signature: fail to json decode' )
388+ }
389+ if result.signatures.len != 1 {
390+ return error ('fn_signature: signatures.len != 1' )
391+ }
392+ if result.signatures[0 ].label.len == 0 {
393+ return error ('fn_signature: label.len == 0' )
394+ }
395+ }
0 commit comments