Mercurial > vim
view src/testdir/test_vim9_import.vim @ 30333:fc0830246f49 v9.0.0502
patch 9.0.0502: a closure in a nested loop in a :def function does not work
Commit: https://github.com/vim/vim/commit/cc34181f9994d64f8c8fa2f5845eaf0cc963067f
Author: Bram Moolenaar <Bram@vim.org>
Date: Mon Sep 19 15:54:34 2022 +0100
patch 9.0.0502: a closure in a nested loop in a :def function does not work
Problem: A closure in a nested loop in a :def function does not work.
Solution: Use an array of loopvars, one per loop level.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Mon, 19 Sep 2022 17:00:07 +0200 |
parents | 9c141768fa0f |
children | 12e6c7bae2a9 |
line wrap: on
line source
" Test import/export of the Vim9 script language. " Also the autoload mechanism. source check.vim source term_util.vim import './vim9.vim' as v9 let s:export_script_lines =<< trim END vim9script var name: string = 'bob' def Concat(arg: string): string return name .. arg enddef g:result = Concat('bie') g:localname = name export const CONST = 1234 export var exported = 9876 export var exp_name = 'John' export def Exported(): string return 'Exported' enddef export def ExportedValue(): number return exported enddef export def ExportedInc() exported += 5 enddef export final theList = [1] export def AddSome(s: string): string return s .. 'some' enddef export var AddRef = AddSome END def s:Undo_export_script_lines() unlet g:result unlet g:localname enddef def Test_vim9_import_export() writefile(s:export_script_lines, 'Xexport.vim') var import_script_lines =<< trim END vim9script var dir = './' var ext = ".vim" import dir .. 'Xexport' .. ext as expo g:exported1 = expo.exported expo.exported += 3 g:exported2 = expo.exported g:exported3 = expo.ExportedValue() expo.ExportedInc() g:exported_i1 = expo.exported g:exported_i2 = expo.ExportedValue() expo.exported = 11 g:exported_s1 = expo.exported g:exported_s2 = expo.ExportedValue() g:imported_func = expo.Exported() def GetExported(): string var local_dict = {ref: expo.Exported} return local_dict.ref() enddef g:funcref_result = GetExported() def GetName(): string return expo.exp_name .. 'son' enddef g:long_name = GetName() g:imported_name = expo.exp_name expo.exp_name ..= ' Doe' expo.exp_name = expo.exp_name .. ' Maar' g:imported_name_appended = expo.exp_name g:exported_later = expo.exported expo.theList->add(2) assert_equal([1, 2], expo.theList) assert_equal('andthensome', 'andthen'->expo.AddSome()) assert_equal('awesome', 'awe'->expo.AddRef()) END writefile(import_script_lines, 'Ximport.vim') source Ximport.vim assert_equal('bobbie', g:result) assert_equal('bob', g:localname) assert_equal(9876, g:exported1) assert_equal(9879, g:exported2) assert_equal(9879, g:exported3) assert_equal(9884, g:exported_i1) assert_equal(9884, g:exported_i2) assert_equal(11, g:exported_s1) assert_equal(11, g:exported_s2) assert_equal(11, g:exported_later) assert_equal('Exported', g:imported_func) assert_equal('Exported', g:funcref_result) assert_equal('John', g:imported_name) assert_equal('Johnson', g:long_name) assert_equal('John Doe Maar', g:imported_name_appended) assert_false(exists('g:name')) Undo_export_script_lines() unlet g:exported1 unlet g:exported2 unlet g:exported3 unlet g:exported_i1 unlet g:exported_i2 unlet g:exported_later unlet g:imported_func unlet g:imported_name g:long_name g:imported_name_appended delete('Ximport.vim') # similar, with line breaks var import_line_break_script_lines =<< trim END vim9script import './Xexport.vim' as expo g:exported = expo.exported expo.exported += 7 g:exported_added = expo.exported g:imported_func = expo.Exported() END writefile(import_line_break_script_lines, 'Ximport_lbr.vim') source Ximport_lbr.vim assert_equal(11, g:exported) assert_equal(18, g:exported_added) assert_equal('Exported', g:imported_func) # exported script not sourced again assert_false(exists('g:result')) unlet g:exported unlet g:exported_added unlet g:imported_func delete('Ximport_lbr.vim') var import_shadows_cmdmod_lines =<< trim END vim9script import './Xexport.vim' as vim9 vim9.exp_name = 'Shadow' assert_equal('Shadow', vim9.exp_name) END v9.CheckScriptSuccess(import_shadows_cmdmod_lines) var line_break_before_dot =<< trim END vim9script import './Xexport.vim' as expo g:exported = expo .exported END writefile(line_break_before_dot, 'Ximport_lbr_before_dot.vim') assert_fails('source Ximport_lbr_before_dot.vim', 'E1060:', '', 3) delete('Ximport_lbr_before_dot.vim') var line_break_after_dot =<< trim END vim9script import './Xexport.vim' as expo g:exported = expo. exported END writefile(line_break_after_dot, 'Ximport_lbr_after_dot.vim') assert_fails('source Ximport_lbr_after_dot.vim', 'E1074:', '', 3) delete('Ximport_lbr_after_dot.vim') var import_star_as_lines =<< trim END vim9script import './Xexport.vim' as Export def UseExport() g:exported_def = Export.exported enddef g:exported_script = Export.exported assert_equal(1, exists('Export.exported')) assert_equal(0, exists('Export.notexported')) UseExport() END writefile(import_star_as_lines, 'Ximport.vim') source Ximport.vim assert_equal(18, g:exported_def) assert_equal(18, g:exported_script) unlet g:exported_def unlet g:exported_script var import_star_as_lines_no_dot =<< trim END vim9script import './Xexport.vim' as Export def Func() var dummy = 1 var imported = Export + dummy enddef defcompile END writefile(import_star_as_lines_no_dot, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1060:', '', 2, 'Func') var import_star_as_lines_dot_space =<< trim END vim9script import './Xexport.vim' as Export def Func() var imported = Export . exported enddef defcompile END writefile(import_star_as_lines_dot_space, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1074:', '', 1, 'Func') writefile(s:export_script_lines, 'Xexport2.vim') var import_as_duplicated =<< trim END vim9script import './Xexport.vim' as expo import './Xexport2.vim' as expo END writefile(import_as_duplicated, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim') delete('Xexport2.vim') var import_star_as_lines_script_no_dot =<< trim END vim9script import './Xexport.vim' as Export g:imported_script = Export exported END writefile(import_star_as_lines_script_no_dot, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1060: Expected dot after name: Export exported') var import_star_as_lines_script_space_after_dot =<< trim END vim9script import './Xexport.vim' as Export g:imported_script = Export. exported END writefile(import_star_as_lines_script_space_after_dot, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1074:') var import_star_as_lines_missing_name =<< trim END vim9script import './Xexport.vim' as Export def Func() var imported = Export. enddef defcompile END writefile(import_star_as_lines_missing_name, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1048:', '', 1, 'Func') var import_star_as_lbr_lines =<< trim END vim9script import './Xexport.vim' as Export def UseExport() g:exported = Export.exported enddef UseExport() END writefile(import_star_as_lbr_lines, 'Ximport.vim') source Ximport.vim assert_equal(18, g:exported) unlet g:exported # try to use something that exists but is not exported var import_not_exported_lines =<< trim END vim9script import './Xexport.vim' as expo echo expo.name END writefile(import_not_exported_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1049:', '', 3, 'Ximport.vim') # try to import something that is already defined var import_already_defined =<< trim END vim9script var exported = 'something' import './Xexport.vim' as exported END writefile(import_already_defined, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim') # try changing an imported const var import_assign_to_const =<< trim END vim9script import './Xexport.vim' as expo def Assign() expo.CONST = 987 enddef defcompile END writefile(import_assign_to_const, 'Ximport.vim') assert_fails('source Ximport.vim', 'E46:', '', 1, '_Assign') # try changing an imported final var import_assign_to_final =<< trim END vim9script import './Xexport.vim' as expo def Assign() expo.theList = [2] enddef defcompile END writefile(import_assign_to_final, 'Ximport.vim') assert_fails('source Ximport.vim', 'E46:', '', 1, '_Assign') var import_no_as_lines =<< trim END vim9script import './Xexport.vim' name END writefile(import_no_as_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E488:', '', 2, 'Ximport.vim') # trailing starts with "as" var import_bad_as_lines =<< trim END vim9script import './Xexport.vim' asname END writefile(import_no_as_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E488:', '', 2, 'Ximport.vim') var import_invalid_string_lines =<< trim END vim9script import Xexport.vim END writefile(import_invalid_string_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E121:', '', 2, 'Ximport.vim') var import_wrong_name_lines =<< trim END vim9script import './XnoExport.vim' END writefile(import_wrong_name_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1053:', '', 2, 'Ximport.vim') var import_redefining_lines =<< trim END vim9script import './Xexport.vim' as exported var exported = 5 END writefile(import_redefining_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1213: Redefining imported item "exported"', '', 3) var import_missing_dot_lines =<< trim END vim9script import './Xexport.vim' as expo def Test() expo = 9 enddef defcompile END writefile(import_missing_dot_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1258:', '', 1) var import_missing_name_lines =<< trim END vim9script import './Xexport.vim' as expo def Test() expo.99 = 9 enddef defcompile END writefile(import_missing_name_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1259:', '', 1) var import_assign_wrong_type_lines =<< trim END vim9script import './Xexport.vim' as expo expo.exported = 'xxx' END writefile(import_assign_wrong_type_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1012: Type mismatch; expected number but got string', '', 3) var import_assign_const_lines =<< trim END vim9script import './Xexport.vim' as expo expo.CONST = 4321 END writefile(import_assign_const_lines, 'Ximport.vim') assert_fails('source Ximport.vim', 'E46: Cannot change read-only variable "CONST"', '', 3) delete('Ximport.vim') delete('Xexport.vim') # Check that in a Vim9 script 'cpo' is set to the Vim default. # Flags added or removed are also applied to the restored value. set cpo=abcd var lines =<< trim END vim9script g:cpo_in_vim9script = &cpo set cpo+=f set cpo-=c g:cpo_after_vim9script = &cpo END writefile(lines, 'Xvim9_script') source Xvim9_script assert_equal('fabd', &cpo) set cpo&vim assert_equal(&cpo, g:cpo_in_vim9script) var newcpo = substitute(&cpo, 'c', '', '') .. 'f' assert_equal(newcpo, g:cpo_after_vim9script) delete('Xvim9_script') enddef def Test_import_very_long_name() var lines =<< trim END vim9script export var verylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongname = 'asdf' END writefile(lines, 'Xverylong.vim') lines =<< trim END vim9script import './Xverylong.vim' g:result = Xverylong.verylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongname END v9.CheckScriptSuccess(lines) assert_equal('asdf', g:result) delete('Xverylong.vim') unlet g:result enddef def Test_import_funcref() var lines =<< trim END vim9script export def F(): number return 42 enddef export const G = F END writefile(lines, 'Xlib.vim') lines =<< trim END vim9script import './Xlib.vim' as lib const Foo = lib.G() assert_equal(42, Foo) def DoTest() const Goo = lib.G() assert_equal(42, Goo) enddef DoTest() END v9.CheckScriptSuccess(lines) delete('Xlib.vim') enddef def Test_import_duplicate_function() # Function Hover() exists in both scripts, partial should refer to the right # one. var lines =<< trim END vim9script def Hover(d: dict<any>): string return 'found it' enddef export def NewLspServer(): dict<any> var d: dict<any> = {} d->extend({hover: function('Hover', [d])}) return d enddef NewLspServer() END writefile(lines, 'Xserver.vim') lines =<< trim END vim9script import './Xserver.vim' as server export def Hover() enddef def AddServer() var d: dict<any> = server.NewLspServer() assert_equal('found it', d.hover()) enddef AddServer() END v9.CheckScriptSuccess(lines) delete('Xserver.vim') enddef def Test_import_fails() writefile([], 'Xfoo.vim') var lines =<< trim END import './Xfoo.vim' as foo foo = 'bar' END v9.CheckDefAndScriptFailure(lines, ['E1094:', 'E1236: Cannot use foo itself']) lines =<< trim END vim9script import './Xfoo.vim' as foo var that = foo END v9.CheckScriptFailure(lines, 'E1060: Expected dot after name: foo') lines =<< trim END vim9script import './Xfoo.vim' as foo var that: any that += foo END v9.CheckScriptFailure(lines, 'E1060: Expected dot after name: foo') lines =<< trim END vim9script import './Xfoo.vim' as foo foo += 9 END v9.CheckScriptFailure(lines, 'E1060: Expected dot after name: foo') lines =<< trim END vim9script import './Xfoo.vim' as 9foo END v9.CheckScriptFailure(lines, 'E1047:') lines =<< trim END vim9script import './Xfoo.vim' as the#foo END v9.CheckScriptFailure(lines, 'E1047:') lines =<< trim END vim9script import './Xfoo.vim' as g:foo END v9.CheckScriptFailure(lines, 'E1047:') lines =<< trim END vim9script def TheFunc() echo 'the func' enddef export var Ref = TheFunc END writefile([], 'Xthat.vim') lines =<< trim END import './Xthat.vim' as That That() END v9.CheckDefAndScriptFailure(lines, ['E1094:', 'E1236: Cannot use That itself']) lines =<< trim END vim9script import './Xthat.vim' as That def Func() echo That() enddef Func() END v9.CheckScriptFailure(lines, 'E1236: Cannot use That itself') lines =<< trim END import './Xthat.vim' as one import './Xthat.vim' as two END v9.CheckScriptFailure(lines, 'E1262:') delete('Xthat.vim') lines =<< trim END vim9script export var item = 'hello' import './Xyourself.vim' END writefile(lines, 'Xyourself.vim') assert_fails('source Xyourself.vim', 'E1088:') delete('Xyourself.vim') mkdir('Ximport') writefile(['vim9script'], 'Ximport/.vim') lines =<< trim END vim9script import './Ximport/.vim' END v9.CheckScriptFailure(lines, 'E1261: Cannot import .vim without using "as"') lines =<< trim END vim9script import './Ximport/.vim' as vim END v9.CheckScriptSuccess(lines) writefile(['vim9script'], 'Ximport/.vimrc') lines =<< trim END vim9script import './Ximport/.vimrc' END v9.CheckScriptFailure(lines, 'E1257: Imported script must use "as" or end in .vim') lines =<< trim END vim9script import './Ximport/.vimrc' as vimrc END v9.CheckScriptSuccess(lines) new setline(1, ['vim9script', 'import "" as abc']) assert_fails('source', 'E1071: Invalid string for :import: "" as abc') setline(2, 'import [] as abc') assert_fails('source', 'E1071: Invalid string for :import: [] as abc') setline(2, 'import test_null_string() as abc') assert_fails('source', 'E1071: Invalid string for :import: test_null_string() as abc') bw! call writefile(['vim9script', "import './Xfoo.vim' ask expo"], 'Xbar.vim') assert_fails('source Xbar.vim', 'E488: Trailing characters: ask expo') writefile([], 'Xtemp') call writefile(['vim9script', "import './Xtemp'"], 'Xbar.vim') assert_fails('source Xbar.vim', 'E1257: Imported script must use "as" or end in .vim: Xtemp') delete('Xtemp') call writefile(['vim9script', "import './Xfoo.vim' as abc | foobar"], 'Xbar.vim') assert_fails('source Xbar.vim', 'E492: Not an editor command: foobar') call delete('Xbar.vim') delete('Ximport', 'rf') delete('Xfoo.vim') enddef func g:Trigger() source Ximport.vim return "echo 'yes'\<CR>" endfunc def Test_import_export_expr_map() # check that :import and :export work when buffer is locked var export_lines =<< trim END vim9script export def That(): string return 'yes' enddef END writefile(export_lines, 'Xexport_that.vim') var import_lines =<< trim END vim9script import './Xexport_that.vim' as that assert_equal('yes', that.That()) END writefile(import_lines, 'Ximport.vim') nnoremap <expr> trigger g:Trigger() feedkeys('trigger', "xt") delete('Xexport_that.vim') delete('Ximport.vim') nunmap trigger enddef def Test_import_in_filetype() # check that :import works when the buffer is locked mkdir('ftplugin', 'p') var export_lines =<< trim END vim9script export var That = 'yes' END writefile(export_lines, 'ftplugin/Xexport_ft.vim') var import_lines =<< trim END vim9script import './Xexport_ft.vim' as ft assert_equal('yes', ft.That) g:did_load_mytpe = 1 END writefile(import_lines, 'ftplugin/qf.vim') var save_rtp = &rtp &rtp = getcwd() .. ',' .. &rtp filetype plugin on copen assert_equal(1, g:did_load_mytpe) quit! delete('Xexport_ft.vim') delete('ftplugin', 'rf') &rtp = save_rtp enddef def Test_use_import_in_mapping() var lines =<< trim END vim9script export def Funcx(nr: number) g:result = nr enddef END writefile(lines, 'XsomeExport.vim') lines =<< trim END vim9script import './XsomeExport.vim' as some var Funcy = some.Funcx nnoremap <F3> :call <sid>Funcy(42)<cr> nnoremap <F4> :call <sid>some.Funcx(44)<cr> END writefile(lines, 'Xmapscript.vim') source Xmapscript.vim feedkeys("\<F3>", "xt") assert_equal(42, g:result) feedkeys("\<F4>", "xt") assert_equal(44, g:result) unlet g:result delete('XsomeExport.vim') delete('Xmapscript.vim') nunmap <F3> nunmap <F4> enddef def Test_use_relative_autoload_import_in_mapping() var lines =<< trim END vim9script export def Func() g:result = 42 enddef END writefile(lines, 'XrelautoloadExport.vim') lines =<< trim END vim9script import autoload './XrelautoloadExport.vim' as some nnoremap <F3> :call <SID>some.Func()<CR> END writefile(lines, 'Xmapscript.vim') source Xmapscript.vim assert_match('\d\+ A: .*XrelautoloadExport.vim', execute('scriptnames')->split("\n")[-1]) var l = getscriptinfo() assert_match('XrelautoloadExport.vim$', l[-1].name) assert_true(l[-1].autoload) feedkeys("\<F3>", "xt") assert_equal(42, g:result) l = getscriptinfo({name: 'XrelautoloadExport'}) assert_true(len(l) == 1) assert_match('XrelautoloadExport.vim$', l[0].name) assert_false(l[0].autoload) assert_equal(999999, l[0].version) unlet g:result delete('XrelautoloadExport.vim') delete('Xmapscript.vim') nunmap <F3> enddef def Test_autoload_import_var() # variable name starts with "autoload" var lines =<< trim END vim9script var autoloaded = "Xtest.vim" import autoloaded END v9.CheckScriptFailure(lines, 'E1053: Could not import "Xtest.vim') enddef def Test_use_autoload_import_in_mapping() var lines =<< trim END vim9script export def Func() g:result = 49 enddef END mkdir('Ximpdir/autoload', 'p') writefile(lines, 'Ximpdir/autoload/XautoloadExport.vim') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Ximpdir' lines =<< trim END vim9script import autoload 'XautoloadExport.vim' as some nnoremap <F3> :call <SID>some.Func()<CR> END writefile(lines, 'Xmapscript.vim') source Xmapscript.vim assert_match('\d\+ A: .*autoload/XautoloadExport.vim', execute('scriptnames')->split("\n")[-1]) feedkeys("\<F3>", "xt") assert_equal(49, g:result) unlet g:result delete('Xmapscript.vim') nunmap <F3> delete('Ximpdir', 'rf') &rtp = save_rtp enddef def Test_use_import_in_command_completion() var lines =<< trim END vim9script export def Complete(..._): list<string> return ['abcd'] enddef END writefile(lines, 'Xscript.vim') lines =<< trim END vim9script import './Xscript.vim' command -nargs=1 -complete=customlist,Xscript.Complete Cmd echo 'ok' feedkeys(":Cmd ab\<Tab>\<C-B>#\<CR>", 'xnt') assert_equal('#Cmd abcd', @:) END v9.CheckScriptSuccess(lines) delcommand Cmd delete('Xscript.vim') enddef def Test_use_import_with_funcref_in_command_completion() var lines =<< trim END vim9script export def Complete(..._): list<string> return ['abcd'] enddef END writefile(lines, 'Xscript.vim') lines =<< trim END vim9script import './Xscript.vim' var Ref = Xscript.Complete exe "command -nargs=1 -complete=customlist," .. expand('<SID>') .. "Ref Cmd echo 'ok'" feedkeys(":Cmd ab\<Tab>\<C-B>#\<CR>", 'xnt') assert_equal('#Cmd abcd', @:) END v9.CheckScriptSuccess(lines) delcommand Cmd delete('Xscript.vim') enddef def Test_use_autoload_import_in_insert_completion() mkdir('Xinsdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xinsdir' var lines =<< trim END vim9script export def ThesaurusFunc(findbase: bool, _): any if findbase return 1 endif return [ 'check', 'experiment', 'test', 'verification' ] enddef g:completion_loaded = 'yes' END writefile(lines, 'Xinsdir/autoload/completion.vim') new lines =<< trim END vim9script g:completion_loaded = 'no' import autoload 'completion.vim' set thesaurusfunc=completion.ThesaurusFunc assert_equal('no', g:completion_loaded) feedkeys("i\<C-X>\<C-T>\<C-N>\<Esc>", 'xt') assert_equal('experiment', getline(1)) assert_equal('yes', g:completion_loaded) END v9.CheckScriptSuccess(lines) set thesaurusfunc= bwipe! delete('Xinsdir', 'rf') &rtp = save_rtp enddef def Test_use_autoload_import_partial_in_opfunc() mkdir('Xpartdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xpartdir' var lines =<< trim END vim9script export def Opfunc1(..._) g:opfunc_called = 'yes' enddef END writefile(lines, 'Xpartdir/autoload/opfunc.vim') new lines =<< trim END vim9script import autoload 'opfunc.vim' nnoremap <expr> <F3> TheFunc() def TheFunc(): string &operatorfunc = function('opfunc.Opfunc1', [0]) return 'g@' enddef feedkeys("\<F3>l", 'xt') assert_equal('yes', g:opfunc_called) END v9.CheckScriptSuccess(lines) set opfunc= bwipe! delete('Xpartdir', 'rf') nunmap <F3> &rtp = save_rtp enddef def Test_set_opfunc_to_autoload_func_directly() mkdir('Xdirdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xdirdir' var lines =<< trim END vim9script export def Opfunc2(..._) g:opfunc_called = 'yes' enddef END writefile(lines, 'Xdirdir/autoload/opfunc.vim') new lines =<< trim END vim9script import autoload 'opfunc.vim' nnoremap <expr> <F3> TheFunc() def TheFunc(): string &operatorfunc = opfunc.Opfunc2 return 'g@' enddef feedkeys("\<F3>l", 'xt') assert_equal('yes', g:opfunc_called) END v9.CheckScriptSuccess(lines) set opfunc= bwipe! delete('Xdirdir', 'rf') nunmap <F3> &rtp = save_rtp enddef def Test_use_autoload_import_in_fold_expression() mkdir('Xfolddir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xfolddir' var lines =<< trim END vim9script export def Expr(): string return getline(v:lnum) =~ '^#' ? '>1' : '1' enddef export def Text(): string return 'fold text' enddef g:fold_loaded = 'yes' END writefile(lines, 'Xfolddir/autoload/fold.vim') lines =<< trim END vim9script import autoload 'fold.vim' &foldexpr = 'fold.Expr()' &foldtext = 'fold.Text()' &foldmethod = 'expr' &debug = 'throw' END new setline(1, ['# one', 'text', '# two', 'text']) g:fold_loaded = 'no' v9.CheckScriptSuccess(lines) assert_equal('no', g:fold_loaded) redraw assert_equal('yes', g:fold_loaded) # Check that script context of 'foldexpr' is copied to another buffer. edit! otherfile redraw set foldexpr= foldtext& foldmethod& debug= bwipe! delete('Xfolddir', 'rf') &rtp = save_rtp enddef def Test_autoload_import_relative() var lines =<< trim END vim9script g:loaded = 'yes' export def RelFunc(): string return 'relfunc' enddef def NotExported() echo 'not' enddef export var someText = 'some text' var notexp = 'bad' END writefile(lines, 'XimportRel.vim') writefile(lines, 'XimportRel2.vim') writefile(lines, 'XimportRel3.vim') writefile(lines, 'XimportRel4.vim') writefile(lines, 'XimportRel5.vim') lines =<< trim END vim9script g:loaded = 'no' import autoload './XimportRel.vim' assert_equal('no', g:loaded) def AFunc(): string var res = '' res ..= XimportRel.RelFunc() res ..= '/' res ..= XimportRel.someText XimportRel.someText = 'from AFunc' return res enddef # script not loaded when compiling defcompile assert_equal('no', g:loaded) assert_equal('relfunc/some text', AFunc()) assert_equal('yes', g:loaded) unlet g:loaded assert_equal('from AFunc', XimportRel.someText) XimportRel.someText = 'from script' assert_equal('from script', XimportRel.someText) END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script import autoload './XimportRel.vim' echo XimportRel.NotExported() END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExported', 3) lines =<< trim END vim9script import autoload './XimportRel.vim' echo XimportRel.notexp END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 3) lines =<< trim END vim9script import autoload './XimportRel.vim' XimportRel.notexp = 'bad' END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 3) lines =<< trim END vim9script import autoload './XimportRel.vim' def Func() echo XimportRel.NotExported() enddef Func() END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExported', 1) lines =<< trim END vim9script import autoload './XimportRel.vim' def Func() echo XimportRel.notexp enddef Func() END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1) # Same, script not imported before lines =<< trim END vim9script import autoload './XimportRel4.vim' def Func() echo XimportRel4.notexp enddef Func() END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1) # does not fail if the script wasn't loaded yet and only compiling g:loaded = 'no' lines =<< trim END vim9script import autoload './XimportRel2.vim' def Func() echo XimportRel2.notexp enddef defcompile END v9.CheckScriptSuccess(lines) assert_equal('no', g:loaded) lines =<< trim END vim9script import autoload './XimportRel.vim' def Func() XimportRel.notexp = 'bad' enddef Func() END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1) # fails with a not loaded import lines =<< trim END vim9script import autoload './XimportRel3.vim' def Func() XimportRel3.notexp = 'bad' enddef Func() END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1) assert_equal('yes', g:loaded) unlet g:loaded lines =<< trim END vim9script import autoload './XimportRel5.vim' def Func() XimportRel5.nosuchvar = 'bad' enddef Func() END v9.CheckScriptFailure(lines, 'E121: Undefined variable: nosuchvar', 1) unlet g:loaded # nasty: delete script after compiling function writefile(['vim9script'], 'XimportRelDel.vim') lines =<< trim END vim9script import autoload './XimportRelDel.vim' def DoIt() echo XimportRelDel.var enddef defcompile delete('XimportRelDel.vim') DoIt() END v9.CheckScriptFailure(lines, 'E484:') delete('XimportRel.vim') delete('XimportRel2.vim') delete('XimportRel3.vim') delete('XimportRel4.vim') delete('XimportRel5.vim') enddef def Test_autoload_import_relative_autoload_dir() mkdir('autoload', 'p') var lines =<< trim END vim9script export def Bar() g:called_bar = 'yes' enddef END writefile(lines, 'autoload/script.vim') lines =<< trim END vim9script import autoload './autoload/script.vim' def Foo() script.Bar() enddef Foo() assert_equal('yes', g:called_bar) END v9.CheckScriptSuccess(lines) unlet g:called_bar delete('autoload', 'rf') enddef def Test_autoload_import_deleted() var lines =<< trim END vim9script export const FOO = 1 END writefile(lines, 'Xa.vim') lines =<< trim END vim9script import autoload './Xa.vim' delete('Xa.vim') var x = Xa.FOO END v9.CheckScriptFailure(lines, 'E484:') delete('Xa.vim') enddef func Test_import_in_diffexpr() CheckExecutable diff call Run_Test_import_in_diffexpr() endfunc def Run_Test_import_in_diffexpr() var lines =<< trim END vim9script export def DiffExpr() # Prepend some text to check diff type detection writefile(['warning', ' message'], v:fname_out) silent exe '!diff ' .. v:fname_in .. ' ' .. v:fname_new .. '>>' .. v:fname_out enddef END writefile(lines, 'Xdiffexpr') lines =<< trim END vim9script import './Xdiffexpr' as diff set diffexpr=diff.DiffExpr() set diffopt=foldcolumn:0 END v9.CheckScriptSuccess(lines) enew! call setline(1, ['one', 'two', 'three']) diffthis botright vert new call setline(1, ['one', 'two', 'three.']) diffthis # we only check if this does not cause errors redraw diffoff! set diffexpr= set diffopt& bwipe! bwipe! delete('Xdiffexpr') enddef def Test_import_in_patchexpr() var lines =<< trim END vim9script export def TPatch() call writefile(['output file'], v:fname_out) enddef END writefile(lines, 'Xpatchexpr') lines =<< trim END vim9script import './Xpatchexpr' as patch set patchexpr=patch.TPatch() END v9.CheckScriptSuccess(lines) call writefile(['input file'], 'Xinput') call writefile(['diff file'], 'Xdiff') :%bwipe! edit Xinput diffpatch Xdiff call assert_equal('output file', getline(1)) call delete('Xinput') call delete('Xdiff') call delete('Xpatchexpr') set patchexpr& :%bwipe! enddef def Test_import_in_formatexpr() var lines =<< trim END vim9script export def MyFormatExpr(): number g:did_format = 'yes' return 0 enddef END writefile(lines, 'Xformatter') lines =<< trim END vim9script import './Xformatter' as format set formatexpr=format.MyFormatExpr() END v9.CheckScriptSuccess(lines) new setline(1, ['a', 'b', 'c']) normal gqG assert_equal('yes', g:did_format) bwipe! delete('Xformatter') unlet g:did_format set formatexpr= enddef def Test_import_in_includeexpr() writefile(['found it'], 'Xthisfile') new var lines =<< trim END vim9script export def DoSub(): string return substitute(v:fname, 'that', 'this', '') enddef END writefile(lines, 'Xinclude.vim') lines =<< trim END vim9script import './Xinclude.vim' set includeexpr=Xinclude.DoSub() END v9.CheckScriptSuccess(lines) setline(1, ['Xthatfile']) exe "normal \<C-W>f" assert_equal('Xthisfile', expand('%')) bwipe! bwipe! set includeexpr= delete('Xinclude.vim') delete('Xthisfile') enddef def Test_import_in_indentexpr() var lines =<< trim END vim9script export def GetIndent(): number return 5 enddef END writefile(lines, 'Xindenter') lines =<< trim END vim9script import './Xindenter' as indent set indentexpr=indent.GetIndent() set debug=throw END v9.CheckScriptSuccess(lines) new setline(1, 'hello') normal == assert_equal(' hello', getline(1)) bwipe! set indentexpr= debug= delete('Xindenter') enddef func Test_import_in_printexpr() CheckFeature postscript call Run_Test_import_in_printexpr() endfunc def Run_Test_import_in_printexpr() var lines =<< trim END vim9script export def PrintFile(): bool g:printed = 'yes' delete('v:fname_in') return false enddef END writefile(lines, 'Xprint.vim') lines =<< trim END vim9script import './Xprint.vim' set printexpr=Xprint.PrintFile() END v9.CheckScriptSuccess(lines) help hardcopy dummy args assert_equal('yes', g:printed) delete('Xprint.vim') set printexpr= enddef def Test_import_in_charconvert() var lines =<< trim END vim9script export def MakeUpper(): bool var data = readfile(v:fname_in) map(data, 'toupper(v:val)') writefile(data, v:fname_out) return false # success enddef END writefile(lines, 'Xconvert.vim') lines =<< trim END vim9script import './Xconvert.vim' as conv set charconvert=conv.MakeUpper() END v9.CheckScriptSuccess(lines) writefile(['one', 'two'], 'Xiicfile') new Xiicfile write ++enc=ucase Xiicfile1 assert_equal(['ONE', 'TWO'], readfile('Xiicfile1')) delete('Xiicfile') delete('Xiicfile1') delete('Xconvert.vim') bwipe! set charconvert& enddef func Test_import_in_spellsuggest_expr() CheckFeature spell call Run_Test_import_in_spellsuggest_expr() endfunc def Run_Test_import_in_spellsuggest_expr() var lines =<< trim END vim9script export def MySuggest(): list<any> return [['Fox', 8], ['Fop', 9]] enddef END writefile(lines, 'Xsuggest.vim') lines =<< trim END vim9script import './Xsuggest.vim' as sugg set spell spellsuggest=expr:sugg.MySuggest() END v9.CheckScriptSuccess(lines) set verbose=1 # report errors call assert_equal(['Fox', 'Fop'], spellsuggest('Fo', 2)) delete('Xsuggest.vim') set nospell spellsuggest& verbose=0 enddef def Test_import_in_lambda_method() var lines =<< trim END vim9script export def Retarg(e: any): any return e enddef END writefile(lines, 'XexportRetarg.vim') lines =<< trim END vim9script import './XexportRetarg.vim' def Lambda(): string var F = (x) => x->XexportRetarg.Retarg() return F('arg') enddef assert_equal('arg', Lambda()) END v9.CheckScriptSuccess(lines) delete('XexportRetarg.vim') enddef def Test_export_shadows_global_function() mkdir('Xglobdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xglobdir' var lines =<< trim END vim9script export def Shadow(): string return 'Shadow()' enddef END writefile(lines, 'Xglobdir/autoload/shadow.vim') lines =<< trim END vim9script def g:Shadow(): string return 'global' enddef import autoload 'shadow.vim' assert_equal('Shadow()', shadow.Shadow()) END v9.CheckScriptSuccess(lines) delfunc g:Shadow bwipe! delete('Xglobdir', 'rf') &rtp = save_rtp enddef def Test_export_fails() v9.CheckScriptFailure(['export var some = 123'], 'E1042:') v9.CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:') v9.CheckScriptFailure(['vim9script', 'export echo 134'], 'E1043:') v9.CheckScriptFailure(['vim9script', 'export function /a1b2c3'], 'E1044:') assert_fails('export something', 'E1043:') enddef func Test_import_fails_without_script() CheckRunVimInTerminal " call indirectly to avoid compilation error for missing functions call Run_Test_import_fails_on_command_line() endfunc def Run_Test_import_fails_on_command_line() var export =<< trim END vim9script export def Foo(): number return 0 enddef END writefile(export, 'XexportCmd.vim') var buf = g:RunVimInTerminal('-c "import Foo from ''./XexportCmd.vim''"', { rows: 6, wait_for_ruler: 0}) g:WaitForAssert(() => assert_match('^E1094:', term_getline(buf, 5))) delete('XexportCmd.vim') g:StopVimInTerminal(buf) enddef def Test_vim9_reload_noclear() var lines =<< trim END vim9script export var exported = 'thexport' export def TheFunc(x = 0) enddef END writefile(lines, 'XExportReload') lines =<< trim END vim9script noclear g:loadCount += 1 var reloaded = 'init' import './XExportReload' as exp def Again(): string return 'again' enddef exp.TheFunc() if exists('loaded') | finish | endif var loaded = true var notReloaded = 'yes' reloaded = 'first' def g:Values(): list<string> return [reloaded, notReloaded, Again(), Once(), exp.exported] enddef def Once(): string return 'once' enddef END writefile(lines, 'XReloaded') g:loadCount = 0 source XReloaded assert_equal(1, g:loadCount) assert_equal(['first', 'yes', 'again', 'once', 'thexport'], g:Values()) source XReloaded assert_equal(2, g:loadCount) assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values()) source XReloaded assert_equal(3, g:loadCount) assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values()) delete('XReloaded') delete('XExportReload') delfunc g:Values unlet g:loadCount lines =<< trim END vim9script def Inner() enddef END lines->writefile('XreloadScript.vim') source XreloadScript.vim lines =<< trim END vim9script def Outer() def Inner() enddef enddef defcompile END lines->writefile('XreloadScript.vim') source XreloadScript.vim delete('XreloadScript.vim') enddef def Test_vim_reload_noclear_arg_count() var lines =<< trim END vim9script noclear if !exists('g:didload') def Test(a: string, b: string) echo a b enddef def Call() Test('a', 'b') enddef else # redefine with one argument less def Test(a: string) echo a enddef endif Call() g:didload = 1 END lines->writefile('XreloadScript_1.vim') source XreloadScript_1.vim assert_fails('source XreloadScript_1.vim', 'E1106: One argument too many') unlet g:didload lines =<< trim END vim9script noclear if !exists('g:didload') def Test(a: string, b: string, c: string) echo a b enddef def Call() Test('a', 'b', 'c') enddef else # redefine with one argument less def Test(a: string) echo a enddef endif Call() g:didload = 1 END lines->writefile('XreloadScript_2.vim') source XreloadScript_2.vim assert_fails('source XreloadScript_2.vim', 'E1106: 2 arguments too many') unlet g:didload lines =<< trim END vim9script noclear if !exists('g:didload') def Test(a: string) echo a enddef def Call() Test('a') enddef else # redefine with one argument extra def Test(a: string, b: string) echo a b enddef endif Call() g:didload = 1 END lines->writefile('XreloadScript_3.vim') source XreloadScript_3.vim assert_fails('source XreloadScript_3.vim', 'E1190: One argument too few') unlet g:didload lines =<< trim END vim9script noclear if !exists('g:didload') def Test(a: string) echo a enddef def Call() Test('a') enddef else # redefine with two arguments extra def Test(a: string, b: string, c: string) echo a b enddef endif Call() g:didload = 1 END lines->writefile('XreloadScript_4.vim') source XreloadScript_4.vim assert_fails('source XreloadScript_4.vim', 'E1190: 2 arguments too few') unlet g:didload delete('XreloadScript_1.vim') delete('XreloadScript_2.vim') delete('XreloadScript_3.vim') delete('XreloadScript_4.vim') enddef def Test_vim9_reload_noclear_error() var lines =<< trim END vim9script noclear if !exists('g:didload') def Test(a: string) echo a enddef def Call() Test('a') enddef else # redefine with a compile error def Test(a: string) echo ax enddef endif Call() g:didload = 1 END lines->writefile('XreloadScriptErr.vim') source XreloadScriptErr.vim assert_fails('source XreloadScriptErr.vim', 'E1001: Variable not found: ax') unlet g:didload delete('XreloadScriptErr.vim') enddef def Test_vim9_reload_import() var lines =<< trim END vim9script const var = '' var valone = 1234 def MyFunc(arg: string) valone = 5678 enddef END var morelines =<< trim END var valtwo = 222 export def GetValtwo(): number return valtwo enddef END writefile(lines + morelines, 'Xreload.vim') source Xreload.vim source Xreload.vim source Xreload.vim # cannot declare a var twice lines =<< trim END vim9script var valone = 1234 var valone = 5678 END writefile(lines, 'Xreload.vim') assert_fails('source Xreload.vim', 'E1041:', '', 3, 'Xreload.vim') delete('Xreload.vim') delete('Ximport.vim') enddef " if a script is reloaded with a script-local variable that changed its type, a " compiled function using that variable must fail. def Test_script_reload_change_type() var lines =<< trim END vim9script noclear var str = 'string' def g:GetStr(): string return str .. 'xxx' enddef END writefile(lines, 'Xreload.vim') source Xreload.vim echo g:GetStr() lines =<< trim END vim9script noclear var str = 1234 END writefile(lines, 'Xreload.vim') source Xreload.vim assert_fails('echo g:GetStr()', 'E1150:') delfunc g:GetStr delete('Xreload.vim') enddef " Define CallFunc so that the test can be compiled command CallFunc echo 'nop' def Test_script_reload_from_function() var lines =<< trim END vim9script if exists('g:loadedThis') finish endif g:loadedThis = 1 delcommand CallFunc command CallFunc Func() def Func() so XreloadFunc.vim g:didTheFunc = 1 enddef END writefile(lines, 'XreloadFunc.vim') source XreloadFunc.vim CallFunc assert_equal(1, g:didTheFunc) delete('XreloadFunc.vim') delcommand CallFunc unlet g:loadedThis unlet g:didTheFunc enddef def s:RetSome(): string return 'some' enddef " Not exported function that is referenced needs to be accessed by the " script-local name. def Test_vim9_funcref() var sortlines =<< trim END vim9script def Compare(i1: number, i2: number): number return i2 - i1 enddef export def FastSort(): list<number> return range(5)->sort(Compare) enddef export def GetString(arg: string): string return arg enddef END writefile(sortlines, 'Xsort.vim') var lines =<< trim END vim9script import './Xsort.vim' def Test() g:result = Xsort.FastSort() enddef Test() END writefile(lines, 'Xscript.vim') source Xscript.vim assert_equal([4, 3, 2, 1, 0], g:result) unlet g:result lines =<< trim END vim9script # using a function imported with "as" import './Xsort.vim' as anAlias assert_equal('yes', anAlias.GetString('yes')) # using the function from a compiled function def TestMore(): string var s = s:anAlias.GetString('foo') return s .. anAlias.GetString('bar') enddef assert_equal('foobar', TestMore()) # error when using a function that isn't exported assert_fails('anAlias.Compare(1, 2)', 'E1049:') END writefile(lines, 'Xscript.vim') delete('Xsort.vim') delete('Xscript.vim') var Funcref = function('s:RetSome') assert_equal('some', Funcref()) enddef " Check that when searching for "FilterFunc" it finds the import in the " script where FastFilter() is called from, both as a string and as a direct " function reference. def Test_vim9_funcref_other_script() var filterLines =<< trim END vim9script export def FilterFunc(idx: number, val: number): bool return idx % 2 == 1 enddef export def FastFilter(): list<number> return range(10)->filter('FilterFunc(v:key, v:val)') enddef export def FastFilterDirect(): list<number> return range(10)->filter(FilterFunc) enddef END writefile(filterLines, 'Xfilter.vim') var lines =<< trim END vim9script import './Xfilter.vim' as filter def Test() var x: list<number> = filter.FastFilter() enddef Test() def TestDirect() var x: list<number> = filter.FastFilterDirect() enddef TestDirect() END v9.CheckScriptSuccess(lines) delete('Xfilter.vim') enddef def Test_import_absolute() var import_lines = [ 'vim9script', 'import "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim" as abs', 'def UseExported()', ' g:imported_abs = abs.exported', ' abs.exported = 8888', ' g:imported_after = abs.exported', 'enddef', 'UseExported()', 'g:import_disassembled = execute("disass UseExported")', ] writefile(import_lines, 'Ximport_abs.vim') writefile(s:export_script_lines, 'Xexport_abs.vim') source Ximport_abs.vim assert_equal(9876, g:imported_abs) assert_equal(8888, g:imported_after) assert_match('<SNR>\d\+_UseExported\_s*' .. 'g:imported_abs = abs.exported\_s*' .. '0 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' .. '1 STOREG g:imported_abs\_s*' .. 'abs.exported = 8888\_s*' .. '2 PUSHNR 8888\_s*' .. '3 STORESCRIPT exported-2 in .*Xexport_abs.vim\_s*' .. 'g:imported_after = abs.exported\_s*' .. '4 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' .. '5 STOREG g:imported_after', g:import_disassembled) Undo_export_script_lines() unlet g:imported_abs unlet g:import_disassembled delete('Ximport_abs.vim') delete('Xexport_abs.vim') enddef def Test_import_rtp() var import_lines = [ 'vim9script', 'import "Xexport_rtp.vim" as rtp', 'g:imported_rtp = rtp.exported', ] writefile(import_lines, 'Ximport_rtp.vim') mkdir('import', 'p') writefile(s:export_script_lines, 'import/Xexport_rtp.vim') var save_rtp = &rtp &rtp = getcwd() source Ximport_rtp.vim &rtp = save_rtp assert_equal(9876, g:imported_rtp) Undo_export_script_lines() unlet g:imported_rtp delete('Ximport_rtp.vim') delete('import', 'rf') enddef def Test_import_compile_error() var export_lines = [ 'vim9script', 'export def ExpFunc(): string', ' return notDefined', 'enddef', ] writefile(export_lines, 'Xexported.vim') var import_lines = [ 'vim9script', 'import "./Xexported.vim" as expo', 'def ImpFunc()', ' echo expo.ExpFunc()', 'enddef', 'defcompile', ] writefile(import_lines, 'Ximport.vim') try source Ximport.vim catch /E1001/ # Error should be before the Xexported.vim file. assert_match('E1001: Variable not found: notDefined', v:exception) assert_match('function <SNR>\d\+_ImpFunc\[1\]..<SNR>\d\+_ExpFunc, line 1', v:throwpoint) endtry delete('Xexported.vim') delete('Ximport.vim') enddef def Test_func_overrules_import_fails() var export_lines =<< trim END vim9script export def Func() echo 'imported' enddef END writefile(export_lines, 'XexportedFunc.vim') var lines =<< trim END vim9script import './XexportedFunc.vim' as Func def Func() echo 'local to function' enddef END v9.CheckScriptFailure(lines, 'E1213: Redefining imported item "Func"') lines =<< trim END vim9script import './XexportedFunc.vim' as Func def Outer() def Func() echo 'local to function' enddef enddef defcompile END v9.CheckScriptFailure(lines, 'E1236:') delete('XexportedFunc.vim') enddef def Test_source_vim9_from_legacy() var vim9_lines =<< trim END vim9script var local = 'local' g:global = 'global' export var exported = 'exported' export def GetText(): string return 'text' enddef END writefile(vim9_lines, 'Xvim9_script.vim') var legacy_lines =<< trim END source Xvim9_script.vim call assert_false(exists('local')) call assert_false(exists('exported')) call assert_false(exists('s:exported')) call assert_equal('global', global) call assert_equal('global', g:global) END writefile(legacy_lines, 'Xlegacy_script.vim') source Xlegacy_script.vim assert_equal('global', g:global) unlet g:global delete('Xlegacy_script.vim') delete('Xvim9_script.vim') enddef def Test_import_vim9_from_legacy() var vim9_lines =<< trim END vim9script var local = 'local' g:global = 'global' export var exported = 'exported' export def GetText(): string return 'text' enddef END writefile(vim9_lines, 'Xvim9_export.vim') var legacy_lines =<< trim END import './Xvim9_export.vim' as vim9 call assert_false(exists('vim9')) call assert_false(exists('local')) call assert_false(exists('s:vim9.local')) call assert_equal('global', global) call assert_equal('global', g:global) call assert_false(exists('exported')) call assert_false(exists('s:exported')) call assert_false(exists('*GetText')) " imported symbol is script-local call assert_equal('exported', s:vim9.exported) call assert_equal('text', s:vim9.GetText()) END writefile(legacy_lines, 'Xlegacy_script.vim') source Xlegacy_script.vim assert_equal('global', g:global) unlet g:global delete('Xlegacy_script.vim') delete('Xvim9_export.vim') enddef def Test_cmdline_win() # if the Vim syntax highlighting uses Vim9 constructs they can be used from # the command line window. mkdir('rtp/syntax', 'p') var export_lines =<< trim END vim9script export var That = 'yes' END writefile(export_lines, 'rtp/syntax/Xexport.vim') var import_lines =<< trim END vim9script import './Xexport.vim' as exp echo exp.That END writefile(import_lines, 'rtp/syntax/vim.vim') var save_rtp = &rtp &rtp = getcwd() .. '/rtp' .. ',' .. &rtp syntax on augroup CmdWin autocmd CmdwinEnter * g:got_there = 'yes' augroup END # this will open and also close the cmdline window feedkeys('q:', 'xt') assert_equal('yes', g:got_there) augroup CmdWin au! augroup END &rtp = save_rtp delete('rtp', 'rf') enddef def Test_import_gone_when_sourced_twice() var exportlines =<< trim END vim9script if exists('g:guard') finish endif g:guard = 1 export var name = 'someName' END writefile(exportlines, 'XexportScript.vim') var lines =<< trim END vim9script import './XexportScript.vim' as expo def g:GetName(): string return expo.name enddef END writefile(lines, 'XscriptImport.vim') so XscriptImport.vim assert_equal('someName', g:GetName()) so XexportScript.vim assert_fails('call g:GetName()', 'E1149:') delfunc g:GetName delete('XexportScript.vim') delete('XscriptImport.vim') unlet g:guard enddef " test using an auto-loaded function and variable def Test_vim9_autoload_full_name() var lines =<< trim END vim9script export def Gettest(): string return 'test' enddef g:some#name = 'name' g:some#dict = {key: 'value'} export def Varargs(a1: string, ...l: list<string>): string return a1 .. l[0] .. l[1] enddef END mkdir('Xfulldir/autoload', 'p') writefile(lines, 'Xfulldir/autoload/some.vim') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xfulldir' assert_equal('test', g:some#Gettest()) assert_equal('name', g:some#name) assert_equal('value', g:some#dict.key) g:some#other = 'other' assert_equal('other', g:some#other) assert_equal('abc', some#Varargs('a', 'b', 'c')) # upper case script name works lines =<< trim END vim9script export def GetOther(): string return 'other' enddef END writefile(lines, 'Xfulldir/autoload/Other.vim') assert_equal('other', g:Other#GetOther()) delete('Xfulldir', 'rf') &rtp = save_rtp enddef def Test_vim9script_autoload() mkdir('Xaldir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xaldir' # when the path has "/autoload/" prefix is not needed var lines =<< trim END vim9script g:prefixed_loaded += 1 export def Gettest(): string return 'test' enddef export var name = 'name' export func GetFunc() return Gettest() .. 'more' .. s:name endfunc export def GetDef(): string return Gettest() .. 'more' .. name enddef export final fname = 'final' export const cname = 'const' END writefile(lines, 'Xaldir/autoload/prefixed.vim') g:prefixed_loaded = 0 g:expected_loaded = 0 lines =<< trim END vim9script import autoload 'prefixed.vim' assert_equal(g:expected_loaded, g:prefixed_loaded) assert_equal('test', prefixed.Gettest()) assert_equal(1, g:prefixed_loaded) assert_equal('testmorename', prefixed.GetFunc()) assert_equal('testmorename', prefixed.GetDef()) assert_equal('name', prefixed.name) assert_equal('final', prefixed.fname) assert_equal('const', prefixed.cname) END v9.CheckScriptSuccess(lines) # can source it again, autoload script not loaded again g:expected_loaded = 1 v9.CheckScriptSuccess(lines) # can also get the items by autoload name lines =<< trim END call assert_equal('test', prefixed#Gettest()) call assert_equal('testmorename', prefixed#GetFunc()) call assert_equal('name', prefixed#name) call assert_equal('final', prefixed#fname) call assert_equal('const', prefixed#cname) END v9.CheckScriptSuccess(lines) unlet g:prefixed_loaded unlet g:expected_loaded delete('Xaldir', 'rf') &rtp = save_rtp enddef def Test_import_autoload_not_exported() mkdir('Xnimdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xnimdir' # error when using an item that is not exported from an autoload script var exportLines =<< trim END vim9script var notExported = 123 def NotExport() echo 'nop' enddef END writefile(exportLines, 'Xnimdir/autoload/notExport1.vim') var lines =<< trim END vim9script import autoload 'notExport1.vim' echo notExport1.notFound END v9.CheckScriptFailure(lines, 'E1048: Item not found in script: notFound') lines =<< trim END vim9script import autoload 'notExport1.vim' echo notExport1.notExported END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notExported') lines =<< trim END vim9script import autoload 'notExport1.vim' echo notExport1.NotFunc() END v9.CheckScriptFailure(lines, 'E1048: Item not found in script: NotFunc') lines =<< trim END vim9script import autoload 'notExport1.vim' echo notExport1.NotExport() END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExport') lines =<< trim END vim9script import autoload 'notExport1.vim' echo 'text'->notExport1.NotFunc() END v9.CheckScriptFailure(lines, 'E1048: Item not found in script: NotFunc') lines =<< trim END vim9script import autoload 'notExport1.vim' echo 'text'->notExport1.NotExport() END v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExport') # using a :def function we use a different autoload script every time so that # the function is compiled without the script loaded writefile(exportLines, 'Xnimdir/autoload/notExport2.vim') lines =<< trim END vim9script import autoload 'notExport2.vim' def Testit() echo notExport2.notFound enddef Testit() END v9.CheckScriptFailure(lines, 'E1048: Item not found in script: notExport2#notFound') writefile(exportLines, 'Xnimdir/autoload/notExport3.vim') lines =<< trim END vim9script import autoload 'notExport3.vim' def Testit() echo notExport3.notExported enddef Testit() END # don't get E1049 because it is too complicated to figure out v9.CheckScriptFailure(lines, 'E1048: Item not found in script: notExport3#notExported') writefile(exportLines, 'Xnimdir/autoload/notExport4.vim') lines =<< trim END vim9script import autoload 'notExport4.vim' def Testit() echo notExport4.NotFunc() enddef Testit() END v9.CheckScriptFailure(lines, 'E117: Unknown function: notExport4#NotFunc') writefile(exportLines, 'Xnimdir/autoload/notExport5.vim') lines =<< trim END vim9script import autoload 'notExport5.vim' def Testit() echo notExport5.NotExport() enddef Testit() END v9.CheckScriptFailure(lines, 'E117: Unknown function: notExport5#NotExport') writefile(exportLines, 'Xnimdir/autoload/notExport6.vim') lines =<< trim END vim9script import autoload 'notExport6.vim' def Testit() echo 'text'->notExport6.NotFunc() enddef Testit() END v9.CheckScriptFailure(lines, 'E117: Unknown function: notExport6#NotFunc') writefile(exportLines, 'Xnimdir/autoload/notExport7.vim') lines =<< trim END vim9script import autoload 'notExport7.vim' def Testit() echo 'text'->notExport7.NotExport() enddef Testit() END v9.CheckScriptFailure(lines, 'E117: Unknown function: notExport7#NotExport') delete('Xnimdir', 'rf') &rtp = save_rtp enddef def Test_vim9script_autoload_call() mkdir('Xcalldir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xcalldir' var lines =<< trim END vim9script export def RetArg(arg: string): string return arg enddef export def Getother() g:result = 'other' enddef END writefile(lines, 'Xcalldir/autoload/another.vim') lines =<< trim END vim9script import autoload 'another.vim' # compile this before 'another.vim' is loaded def CallAnother() assert_equal('foo', 'foo'->another.RetArg()) enddef CallAnother() call another.Getother() assert_equal('other', g:result) assert_equal('arg', call('another.RetArg', ['arg'])) verbose function another.Getother # should we disallow this? verbose function another#Getother END v9.CheckScriptSuccess(lines) unlet g:result delete('Xcalldir', 'rf') &rtp = save_rtp enddef def Test_vim9script_noclear_autoload() mkdir('Xnocdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xnocdir' var lines =<< trim END vim9script export def Func(): string return 'called' enddef g:double_loaded = 'yes' END writefile(lines, 'Xnocdir/autoload/double.vim') lines =<< trim END vim9script noclear if exists('g:script_loaded') finish endif g:script_loaded = true import autoload 'double.vim' nnoremap <F3> <ScriptCmd>g:result = double.Func()<CR> END g:double_loaded = 'no' writefile(lines, 'Xloaddouble') source Xloaddouble assert_equal('no', g:double_loaded) assert_equal(true, g:script_loaded) source Xloaddouble feedkeys("\<F3>", 'xt') assert_equal('called', g:result) assert_equal('yes', g:double_loaded) delete('Xloaddouble') unlet g:double_loaded unlet g:script_loaded unlet g:result delete('Xnocdir', 'rf') &rtp = save_rtp enddef def Test_vim9script_autoload_duplicate() mkdir('Xdupdir/autoload', 'p') var lines =<< trim END vim9script export def Func() enddef def Func() enddef END writefile(lines, 'Xdupdir/autoload/dupfunc.vim') assert_fails('source Xdupdir/autoload/dupfunc.vim', 'E1073:') lines =<< trim END vim9script def Func() enddef export def Func() enddef END writefile(lines, 'Xdupdir/autoload/dup2func.vim') assert_fails('source Xdupdir/autoload/dup2func.vim', 'E1073:') lines =<< trim END vim9script def Func() enddef export var Func = 'asdf' END writefile(lines, 'Xdupdir/autoload/dup3func.vim') assert_fails('source Xdupdir/autoload/dup3func.vim', 'E1041: Redefining script item: "Func"') lines =<< trim END vim9script export var Func = 'asdf' def Func() enddef END writefile(lines, 'Xdupdir/autoload/dup4func.vim') assert_fails('source Xdupdir/autoload/dup4func.vim', 'E707:') lines =<< trim END vim9script var Func = 'asdf' export def Func() enddef END writefile(lines, 'Xdupdir/autoload/dup5func.vim') assert_fails('source Xdupdir/autoload/dup5func.vim', 'E707:') lines =<< trim END vim9script export def Func() enddef var Func = 'asdf' END writefile(lines, 'Xdupdir/autoload/dup6func.vim') assert_fails('source Xdupdir/autoload/dup6func.vim', 'E1041: Redefining script item: "Func"') delete('Xdupdir', 'rf') enddef def Test_autoload_missing_function_name() mkdir('Xmisdir/autoload', 'p') var lines =<< trim END vim9script def loadme#() enddef END writefile(lines, 'Xmisdir/autoload/loadme.vim') assert_fails('source Xmisdir/autoload/loadme.vim', 'E129:') delete('Xmisdir', 'rf') enddef def Test_autoload_name_wrong() var lines =<< trim END def Xscriptname#Func() enddef END writefile(lines, 'Xscriptname.vim') v9.CheckScriptFailure(lines, 'E746:') delete('Xscriptname.vim') mkdir('Xwrodir/autoload', 'p') lines =<< trim END vim9script def somescript#Func() enddef END writefile(lines, 'Xwrodir/autoload/somescript.vim') assert_fails('source Xwrodir/autoload/somescript.vim', 'E1263:') delete('Xwrodir', 'rf') enddef def Test_import_autoload_postponed() mkdir('Xpostdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xpostdir' var lines =<< trim END vim9script g:loaded_postponed = 'true' export var variable = 'bla' export def Function(): string return 'bla' enddef END writefile(lines, 'Xpostdir/autoload/postponed.vim') lines =<< trim END vim9script import autoload 'postponed.vim' def Tryit() echo postponed.variable echo postponed.Function() enddef defcompile END v9.CheckScriptSuccess(lines) assert_false(exists('g:loaded_postponed')) v9.CheckScriptSuccess(lines + ['Tryit()']) assert_equal('true', g:loaded_postponed) unlet g:loaded_postponed delete('Xpostdir', 'rf') &rtp = save_rtp enddef def Test_import_autoload_override() mkdir('Xoverdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xoverdir' test_override('autoload', 1) var lines =<< trim END vim9script g:loaded_override = 'true' export var variable = 'bla' export def Function(): string return 'bla' enddef END writefile(lines, 'Xoverdir/autoload/override.vim') lines =<< trim END vim9script import autoload 'override.vim' assert_equal('true', g:loaded_override) def Tryit() echo override.doesNotExist enddef defcompile END v9.CheckScriptFailure(lines, 'E1048: Item not found in script: doesNotExist', 1) test_override('autoload', 0) unlet g:loaded_override delete('Xoverdir', 'rf') &rtp = save_rtp enddef def Test_autoload_mapping() mkdir('Xmapdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xmapdir' var lines =<< trim END vim9script g:toggle_loaded = 'yes' export def Toggle(): string return ":g:toggle_called = 'yes'\<CR>" enddef export def Doit() g:doit_called = 'yes' enddef END writefile(lines, 'Xmapdir/autoload/toggle.vim') lines =<< trim END vim9script import autoload 'toggle.vim' nnoremap <silent> <expr> tt toggle.Toggle() nnoremap <silent> xx <ScriptCmd>toggle.Doit()<CR> nnoremap <silent> yy <Cmd>toggle.Doit()<CR> END v9.CheckScriptSuccess(lines) assert_false(exists("g:toggle_loaded")) assert_false(exists("g:toggle_called")) assert_match('\d A: \f*[/\\]toggle.vim', execute('scriptnames')) feedkeys("tt", 'xt') assert_equal('yes', g:toggle_loaded) assert_equal('yes', g:toggle_called) assert_match('\d: \f*[/\\]toggle.vim', execute('scriptnames')) feedkeys("xx", 'xt') assert_equal('yes', g:doit_called) assert_fails('call feedkeys("yy", "xt")', 'E121: Undefined variable: toggle') nunmap tt nunmap xx nunmap yy unlet g:toggle_loaded unlet g:toggle_called delete('Xmapdir', 'rf') &rtp = save_rtp enddef def Test_vim9script_autoload_fails() var lines =<< trim END vim9script autoload var n = 0 END v9.CheckScriptFailure(lines, 'E475: Invalid argument: autoload') lines =<< trim END vim9script noclear noclear var n = 0 END v9.CheckScriptFailure(lines, 'E983: Duplicate argument: noclear') lines =<< trim END vim9script noclears var n = 0 END v9.CheckScriptFailure(lines, 'E475: Invalid argument: noclears') enddef def Test_import_autoload_fails() var lines =<< trim END vim9script import autoload autoload 'prefixed.vim' END v9.CheckScriptFailure(lines, 'E121: Undefined variable: autoload') lines =<< trim END vim9script import autoload './doesNotExist.vim' END v9.CheckScriptFailure(lines, 'E282:', 2) lines =<< trim END vim9script import autoload '/dir/doesNotExist.vim' END v9.CheckScriptFailure(lines, 'E282:', 2) lines =<< trim END vim9script import autoload '../testdir' END v9.CheckScriptFailure(lines, 'E17:', 2) lines =<< trim END vim9script import autoload 'doesNotExist.vim' END v9.CheckScriptFailure(lines, 'E1053: Could not import "doesNotExist.vim"') enddef " test disassembling an auto-loaded function starting with "debug" def Test_vim9_autoload_disass() mkdir('Xdasdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xdasdir' var lines =<< trim END vim9script export def Test(): string return 'debug' enddef END writefile(lines, 'Xdasdir/autoload/debugit.vim') lines =<< trim END vim9script export def Test(): string return 'profile' enddef END writefile(lines, 'Xdasdir/autoload/profileit.vim') lines =<< trim END vim9script assert_equal('debug', debugit#Test()) disass debugit#Test assert_equal('profile', profileit#Test()) disass profileit#Test END v9.CheckScriptSuccess(lines) delete('Xdasdir', 'rf') &rtp = save_rtp enddef " test using a vim9script that is auto-loaded from an autocmd def Test_vim9_aucmd_autoload() var lines =<< trim END vim9script export def Test() echomsg getreg('"') enddef END mkdir('Xauldir/autoload', 'p') writefile(lines, 'Xauldir/autoload/foo.vim') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xauldir' augroup test autocmd TextYankPost * call foo#Test() augroup END normal Y augroup test autocmd! augroup END delete('Xauldir', 'rf') &rtp = save_rtp enddef " test using a autoloaded file that is case sensitive def Test_vim9_autoload_case_sensitive() var lines =<< trim END vim9script export def CaseSensitive(): string return 'done' enddef END mkdir('Xcasedir/autoload', 'p') writefile(lines, 'Xcasedir/autoload/CaseSensitive.vim') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xcasedir' lines =<< trim END vim9script import autoload 'CaseSensitive.vim' assert_equal('done', CaseSensitive.CaseSensitive()) END v9.CheckScriptSuccess(lines) if !has('fname_case') lines =<< trim END vim9script import autoload 'CaseSensitive.vim' import autoload 'casesensitive.vim' END v9.CheckScriptFailure(lines, 'E1262:') endif delete('Xcasedir', 'rf') &rtp = save_rtp enddef " This was causing a crash because suppress_errthrow wasn't reset. def Test_vim9_autoload_error() var lines =<< trim END vim9script def crash#func() try for x in List() endfor catch endtry g:ok = true enddef fu List() invalid endfu try alsoinvalid catch /wontmatch/ endtry END call mkdir('Xruntime/autoload', 'p') call writefile(lines, 'Xruntime/autoload/crash.vim') # run in a separate Vim to avoid the side effects of assert_fails() lines =<< trim END exe 'set rtp^=' .. getcwd() .. '/Xruntime' call crash#func() call writefile(['ok'], 'Xdidit') qall! END writefile(lines, 'Xscript') g:RunVim([], [], '-S Xscript') assert_equal(['ok'], readfile('Xdidit')) delete('Xdidit') delete('Xscript') delete('Xruntime', 'rf') lines =<< trim END vim9script var foo#bar = 'asdf' END v9.CheckScriptFailure(lines, 'E461: Illegal variable name: foo#bar', 2) enddef def Test_vim9_import_symlink() if !has('unix') CheckUnix else mkdir('Xto/plugin', 'p') var lines =<< trim END vim9script import autoload 'bar.vim' g:resultFunc = bar.Func() g:resultValue = bar.value END writefile(lines, 'Xto/plugin/foo.vim') mkdir('Xto/autoload', 'p') lines =<< trim END vim9script export def Func(): string return 'func' enddef export var value = 'val' END writefile(lines, 'Xto/autoload/bar.vim') var save_rtp = &rtp &rtp = getcwd() .. '/Xfrom' system('ln -s ' .. getcwd() .. '/Xto Xfrom') source Xfrom/plugin/foo.vim assert_equal('func', g:resultFunc) assert_equal('val', g:resultValue) var infoTo = getscriptinfo()->filter((_, v) => v.name =~ 'Xto/autoload/bar') var infoFrom = getscriptinfo()->filter((_, v) => v.name =~ 'Xfrom/autoload/bar') assert_equal(1, len(infoTo)) assert_equal(1, len(infoFrom)) assert_equal(infoTo[0].sid, infoFrom[0].sourced) var output: string redir => output scriptnames redir END assert_match(infoFrom[0].sid .. '->' .. infoFrom[0].sourced .. '.*Xfrom', output) unlet g:resultFunc unlet g:resultValue &rtp = save_rtp delete('Xto', 'rf') delete('Xfrom', 'rf') endif enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker