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
32let g:existing = 'yes'
33
34def Test_assignment()
35  let bool1: bool = true
36  assert_equal(v:true, bool1)
37  let bool2: bool = false
38  assert_equal(v:false, bool2)
39
40  let list1: list<bool> = [false, true, false]
41  let list2: list<number> = [1, 2, 3]
42  let list3: list<string> = ['sdf', 'asdf']
43  let list4: list<any> = ['yes', true, 1234]
44  let list5: list<blob> = [0z01, 0z02]
45
46  let listS: list<string> = []
47  let listN: list<number> = []
48
49  let dict1: dict<bool> = #{one: false, two: true}
50  let dict2: dict<number> = #{one: 1, two: 2}
51  let dict3: dict<string> = #{key: 'value'}
52  let dict4: dict<any> = #{one: 1, two: '2'}
53  let dict5: dict<blob> = #{one: 0z01, tw: 0z02}
54
55  g:newvar = 'new'
56  assert_equal('new', g:newvar)
57
58  assert_equal('yes', g:existing)
59  g:existing = 'no'
60  assert_equal('no', g:existing)
61
62  v:char = 'abc'
63  assert_equal('abc', v:char)
64
65  $ENVVAR = 'foobar'
66  assert_equal('foobar', $ENVVAR)
67  $ENVVAR = ''
68
69  s:appendToMe ..= 'yyy'
70  assert_equal('xxxyyy', s:appendToMe)
71  s:addToMe += 222
72  assert_equal(333, s:addToMe)
73  s:newVar = 'new'
74  assert_equal('new', s:newVar)
75enddef
76
77func Test_assignment_failure()
78  call CheckDefFailure(['let var=234'], 'E1004:')
79  call CheckDefFailure(['let var =234'], 'E1004:')
80  call CheckDefFailure(['let var= 234'], 'E1004:')
81
82  call CheckDefFailure(['let true = 1'], 'E1034:')
83  call CheckDefFailure(['let false = 1'], 'E1034:')
84
85  call CheckDefFailure(['let var: list<string> = [123]'], 'expected list<string> but got list<number>')
86  call CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')
87
88  call CheckDefFailure(['let var: dict<string> = #{key: 123}'], 'expected dict<string> but got dict<number>')
89  call CheckDefFailure(['let var: dict<number> = #{key: "xx"}'], 'expected dict<number> but got dict<string>')
90
91  call CheckDefFailure(['let var = feedkeys("0")'], 'E1031:')
92  call CheckDefFailure(['let var: number = feedkeys("0")'], 'expected number but got void')
93endfunc
94
95func Test_const()
96  call CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:')
97  call CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:')
98  call CheckDefFailure(['const two'], 'E1021:')
99endfunc
100
101def Test_block()
102  let outer = 1
103  {
104    let inner = 2
105    assert_equal(1, outer)
106    assert_equal(2, inner)
107  }
108  assert_equal(1, outer)
109enddef
110
111func Test_block_failure()
112  call CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:')
113endfunc
114
115def ReturnString(): string
116  return 'string'
117enddef
118
119def ReturnNumber(): number
120  return 123
121enddef
122
123let g:notNumber = 'string'
124
125def ReturnGlobal(): number
126  return g:notNumber
127enddef
128
129def Test_return_string()
130  assert_equal('string', ReturnString())
131  assert_equal(123, ReturnNumber())
132  assert_fails('call ReturnGlobal()', 'E1029: Expected number but got string')
133enddef
134
135func Increment()
136  let g:counter += 1
137endfunc
138
139def Test_call_ufunc_count()
140  g:counter = 1
141  Increment()
142  Increment()
143  Increment()
144  " works with and without :call
145  assert_equal(4, g:counter)
146  call assert_equal(4, g:counter)
147  unlet g:counter
148enddef
149
150def MyVarargs(arg: string, ...rest: list<string>): string
151  let res = arg
152  for s in rest
153    res ..= ',' .. s
154  endfor
155  return res
156enddef
157
158def Test_call_varargs()
159  assert_equal('one', MyVarargs('one'))
160  assert_equal('one,two', MyVarargs('one', 'two'))
161  assert_equal('one,two,three', MyVarargs('one', 'two', 'three'))
162enddef
163
164def MyDefaultArgs(name = 'string'): string
165  return name
166enddef
167
168def Test_call_default_args()
169  assert_equal('string', MyDefaultArgs())
170  assert_equal('one', MyDefaultArgs('one'))
171  assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
172enddef
173
174func Test_call_default_args_from_func()
175  call assert_equal('string', MyDefaultArgs())
176  call assert_equal('one', MyDefaultArgs('one'))
177  call assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
178endfunc
179
180" Default arg and varargs
181def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
182  let res = one .. ',' .. two
183  for s in rest
184    res ..= ',' .. s
185  endfor
186  return res
187enddef
188
189def Test_call_def_varargs()
190  call assert_fails('call MyDefVarargs()', 'E119:')
191  assert_equal('one,foo', MyDefVarargs('one'))
192  assert_equal('one,two', MyDefVarargs('one', 'two'))
193  assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three'))
194enddef
195
196
197"def Test_call_func_defined_later()
198"  call assert_equal('one', DefineLater('one'))
199"  call assert_fails('call NotDefined("one")', 'E99:')
200"enddef
201
202func DefineLater(arg)
203  return a:arg
204endfunc
205
206def Test_return_type_wrong()
207  CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
208  CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
209  CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef'], 'expected void but got string')
210  CheckScriptFailure(['def Func()', 'return "a"', 'enddef'], 'expected void but got string')
211enddef
212
213def Test_arg_type_wrong()
214  CheckScriptFailure(['def Func3(items: list)', 'echo "a"', 'enddef'], 'E1008: Missing <type>')
215enddef
216
217def Test_try_catch()
218  let l = []
219  try
220    add(l, '1')
221    throw 'wrong'
222    add(l, '2')
223  catch
224    add(l, v:exception)
225  finally
226    add(l, '3')
227  endtry
228  assert_equal(['1', 'wrong', '3'], l)
229enddef
230
231def ThrowFromDef()
232  throw 'getout'
233enddef
234
235func CatchInFunc()
236  try
237    call ThrowFromDef()
238  catch
239    let g:thrown_func = v:exception
240  endtry
241endfunc
242
243def CatchInDef()
244  try
245    ThrowFromDef()
246  catch
247    g:thrown_def = v:exception
248  endtry
249enddef
250
251def ReturnFinally(): string
252  try
253    return 'intry'
254  finally
255    g:in_finally = 'finally'
256  endtry
257  return 'end'
258enddef
259
260def Test_try_catch_nested()
261  CatchInFunc()
262  assert_equal('getout', g:thrown_func)
263
264  CatchInDef()
265  assert_equal('getout', g:thrown_def)
266
267  assert_equal('intry', ReturnFinally())
268  assert_equal('finally', g:in_finally)
269enddef
270
271def Test_try_catch_match()
272  let seq = 'a'
273  try
274    throw 'something'
275  catch /nothing/
276    seq ..= 'x'
277  catch /some/
278    seq ..= 'b'
279  catch /asdf/
280    seq ..= 'x'
281  finally
282    seq ..= 'c'
283  endtry
284  assert_equal('abc', seq)
285enddef
286
287let s:export_script_lines =<< trim END
288  vim9script
289  let name: string = 'bob'
290  def Concat(arg: string): string
291    return name .. arg
292  enddef
293  let g:result = Concat('bie')
294  let g:localname = name
295
296  export const CONST = 1234
297  export let exported = 9876
298  export let exp_name = 'John'
299  export def Exported(): string
300    return 'Exported'
301  enddef
302END
303
304def Test_vim9script()
305  let import_script_lines =<< trim END
306    vim9script
307    import {exported, Exported} from './Xexport.vim'
308    g:imported = exported
309    exported += 3
310    g:imported_added = exported
311    g:imported_func = Exported()
312
313    import {exp_name} from './Xexport.vim'
314    g:imported_name = exp_name
315    exp_name ..= ' Doe'
316    g:imported_name_appended = exp_name
317  END
318
319  writefile(import_script_lines, 'Ximport.vim')
320  writefile(s:export_script_lines, 'Xexport.vim')
321
322  source Ximport.vim
323
324  assert_equal('bobbie', g:result)
325  assert_equal('bob', g:localname)
326  assert_equal(9876, g:imported)
327  assert_equal(9879, g:imported_added)
328  assert_equal('Exported', g:imported_func)
329  assert_equal('John', g:imported_name)
330  assert_equal('John Doe', g:imported_name_appended)
331  assert_false(exists('g:name'))
332
333  unlet g:result
334  unlet g:localname
335  unlet g:imported
336  unlet g:imported_added
337  unlet g:imported_func
338  unlet g:imported_name g:imported_name_appended
339  delete('Ximport.vim')
340
341  let import_star_as_lines =<< trim END
342    vim9script
343    import * as Export from './Xexport.vim'
344    def UseExport()
345      g:imported = Export.exported
346    enddef
347    UseExport()
348  END
349  writefile(import_star_as_lines, 'Ximport.vim')
350  source Ximport.vim
351  assert_equal(9876, g:imported)
352
353  let import_star_lines =<< trim END
354    vim9script
355    import * from './Xexport.vim'
356    g:imported = exported
357  END
358  writefile(import_star_lines, 'Ximport.vim')
359  assert_fails('source Ximport.vim', 'E1045:')
360
361  " try to import something that exists but is not exported
362  let import_not_exported_lines =<< trim END
363    vim9script
364    import name from './Xexport.vim'
365  END
366  writefile(import_not_exported_lines, 'Ximport.vim')
367  assert_fails('source Ximport.vim', 'E1049:')
368
369  " import a very long name, requires making a copy
370  let import_long_name_lines =<< trim END
371    vim9script
372    import name012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 from './Xexport.vim'
373  END
374  writefile(import_long_name_lines, 'Ximport.vim')
375  assert_fails('source Ximport.vim', 'E1048:')
376
377  let import_no_from_lines =<< trim END
378    vim9script
379    import name './Xexport.vim'
380  END
381  writefile(import_no_from_lines, 'Ximport.vim')
382  assert_fails('source Ximport.vim', 'E1070:')
383
384  let import_invalid_string_lines =<< trim END
385    vim9script
386    import name from Xexport.vim
387  END
388  writefile(import_invalid_string_lines, 'Ximport.vim')
389  assert_fails('source Ximport.vim', 'E1071:')
390
391  let import_wrong_name_lines =<< trim END
392    vim9script
393    import name from './XnoExport.vim'
394  END
395  writefile(import_wrong_name_lines, 'Ximport.vim')
396  assert_fails('source Ximport.vim', 'E1053:')
397
398  let import_missing_comma_lines =<< trim END
399    vim9script
400    import {exported name} from './Xexport.vim'
401  END
402  writefile(import_missing_comma_lines, 'Ximport.vim')
403  assert_fails('source Ximport.vim', 'E1046:')
404
405  delete('Ximport.vim')
406  delete('Xexport.vim')
407
408  " Check that in a Vim9 script 'cpo' is set to the Vim default.
409  set cpo&vi
410  let cpo_before = &cpo
411  let lines =<< trim END
412    vim9script
413    g:cpo_in_vim9script = &cpo
414  END
415  writefile(lines, 'Xvim9_script')
416  source Xvim9_script
417  assert_equal(cpo_before, &cpo)
418  set cpo&vim
419  assert_equal(&cpo, g:cpo_in_vim9script)
420  delete('Xvim9_script')
421enddef
422
423def Test_vim9script_fails()
424  CheckScriptFailure(['scriptversion 2', 'vim9script'], 'E1039:')
425  CheckScriptFailure(['vim9script', 'scriptversion 2'], 'E1040:')
426  CheckScriptFailure(['export let some = 123'], 'E1042:')
427  CheckScriptFailure(['import some from "./Xexport.vim"'], 'E1042:')
428  CheckScriptFailure(['vim9script', 'export let g:some'], 'E1044:')
429  CheckScriptFailure(['vim9script', 'export echo 134'], 'E1043:')
430
431  assert_fails('vim9script', 'E1038')
432  assert_fails('export something', 'E1042')
433enddef
434
435def Test_vim9script_call()
436  let lines =<< trim END
437    vim9script
438    let var = ''
439    def MyFunc(arg: string)
440       var = arg
441    enddef
442    MyFunc('foobar')
443    assert_equal('foobar', var)
444
445    let str = 'barfoo'
446    str->MyFunc()
447    assert_equal('barfoo', var)
448
449    let g:value = 'value'
450    g:value->MyFunc()
451    assert_equal('value', var)
452
453    let listvar = []
454    def ListFunc(arg: list<number>)
455       listvar = arg
456    enddef
457    [1, 2, 3]->ListFunc()
458    assert_equal([1, 2, 3], listvar)
459
460    let dictvar = {}
461    def DictFunc(arg: dict<number>)
462       dictvar = arg
463    enddef
464    {'a': 1, 'b': 2}->DictFunc()
465    assert_equal(#{a: 1, b: 2}, dictvar)
466    #{a: 3, b: 4}->DictFunc()
467    assert_equal(#{a: 3, b: 4}, dictvar)
468
469    ('text')->MyFunc()
470    assert_equal('text', var)
471    ("some")->MyFunc()
472    assert_equal('some', var)
473  END
474  writefile(lines, 'Xcall.vim')
475  source Xcall.vim
476  delete('Xcall.vim')
477enddef
478
479def Test_vim9script_call_fail_decl()
480  let lines =<< trim END
481    vim9script
482    let var = ''
483    def MyFunc(arg: string)
484       let var = 123
485    enddef
486  END
487  writefile(lines, 'Xcall_decl.vim')
488  assert_fails('source Xcall_decl.vim', 'E1054:')
489  delete('Xcall_decl.vim')
490enddef
491
492def Test_vim9script_call_fail_const()
493  let lines =<< trim END
494    vim9script
495    const var = ''
496    def MyFunc(arg: string)
497       var = 'asdf'
498    enddef
499  END
500  writefile(lines, 'Xcall_const.vim')
501  assert_fails('source Xcall_const.vim', 'E46:')
502  delete('Xcall_const.vim')
503enddef
504
505def Test_vim9script_reload()
506  let lines =<< trim END
507    vim9script
508    const var = ''
509    let valone = 1234
510    def MyFunc(arg: string)
511       valone = 5678
512    enddef
513  END
514  let morelines =<< trim END
515    let valtwo = 222
516    export def GetValtwo(): number
517      return valtwo
518    enddef
519  END
520  writefile(lines + morelines, 'Xreload.vim')
521  source Xreload.vim
522  source Xreload.vim
523  source Xreload.vim
524
525  let testlines =<< trim END
526    vim9script
527    def TheFunc()
528      import GetValtwo from './Xreload.vim'
529      assert_equal(222, GetValtwo())
530    enddef
531    TheFunc()
532  END
533  writefile(testlines, 'Ximport.vim')
534  source Ximport.vim
535
536  " test that when not using "morelines" valtwo is still defined
537  " need to source Xreload.vim again, import doesn't reload a script
538  writefile(lines, 'Xreload.vim')
539  source Xreload.vim
540  source Ximport.vim
541
542  " cannot declare a var twice
543  lines =<< trim END
544    vim9script
545    let valone = 1234
546    let valone = 5678
547  END
548  writefile(lines, 'Xreload.vim')
549  assert_fails('source Xreload.vim', 'E1041:')
550
551  delete('Xreload.vim')
552  delete('Ximport.vim')
553enddef
554
555def Test_import_absolute()
556  let import_lines = [
557        \ 'vim9script',
558        \ 'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"',
559        \ 'def UseExported()',
560        \ '  g:imported_abs = exported',
561        \ '  exported = 8888',
562        \ '  g:imported_after = exported',
563        \ 'enddef',
564        \ 'UseExported()',
565        \ 'g:import_disassembled = execute("disass UseExported")',
566        \ ]
567  writefile(import_lines, 'Ximport_abs.vim')
568  writefile(s:export_script_lines, 'Xexport_abs.vim')
569
570  source Ximport_abs.vim
571
572  assert_equal(9876, g:imported_abs)
573  assert_equal(8888, g:imported_after)
574  assert_match('<SNR>\d\+_UseExported.*'
575        \ .. 'g:imported_abs = exported.*'
576        \ .. '0 LOADSCRIPT exported from .*Xexport_abs.vim.*'
577        \ .. '1 STOREG g:imported_abs.*'
578        \ .. 'exported = 8888.*'
579        \ .. '3 STORESCRIPT exported in .*Xexport_abs.vim.*'
580        \ .. 'g:imported_after = exported.*'
581        \ .. '4 LOADSCRIPT exported from .*Xexport_abs.vim.*'
582        \ .. '5 STOREG g:imported_after.*'
583        \, g:import_disassembled)
584  unlet g:imported_abs
585  unlet g:import_disassembled
586
587  delete('Ximport_abs.vim')
588  delete('Xexport_abs.vim')
589enddef
590
591def Test_import_rtp()
592  let import_lines = [
593        \ 'vim9script',
594        \ 'import exported from "Xexport_rtp.vim"',
595        \ 'g:imported_rtp = exported',
596        \ ]
597  writefile(import_lines, 'Ximport_rtp.vim')
598  mkdir('import')
599  writefile(s:export_script_lines, 'import/Xexport_rtp.vim')
600
601  let save_rtp = &rtp
602  &rtp = getcwd()
603  source Ximport_rtp.vim
604  &rtp = save_rtp
605
606  assert_equal(9876, g:imported_rtp)
607  unlet g:imported_rtp
608
609  delete('Ximport_rtp.vim')
610  delete('import/Xexport_rtp.vim')
611  delete('import', 'd')
612enddef
613
614def Test_fixed_size_list()
615  " will be allocated as one piece of memory, check that changes work
616  let l = [1, 2, 3, 4]
617  l->remove(0)
618  l->add(5)
619  l->insert(99, 1)
620  assert_equal([2, 99, 3, 4, 5], l)
621enddef
622
623" Test that inside :function a Python function can be defined, :def is not
624" recognized.
625func Test_function_python()
626  CheckFeature python3
627  let py = 'python3'
628  execute py "<< EOF"
629def do_something():
630  return 1
631EOF
632endfunc
633
634def IfElse(what: number): string
635  let res = ''
636  if what == 1
637    res = "one"
638  elseif what == 2
639    res = "two"
640  else
641    res = "three"
642  endif
643  return res
644enddef
645
646def Test_if_elseif_else()
647  assert_equal('one', IfElse(1))
648  assert_equal('two', IfElse(2))
649  assert_equal('three', IfElse(3))
650enddef
651
652def Test_delfunc()
653  let lines =<< trim END
654    vim9script
655    def GoneSoon()
656      echo 'hello'
657    enddef
658
659    def CallGoneSoon()
660      GoneSoon()
661    enddef
662
663    delfunc GoneSoon
664    CallGoneSoon()
665  END
666  writefile(lines, 'XToDelFunc')
667  assert_fails('so XToDelFunc', 'E933')
668  assert_fails('so XToDelFunc', 'E933')
669
670  delete('XToDelFunc')
671enddef
672
673def Test_substitute_cmd()
674  new
675  setline(1, 'something')
676  :substitute(some(other(
677  assert_equal('otherthing', getline(1))
678  bwipe!
679
680  " also when the context is Vim9 script
681  let lines =<< trim END
682    vim9script
683    new
684    setline(1, 'something')
685    :substitute(some(other(
686    assert_equal('otherthing', getline(1))
687    bwipe!
688  END
689  writefile(lines, 'Xvim9lines')
690  source Xvim9lines
691
692  delete('Xvim9lines')
693enddef
694
695
696" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
697