1" Test various aspects of the Vim9 script language.
2
3source check.vim
4
5" Check that "lines" inside ":def" results in an "error" message.
6func CheckDefFailure(lines, error)
7  call writefile(['def Func()'] + a:lines + ['enddef'], 'Xdef')
8  call assert_fails('so Xdef', a:error, a:lines)
9  call delete('Xdef')
10endfunc
11
12func CheckScriptFailure(lines, error)
13  call writefile(a:lines, 'Xdef')
14  call assert_fails('so Xdef', a:error, a:lines)
15  call delete('Xdef')
16endfunc
17
18def Test_syntax()
19  let var = 234
20  let other: list<string> = ['asdf']
21enddef
22
23func Test_def_basic()
24  def SomeFunc(): string
25    return 'yes'
26  enddef
27  call assert_equal('yes', SomeFunc())
28endfunc
29
30let s:appendToMe = 'xxx'
31let s:addToMe = 111
32
33def Test_assignment()
34  let bool1: bool = true
35  assert_equal(v:true, bool1)
36  let bool2: bool = false
37  assert_equal(v:false, bool2)
38
39  let list1: list<string> = ['sdf', 'asdf']
40  let list2: list<number> = [1, 2, 3]
41
42  " TODO: does not work yet
43  " let listS: list<string> = []
44  " let listN: list<number> = []
45
46  let dict1: dict<string> = #{key: 'value'}
47  let dict2: dict<number> = #{one: 1, two: 2}
48
49  v:char = 'abc'
50  assert_equal('abc', v:char)
51
52  $ENVVAR = 'foobar'
53  assert_equal('foobar', $ENVVAR)
54  $ENVVAR = ''
55
56  appendToMe ..= 'yyy'
57  assert_equal('xxxyyy', appendToMe)
58  addToMe += 222
59  assert_equal(333, addToMe)
60enddef
61
62func Test_assignment_failure()
63  call CheckDefFailure(['let var=234'], 'E1004:')
64  call CheckDefFailure(['let var =234'], 'E1004:')
65  call CheckDefFailure(['let var= 234'], 'E1004:')
66
67  call CheckDefFailure(['let true = 1'], 'E1034:')
68  call CheckDefFailure(['let false = 1'], 'E1034:')
69
70  call CheckDefFailure(['let var: list<string> = [123]'], 'expected list<string> but got list<number>')
71  call CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')
72
73  call CheckDefFailure(['let var: dict<string> = #{key: 123}'], 'expected dict<string> but got dict<number>')
74  call CheckDefFailure(['let var: dict<number> = #{key: "xx"}'], 'expected dict<number> but got dict<string>')
75
76  call CheckDefFailure(['let var = feedkeys("0")'], 'E1031:')
77  call CheckDefFailure(['let var: number = feedkeys("0")'], 'expected number but got void')
78endfunc
79
80func Test_const()
81  call CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:')
82  call CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:')
83  call CheckDefFailure(['const two'], 'E1021:')
84endfunc
85
86def Test_block()
87  let outer = 1
88  {
89    let inner = 2
90    assert_equal(1, outer)
91    assert_equal(2, inner)
92  }
93  assert_equal(1, outer)
94enddef
95
96func Test_block_failure()
97  call CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:')
98endfunc
99
100def ReturnString(): string
101  return 'string'
102enddef
103
104def ReturnNumber(): number
105  return 123
106enddef
107
108def Test_return_string()
109  assert_equal('string', ReturnString())
110  assert_equal(123, ReturnNumber())
111enddef
112
113func Increment()
114  let g:counter += 1
115endfunc
116
117def Test_call_ufunc_count()
118  g:counter = 1
119  Increment()
120  Increment()
121  Increment()
122  " works with and without :call
123  assert_equal(4, g:counter)
124  call assert_equal(4, g:counter)
125  unlet g:counter
126enddef
127
128def MyVarargs(arg: string, ...rest: list<string>): string
129  let res = arg
130  for s in rest
131    res ..= ',' .. s
132  endfor
133  return res
134enddef
135
136def Test_call_varargs()
137  assert_equal('one', MyVarargs('one'))
138  assert_equal('one,two', MyVarargs('one', 'two'))
139  assert_equal('one,two,three', MyVarargs('one', 'two', 'three'))
140enddef
141
142def MyDefaultArgs(name = 'string'): string
143  return name
144enddef
145
146def Test_call_default_args()
147  assert_equal('string', MyDefaultArgs())
148  assert_equal('one', MyDefaultArgs('one'))
149  assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
150enddef
151
152func Test_call_default_args_from_func()
153  call assert_equal('string', MyDefaultArgs())
154  call assert_equal('one', MyDefaultArgs('one'))
155  call assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
156endfunc
157
158" Default arg and varargs
159def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
160  let res = one .. ',' .. two
161  for s in rest
162    res ..= ',' .. s
163  endfor
164  return res
165enddef
166
167def Test_call_def_varargs()
168  call assert_fails('call MyDefVarargs()', 'E119:')
169  assert_equal('one,foo', MyDefVarargs('one'))
170  assert_equal('one,two', MyDefVarargs('one', 'two'))
171  assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three'))
172enddef
173
174
175"def Test_call_func_defined_later()
176"  call assert_equal('one', DefineLater('one'))
177"  call assert_fails('call NotDefined("one")', 'E99:')
178"enddef
179
180func DefineLater(arg)
181  return a:arg
182endfunc
183
184def Test_return_type_wrong()
185  CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
186  CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
187  CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef'], 'expected void but got string')
188  CheckScriptFailure(['def Func()', 'return "a"', 'enddef'], 'expected void but got string')
189enddef
190
191def Test_arg_type_wrong()
192  CheckScriptFailure(['def Func3(items: list)', 'echo "a"', 'enddef'], 'E1008: Missing <type>')
193enddef
194
195def Test_try_catch()
196  let l = []
197  try
198    add(l, '1')
199    throw 'wrong'
200    add(l, '2')
201  catch
202    add(l, v:exception)
203  finally
204    add(l, '3')
205  endtry
206  assert_equal(['1', 'wrong', '3'], l)
207enddef
208
209let s:export_script_lines =<< trim END
210  vim9script
211  let name: string = 'bob'
212  def Concat(arg: string): string
213    return name .. arg
214  enddef
215  let g:result = Concat('bie')
216  let g:localname = name
217
218  export const CONST = 1234
219  export let exported = 9876
220  export let exp_name = 'John'
221  export def Exported(): string
222    return 'Exported'
223  enddef
224END
225
226def Test_vim9script()
227  let import_script_lines =<< trim END
228    vim9script
229    import {exported, Exported} from './Xexport.vim'
230    g:imported = exported
231    exported += 3
232    g:imported_added = exported
233    g:imported_func = Exported()
234
235    import {exp_name} from './Xexport.vim'
236    g:imported_name = exp_name
237    exp_name ..= ' Doe'
238    g:imported_name_appended = exp_name
239  END
240
241  writefile(import_script_lines, 'Ximport.vim')
242  writefile(s:export_script_lines, 'Xexport.vim')
243
244  source Ximport.vim
245
246  assert_equal('bobbie', g:result)
247  assert_equal('bob', g:localname)
248  assert_equal(9876, g:imported)
249  assert_equal(9879, g:imported_added)
250  assert_equal('Exported', g:imported_func)
251  assert_equal('John', g:imported_name)
252  assert_equal('John Doe', g:imported_name_appended)
253  assert_false(exists('g:name'))
254
255  unlet g:result
256  unlet g:localname
257  unlet g:imported
258  unlet g:imported_added
259  unlet g:imported_func
260  unlet g:imported_name g:imported_name_appended
261  delete('Ximport.vim')
262  delete('Xexport.vim')
263
264  CheckScriptFailure(['scriptversion 2', 'vim9script'], 'E1039:')
265  CheckScriptFailure(['vim9script', 'scriptversion 2'], 'E1040:')
266enddef
267
268def Test_vim9script_call()
269  let lines =<< trim END
270    vim9script
271    let var = ''
272    def MyFunc(arg: string)
273       var = arg
274    enddef
275    MyFunc('foobar')
276    assert_equal('foobar', var)
277
278    let str = 'barfoo'
279    str->MyFunc()
280    assert_equal('barfoo', var)
281
282    let g:value = 'value'
283    g:value->MyFunc()
284    assert_equal('value', var)
285
286    let listvar = []
287    def ListFunc(arg: list<number>)
288       listvar = arg
289    enddef
290    [1, 2, 3]->ListFunc()
291    assert_equal([1, 2, 3], listvar)
292
293    let dictvar = {}
294    def DictFunc(arg: dict<number>)
295       dictvar = arg
296    enddef
297    {'a': 1, 'b': 2}->DictFunc()
298    assert_equal(#{a: 1, b: 2}, dictvar)
299    #{a: 3, b: 4}->DictFunc()
300    assert_equal(#{a: 3, b: 4}, dictvar)
301  END
302  writefile(lines, 'Xcall.vim')
303  source Xcall.vim
304  delete('Xcall.vim')
305enddef
306
307def Test_vim9script_call_fail_decl()
308  let lines =<< trim END
309    vim9script
310    let var = ''
311    def MyFunc(arg: string)
312       let var = 123
313    enddef
314  END
315  writefile(lines, 'Xcall_decl.vim')
316  assert_fails('source Xcall_decl.vim', 'E1054:')
317  delete('Xcall_decl.vim')
318enddef
319
320def Test_vim9script_call_fail_const()
321  let lines =<< trim END
322    vim9script
323    const var = ''
324    def MyFunc(arg: string)
325       var = 'asdf'
326    enddef
327  END
328  writefile(lines, 'Xcall_const.vim')
329  assert_fails('source Xcall_const.vim', 'E46:')
330  delete('Xcall_const.vim')
331enddef
332
333def Test_vim9script_reload()
334  let lines =<< trim END
335    vim9script
336    const var = ''
337    let valone = 1234
338    def MyFunc(arg: string)
339       valone = 5678
340    enddef
341  END
342  let morelines =<< trim END
343    let valtwo = 222
344    export def GetValtwo(): number
345      return valtwo
346    enddef
347  END
348  writefile(lines + morelines, 'Xreload.vim')
349  source Xreload.vim
350  source Xreload.vim
351  source Xreload.vim
352
353  let testlines =<< trim END
354    vim9script
355    def TheFunc()
356      import GetValtwo from './Xreload.vim'
357      assert_equal(222, GetValtwo())
358    enddef
359    TheFunc()
360  END
361  writefile(testlines, 'Ximport.vim')
362  source Ximport.vim
363
364  " test that when not using "morelines" valtwo is still defined
365  " need to source Xreload.vim again, import doesn't reload a script
366  writefile(lines, 'Xreload.vim')
367  source Xreload.vim
368  source Ximport.vim
369
370  " cannot declare a var twice
371  lines =<< trim END
372    vim9script
373    let valone = 1234
374    let valone = 5678
375  END
376  writefile(lines, 'Xreload.vim')
377  assert_fails('source Xreload.vim', 'E1041:')
378
379  delete('Xreload.vim')
380  delete('Ximport.vim')
381enddef
382
383def Test_import_absolute()
384  let import_lines = [
385        \ 'vim9script',
386        \ 'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"',
387        \ 'def UseExported()',
388        \ '  g:imported_abs = exported',
389        \ '  exported = 8888',
390        \ '  g:imported_after = exported',
391        \ 'enddef',
392        \ 'UseExported()',
393        \ 'g:import_disassembled = execute("disass UseExported")',
394        \ ]
395  writefile(import_lines, 'Ximport_abs.vim')
396  writefile(s:export_script_lines, 'Xexport_abs.vim')
397
398  source Ximport_abs.vim
399
400  assert_equal(9876, g:imported_abs)
401  assert_equal(8888, g:imported_after)
402  assert_match('<SNR>\d\+_UseExported.*'
403        \ .. 'g:imported_abs = exported.*'
404        \ .. '0 LOADSCRIPT exported from .*Xexport_abs.vim.*'
405        \ .. '1 STOREG g:imported_abs.*'
406        \ .. 'exported = 8888.*'
407        \ .. '3 STORESCRIPT exported in .*Xexport_abs.vim.*'
408        \ .. 'g:imported_after = exported.*'
409        \ .. '4 LOADSCRIPT exported from .*Xexport_abs.vim.*'
410        \ .. '5 STOREG g:imported_after.*'
411        \, g:import_disassembled)
412  unlet g:imported_abs
413  unlet g:import_disassembled
414
415  delete('Ximport_abs.vim')
416  delete('Xexport_abs.vim')
417enddef
418
419def Test_import_rtp()
420  let import_lines = [
421        \ 'vim9script',
422        \ 'import exported from "Xexport_rtp.vim"',
423        \ 'g:imported_rtp = exported',
424        \ ]
425  writefile(import_lines, 'Ximport_rtp.vim')
426  mkdir('import')
427  writefile(s:export_script_lines, 'import/Xexport_rtp.vim')
428
429  let save_rtp = &rtp
430  &rtp = getcwd()
431  source Ximport_rtp.vim
432  &rtp = save_rtp
433
434  assert_equal(9876, g:imported_rtp)
435  unlet g:imported_rtp
436
437  delete('Ximport_rtp.vim')
438  delete('import/Xexport_rtp.vim')
439  delete('import', 'd')
440enddef
441
442def Test_fixed_size_list()
443  " will be allocated as one piece of memory, check that changes work
444  let l = [1, 2, 3, 4]
445  l->remove(0)
446  l->add(5)
447  l->insert(99, 1)
448  assert_equal([2, 99, 3, 4, 5], l)
449enddef
450
451" Test that inside :function a Python function can be defined, :def is not
452" recognized.
453func Test_function_python()
454  CheckFeature python3
455  let py = 'python3'
456  execute py "<< EOF"
457def do_something():
458  return 1
459EOF
460endfunc
461
462def IfElse(what: number): string
463  let res = ''
464  if what == 1
465    res = "one"
466  elseif what == 2
467    res = "two"
468  else
469    res = "three"
470  endif
471  return res
472enddef
473
474def Test_if_elseif_else()
475  assert_equal('one', IfElse(1))
476  assert_equal('two', IfElse(2))
477  assert_equal('three', IfElse(3))
478enddef
479
480
481" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
482