blob: 553ef8fdccc42698a500b2d731139a51a3930f13 [file] [log] [blame]
Bram Moolenaard8448622022-01-07 21:39:52 +00001" Test import/export of the Vim9 script language.
Bram Moolenaar160aa862022-01-10 21:29:57 +00002" Also the autoload mechanism.
Bram Moolenaard8448622022-01-07 21:39:52 +00003
4source check.vim
5source term_util.vim
6source vim9.vim
7
8let s:export_script_lines =<< trim END
9 vim9script
10 var name: string = 'bob'
11 def Concat(arg: string): string
12 return name .. arg
13 enddef
14 g:result = Concat('bie')
15 g:localname = name
16
17 export const CONST = 1234
18 export var exported = 9876
19 export var exp_name = 'John'
20 export def Exported(): string
21 return 'Exported'
22 enddef
23 export def ExportedValue(): number
24 return exported
25 enddef
26 export def ExportedInc()
27 exported += 5
28 enddef
29 export final theList = [1]
30END
31
32def Undo_export_script_lines()
33 unlet g:result
34 unlet g:localname
35enddef
36
37def Test_vim9_import_export()
38 writefile(s:export_script_lines, 'Xexport.vim')
39 var import_script_lines =<< trim END
40 vim9script
41 var dir = './'
42 var ext = ".vim"
43 import dir .. 'Xexport' .. ext as expo
44
45 g:exported1 = expo.exported
46 expo.exported += 3
47 g:exported2 = expo.exported
48 g:exported3 = expo.ExportedValue()
49
50 expo.ExportedInc()
51 g:exported_i1 = expo.exported
52 g:exported_i2 = expo.ExportedValue()
53
54 expo.exported = 11
55 g:exported_s1 = expo.exported
56 g:exported_s2 = expo.ExportedValue()
57
58 g:imported_func = expo.Exported()
59
60 def GetExported(): string
61 var local_dict = {ref: expo.Exported}
62 return local_dict.ref()
63 enddef
64 g:funcref_result = GetExported()
65
66 g:imported_name = expo.exp_name
67 expo.exp_name ..= ' Doe'
68 g:imported_name_appended = expo.exp_name
69 g:exported_later = expo.exported
70
71 expo.theList->add(2)
72 assert_equal([1, 2], expo.theList)
73 END
74 writefile(import_script_lines, 'Ximport.vim')
75 source Ximport.vim
76
77 assert_equal('bobbie', g:result)
78 assert_equal('bob', g:localname)
79 assert_equal(9876, g:exported1)
80 assert_equal(9879, g:exported2)
81 assert_equal(9879, g:exported3)
82
83 assert_equal(9884, g:exported_i1)
84 assert_equal(9884, g:exported_i2)
85
86 assert_equal(11, g:exported_s1)
87 assert_equal(11, g:exported_s2)
88 assert_equal(11, g:exported_later)
89
90 assert_equal('Exported', g:imported_func)
91 assert_equal('Exported', g:funcref_result)
92 assert_equal('John', g:imported_name)
93 assert_equal('John Doe', g:imported_name_appended)
94 assert_false(exists('g:name'))
95
96 Undo_export_script_lines()
97 unlet g:exported1
98 unlet g:exported2
99 unlet g:exported3
100 unlet g:exported_i1
101 unlet g:exported_i2
102 unlet g:exported_later
103 unlet g:imported_func
104 unlet g:imported_name g:imported_name_appended
105 delete('Ximport.vim')
106
107 # similar, with line breaks
108 var import_line_break_script_lines =<< trim END
109 vim9script
110 import './Xexport.vim'
111 as expo
112 g:exported = expo.exported
113 expo.exported += 7
114 g:exported_added = expo.exported
115 g:imported_func = expo.Exported()
116 END
117 writefile(import_line_break_script_lines, 'Ximport_lbr.vim')
118 source Ximport_lbr.vim
119
120 assert_equal(11, g:exported)
121 assert_equal(18, g:exported_added)
122 assert_equal('Exported', g:imported_func)
123
124 # exported script not sourced again
125 assert_false(exists('g:result'))
126 unlet g:exported
127 unlet g:exported_added
128 unlet g:imported_func
129 delete('Ximport_lbr.vim')
130
131 var line_break_before_dot =<< trim END
132 vim9script
133 import './Xexport.vim' as expo
134 g:exported = expo
135 .exported
136 END
137 writefile(line_break_before_dot, 'Ximport_lbr_before_dot.vim')
138 assert_fails('source Ximport_lbr_before_dot.vim', 'E1060:', '', 3)
139 delete('Ximport_lbr_before_dot.vim')
140
141 var line_break_after_dot =<< trim END
142 vim9script
143 import './Xexport.vim' as expo
144 g:exported = expo.
145 exported
146 END
147 writefile(line_break_after_dot, 'Ximport_lbr_after_dot.vim')
148 assert_fails('source Ximport_lbr_after_dot.vim', 'E1074:', '', 3)
149 delete('Ximport_lbr_after_dot.vim')
150
151 var import_star_as_lines =<< trim END
152 vim9script
153 import './Xexport.vim' as Export
154 def UseExport()
155 g:exported_def = Export.exported
156 enddef
157 g:exported_script = Export.exported
158 assert_equal(1, exists('Export.exported'))
159 assert_equal(0, exists('Export.notexported'))
160 UseExport()
161 END
162 writefile(import_star_as_lines, 'Ximport.vim')
163 source Ximport.vim
164
165 assert_equal(18, g:exported_def)
166 assert_equal(18, g:exported_script)
167 unlet g:exported_def
168 unlet g:exported_script
169
170 var import_star_as_lines_no_dot =<< trim END
171 vim9script
172 import './Xexport.vim' as Export
173 def Func()
174 var dummy = 1
175 var imported = Export + dummy
176 enddef
177 defcompile
178 END
179 writefile(import_star_as_lines_no_dot, 'Ximport.vim')
180 assert_fails('source Ximport.vim', 'E1060:', '', 2, 'Func')
181
182 var import_star_as_lines_dot_space =<< trim END
183 vim9script
184 import './Xexport.vim' as Export
185 def Func()
186 var imported = Export . exported
187 enddef
188 defcompile
189 END
190 writefile(import_star_as_lines_dot_space, 'Ximport.vim')
191 assert_fails('source Ximport.vim', 'E1074:', '', 1, 'Func')
192
Bram Moolenaar7c24dfd2022-01-08 17:03:55 +0000193 writefile(s:export_script_lines, 'Xexport2.vim')
194 var import_as_duplicated =<< trim END
Bram Moolenaard8448622022-01-07 21:39:52 +0000195 vim9script
196 import './Xexport.vim' as expo
Bram Moolenaar7c24dfd2022-01-08 17:03:55 +0000197 import './Xexport2.vim' as expo
Bram Moolenaard8448622022-01-07 21:39:52 +0000198 END
Bram Moolenaar7c24dfd2022-01-08 17:03:55 +0000199 writefile(import_as_duplicated, 'Ximport.vim')
Bram Moolenaard8448622022-01-07 21:39:52 +0000200 assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim')
Bram Moolenaar7c24dfd2022-01-08 17:03:55 +0000201 delete('Xexport2.vim')
Bram Moolenaard8448622022-01-07 21:39:52 +0000202
203 var import_star_as_lines_script_no_dot =<< trim END
204 vim9script
205 import './Xexport.vim' as Export
206 g:imported_script = Export exported
207 END
208 writefile(import_star_as_lines_script_no_dot, 'Ximport.vim')
209 assert_fails('source Ximport.vim', 'E1060: Expected dot after name: Export exported')
210
211 var import_star_as_lines_script_space_after_dot =<< trim END
212 vim9script
213 import './Xexport.vim' as Export
214 g:imported_script = Export. exported
215 END
216 writefile(import_star_as_lines_script_space_after_dot, 'Ximport.vim')
217 assert_fails('source Ximport.vim', 'E1074:')
218
219 var import_star_as_lines_missing_name =<< trim END
220 vim9script
221 import './Xexport.vim' as Export
222 def Func()
223 var imported = Export.
224 enddef
225 defcompile
226 END
227 writefile(import_star_as_lines_missing_name, 'Ximport.vim')
228 assert_fails('source Ximport.vim', 'E1048:', '', 1, 'Func')
229
230 var import_star_as_lbr_lines =<< trim END
231 vim9script
232 import './Xexport.vim'
233 as Export
234 def UseExport()
235 g:exported = Export.exported
236 enddef
237 UseExport()
238 END
239 writefile(import_star_as_lbr_lines, 'Ximport.vim')
240 source Ximport.vim
241 assert_equal(18, g:exported)
242 unlet g:exported
243
244 # try to use something that exists but is not exported
245 var import_not_exported_lines =<< trim END
246 vim9script
247 import './Xexport.vim' as expo
248 echo expo.name
249 END
250 writefile(import_not_exported_lines, 'Ximport.vim')
251 assert_fails('source Ximport.vim', 'E1049:', '', 3, 'Ximport.vim')
252
253 # try to import something that is already defined
254 var import_already_defined =<< trim END
255 vim9script
256 var exported = 'something'
257 import './Xexport.vim' as exported
258 END
259 writefile(import_already_defined, 'Ximport.vim')
260 assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim')
261
262 # try changing an imported const
263 var import_assign_to_const =<< trim END
264 vim9script
265 import './Xexport.vim' as expo
266 def Assign()
267 expo.CONST = 987
268 enddef
269 defcompile
270 END
271 writefile(import_assign_to_const, 'Ximport.vim')
272 assert_fails('source Ximport.vim', 'E46:', '', 1, '_Assign')
273
274 # try changing an imported final
275 var import_assign_to_final =<< trim END
276 vim9script
277 import './Xexport.vim' as expo
278 def Assign()
279 expo.theList = [2]
280 enddef
281 defcompile
282 END
283 writefile(import_assign_to_final, 'Ximport.vim')
284 assert_fails('source Ximport.vim', 'E46:', '', 1, '_Assign')
285
286 var import_no_as_lines =<< trim END
287 vim9script
288 import './Xexport.vim' name
289 END
290 writefile(import_no_as_lines, 'Ximport.vim')
291 assert_fails('source Ximport.vim', 'E488:', '', 2, 'Ximport.vim')
292
293 var import_invalid_string_lines =<< trim END
294 vim9script
295 import Xexport.vim
296 END
297 writefile(import_invalid_string_lines, 'Ximport.vim')
298 assert_fails('source Ximport.vim', 'E121:', '', 2, 'Ximport.vim')
299
300 var import_wrong_name_lines =<< trim END
301 vim9script
302 import './XnoExport.vim'
303 END
304 writefile(import_wrong_name_lines, 'Ximport.vim')
305 assert_fails('source Ximport.vim', 'E1053:', '', 2, 'Ximport.vim')
306
307 var import_redefining_lines =<< trim END
308 vim9script
309 import './Xexport.vim' as exported
310 var exported = 5
311 END
312 writefile(import_redefining_lines, 'Ximport.vim')
313 assert_fails('source Ximport.vim', 'E1213: Redefining imported item "exported"', '', 3)
314
Bram Moolenaar160aa862022-01-10 21:29:57 +0000315 var import_missing_dot_lines =<< trim END
316 vim9script
317 import './Xexport.vim' as expo
318 def Test()
319 expo = 9
320 enddef
321 defcompile
322 END
323 writefile(import_missing_dot_lines, 'Ximport.vim')
324 assert_fails('source Ximport.vim', 'E1258:', '', 1)
325
326 var import_missing_name_lines =<< trim END
327 vim9script
328 import './Xexport.vim' as expo
329 def Test()
330 expo.99 = 9
331 enddef
332 defcompile
333 END
334 writefile(import_missing_name_lines, 'Ximport.vim')
Bram Moolenaar76283822022-01-10 21:39:03 +0000335 assert_fails('source Ximport.vim', 'E1259:', '', 1)
Bram Moolenaar160aa862022-01-10 21:29:57 +0000336
Bram Moolenaard8448622022-01-07 21:39:52 +0000337 var import_assign_wrong_type_lines =<< trim END
338 vim9script
339 import './Xexport.vim' as expo
340 expo.exported = 'xxx'
341 END
342 writefile(import_assign_wrong_type_lines, 'Ximport.vim')
343 assert_fails('source Ximport.vim', 'E1012: Type mismatch; expected number but got string', '', 3)
344
345 var import_assign_const_lines =<< trim END
346 vim9script
347 import './Xexport.vim' as expo
348 expo.CONST = 4321
349 END
350 writefile(import_assign_const_lines, 'Ximport.vim')
351 assert_fails('source Ximport.vim', 'E46: Cannot change read-only variable "CONST"', '', 3)
352
353 delete('Ximport.vim')
354 delete('Ximport3.vim')
355 delete('Xexport.vim')
356
357 # Check that in a Vim9 script 'cpo' is set to the Vim default.
358 # Flags added or removed are also applied to the restored value.
359 set cpo=abcd
360 var lines =<< trim END
361 vim9script
362 g:cpo_in_vim9script = &cpo
363 set cpo+=f
364 set cpo-=c
365 g:cpo_after_vim9script = &cpo
366 END
367 writefile(lines, 'Xvim9_script')
368 source Xvim9_script
369 assert_equal('fabd', &cpo)
370 set cpo&vim
371 assert_equal(&cpo, g:cpo_in_vim9script)
372 var newcpo = substitute(&cpo, 'c', '', '') .. 'f'
373 assert_equal(newcpo, g:cpo_after_vim9script)
374
375 delete('Xvim9_script')
376enddef
377
378def Test_import_funcref()
379 var lines =<< trim END
380 vim9script
381 export def F(): number
382 return 42
383 enddef
384 export const G = F
385 END
386 writefile(lines, 'Xlib.vim')
387
388 lines =<< trim END
389 vim9script
390 import './Xlib.vim' as lib
391 const Foo = lib.G()
392 assert_equal(42, Foo)
393
394 def DoTest()
395 const Goo = lib.G()
396 assert_equal(42, Goo)
397 enddef
398 DoTest()
399 END
400 CheckScriptSuccess(lines)
401
402 delete('Xlib.vim')
403enddef
404
405def Test_import_fails()
406 writefile([], 'Xfoo.vim')
407 var lines =<< trim END
408 import './Xfoo.vim' as foo
409 foo = 'bar'
410 END
411 CheckDefAndScriptFailure(lines, ['E1094:', 'E1236: Cannot use foo itself'])
412 lines =<< trim END
413 vim9script
414 import './Xfoo.vim' as foo
415 var that = foo
416 END
417 CheckScriptFailure(lines, 'E1060: Expected dot after name: foo')
418
419 lines =<< trim END
420 vim9script
421 import './Xfoo.vim' as 9foo
422 END
423 CheckScriptFailure(lines, 'E1047:')
424 lines =<< trim END
425 vim9script
426 import './Xfoo.vim' as the#foo
427 END
428 CheckScriptFailure(lines, 'E1047:')
429 lines =<< trim END
430 vim9script
431 import './Xfoo.vim' as g:foo
432 END
433 CheckScriptFailure(lines, 'E1047:')
434
435 delete('Xfoo.vim')
436
437 lines =<< trim END
438 vim9script
439 def TheFunc()
440 echo 'the func'
441 enddef
442 export var Ref = TheFunc
443 END
444 writefile([], 'Xthat.vim')
Bram Moolenaar7c24dfd2022-01-08 17:03:55 +0000445
Bram Moolenaard8448622022-01-07 21:39:52 +0000446 lines =<< trim END
447 import './Xthat.vim' as That
448 That()
449 END
450 CheckDefAndScriptFailure(lines, ['E1094:', 'E1236: Cannot use That itself'])
Bram Moolenaar7c24dfd2022-01-08 17:03:55 +0000451
452 lines =<< trim END
453 import './Xthat.vim' as one
454 import './Xthat.vim' as two
455 END
456 CheckScriptFailure(lines, 'E1262:')
457
458 delete('Xthat.vim')
Bram Moolenaard8448622022-01-07 21:39:52 +0000459
460 mkdir('Ximport')
461
462 writefile(['vim9script'], 'Ximport/.vim')
463 lines =<< trim END
464 vim9script
465 import './Ximport/.vim'
466 END
467 CheckScriptFailure(lines, 'E1261: Cannot import .vim without using "as"')
468 lines =<< trim END
469 vim9script
470 import './Ximport/.vim' as vim
471 END
472 CheckScriptSuccess(lines)
473
474 writefile(['vim9script'], 'Ximport/.vimrc')
475 lines =<< trim END
476 vim9script
477 import './Ximport/.vimrc'
478 END
479 CheckScriptFailure(lines, 'E1257: Imported script must use "as" or end in .vim')
480 lines =<< trim END
481 vim9script
482 import './Ximport/.vimrc' as vimrc
483 END
484 CheckScriptSuccess(lines)
485
486 delete('Ximport', 'rf')
487enddef
488
489func g:Trigger()
490 source Ximport.vim
491 return "echo 'yes'\<CR>"
492endfunc
493
494def Test_import_export_expr_map()
495 # check that :import and :export work when buffer is locked
496 var export_lines =<< trim END
497 vim9script
498 export def That(): string
499 return 'yes'
500 enddef
501 END
502 writefile(export_lines, 'Xexport_that.vim')
503
504 var import_lines =<< trim END
505 vim9script
506 import './Xexport_that.vim' as that
507 assert_equal('yes', that.That())
508 END
509 writefile(import_lines, 'Ximport.vim')
510
511 nnoremap <expr> trigger g:Trigger()
512 feedkeys('trigger', "xt")
513
514 delete('Xexport_that.vim')
515 delete('Ximport.vim')
516 nunmap trigger
517enddef
518
519def Test_import_in_filetype()
520 # check that :import works when the buffer is locked
521 mkdir('ftplugin', 'p')
522 var export_lines =<< trim END
523 vim9script
524 export var That = 'yes'
525 END
526 writefile(export_lines, 'ftplugin/Xexport_ft.vim')
527
528 var import_lines =<< trim END
529 vim9script
530 import './Xexport_ft.vim' as ft
531 assert_equal('yes', ft.That)
532 g:did_load_mytpe = 1
533 END
534 writefile(import_lines, 'ftplugin/qf.vim')
535
536 var save_rtp = &rtp
537 &rtp = getcwd() .. ',' .. &rtp
538
539 filetype plugin on
540 copen
541 assert_equal(1, g:did_load_mytpe)
542
543 quit!
544 delete('Xexport_ft.vim')
545 delete('ftplugin', 'rf')
546 &rtp = save_rtp
547enddef
548
549def Test_use_import_in_mapping()
550 var lines =<< trim END
551 vim9script
552 export def Funcx()
553 g:result = 42
554 enddef
555 END
556 writefile(lines, 'XsomeExport.vim')
557 lines =<< trim END
558 vim9script
559 import './XsomeExport.vim' as some
560 var Funcy = some.Funcx
561 nnoremap <F3> :call <sid>Funcy()<cr>
562 END
563 writefile(lines, 'Xmapscript.vim')
564
565 source Xmapscript.vim
566 feedkeys("\<F3>", "xt")
567 assert_equal(42, g:result)
568
569 unlet g:result
570 delete('XsomeExport.vim')
571 delete('Xmapscript.vim')
572 nunmap <F3>
573enddef
574
575def Test_export_fails()
576 CheckScriptFailure(['export var some = 123'], 'E1042:')
577 CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:')
578 CheckScriptFailure(['vim9script', 'export echo 134'], 'E1043:')
579
580 assert_fails('export something', 'E1043:')
581enddef
582
583func Test_import_fails_without_script()
584 CheckRunVimInTerminal
585
586 " call indirectly to avoid compilation error for missing functions
587 call Run_Test_import_fails_on_command_line()
588endfunc
589
590def Run_Test_import_fails_on_command_line()
591 var export =<< trim END
592 vim9script
593 export def Foo(): number
594 return 0
595 enddef
596 END
597 writefile(export, 'XexportCmd.vim')
598
599 var buf = RunVimInTerminal('-c "import Foo from ''./XexportCmd.vim''"', {
600 rows: 6, wait_for_ruler: 0})
601 WaitForAssert(() => assert_match('^E1094:', term_getline(buf, 5)))
602
603 delete('XexportCmd.vim')
604 StopVimInTerminal(buf)
605enddef
606
607def Test_vim9_reload_noclear()
608 var lines =<< trim END
609 vim9script
610 export var exported = 'thexport'
611
612 export def TheFunc(x = 0)
613 enddef
614 END
615 writefile(lines, 'XExportReload')
616 lines =<< trim END
617 vim9script noclear
618 g:loadCount += 1
619 var s:reloaded = 'init'
620 import './XExportReload' as exp
621
622 def Again(): string
623 return 'again'
624 enddef
625
626 exp.TheFunc()
627
628 if exists('s:loaded') | finish | endif
629 var s:loaded = true
630
631 var s:notReloaded = 'yes'
632 s:reloaded = 'first'
633 def g:Values(): list<string>
634 return [s:reloaded, s:notReloaded, Again(), Once(), exp.exported]
635 enddef
636
637 def Once(): string
638 return 'once'
639 enddef
640 END
641 writefile(lines, 'XReloaded')
642 g:loadCount = 0
643 source XReloaded
644 assert_equal(1, g:loadCount)
645 assert_equal(['first', 'yes', 'again', 'once', 'thexport'], g:Values())
646 source XReloaded
647 assert_equal(2, g:loadCount)
648 assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values())
649 source XReloaded
650 assert_equal(3, g:loadCount)
651 assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values())
652
653 delete('XReloaded')
654 delete('XExportReload')
655 delfunc g:Values
656 unlet g:loadCount
657
658 lines =<< trim END
659 vim9script
660 def Inner()
661 enddef
662 END
663 lines->writefile('XreloadScript.vim')
664 source XreloadScript.vim
665
666 lines =<< trim END
667 vim9script
668 def Outer()
669 def Inner()
670 enddef
671 enddef
672 defcompile
673 END
674 lines->writefile('XreloadScript.vim')
675 source XreloadScript.vim
676
677 delete('XreloadScript.vim')
678enddef
679
680def Test_vim9_reload_import()
681 var lines =<< trim END
682 vim9script
683 const var = ''
684 var valone = 1234
685 def MyFunc(arg: string)
686 valone = 5678
687 enddef
688 END
689 var morelines =<< trim END
690 var valtwo = 222
691 export def GetValtwo(): number
692 return valtwo
693 enddef
694 END
695 writefile(lines + morelines, 'Xreload.vim')
696 source Xreload.vim
697 source Xreload.vim
698 source Xreload.vim
699
700 # cannot declare a var twice
701 lines =<< trim END
702 vim9script
703 var valone = 1234
704 var valone = 5678
705 END
706 writefile(lines, 'Xreload.vim')
707 assert_fails('source Xreload.vim', 'E1041:', '', 3, 'Xreload.vim')
708
709 delete('Xreload.vim')
710 delete('Ximport.vim')
711enddef
712
713" if a script is reloaded with a script-local variable that changed its type, a
714" compiled function using that variable must fail.
715def Test_script_reload_change_type()
716 var lines =<< trim END
717 vim9script noclear
718 var str = 'string'
719 def g:GetStr(): string
720 return str .. 'xxx'
721 enddef
722 END
723 writefile(lines, 'Xreload.vim')
724 source Xreload.vim
725 echo g:GetStr()
726
727 lines =<< trim END
728 vim9script noclear
729 var str = 1234
730 END
731 writefile(lines, 'Xreload.vim')
732 source Xreload.vim
733 assert_fails('echo g:GetStr()', 'E1150:')
734
735 delfunc g:GetStr
736 delete('Xreload.vim')
737enddef
738
739" Define CallFunc so that the test can be compiled
740command CallFunc echo 'nop'
741
742def Test_script_reload_from_function()
743 var lines =<< trim END
744 vim9script
745
746 if exists('g:loaded')
747 finish
748 endif
749 g:loaded = 1
750 delcommand CallFunc
751 command CallFunc Func()
752 def Func()
753 so XreloadFunc.vim
754 g:didTheFunc = 1
755 enddef
756 END
757 writefile(lines, 'XreloadFunc.vim')
758 source XreloadFunc.vim
759 CallFunc
760 assert_equal(1, g:didTheFunc)
761
762 delete('XreloadFunc.vim')
763 delcommand CallFunc
764 unlet g:loaded
765 unlet g:didTheFunc
766enddef
767
768def s:RetSome(): string
769 return 'some'
770enddef
771
772" Not exported function that is referenced needs to be accessed by the
773" script-local name.
774def Test_vim9_funcref()
775 var sortlines =<< trim END
776 vim9script
777 def Compare(i1: number, i2: number): number
778 return i2 - i1
779 enddef
780
781 export def FastSort(): list<number>
782 return range(5)->sort(Compare)
783 enddef
784
785 export def GetString(arg: string): string
786 return arg
787 enddef
788 END
789 writefile(sortlines, 'Xsort.vim')
790
791 var lines =<< trim END
792 vim9script
793 import './Xsort.vim'
794 def Test()
795 g:result = Xsort.FastSort()
796 enddef
797 Test()
Bram Moolenaar7c24dfd2022-01-08 17:03:55 +0000798 END
799 writefile(lines, 'Xscript.vim')
800 source Xscript.vim
801 assert_equal([4, 3, 2, 1, 0], g:result)
802 unlet g:result
Bram Moolenaard8448622022-01-07 21:39:52 +0000803
Bram Moolenaar7c24dfd2022-01-08 17:03:55 +0000804 lines =<< trim END
805 vim9script
Bram Moolenaard8448622022-01-07 21:39:52 +0000806 # using a function imported with "as"
807 import './Xsort.vim' as anAlias
808 assert_equal('yes', anAlias.GetString('yes'))
809
810 # using the function from a compiled function
811 def TestMore(): string
812 var s = s:anAlias.GetString('foo')
813 return s .. anAlias.GetString('bar')
814 enddef
815 assert_equal('foobar', TestMore())
816
817 # error when using a function that isn't exported
818 assert_fails('anAlias.Compare(1, 2)', 'E1049:')
819 END
820 writefile(lines, 'Xscript.vim')
821
Bram Moolenaard8448622022-01-07 21:39:52 +0000822 delete('Xsort.vim')
823 delete('Xscript.vim')
824
825 var Funcref = function('s:RetSome')
826 assert_equal('some', Funcref())
827enddef
828
829" Check that when searching for "FilterFunc" it finds the import in the
830" script where FastFilter() is called from, both as a string and as a direct
831" function reference.
832def Test_vim9_funcref_other_script()
833 var filterLines =<< trim END
834 vim9script
835 export def FilterFunc(idx: number, val: number): bool
836 return idx % 2 == 1
837 enddef
838 export def FastFilter(): list<number>
839 return range(10)->filter('FilterFunc(v:key, v:val)')
840 enddef
841 export def FastFilterDirect(): list<number>
842 return range(10)->filter(FilterFunc)
843 enddef
844 END
845 writefile(filterLines, 'Xfilter.vim')
846
847 var lines =<< trim END
848 vim9script
849 import './Xfilter.vim' as filter
850 def Test()
851 var x: list<number> = filter.FastFilter()
852 enddef
853 Test()
854 def TestDirect()
855 var x: list<number> = filter.FastFilterDirect()
856 enddef
857 TestDirect()
858 END
859 CheckScriptSuccess(lines)
860 delete('Xfilter.vim')
861enddef
862
863def Test_import_absolute()
864 var import_lines = [
865 'vim9script',
866 'import "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim" as abs',
867 'def UseExported()',
868 ' g:imported_abs = abs.exported',
869 ' abs.exported = 8888',
870 ' g:imported_after = abs.exported',
871 'enddef',
872 'UseExported()',
873 'g:import_disassembled = execute("disass UseExported")',
874 ]
875 writefile(import_lines, 'Ximport_abs.vim')
876 writefile(s:export_script_lines, 'Xexport_abs.vim')
877
878 source Ximport_abs.vim
879
880 assert_equal(9876, g:imported_abs)
881 assert_equal(8888, g:imported_after)
882 assert_match('<SNR>\d\+_UseExported\_s*' ..
883 'g:imported_abs = abs.exported\_s*' ..
884 '0 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' ..
885 '1 STOREG g:imported_abs\_s*' ..
886 'abs.exported = 8888\_s*' ..
887 '2 PUSHNR 8888\_s*' ..
888 '3 STORESCRIPT exported-2 in .*Xexport_abs.vim\_s*' ..
889 'g:imported_after = abs.exported\_s*' ..
890 '4 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' ..
891 '5 STOREG g:imported_after',
892 g:import_disassembled)
893
894 Undo_export_script_lines()
895 unlet g:imported_abs
896 unlet g:import_disassembled
897
898 delete('Ximport_abs.vim')
899 delete('Xexport_abs.vim')
900enddef
901
902def Test_import_rtp()
903 var import_lines = [
904 'vim9script',
905 'import "Xexport_rtp.vim" as rtp',
906 'g:imported_rtp = rtp.exported',
907 ]
908 writefile(import_lines, 'Ximport_rtp.vim')
909 mkdir('import', 'p')
910 writefile(s:export_script_lines, 'import/Xexport_rtp.vim')
911
912 var save_rtp = &rtp
913 &rtp = getcwd()
914 source Ximport_rtp.vim
915 &rtp = save_rtp
916
917 assert_equal(9876, g:imported_rtp)
918
919 Undo_export_script_lines()
920 unlet g:imported_rtp
921 delete('Ximport_rtp.vim')
922 delete('import', 'rf')
923enddef
924
925def Test_import_compile_error()
926 var export_lines = [
927 'vim9script',
928 'export def ExpFunc(): string',
929 ' return notDefined',
930 'enddef',
931 ]
932 writefile(export_lines, 'Xexported.vim')
933
934 var import_lines = [
935 'vim9script',
936 'import "./Xexported.vim" as expo',
937 'def ImpFunc()',
938 ' echo expo.ExpFunc()',
939 'enddef',
940 'defcompile',
941 ]
942 writefile(import_lines, 'Ximport.vim')
943
944 try
945 source Ximport.vim
946 catch /E1001/
947 # Error should be before the Xexported.vim file.
948 assert_match('E1001: Variable not found: notDefined', v:exception)
949 assert_match('function <SNR>\d\+_ImpFunc\[1\]..<SNR>\d\+_ExpFunc, line 1', v:throwpoint)
950 endtry
951
952 delete('Xexported.vim')
953 delete('Ximport.vim')
954enddef
955
956def Test_func_overrules_import_fails()
957 var export_lines =<< trim END
958 vim9script
959 export def Func()
960 echo 'imported'
961 enddef
962 END
963 writefile(export_lines, 'XexportedFunc.vim')
964
965 var lines =<< trim END
966 vim9script
967 import './XexportedFunc.vim' as Func
968 def Func()
969 echo 'local to function'
970 enddef
971 END
972 CheckScriptFailure(lines, 'E1236:')
973
974 lines =<< trim END
975 vim9script
976 import './XexportedFunc.vim' as Func
977 def Outer()
978 def Func()
979 echo 'local to function'
980 enddef
981 enddef
982 defcompile
983 END
984 CheckScriptFailure(lines, 'E1236:')
985
986 delete('XexportedFunc.vim')
987enddef
988
989def Test_source_vim9_from_legacy()
990 var vim9_lines =<< trim END
991 vim9script
992 var local = 'local'
993 g:global = 'global'
994 export var exported = 'exported'
995 export def GetText(): string
996 return 'text'
997 enddef
998 END
999 writefile(vim9_lines, 'Xvim9_script.vim')
1000
1001 var legacy_lines =<< trim END
1002 source Xvim9_script.vim
1003
1004 call assert_false(exists('local'))
1005 call assert_false(exists('exported'))
1006 call assert_false(exists('s:exported'))
1007 call assert_equal('global', global)
1008 call assert_equal('global', g:global)
Bram Moolenaard8448622022-01-07 21:39:52 +00001009 END
1010 writefile(legacy_lines, 'Xlegacy_script.vim')
1011
1012 source Xlegacy_script.vim
1013 assert_equal('global', g:global)
1014 unlet g:global
1015
1016 delete('Xlegacy_script.vim')
1017 delete('Xvim9_script.vim')
1018enddef
1019
Bram Moolenaarc43e6232022-01-13 20:51:56 +00001020def Test_import_vim9_from_legacy()
1021 var vim9_lines =<< trim END
1022 vim9script
1023 var local = 'local'
1024 g:global = 'global'
1025 export var exported = 'exported'
1026 export def GetText(): string
1027 return 'text'
1028 enddef
1029 END
1030 writefile(vim9_lines, 'Xvim9_export.vim')
1031
1032 var legacy_lines =<< trim END
1033 import './Xvim9_export.vim' as vim9
1034
1035 call assert_false(exists('vim9'))
1036 call assert_false(exists('local'))
1037 call assert_false(exists('s:vim9.local'))
1038 call assert_equal('global', global)
1039 call assert_equal('global', g:global)
1040 call assert_false(exists('exported'))
1041 call assert_false(exists('s:exported'))
1042 call assert_false(exists('*GetText'))
1043
1044 " imported symbol is script-local
1045 call assert_equal('exported', s:vim9.exported)
1046 call assert_equal('text', s:vim9.GetText())
1047 END
1048 writefile(legacy_lines, 'Xlegacy_script.vim')
1049
1050 source Xlegacy_script.vim
1051 assert_equal('global', g:global)
1052 unlet g:global
1053
1054 delete('Xlegacy_script.vim')
1055 delete('Xvim9_export.vim')
1056enddef
1057
Bram Moolenaard8448622022-01-07 21:39:52 +00001058def Test_cmdline_win()
1059 # if the Vim syntax highlighting uses Vim9 constructs they can be used from
1060 # the command line window.
1061 mkdir('rtp/syntax', 'p')
1062 var export_lines =<< trim END
1063 vim9script
1064 export var That = 'yes'
1065 END
1066 writefile(export_lines, 'rtp/syntax/Xexport.vim')
1067 var import_lines =<< trim END
1068 vim9script
1069 import './Xexport.vim' as exp
1070 echo exp.That
1071 END
1072 writefile(import_lines, 'rtp/syntax/vim.vim')
1073 var save_rtp = &rtp
1074 &rtp = getcwd() .. '/rtp' .. ',' .. &rtp
1075 syntax on
1076 augroup CmdWin
1077 autocmd CmdwinEnter * g:got_there = 'yes'
1078 augroup END
1079 # this will open and also close the cmdline window
1080 feedkeys('q:', 'xt')
1081 assert_equal('yes', g:got_there)
1082
1083 augroup CmdWin
1084 au!
1085 augroup END
1086 &rtp = save_rtp
1087 delete('rtp', 'rf')
1088enddef
1089
1090def Test_import_gone_when_sourced_twice()
1091 var exportlines =<< trim END
1092 vim9script
1093 if exists('g:guard')
1094 finish
1095 endif
1096 g:guard = 1
1097 export var name = 'someName'
1098 END
1099 writefile(exportlines, 'XexportScript.vim')
1100
1101 var lines =<< trim END
1102 vim9script
1103 import './XexportScript.vim' as expo
1104 def g:GetName(): string
1105 return expo.name
1106 enddef
1107 END
1108 writefile(lines, 'XscriptImport.vim')
1109 so XscriptImport.vim
1110 assert_equal('someName', g:GetName())
1111
1112 so XexportScript.vim
1113 assert_fails('call g:GetName()', 'E1149:')
1114
1115 delfunc g:GetName
1116 delete('XexportScript.vim')
1117 delete('XscriptImport.vim')
1118 unlet g:guard
1119enddef
1120
Bram Moolenaar160aa862022-01-10 21:29:57 +00001121" test using an auto-loaded function and variable
Bram Moolenaar0e3e7ba2022-01-13 20:18:56 +00001122def Test_vim9_autoload_full_name()
Bram Moolenaar160aa862022-01-10 21:29:57 +00001123 var lines =<< trim END
1124 vim9script
1125 def some#gettest(): string
1126 return 'test'
1127 enddef
1128 g:some#name = 'name'
1129 g:some#dict = {key: 'value'}
1130
1131 def some#varargs(a1: string, ...l: list<string>): string
1132 return a1 .. l[0] .. l[1]
1133 enddef
1134 END
1135
1136 mkdir('Xdir/autoload', 'p')
1137 writefile(lines, 'Xdir/autoload/some.vim')
1138 var save_rtp = &rtp
1139 exe 'set rtp^=' .. getcwd() .. '/Xdir'
1140
1141 assert_equal('test', g:some#gettest())
1142 assert_equal('name', g:some#name)
1143 assert_equal('value', g:some#dict.key)
1144 g:some#other = 'other'
1145 assert_equal('other', g:some#other)
1146
1147 assert_equal('abc', some#varargs('a', 'b', 'c'))
1148
1149 # upper case script name works
1150 lines =<< trim END
1151 vim9script
1152 def Other#getOther(): string
1153 return 'other'
1154 enddef
1155 END
1156 writefile(lines, 'Xdir/autoload/Other.vim')
1157 assert_equal('other', g:Other#getOther())
1158
1159 delete('Xdir', 'rf')
1160 &rtp = save_rtp
1161enddef
1162
1163def Test_vim9script_autoload()
1164 mkdir('Xdir/autoload', 'p')
1165 var save_rtp = &rtp
1166 exe 'set rtp^=' .. getcwd() .. '/Xdir'
1167
1168 # when using "vim9script autoload" prefix is not needed
1169 var lines =<< trim END
1170 vim9script autoload
Bram Moolenaar17d36cb2022-01-12 11:46:40 +00001171 g:prefixed_loaded += 1
Bram Moolenaar160aa862022-01-10 21:29:57 +00001172
1173 export def Gettest(): string
1174 return 'test'
1175 enddef
1176
Bram Moolenaar0e3e7ba2022-01-13 20:18:56 +00001177 export var name = 'name'
1178
1179 export func GetFunc()
1180 return Gettest() .. 'more' .. s:name
Bram Moolenaar160aa862022-01-10 21:29:57 +00001181 endfunc
1182
Bram Moolenaar0e3e7ba2022-01-13 20:18:56 +00001183 export def GetDef(): string
1184 return Gettest() .. 'more' .. name
1185 enddef
1186
Bram Moolenaar160aa862022-01-10 21:29:57 +00001187 export final fname = 'final'
1188 export const cname = 'const'
1189 END
1190 writefile(lines, 'Xdir/autoload/prefixed.vim')
1191
Bram Moolenaar17d36cb2022-01-12 11:46:40 +00001192 g:prefixed_loaded = 0
1193 g:expected_loaded = 0
Bram Moolenaar160aa862022-01-10 21:29:57 +00001194 lines =<< trim END
1195 vim9script
1196 import autoload 'prefixed.vim'
Bram Moolenaar17d36cb2022-01-12 11:46:40 +00001197 assert_equal(g:expected_loaded, g:prefixed_loaded)
Bram Moolenaar160aa862022-01-10 21:29:57 +00001198 assert_equal('test', prefixed.Gettest())
Bram Moolenaar17d36cb2022-01-12 11:46:40 +00001199 assert_equal(1, g:prefixed_loaded)
Bram Moolenaar160aa862022-01-10 21:29:57 +00001200
Bram Moolenaar0e3e7ba2022-01-13 20:18:56 +00001201 assert_equal('testmorename', prefixed.GetFunc())
1202 assert_equal('testmorename', prefixed.GetDef())
Bram Moolenaar160aa862022-01-10 21:29:57 +00001203 assert_equal('name', prefixed.name)
1204 assert_equal('final', prefixed.fname)
1205 assert_equal('const', prefixed.cname)
1206 END
1207 CheckScriptSuccess(lines)
Bram Moolenaar17d36cb2022-01-12 11:46:40 +00001208 # can source it again, autoload script not loaded again
1209 g:expected_loaded = 1
1210 CheckScriptSuccess(lines)
Bram Moolenaar160aa862022-01-10 21:29:57 +00001211
1212 # can also get the items by autoload name
1213 lines =<< trim END
1214 call assert_equal('test', prefixed#Gettest())
Bram Moolenaar0e3e7ba2022-01-13 20:18:56 +00001215 call assert_equal('testmorename', prefixed#GetFunc())
Bram Moolenaar160aa862022-01-10 21:29:57 +00001216 call assert_equal('name', prefixed#name)
1217 call assert_equal('final', prefixed#fname)
1218 call assert_equal('const', prefixed#cname)
1219 END
1220 CheckScriptSuccess(lines)
1221
Bram Moolenaarf111cdf2022-01-12 12:48:17 +00001222 unlet g:prefixed_loaded
1223 unlet g:expected_loaded
1224 delete('Xdir', 'rf')
1225 &rtp = save_rtp
1226enddef
1227
1228def Test_vim9script_autoload_call()
1229 mkdir('Xdir/autoload', 'p')
1230 var save_rtp = &rtp
1231 exe 'set rtp^=' .. getcwd() .. '/Xdir'
1232
1233 var lines =<< trim END
1234 vim9script autoload
1235
1236 export def Getother()
1237 g:result = 'other'
1238 enddef
1239 END
Bram Moolenaar5d982692022-01-12 15:15:27 +00001240 writefile(lines, 'Xdir/autoload/another.vim')
Bram Moolenaarf111cdf2022-01-12 12:48:17 +00001241
1242 lines =<< trim END
1243 vim9script
Bram Moolenaar5d982692022-01-12 15:15:27 +00001244 import autoload 'another.vim'
1245 call another.Getother()
Bram Moolenaarf111cdf2022-01-12 12:48:17 +00001246 assert_equal('other', g:result)
1247 END
1248 CheckScriptSuccess(lines)
1249
1250 unlet g:result
Bram Moolenaar160aa862022-01-10 21:29:57 +00001251 delete('Xdir', 'rf')
1252 &rtp = save_rtp
1253enddef
1254
Bram Moolenaard041f422022-01-12 19:54:00 +00001255def Test_import_autoload_postponed()
1256 mkdir('Xdir/autoload', 'p')
1257 var save_rtp = &rtp
1258 exe 'set rtp^=' .. getcwd() .. '/Xdir'
1259
1260 var lines =<< trim END
1261 vim9script autoload
1262
1263 g:loaded_postponed = 'true'
1264 export var variable = 'bla'
1265 export def Function(): string
1266 return 'bla'
1267 enddef
1268 END
1269 writefile(lines, 'Xdir/autoload/postponed.vim')
1270
1271 lines =<< trim END
1272 vim9script
1273
1274 import autoload 'postponed.vim'
1275 def Tryit()
1276 echo postponed.variable
1277 echo postponed.Function()
1278 enddef
1279 defcompile
1280 END
1281 CheckScriptSuccess(lines)
1282 assert_false(exists('g:loaded_postponed'))
1283 CheckScriptSuccess(lines + ['Tryit()'])
1284 assert_equal('true', g:loaded_postponed)
1285
1286 unlet g:loaded_postponed
1287 delete('Xdir', 'rf')
1288 &rtp = save_rtp
1289enddef
1290
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001291def Test_autoload_mapping()
1292 mkdir('Xdir/autoload', 'p')
1293 var save_rtp = &rtp
1294 exe 'set rtp^=' .. getcwd() .. '/Xdir'
1295
1296 var lines =<< trim END
1297 vim9script autoload
1298
1299 g:toggle_loaded = 'yes'
1300
1301 export def Toggle(): string
1302 return ":g:toggle_called = 'yes'\<CR>"
1303 enddef
1304 END
1305 writefile(lines, 'Xdir/autoload/toggle.vim')
1306
1307 lines =<< trim END
1308 vim9script
1309
1310 import autoload 'toggle.vim'
1311
1312 nnoremap <silent> <expr> tt toggle.Toggle()
1313 END
1314 CheckScriptSuccess(lines)
1315 assert_false(exists("g:toggle_loaded"))
1316 assert_false(exists("g:toggle_called"))
1317
1318 feedkeys("tt", 'xt')
1319 assert_equal('yes', g:toggle_loaded)
1320 assert_equal('yes', g:toggle_called)
1321
1322 nunmap tt
1323 unlet g:toggle_loaded
1324 unlet g:toggle_called
1325 delete('Xdir', 'rf')
1326 &rtp = save_rtp
1327enddef
1328
Bram Moolenaar160aa862022-01-10 21:29:57 +00001329def Test_vim9script_autoload_fails()
1330 var lines =<< trim END
1331 vim9script autoload
1332 var n = 0
1333 END
1334 CheckScriptFailure(lines, 'E1263:')
1335enddef
1336
1337def Test_import_autoload_fails()
1338 var lines =<< trim END
1339 vim9script
1340 import autoload autoload 'prefixed.vim'
1341 END
1342 CheckScriptFailure(lines, 'E121: Undefined variable: autoload')
1343
1344 lines =<< trim END
1345 vim9script
1346 import autoload 'doesNotExist.vim'
1347 END
1348 CheckScriptFailure(lines, 'E1264:')
1349enddef
1350
1351" test disassembling an auto-loaded function starting with "debug"
1352def Test_vim9_autoload_disass()
1353 mkdir('Xdir/autoload', 'p')
1354 var save_rtp = &rtp
1355 exe 'set rtp^=' .. getcwd() .. '/Xdir'
1356
1357 var lines =<< trim END
1358 vim9script
1359 def debugit#test(): string
1360 return 'debug'
1361 enddef
1362 END
1363 writefile(lines, 'Xdir/autoload/debugit.vim')
1364
1365 lines =<< trim END
1366 vim9script
1367 def profileit#test(): string
1368 return 'profile'
1369 enddef
1370 END
1371 writefile(lines, 'Xdir/autoload/profileit.vim')
1372
1373 lines =<< trim END
1374 vim9script
1375 assert_equal('debug', debugit#test())
1376 disass debugit#test
1377 assert_equal('profile', profileit#test())
1378 disass profileit#test
1379 END
1380 CheckScriptSuccess(lines)
1381
1382 delete('Xdir', 'rf')
1383 &rtp = save_rtp
1384enddef
1385
1386" test using a vim9script that is auto-loaded from an autocmd
1387def Test_vim9_aucmd_autoload()
1388 var lines =<< trim END
1389 vim9script
1390 def foo#test()
1391 echomsg getreg('"')
1392 enddef
1393 END
1394
1395 mkdir('Xdir/autoload', 'p')
1396 writefile(lines, 'Xdir/autoload/foo.vim')
1397 var save_rtp = &rtp
1398 exe 'set rtp^=' .. getcwd() .. '/Xdir'
1399 augroup test
1400 autocmd TextYankPost * call foo#test()
1401 augroup END
1402
1403 normal Y
1404
1405 augroup test
1406 autocmd!
1407 augroup END
1408 delete('Xdir', 'rf')
1409 &rtp = save_rtp
1410enddef
1411
Bram Moolenaar3049fcf2022-01-13 19:25:50 +00001412" test using a autoloaded file that is case sensitive
1413def Test_vim9_autoload_case_sensitive()
1414 var lines =<< trim END
1415 vim9script autoload
1416 export def CaseSensitive(): string
1417 return 'done'
1418 enddef
1419 END
1420
1421 mkdir('Xdir/autoload', 'p')
1422 writefile(lines, 'Xdir/autoload/CaseSensitive.vim')
1423 var save_rtp = &rtp
1424 exe 'set rtp^=' .. getcwd() .. '/Xdir'
1425
1426 lines =<< trim END
1427 vim9script
1428 import autoload 'CaseSensitive.vim'
1429 assert_equal('done', CaseSensitive.CaseSensitive())
1430 END
1431 CheckScriptSuccess(lines)
1432
1433 delete('Xdir', 'rf')
1434 &rtp = save_rtp
1435enddef
1436
Bram Moolenaar160aa862022-01-10 21:29:57 +00001437" This was causing a crash because suppress_errthrow wasn't reset.
1438def Test_vim9_autoload_error()
1439 var lines =<< trim END
1440 vim9script
1441 def crash#func()
1442 try
1443 for x in List()
1444 endfor
1445 catch
1446 endtry
1447 g:ok = true
1448 enddef
1449 fu List()
1450 invalid
1451 endfu
1452 try
1453 alsoinvalid
1454 catch /wontmatch/
1455 endtry
1456 END
1457 call mkdir('Xruntime/autoload', 'p')
1458 call writefile(lines, 'Xruntime/autoload/crash.vim')
1459
1460 # run in a separate Vim to avoid the side effects of assert_fails()
1461 lines =<< trim END
1462 exe 'set rtp^=' .. getcwd() .. '/Xruntime'
1463 call crash#func()
1464 call writefile(['ok'], 'Xdidit')
1465 qall!
1466 END
1467 writefile(lines, 'Xscript')
1468 RunVim([], [], '-S Xscript')
1469 assert_equal(['ok'], readfile('Xdidit'))
1470
1471 delete('Xdidit')
1472 delete('Xscript')
1473 delete('Xruntime', 'rf')
1474
1475 lines =<< trim END
1476 vim9script
1477 var foo#bar = 'asdf'
1478 END
1479 CheckScriptFailure(lines, 'E461: Illegal variable name: foo#bar', 2)
1480enddef
1481
Bram Moolenaard8448622022-01-07 21:39:52 +00001482
1483" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker