Mercurial > vim
view src/testdir/test_vim9_script.vim @ 34336:d2ad8733db75 v9.1.0101
patch 9.1.0101: upper-case of German sharp s should be U+1E9E
Commit: https://github.com/vim/vim/commit/bd1232a1faf56b614a1e74c4ce51bc6e0650ae00
Author: glepnir <glephunter@gmail.com>
Date: Mon Feb 12 22:14:53 2024 +0100
patch 9.1.0101: upper-case of German sharp s should be U+1E9E
Problem: upper-case of ? should be U+1E9E (CAPITAL LETTER SHARP S)
(fenuks)
Solution: Make gU, ~ and g~ convert the U+00DF LATIN SMALL LETTER SHARP S (?)
to U+1E9E LATIN CAPITAL LETTER SHARP S (?), update tests
(glepnir)
This is part of Unicode 5.1.0 from April 2008, so should be fairly safe
to use now and since 2017 is part of the German standard orthography,
according to Wikipedia:
https://en.wikipedia.org/wiki/Capital_%E1%BA%9E#cite_note-auto-12
There is however one exception: UnicodeData.txt for U+00DF
LATIN SMALL LETTER SHARP S does NOT define U+1E9E LATIN CAPITAL LETTER
SHARP S as its upper case version. Therefore, toupper() won't be able
to convert from lower sharp s to upper case sharp s (the other way
around however works, since U+00DF is considered the lower case
character of U+1E9E and therefore tolower() works correctly for the
upper case version).
fixes: #5573
closes: #14018
Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Mon, 12 Feb 2024 22:45:02 +0100 |
parents | c4ad4778946c |
children | 75f7b311ee25 |
line wrap: on
line source
" Test various aspects of the Vim9 script language. source check.vim source term_util.vim import './vim9.vim' as v9 source screendump.vim source shared.vim def Test_vim9script_feature() # example from the help, here the feature is always present var lines =<< trim END " old style comment if !has('vim9script') " legacy commands would go here finish endif vim9script # Vim9 script commands go here g:didit = true END v9.CheckScriptSuccess(lines) assert_equal(true, g:didit) unlet g:didit enddef def Test_range_only() new setline(1, ['blah', 'Blah']) :/Blah/ assert_equal(2, getcurpos()[1]) bwipe! # without range commands use current line new setline(1, ['one', 'two', 'three']) :2 print assert_equal('two', g:Screenline(&lines)) :3 list assert_equal('three$', g:Screenline(&lines)) # missing command does not print the line var lines =<< trim END vim9script :1| assert_equal('three$', g:Screenline(&lines)) :| assert_equal('three$', g:Screenline(&lines)) END v9.CheckScriptSuccess(lines) bwipe! lines =<< trim END set cpo+=- :1,999 END v9.CheckDefExecAndScriptFailure(lines, 'E16:', 2) set cpo&vim v9.CheckDefExecAndScriptFailure([":'x"], 'E20:', 1) # won't generate anything if false :123 endif enddef def Test_invalid_range() var lines =<< trim END :123 eval 1 + 2 END v9.CheckDefAndScriptFailure(lines, 'E481:', 1) lines =<< trim END :123 if true endif END v9.CheckDefAndScriptFailure(lines, 'E481:', 1) lines =<< trim END :123 echo 'yes' END v9.CheckDefAndScriptFailure(lines, 'E481:', 1) lines =<< trim END :123 cd there END v9.CheckDefAndScriptFailure(lines, 'E481:', 1) enddef let g:alist = [7] let g:astring = 'text' let g:anumber = 123 def Test_delfunction() # Check function is defined in script namespace v9.CheckScriptSuccess([ 'vim9script', 'func CheckMe()', ' return 123', 'endfunc', 'func DoTest()', ' call assert_equal(123, s:CheckMe())', 'endfunc', 'DoTest()', ]) # Check function in script namespace cannot be deleted v9.CheckScriptFailure([ 'vim9script', 'func DeleteMe1()', 'endfunc', 'delfunction DeleteMe1', ], 'E1084:') v9.CheckScriptFailure([ 'vim9script', 'func DeleteMe2()', 'endfunc', 'def DoThat()', ' delfunction DeleteMe2', 'enddef', 'DoThat()', ], 'E1084:') v9.CheckScriptFailure([ 'vim9script', 'def DeleteMe3()', 'enddef', 'delfunction DeleteMe3', ], 'E1084:') v9.CheckScriptFailure([ 'vim9script', 'def DeleteMe4()', 'enddef', 'def DoThat()', ' delfunction DeleteMe4', 'enddef', 'DoThat()', ], 'E1084:') # Check that global :def function can be replaced and deleted var lines =<< trim END vim9script def g:Global(): string return "yes" enddef assert_equal("yes", g:Global()) def! g:Global(): string return "no" enddef assert_equal("no", g:Global()) delfunc g:Global assert_false(exists('*g:Global')) END v9.CheckScriptSuccess(lines) # Check that global function can be replaced by a :def function and deleted lines =<< trim END vim9script func g:Global() return "yes" endfunc assert_equal("yes", g:Global()) def! g:Global(): string return "no" enddef assert_equal("no", g:Global()) delfunc g:Global assert_false(exists('*g:Global')) END v9.CheckScriptSuccess(lines) # Check that global :def function can be replaced by a function and deleted lines =<< trim END vim9script def g:Global(): string return "yes" enddef assert_equal("yes", g:Global()) func! g:Global() return "no" endfunc assert_equal("no", g:Global()) delfunc g:Global assert_false(exists('*g:Global')) END v9.CheckScriptSuccess(lines) enddef def Test_wrong_type() v9.CheckDefFailure(['var name: list<nothing>'], 'E1010:') v9.CheckDefFailure(['var name: list<list<nothing>>'], 'E1010:') v9.CheckDefFailure(['var name: dict<nothing>'], 'E1010:') v9.CheckDefFailure(['var name: dict<dict<nothing>>'], 'E1010:') v9.CheckDefFailure(['var name: dict<number'], 'E1009:') v9.CheckDefFailure(['var name: dict<list<number>'], 'E1009:') v9.CheckDefFailure(['var name: ally'], 'E1010:') v9.CheckDefFailure(['var name: bram'], 'E1010:') v9.CheckDefFailure(['var name: cathy'], 'E1010:') v9.CheckDefFailure(['var name: dom'], 'E1010:') v9.CheckDefFailure(['var name: freddy'], 'E1010:') v9.CheckDefFailure(['var name: john'], 'E1010:') v9.CheckDefFailure(['var name: larry'], 'E1010:') v9.CheckDefFailure(['var name: ned'], 'E1010:') v9.CheckDefFailure(['var name: pam'], 'E1010:') v9.CheckDefFailure(['var name: sam'], 'E1010:') v9.CheckDefFailure(['var name: vim'], 'E1010:') v9.CheckDefFailure(['var Ref: number', 'Ref()'], 'E1085:') v9.CheckDefFailure(['var Ref: string', 'var res = Ref()'], 'E1085:') enddef def Test_script_namespace() # defining a function or variable with s: is not allowed var lines =<< trim END vim9script def s:Function() enddef END v9.CheckScriptFailure(lines, 'E1268:') for decl in ['var', 'const', 'final'] lines =<< trim END vim9script var s:var = 'var' END v9.CheckScriptFailure([ 'vim9script', decl .. ' s:var = "var"', ], 'E1268:') endfor # Calling a function or using a variable with s: is not allowed at script # level lines =<< trim END vim9script def Function() enddef s:Function() END v9.CheckScriptFailure(lines, 'E1268:') lines =<< trim END vim9script def Function() enddef call s:Function() END v9.CheckScriptFailure(lines, 'E1268:') lines =<< trim END vim9script var var = 'var' echo s:var END v9.CheckScriptFailure(lines, 'E1268:') enddef def Test_script_wrong_type() var lines =<< trim END vim9script var dict: dict<string> dict['a'] = ['x'] END v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got list<string>', 3) enddef def Test_const() v9.CheckDefFailure(['final name = 234', 'name = 99'], 'E1018:') v9.CheckDefFailure(['final one = 234', 'var one = 99'], 'E1017:') v9.CheckDefFailure(['final list = [1, 2]', 'var list = [3, 4]'], 'E1017:') v9.CheckDefFailure(['final two'], 'E1125:') v9.CheckDefFailure(['final &option'], 'E996:') var lines =<< trim END final list = [1, 2, 3] list[0] = 4 list->assert_equal([4, 2, 3]) const other = [5, 6, 7] other->assert_equal([5, 6, 7]) var varlist = [7, 8] const constlist = [1, varlist, 3] varlist[0] = 77 constlist[1][1] = 88 var cl = constlist[1] cl[1] = 88 constlist->assert_equal([1, [77, 88], 3]) var vardict = {five: 5, six: 6} const constdict = {one: 1, two: vardict, three: 3} vardict['five'] = 55 constdict['two']['six'] = 66 var cd = constdict['two'] cd['six'] = 66 constdict->assert_equal({one: 1, two: {five: 55, six: 66}, three: 3}) END v9.CheckDefAndScriptSuccess(lines) # "any" type with const flag is recognized as "any" lines =<< trim END const dict: dict<any> = {foo: {bar: 42}} const foo = dict.foo assert_equal(v:t_number, type(foo.bar)) END v9.CheckDefAndScriptSuccess(lines) # also when used as a builtin function argument lines =<< trim END vim9script def SorterFunc(lhs: dict<string>, rhs: dict<string>): number return lhs.name <# rhs.name ? -1 : 1 enddef def Run(): void var list = [{name: "3"}, {name: "2"}] const Sorter = get({}, "unknown", SorterFunc) sort(list, Sorter) assert_equal([{name: "2"}, {name: "3"}], list) enddef Run() END v9.CheckScriptSuccess(lines) enddef def Test_const_bang() var lines =<< trim END const var = 234 var = 99 END v9.CheckDefExecFailure(lines, 'E1018:', 2) v9.CheckScriptFailure(['vim9script'] + lines, 'E46:', 3) lines =<< trim END const ll = [2, 3, 4] ll[0] = 99 END v9.CheckDefExecFailure(lines, 'E1119:', 2) v9.CheckScriptFailure(['vim9script'] + lines, 'E741:', 3) lines =<< trim END const ll = [2, 3, 4] ll[3] = 99 END v9.CheckDefExecFailure(lines, 'E1118:', 2) v9.CheckScriptFailure(['vim9script'] + lines, 'E684:', 3) lines =<< trim END const dd = {one: 1, two: 2} dd["one"] = 99 END v9.CheckDefExecFailure(lines, 'E1121:', 2) v9.CheckScriptFailure(['vim9script'] + lines, 'E741:', 3) lines =<< trim END const dd = {one: 1, two: 2} dd["three"] = 99 END v9.CheckDefExecFailure(lines, 'E1120:') v9.CheckScriptFailure(['vim9script'] + lines, 'E741:', 3) enddef def Test_range_no_colon() v9.CheckDefFailure(['%s/a/b/'], 'E1050:') v9.CheckDefFailure(['+ s/a/b/'], 'E1050:') v9.CheckDefFailure(['- s/a/b/'], 'E1050:') v9.CheckDefFailure(['. s/a/b/'], 'E1050:') enddef def Test_block() var outer = 1 { var inner = 2 assert_equal(1, outer) assert_equal(2, inner) } assert_equal(1, outer) {|echo 'yes'|} enddef def Test_block_failure() v9.CheckDefFailure(['{', 'var inner = 1', '}', 'echo inner'], 'E1001:') v9.CheckDefFailure(['}'], 'E1025:') v9.CheckDefFailure(['{', 'echo 1'], 'E1026:') enddef def Test_block_local_vars() var lines =<< trim END vim9script v:testing = 1 if true var text = ['hello'] def SayHello(): list<string> return text enddef def SetText(v: string) text = [v] enddef endif if true var text = ['again'] def SayAgain(): list<string> return text enddef endif # test that the "text" variables are not cleaned up test_garbagecollect_now() defcompile assert_equal(['hello'], SayHello()) assert_equal(['again'], SayAgain()) SetText('foobar') assert_equal(['foobar'], SayHello()) call writefile(['ok'], 'Xdidit') qall! END # need to execute this with a separate Vim instance to avoid the current # context gets garbage collected. writefile(lines, 'Xscript', 'D') g:RunVim([], [], '-S Xscript') assert_equal(['ok'], readfile('Xdidit')) delete('Xdidit') enddef def Test_block_local_vars_with_func() var lines =<< trim END vim9script if true var foo = 'foo' if true var bar = 'bar' def Func(): list<string> return [foo, bar] enddef endif endif # function is compiled here, after blocks have finished, can still access # "foo" and "bar" assert_equal(['foo', 'bar'], Func()) END v9.CheckScriptSuccess(lines) enddef " legacy func for command that's defined later func s:InvokeSomeCommand() SomeCommand endfunc def Test_autocommand_block() com SomeCommand { g:someVar = 'some' } InvokeSomeCommand() assert_equal('some', g:someVar) delcommand SomeCommand unlet g:someVar enddef def Test_command_block() au BufNew *.xml { g:otherVar = 'other' } split other.xml assert_equal('other', g:otherVar) bwipe! au! BufNew *.xml unlet g:otherVar enddef func g:NoSuchFunc() echo 'none' endfunc def Test_try_catch_throw() var l = [] try # comment add(l, '1') throw 'wrong' add(l, '2') # "unreachable code" catch # comment add(l, v:exception) finally # comment add(l, '3') endtry # comment assert_equal(['1', 'wrong', '3'], l) l = [] try try add(l, '1') throw 'wrong' add(l, '2') # "unreachable code" catch /right/ add(l, v:exception) endtry catch /wrong/ add(l, 'caught') finally add(l, 'finally') endtry assert_equal(['1', 'caught', 'finally'], l) var n: number try n = l[3] catch /E684:/ n = 99 endtry assert_equal(99, n) var done = 'no' if 0 try | catch | endtry else done = 'yes' endif assert_equal('yes', done) done = 'no' if 1 done = 'yes' else try | catch | endtry done = 'never' endif assert_equal('yes', done) if 1 else try | catch /pat/ | endtry try | catch /pat/ endtry try catch /pat/ | endtry try catch /pat/ endtry endif try # string slice returns a string, not a number n = g:astring[3] catch /E1012:/ n = 77 endtry assert_equal(77, n) try n = l[g:astring] catch /E1012:/ n = 88 endtry assert_equal(88, n) try n = s:does_not_exist catch /E121:/ n = 111 endtry assert_equal(111, n) try n = g:does_not_exist catch /E121:/ n = 121 endtry assert_equal(121, n) var d = {one: 1} try n = d[g:astring] catch /E716:/ n = 222 endtry assert_equal(222, n) try n = -g:astring catch /E1012:/ n = 233 endtry assert_equal(233, n) try n = +g:astring catch /E1012:/ n = 244 endtry assert_equal(244, n) try n = +g:alist catch /E1012:/ n = 255 endtry assert_equal(255, n) var nd: dict<any> try nd = {[g:alist]: 1} catch /E1105:/ n = 266 endtry assert_equal(266, n) l = [1, 2, 3] try [n] = l catch /E1093:/ n = 277 endtry assert_equal(277, n) try &ts = g:astring catch /E1012:/ n = 288 endtry assert_equal(288, n) try &backspace = 'asdf' catch /E474:/ n = 299 endtry assert_equal(299, n) l = [1] try l[3] = 3 catch /E684:/ n = 300 endtry assert_equal(300, n) try unlet g:does_not_exist catch /E108:/ n = 322 endtry assert_equal(322, n) try d = {text: 1, [g:astring]: 2} catch /E721:/ n = 333 endtry assert_equal(333, n) try l = g:DeletedFunc() catch /E933:/ n = 344 endtry assert_equal(344, n) try echo range(1, 2, 0) catch /E726:/ n = 355 endtry assert_equal(355, n) var P = function('g:NoSuchFunc') delfunc g:NoSuchFunc try echo P() catch /E117:/ n = 366 endtry assert_equal(366, n) try echo g:NoSuchFunc() catch /E117:/ n = 377 endtry assert_equal(377, n) try echo g:alist + 4 catch /E745:/ n = 388 endtry assert_equal(388, n) try echo 4 + g:alist catch /E745:/ n = 399 endtry assert_equal(399, n) try echo g:alist.member catch /E715:/ n = 400 endtry assert_equal(400, n) try echo d.member catch /E716:/ n = 411 endtry assert_equal(411, n) var counter = 0 for i in range(4) try eval [][0] catch endtry counter += 1 endfor assert_equal(4, counter) # no requirement for spaces before | try|echo 0|catch|endtry # return in try with finally def ReturnInTry(): number var ret = 4 try return ret catch /this/ return -1 catch /that/ return -1 finally # changing ret has no effect ret = 7 endtry return -2 enddef assert_equal(4, ReturnInTry()) # return in catch with finally def ReturnInCatch(): number var ret = 5 try throw 'getout' return -1 # "unreachable code" catch /getout/ # ret is evaluated here return ret finally # changing ret later has no effect ret = -3 endtry return -2 enddef assert_equal(5, ReturnInCatch()) # return in finally after empty catch def ReturnInFinally(): number try finally return 6 endtry enddef assert_equal(6, ReturnInFinally()) var lines =<< trim END vim9script try acos('0.5') ->setline(1) catch g:caught = v:exception endtry END v9.CheckScriptSuccess(lines) assert_match('E1219: Float or Number required for argument 1', g:caught) unlet g:caught # missing catch and/or finally lines =<< trim END vim9script try echo 'something' endtry END v9.CheckScriptFailure(lines, 'E1032:') # skipping try-finally-endtry when try-finally-endtry is used in another block lines =<< trim END if v:true try finally endtry else try finally endtry endif END v9.CheckDefAndScriptSuccess(lines) enddef def Test_unreachable_after() var lines =<< trim END try throw 'Error' echo 'not reached' catch /Error/ endtry END v9.CheckDefFailure(lines, 'E1095: Unreachable code after :throw') lines =<< trim END def SomeFunc(): number try return 3 echo 'not reached' catch /Error/ endtry return 4 enddef defcompile END v9.CheckScriptFailure(lines, 'E1095: Unreachable code after :return') enddef def Test_throw_in_nested_try() var lines =<< trim END vim9script def Try(F: func(): void) try F() catch endtry enddef class X def F() try throw 'Foobar' catch throw v:exception endtry enddef endclass def Test_TryMethod() var x = X.new() Try(() => x.F()) enddef try Test_TryMethod() catch endtry END v9.CheckScriptSuccess(lines) enddef def Test_try_var_decl() var lines =<< trim END vim9script try var in_try = 1 assert_equal(1, get(s:, 'in_try', -1)) throw "getout" catch var in_catch = 2 assert_equal(-1, get(s:, 'in_try', -1)) assert_equal(2, get(s:, 'in_catch', -1)) finally var in_finally = 3 assert_equal(-1, get(s:, 'in_try', -1)) assert_equal(-1, get(s:, 'in_catch', -1)) assert_equal(3, get(s:, 'in_finally', -1)) endtry assert_equal(-1, get(s:, 'in_try', -1)) assert_equal(-1, get(s:, 'in_catch', -1)) assert_equal(-1, get(s:, 'in_finally', -1)) END v9.CheckScriptSuccess(lines) enddef def Test_try_ends_in_return() var lines =<< trim END vim9script def Foo(): string try return 'foo' catch return 'caught' endtry enddef assert_equal('foo', Foo()) END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script def Foo(): string try return 'foo' catch return 'caught' endtry echo 'notreached' enddef assert_equal('foo', Foo()) END v9.CheckScriptFailure(lines, 'E1095:') lines =<< trim END vim9script def Foo(): string try return 'foo' catch /x/ return 'caught' endtry enddef assert_equal('foo', Foo()) END v9.CheckScriptFailure(lines, 'E1027:') lines =<< trim END vim9script def Foo(): string try echo 'foo' catch echo 'caught' finally return 'done' endtry enddef assert_equal('done', Foo()) END v9.CheckScriptSuccess(lines) enddef def Test_try_in_catch() var lines =<< trim END vim9script var seq = [] def DoIt() try seq->add('throw 1') eval [][0] seq->add('notreached') catch seq->add('catch') try seq->add('throw 2') eval [][0] seq->add('notreached') catch /nothing/ seq->add('notreached') endtry seq->add('done') endtry enddef DoIt() assert_equal(['throw 1', 'catch', 'throw 2', 'done'], seq) END enddef def Test_error_in_catch() var lines =<< trim END try eval [][0] catch /E684:/ eval [][0] endtry END v9.CheckDefExecFailure(lines, 'E684:', 4) enddef " :while at the very start of a function that :continue jumps to def s:TryContinueFunc() while g:Count < 2 g:sequence ..= 't' try echoerr 'Test' catch g:Count += 1 g:sequence ..= 'c' continue endtry g:sequence ..= 'e' g:Count += 1 endwhile enddef def Test_continue_in_try_in_while() g:Count = 0 g:sequence = '' TryContinueFunc() assert_equal('tctc', g:sequence) unlet g:Count unlet g:sequence enddef def Test_break_in_try_in_for() var lines =<< trim END vim9script def Ls(): list<string> var ls: list<string> for s in ['abc', 'def'] for _ in [123, 456] try eval [][0] catch break endtry endfor ls += [s] endfor return ls enddef assert_equal(['abc', 'def'], Ls()) END v9.CheckScriptSuccess(lines) enddef def Test_nocatch_return_in_try() # return in try block returns normally def ReturnInTry(): string try return '"some message"' catch endtry return 'not reached' enddef exe 'echoerr ' .. ReturnInTry() enddef def Test_cnext_works_in_catch() var lines =<< trim END vim9script au BufEnter * eval 1 + 2 writefile(['text'], 'Xcncfile1') writefile(['text'], 'Xcncfile2') var items = [ {lnum: 1, filename: 'Xcncfile1', valid: true}, {lnum: 1, filename: 'Xcncfile2', valid: true} ] setqflist([], ' ', {items: items}) cwindow def CnextOrCfirst() # if cnext fails, cfirst is used try cnext catch cfirst endtry enddef CnextOrCfirst() CnextOrCfirst() writefile([getqflist({idx: 0}).idx], 'Xcncresult') qall END writefile(lines, 'XCatchCnext', 'D') g:RunVim([], [], '--clean -S XCatchCnext') assert_equal(['1'], readfile('Xcncresult')) delete('Xcncfile1') delete('Xcncfile2') delete('Xcncresult') enddef def Test_throw_skipped() if 0 throw dontgethere endif enddef def Test_nocatch_throw_silenced() var lines =<< trim END vim9script def Func() throw 'error' enddef silent! Func() END writefile(lines, 'XthrowSilenced', 'D') source XthrowSilenced enddef " g:DeletedFunc() is found when compiling Test_try_catch_throw() and then " deleted, this should give a runtime error. def DeletedFunc(): list<any> return ['delete me'] enddef defcompile DeletedFunc call test_override('unreachable', 1) defcompile Test_try_catch_throw call test_override('unreachable', 0) delfunc DeletedFunc def s:ThrowFromDef() throw "getout" # comment enddef func s:CatchInFunc() try call s:ThrowFromDef() catch let g:thrown_func = v:exception endtry endfunc def s:CatchInDef() try ThrowFromDef() catch g:thrown_def = v:exception endtry enddef def s:ReturnFinally(): string try return 'intry' finally g:in_finally = 'finally' endtry return 'end' enddef def Test_try_catch_nested() CatchInFunc() assert_equal('getout', g:thrown_func) CatchInDef() assert_equal('getout', g:thrown_def) assert_equal('intry', ReturnFinally()) assert_equal('finally', g:in_finally) var l = [] try l->add('1') throw 'bad' l->add('x') # "unreachable code" catch /bad/ l->add('2') try l->add('3') throw 'one' l->add('x') catch /one/ l->add('4') try l->add('5') throw 'more' l->add('x') catch /more/ l->add('6') endtry endtry endtry assert_equal(['1', '2', '3', '4', '5', '6'], l) l = [] try try l->add('1') throw 'foo' l->add('x') catch l->add('2') throw 'bar' l->add('x') finally l->add('3') endtry l->add('x') catch /bar/ l->add('4') endtry assert_equal(['1', '2', '3', '4'], l) enddef call test_override('unreachable', 1) defcompile Test_try_catch_nested call test_override('unreachable', 0) def s:TryOne(): number try return 0 catch endtry return 0 enddef def s:TryTwo(n: number): string try var x = {} catch endtry return 'text' enddef def Test_try_catch_twice() assert_equal('text', TryOne()->TryTwo()) enddef def Test_try_catch_match() var seq = 'a' try throw 'something' catch /nothing/ seq ..= 'x' catch /some/ seq ..= 'b' catch /asdf/ seq ..= 'x' catch ?a\?sdf? seq ..= 'y' finally seq ..= 'c' endtry assert_equal('abc', seq) enddef def Test_try_catch_fails() v9.CheckDefFailure(['catch'], 'E603:') v9.CheckDefFailure(['try', 'echo 0', 'catch', 'catch'], 'E1033:') v9.CheckDefFailure(['try', 'echo 0', 'catch /pat'], 'E1067:') v9.CheckDefFailure(['finally'], 'E606:') v9.CheckDefFailure(['try', 'echo 0', 'finally', 'echo 1', 'finally'], 'E607:') v9.CheckDefFailure(['endtry'], 'E602:') v9.CheckDefFailure(['while 1', 'endtry'], 'E170:') v9.CheckDefFailure(['for i in range(5)', 'endtry'], 'E170:') v9.CheckDefFailure(['if 1', 'endtry'], 'E171:') v9.CheckDefFailure(['try', 'echo 1', 'endtry'], 'E1032:') v9.CheckDefFailure(['throw'], 'E1143:') v9.CheckDefFailure(['throw xxx'], 'E1001:') enddef def Try_catch_skipped() var l = [] try finally endtry if 1 else try endtry endif enddef " The skipped try/endtry was updating the wrong instruction. def Test_try_catch_skipped() var instr = execute('disassemble Try_catch_skipped') assert_match("NEWLIST size 0\n", instr) enddef def Test_throw_line_number() def Func() eval 1 + 1 eval 2 + 2 throw 'exception' enddef try Func() catch /exception/ assert_match('line 3', v:throwpoint) endtry enddef def Test_throw_vimscript() # only checks line continuation var lines =<< trim END vim9script try throw 'one' .. 'two' catch assert_equal('onetwo', v:exception) endtry END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script @r = '' def Func() throw @r enddef var result = '' try Func() catch /E1129:/ result = 'caught' endtry assert_equal('caught', result) END v9.CheckScriptSuccess(lines) enddef def Test_error_in_nested_function() # an error in a nested :function aborts executing in the calling :def function var lines =<< trim END vim9script def Func() Error() g:test_var = 1 enddef func Error() abort eval [][0] endfunc Func() END g:test_var = 0 v9.CheckScriptFailure(lines, 'E684:') assert_equal(0, g:test_var) enddef def Test_abort_after_error() var lines =<< trim END vim9script while true echo notfound endwhile g:gotthere = true END g:gotthere = false v9.CheckScriptFailure(lines, 'E121:') assert_false(g:gotthere) unlet g:gotthere enddef def Test_cexpr_vimscript() # only checks line continuation set errorformat=File\ %f\ line\ %l var lines =<< trim END vim9script cexpr 'File' .. ' someFile' .. ' line 19' assert_equal(19, getqflist()[0].lnum) END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script def CexprFail() au QuickfixCmdPre * echo g:doesnotexist cexpr 'File otherFile line 99' g:didContinue = 'yes' enddef CexprFail() g:didContinue = 'also' END g:didContinue = 'no' v9.CheckScriptFailure(lines, 'E121: Undefined variable: g:doesnotexist') assert_equal('no', g:didContinue) au! QuickfixCmdPre lines =<< trim END vim9script def CexprFail() cexpr g:aNumber g:didContinue = 'yes' enddef CexprFail() g:didContinue = 'also' END g:aNumber = 123 g:didContinue = 'no' v9.CheckScriptFailure(lines, 'E777: String or List expected') assert_equal('no', g:didContinue) unlet g:didContinue set errorformat& enddef def Test_statusline_syntax() # legacy syntax is used for 'statusline' var lines =<< trim END vim9script func g:Status() return '%{"x" is# "x"}' endfunc set laststatus=2 statusline=%!Status() redrawstatus set laststatus statusline= END v9.CheckScriptSuccess(lines) enddef def Test_list_vimscript() # checks line continuation and comments var lines =<< trim END vim9script var mylist = [ 'one', # comment 'two', # empty line follows 'three', ] assert_equal(['one', 'two', 'three'], mylist) END v9.CheckScriptSuccess(lines) # check all lines from heredoc are kept lines =<< trim END # comment 1 two # comment 3 five # comment 6 END assert_equal(['# comment 1', 'two', '# comment 3', '', 'five', '# comment 6'], lines) lines =<< trim END [{ a: 0}]->string()->assert_equal("[{'a': 0}]") END v9.CheckDefAndScriptSuccess(lines) enddef if has('channel') let someJob = test_null_job() def FuncWithError() echomsg g:someJob enddef func Test_convert_emsg_to_exception() try call FuncWithError() catch call assert_match('Vim:E908:', v:exception) endtry endfunc endif def Test_vim9script_mix() var lines =<< trim END if has(g:feature) " legacy script let g:legacy = 1 finish endif vim9script g:legacy = 0 END g:feature = 'eval' g:legacy = -1 v9.CheckScriptSuccess(lines) assert_equal(1, g:legacy) g:feature = 'noteval' g:legacy = -1 v9.CheckScriptSuccess(lines) assert_equal(0, g:legacy) enddef def Test_vim9script_fails() v9.CheckScriptFailure(['scriptversion 2', 'vim9script'], 'E1039:') v9.CheckScriptFailure(['vim9script', 'scriptversion 2'], 'E1040:') v9.CheckScriptFailure(['vim9script', 'var str: string', 'str = 1234'], 'E1012:') v9.CheckScriptFailure(['vim9script', 'const str = "asdf"', 'str = "xxx"'], 'E46:') assert_fails('vim9script', 'E1038:') v9.CheckDefFailure(['vim9script'], 'E1038:') # no error when skipping if has('nothing') vim9script endif enddef def Test_script_var_shadows_function() var lines =<< trim END vim9script def Func(): number return 123 enddef var Func = 1 END v9.CheckScriptFailure(lines, 'E1041:', 5) enddef def Test_function_shadows_script_var() var lines =<< trim END vim9script var Func = 1 def Func(): number return 123 enddef END v9.CheckScriptFailure(lines, 'E1041:', 3) enddef def Test_script_var_shadows_command() var lines =<< trim END var undo = 1 undo = 2 assert_equal(2, undo) END v9.CheckDefAndScriptSuccess(lines) lines =<< trim END var undo = 1 undo END v9.CheckDefAndScriptFailure(lines, 'E1207:', 2) enddef def Test_vim9script_call_wrong_type() var lines =<< trim END vim9script var Time = 'localtime' Time() END v9.CheckScriptFailure(lines, 'E1085:') enddef def Test_vim9script_reload_delfunc() var first_lines =<< trim END vim9script def FuncYes(): string return 'yes' enddef END var withno_lines =<< trim END def FuncNo(): string return 'no' enddef def g:DoCheck(no_exists: bool) assert_equal('yes', FuncYes()) assert_equal('no', FuncNo()) enddef END var nono_lines =<< trim END def g:DoCheck(no_exists: bool) assert_equal('yes', FuncYes()) assert_fails('FuncNo()', 'E117:', '', 2, 'DoCheck') enddef END # FuncNo() is defined writefile(first_lines + withno_lines, 'Xreloaded.vim', 'D') source Xreloaded.vim g:DoCheck(true) # FuncNo() is not redefined writefile(first_lines + nono_lines, 'Xreloaded.vim') source Xreloaded.vim g:DoCheck(false) # FuncNo() is back writefile(first_lines + withno_lines, 'Xreloaded.vim') source Xreloaded.vim g:DoCheck(false) enddef def Test_vim9script_reload_delvar() # write the script with a script-local variable var lines =<< trim END vim9script var name = 'string' END writefile(lines, 'XreloadVar.vim', 'D') source XreloadVar.vim # now write the script using the same variable locally - works lines =<< trim END vim9script def Func() var name = 'string' enddef END writefile(lines, 'XreloadVar.vim') source XreloadVar.vim enddef def Test_func_redefine_error() var lines = [ 'vim9script', 'def Func()', ' eval [][0]', 'enddef', 'Func()', ] writefile(lines, 'Xtestscript.vim', 'D') for count in range(3) try source Xtestscript.vim catch /E684/ # function name should contain <SNR> every time assert_match('E684: List index out of range', v:exception) assert_match('function <SNR>\d\+_Func, line 1', v:throwpoint) endtry endfor enddef def Test_func_redefine_fails() var lines =<< trim END vim9script def Func() echo 'one' enddef def Func() echo 'two' enddef END v9.CheckScriptFailure(lines, 'E1073:') lines =<< trim END vim9script def Foo(): string return 'foo' enddef def Func() var Foo = {-> 'lambda'} enddef defcompile END v9.CheckScriptFailure(lines, 'E1073:') enddef def Test_lambda_split() # this was using freed memory, because of the split expression var lines =<< trim END vim9script try 0 0->(0 ->a.0( ->u END v9.CheckScriptFailure(lines, 'E1050:') enddef def Test_fixed_size_list() # will be allocated as one piece of memory, check that changes work var l = [1, 2, 3, 4] l->remove(0) l->add(5) l->insert(99, 1) assert_equal([2, 99, 3, 4, 5], l) enddef def Test_no_insert_xit() v9.CheckDefExecFailure(['a = 1'], 'E1100:') v9.CheckDefExecFailure(['c = 1'], 'E1100:') v9.CheckDefExecFailure(['i = 1'], 'E1100:') v9.CheckDefExecFailure(['t = 1'], 'E1100:') v9.CheckDefExecFailure(['x = 1'], 'E1100:') v9.CheckScriptFailure(['vim9script', 'a = 1'], 'E488:') v9.CheckScriptFailure(['vim9script', 'a'], 'E1100:') v9.CheckScriptFailure(['vim9script', 'c = 1'], 'E488:') v9.CheckScriptFailure(['vim9script', 'c'], 'E1100:') v9.CheckScriptFailure(['vim9script', 'i = 1'], 'E488:') v9.CheckScriptFailure(['vim9script', 'i'], 'E1100:') v9.CheckScriptFailure(['vim9script', 'o = 1'], 'E1100:') v9.CheckScriptFailure(['vim9script', 'o'], 'E1100:') v9.CheckScriptFailure(['vim9script', 't'], 'E1100:') v9.CheckScriptFailure(['vim9script', 't = 1'], 'E1100:') v9.CheckScriptFailure(['vim9script', 'x = 1'], 'E1100:') enddef def s:IfElse(what: number): string var res = '' if what == 1 res = "one" elseif what == 2 res = "two" else res = "three" endif return res enddef def Test_if_elseif_else() assert_equal('one', IfElse(1)) assert_equal('two', IfElse(2)) assert_equal('three', IfElse(3)) enddef def Test_if_elseif_else_fails() v9.CheckDefFailure(['elseif true'], 'E582:') v9.CheckDefFailure(['else'], 'E581:') v9.CheckDefFailure(['endif'], 'E580:') v9.CheckDefFailure(['if g:abool', 'elseif xxx'], 'E1001:') v9.CheckDefFailure(['if true', 'echo 1'], 'E171:') var lines =<< trim END var s = '' if s = '' endif END v9.CheckDefFailure(lines, 'E488:') lines =<< trim END var s = '' if s == '' elseif s = '' endif END v9.CheckDefFailure(lines, 'E488:') lines =<< trim END var cond = true if cond echo 'true' elseif echo 'false' endif END v9.CheckDefAndScriptFailure(lines, ['E1143:', 'E15:'], 4) enddef def Test_if_else_func_using_var() var lines =<< trim END vim9script const debug = true if debug var mode_chars = 'something' def Bits2Ascii() var x = mode_chars g:where = 'in true' enddef else def Bits2Ascii() g:where = 'in false' enddef endif Bits2Ascii() END v9.CheckScriptSuccess(lines) assert_equal('in true', g:where) unlet g:where lines =<< trim END vim9script const debug = false if debug var mode_chars = 'something' def Bits2Ascii() g:where = 'in true' enddef else def Bits2Ascii() var x = mode_chars g:where = 'in false' enddef endif Bits2Ascii() END v9.CheckScriptFailure(lines, 'E1001: Variable not found: mode_chars') enddef let g:bool_true = v:true let g:bool_false = v:false def Test_if_const_expr() var res = false if true ? true : false res = true endif assert_equal(true, res) g:glob = 2 if false execute('g:glob = 3') endif assert_equal(2, g:glob) if true execute('g:glob = 3') endif assert_equal(3, g:glob) res = false if g:bool_true ? true : false res = true endif assert_equal(true, res) res = false if true ? g:bool_true : false res = true endif assert_equal(true, res) res = false if true ? true : g:bool_false res = true endif assert_equal(true, res) res = false if true ? false : true res = true endif assert_equal(false, res) res = false if false ? false : true res = true endif assert_equal(true, res) res = false if false ? true : false res = true endif assert_equal(false, res) res = false if has('xyz') ? true : false res = true endif assert_equal(false, res) res = false if true && true res = true endif assert_equal(true, res) res = false if true && false res = true endif assert_equal(false, res) res = false if g:bool_true && false res = true endif assert_equal(false, res) res = false if true && g:bool_false res = true endif assert_equal(false, res) res = false if false && false res = true endif assert_equal(false, res) res = false if true || false res = true endif assert_equal(true, res) res = false if g:bool_true || false res = true endif assert_equal(true, res) res = false if true || g:bool_false res = true endif assert_equal(true, res) res = false if false || false res = true endif assert_equal(false, res) # with constant "false" expression may be invalid so long as the syntax is OK if false | eval 1 + 2 | endif if false | eval burp + 234 | endif if false | echo burp 234 'asd' | endif if false burp endif if 0 if 1 echo nothing elseif 1 echo still nothing endif endif # expression with line breaks skipped if false ('aaa' .. 'bbb' .. 'ccc' )->setline(1) endif enddef def Test_if_const_expr_fails() v9.CheckDefFailure(['if "aaa" == "bbb'], 'E114:') v9.CheckDefFailure(["if 'aaa' == 'bbb"], 'E115:') v9.CheckDefFailure(["if has('aaa'"], 'E110:') v9.CheckDefFailure(["if has('aaa') ? true false"], 'E109:') enddef def s:RunNested(i: number): number var x: number = 0 if i % 2 if 1 # comment else # comment endif x += 1 else x += 1000 endif return x enddef def Test_nested_if() assert_equal(1, RunNested(1)) assert_equal(1000, RunNested(2)) enddef def Test_execute_cmd() # missing argument is ignored execute execute # comment new setline(1, 'default') execute 'setline(1, "execute-string")' assert_equal('execute-string', getline(1)) execute "setline(1, 'execute-string')" assert_equal('execute-string', getline(1)) var cmd1 = 'setline(1,' var cmd2 = '"execute-var")' execute cmd1 cmd2 # comment assert_equal('execute-var', getline(1)) execute cmd1 cmd2 '|setline(1, "execute-var-string")' assert_equal('execute-var-string', getline(1)) var cmd_first = 'call ' var cmd_last = 'setline(1, "execute-var-var")' execute cmd_first .. cmd_last assert_equal('execute-var-var', getline(1)) bwipe! var n = true execute 'echomsg' (n ? '"true"' : '"no"') assert_match('^true$', g:Screenline(&lines)) echomsg [1, 2, 3] {a: 1, b: 2} assert_match('^\[1, 2, 3\] {''a'': 1, ''b'': 2}$', g:Screenline(&lines)) v9.CheckDefFailure(['execute xxx'], 'E1001:', 1) v9.CheckDefExecFailure(['execute "tabnext " .. 8'], 'E475:', 1) v9.CheckDefFailure(['execute "cmd"# comment'], 'E488:', 1) if has('channel') v9.CheckDefExecFailure(['execute test_null_channel()'], 'E908:', 1) endif enddef def Test_execute_cmd_vimscript() # only checks line continuation var lines =<< trim END vim9script execute 'g:someVar' .. ' = ' .. '28' assert_equal(28, g:someVar) unlet g:someVar END v9.CheckScriptSuccess(lines) enddef def Test_execute_finish() # the empty lines are relevant here var lines =<< trim END vim9script var vname = "g:hello" if exists(vname) | finish | endif | execute vname '= "world"' assert_equal('world', g:hello) if exists(vname) | finish | endif | execute vname '= "world"' assert_report('should not be reached') END v9.CheckScriptSuccess(lines) enddef def Test_echo_cmd() echo 'some' # comment echon 'thing' assert_match('^something$', g:Screenline(&lines)) echo "some" # comment echon "thing" assert_match('^something$', g:Screenline(&lines)) var str1 = 'some' var str2 = 'more' echo str1 str2 assert_match('^some more$', g:Screenline(&lines)) echo "one\ntwo" assert_match('^one$', g:Screenline(&lines - 1)) assert_match('^two$', g:Screenline(&lines)) v9.CheckDefFailure(['echo "xxx"# comment'], 'E488:') enddef def Test_echomsg_cmd() echomsg 'some' 'more' # comment assert_match('^some more$', g:Screenline(&lines)) echo 'clear' :1messages assert_match('^some more$', g:Screenline(&lines)) v9.CheckDefFailure(['echomsg "xxx"# comment'], 'E488:') enddef def Test_echomsg_cmd_vimscript() # only checks line continuation var lines =<< trim END vim9script echomsg 'here' .. ' is ' .. 'a message' assert_match('^here is a message$', g:Screenline(&lines)) END v9.CheckScriptSuccess(lines) enddef def Test_echoerr_cmd() var local = 'local' try echoerr 'something' local 'wrong' # comment catch assert_match('something local wrong', v:exception) endtry enddef def Test_echoerr_cmd_vimscript() # only checks line continuation var lines =<< trim END vim9script try echoerr 'this' .. ' is ' .. 'wrong' catch assert_match('this is wrong', v:exception) endtry END v9.CheckScriptSuccess(lines) enddef def Test_echoconsole_cmd() var local = 'local' echoconsole 'something' local # comment # output goes anywhere enddef def Test_echowindow_cmd() var local = 'local' echowindow 'something' local # comment # with modifier unsilent echowin 'loud' # output goes in message window popup_clear() enddef def Test_for_outside_of_function() var lines =<< trim END vim9script new for var in range(0, 3) append(line('$'), var) endfor assert_equal(['', '0', '1', '2', '3'], getline(1, '$')) bwipe! var result = '' for i in [1, 2, 3] var loop = ' loop ' .. i result ..= loop endfor assert_equal(' loop 1 loop 2 loop 3', result) END writefile(lines, 'Xvim9for.vim', 'D') source Xvim9for.vim enddef def Test_for_skipped_block() # test skipped blocks at outside of function var lines =<< trim END var result = [] if true for n in [1, 2] result += [n] endfor else for n in [3, 4] result += [n] endfor endif assert_equal([1, 2], result) result = [] if false for n in [1, 2] result += [n] endfor else for n in [3, 4] result += [n] endfor endif assert_equal([3, 4], result) END v9.CheckDefAndScriptSuccess(lines) # test skipped blocks at inside of function lines =<< trim END def DefTrue() var result = [] if true for n in [1, 2] result += [n] endfor else for n in [3, 4] result += [n] endfor endif assert_equal([1, 2], result) enddef DefTrue() def DefFalse() var result = [] if false for n in [1, 2] result += [n] endfor else for n in [3, 4] result += [n] endfor endif assert_equal([3, 4], result) enddef DefFalse() def BuildDiagrams() var diagrams: list<any> if false var max = 0 for v in diagrams var l = 3 if max < l | max = l | endif v->add(l) endfor endif enddef BuildDiagrams() END v9.CheckDefAndScriptSuccess(lines) enddef def Test_skipped_redir() var lines =<< trim END def Tredir() if 0 redir => l[0] redir END endif enddef defcompile END v9.CheckScriptSuccess(lines) delfunc g:Tredir lines =<< trim END def Tredir() if 0 redir => l[0] endif echo 'executed' if 0 redir END endif enddef defcompile END v9.CheckScriptSuccess(lines) delfunc g:Tredir lines =<< trim END def Tredir() var l = [''] if 1 redir => l[0] endif echo 'executed' if 0 redir END else redir END endif enddef defcompile END v9.CheckScriptSuccess(lines) delfunc g:Tredir lines =<< trim END let doit = 1 def Tredir() var l = [''] if g:doit redir => l[0] endif echo 'executed' if g:doit redir END endif enddef defcompile END v9.CheckScriptSuccess(lines) delfunc g:Tredir enddef def Test_for_loop() var lines =<< trim END var result = '' for cnt in range(7) if cnt == 4 break endif if cnt == 2 continue endif result ..= cnt .. '_' endfor assert_equal('0_1_3_', result) var concat = '' for str in eval('["one", "two"]') concat ..= str endfor assert_equal('onetwo', concat) var total = 0 for nr in [1, 2, 3] total += nr endfor assert_equal(6, total) total = 0 for nr in [1, 2, 3] total += nr endfor assert_equal(6, total) total = 0 for nr in [1, 2, 3] total += nr endfor assert_equal(6, total) # with type total = 0 for n: number in [1, 2, 3] total += n endfor assert_equal(6, total) total = 0 for b in 0z010203 total += b endfor assert_equal(6, total) var chars = '' for s: string in 'foobar' chars ..= s endfor assert_equal('foobar', chars) chars = '' for x: string in {a: 'a', b: 'b'}->values() chars ..= x endfor assert_equal('ab', chars) # unpack with type var res = '' for [n: number, s: string] in [[1, 'a'], [2, 'b']] res ..= n .. s endfor assert_equal('1a2b', res) # unpack with one var var reslist = [] for [x] in [['aaa'], ['bbb']] reslist->add(x) endfor assert_equal(['aaa', 'bbb'], reslist) # loop over string res = '' for c in 'aéc̀d' res ..= c .. '-' endfor assert_equal('a-é-c̀-d-', res) res = '' for c in '' res ..= c .. '-' endfor assert_equal('', res) res = '' for c in test_null_string() res ..= c .. '-' endfor assert_equal('', res) total = 0 for c in null_list total += 1 endfor assert_equal(0, total) for c in null_blob total += 1 endfor assert_equal(0, total) var foo: list<dict<any>> = [ {a: 'Cat'} ] for dd in foo dd.counter = 12 endfor assert_equal([{a: 'Cat', counter: 12}], foo) reslist = [] for _ in range(3) reslist->add('x') endfor assert_equal(['x', 'x', 'x'], reslist) END v9.CheckDefAndScriptSuccess(lines) enddef def Test_for_loop_list_of_lists() # loop variable is final, not const var lines =<< trim END # Filter out all odd numbers in each sublist var list: list<list<number>> = [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]] for i in list filter(i, (_, n: number): bool => n % 2 == 0) endfor assert_equal([[], [2], [2], [2, 4]], list) END v9.CheckDefAndScriptSuccess(lines) enddef def Test_for_loop_with_closure() # using the loop variable in a closure results in the last used value var lines =<< trim END var flist: list<func> for i in range(5) flist[i] = () => i endfor for i in range(5) assert_equal(4, flist[i]()) endfor END v9.CheckDefAndScriptSuccess(lines) # also works when the loop variable is used only once halfway the loops lines =<< trim END var Clo: func for i in range(5) if i == 3 Clo = () => i endif endfor assert_equal(4, Clo()) END v9.CheckDefAndScriptSuccess(lines) # using a local variable set to the loop variable in a closure results in the # value at that moment lines =<< trim END var flist: list<func> for i in range(5) var inloop = i flist[i] = () => inloop endfor for i in range(5) assert_equal(i, flist[i]()) endfor END v9.CheckDefAndScriptSuccess(lines) # also with an extra block level lines =<< trim END var flist: list<func> for i in range(5) { var inloop = i flist[i] = () => inloop } endfor for i in range(5) assert_equal(i, flist[i]()) endfor END v9.CheckDefAndScriptSuccess(lines) # and declaration in higher block lines =<< trim END var flist: list<func> for i in range(5) var inloop = i { flist[i] = () => inloop } endfor for i in range(5) assert_equal(i, flist[i]()) endfor END v9.CheckDefAndScriptSuccess(lines) lines =<< trim END var flist: list<func> for i in range(5) var inloop = i flist[i] = () => { return inloop } endfor for i in range(5) assert_equal(i, flist[i]()) endfor END v9.CheckDefAndScriptSuccess(lines) # Also works for a nested loop lines =<< trim END var flist: list<func> var n = 0 for i in range(3) var ii = i for a in ['a', 'b', 'c'] var aa = a flist[n] = () => ii .. aa ++n endfor endfor n = 0 for i in range(3) for a in ['a', 'b', 'c'] assert_equal(i .. a, flist[n]()) ++n endfor endfor END v9.CheckDefAndScriptSuccess(lines) # using two loop variables lines =<< trim END var lv_list: list<func> var copy_list: list<func> for [idx, c] in items('word') var lidx = idx var lc = c lv_list[idx] = () => { return idx .. c } copy_list[idx] = () => { return lidx .. lc } endfor for [i, c] in items('word') assert_equal(3 .. 'd', lv_list[i]()) assert_equal(i .. c, copy_list[i]()) endfor END v9.CheckDefAndScriptSuccess(lines) enddef def Test_define_global_closure_in_loops() var lines =<< trim END vim9script def Func() for i in range(3) var ii = i for a in ['a', 'b', 'c'] var aa = a if ii == 0 && aa == 'a' def g:Global_0a(): string return ii .. aa enddef endif if ii == 1 && aa == 'b' def g:Global_1b(): string return ii .. aa enddef endif if ii == 2 && aa == 'c' def g:Global_2c(): string return ii .. aa enddef endif endfor endfor enddef Func() END v9.CheckScriptSuccess(lines) assert_equal("0a", g:Global_0a()) assert_equal("1b", g:Global_1b()) assert_equal("2c", g:Global_2c()) delfunc g:Global_0a delfunc g:Global_1b delfunc g:Global_2c enddef def Test_for_loop_fails() v9.CheckDefAndScriptFailure(['for '], ['E1097:', 'E690:']) v9.CheckDefAndScriptFailure(['for x'], ['E1097:', 'E690:']) v9.CheckDefAndScriptFailure(['for x in'], ['E1097:', 'E15:']) v9.CheckDefAndScriptFailure(['for # in range(5)'], 'E690:') v9.CheckDefAndScriptFailure(['for i In range(5)'], 'E690:') v9.CheckDefAndScriptFailure(['var x = 5', 'for x in range(5)', 'endfor'], ['E1017:', 'E1041:']) v9.CheckScriptFailure(['vim9script', 'var x = 5', 'for x in range(5)', '# comment', 'endfor'], 'E1041:', 3) v9.CheckScriptFailure(['def Func(arg: any)', 'for arg in range(5)', 'enddef', 'defcompile'], 'E1006:') delfunc! g:Func v9.CheckDefFailure(['for i in xxx'], 'E1001:') v9.CheckDefFailure(['endfor'], 'E588:') v9.CheckDefFailure(['for i in range(3)', 'echo 3'], 'E170:') # wrong type detected at compile time v9.CheckDefFailure(['for i in {a: 1}', 'echo 3', 'endfor'], 'E1177: For loop on dict not supported') # wrong type detected at runtime g:adict = {a: 1} v9.CheckDefExecFailure(['for i in g:adict', 'echo 3', 'endfor'], 'E1177: For loop on dict not supported') unlet g:adict var lines =<< trim END var d: list<dict<any>> = [{a: 0}] for e in d e = {a: 0, b: ''} endfor END v9.CheckDefAndScriptFailure(lines, ['E1018:', 'E46:'], 3) lines =<< trim END for nr: number in ['foo'] endfor END v9.CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got string', 1) lines =<< trim END for n : number in [1, 2] echo n endfor END v9.CheckDefAndScriptFailure(lines, 'E1059:', 1) lines =<< trim END var d: dict<number> = {a: 1, b: 2} for [k: job, v: job] in d->items() echo k v endfor END v9.CheckDefExecAndScriptFailure(lines, ['E1163: Variable 1: type mismatch, expected job but got string', 'E1012: Type mismatch; expected job but got string'], 2) lines =<< trim END var i = 0 for i in [1, 2, 3] echo i endfor END v9.CheckDefExecAndScriptFailure(lines, ['E1017:', 'E1041:']) lines =<< trim END var l = [0] for l[0] in [1, 2, 3] echo l[0] endfor END v9.CheckDefExecAndScriptFailure(lines, ['E461:', 'E1017:']) lines =<< trim END var d = {x: 0} for d.x in [1, 2, 3] echo d.x endfor END v9.CheckDefExecAndScriptFailure(lines, ['E461:', 'E1017:']) lines =<< trim END var l: list<dict<any>> = [{a: 1, b: 'x'}] for item: dict<number> in l echo item endfor END v9.CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected dict<number> but got dict<any>') lines =<< trim END var l: list<dict<any>> = [{n: 1}] for item: dict<number> in l var d = {s: ''} d->extend(item) endfor END v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected dict<string> but got dict<number>') lines =<< trim END for a in range(3) while a > 3 for b in range(2) while b < 0 for c in range(5) while c > 6 while c < 0 for d in range(1) for e in range(3) while e > 3 endwhile endfor endfor endwhile endwhile endfor endwhile endfor endwhile endfor END v9.CheckDefSuccess(lines) v9.CheckDefFailure(['for x in range(3)'] + lines + ['endfor'], 'E1306:') # Test for too many for loops lines =<< trim END vim9script def Foo() for a in range(1) for b in range(1) for c in range(1) for d in range(1) for e in range(1) for f in range(1) for g in range(1) for h in range(1) for i in range(1) for j in range(1) for k in range(1) endfor endfor endfor endfor endfor endfor endfor endfor endfor endfor endfor enddef defcompile END v9.CheckSourceFailure(lines, 'E1306: Loop nesting too deep', 11) enddef def Test_for_loop_script_var() # cannot use s:var in a :def function v9.CheckDefFailure(['for s:var in range(3)', 'echo 3'], 'E1254:') # can use s:var in Vim9 script, with or without s: var lines =<< trim END vim9script var total = 0 for s:var in [1, 2, 3] total += s:var endfor assert_equal(6, total) total = 0 for var in [1, 2, 3] total += var endfor assert_equal(6, total) END enddef def Test_for_loop_unpack() var lines =<< trim END var result = [] for [v1, v2] in [[1, 2], [3, 4]] result->add(v1) result->add(v2) endfor assert_equal([1, 2, 3, 4], result) result = [] for [v1, v2; v3] in [[1, 2], [3, 4, 5, 6]] result->add(v1) result->add(v2) result->add(v3) endfor assert_equal([1, 2, [], 3, 4, [5, 6]], result) result = [] for [&ts, &sw] in [[1, 2], [3, 4]] result->add(&ts) result->add(&sw) endfor assert_equal([1, 2, 3, 4], result) var slist: list<string> for [$LOOPVAR, @r, v:errmsg] in [['a', 'b', 'c'], ['d', 'e', 'f']] slist->add($LOOPVAR) slist->add(@r) slist->add(v:errmsg) endfor assert_equal(['a', 'b', 'c', 'd', 'e', 'f'], slist) slist = [] for [g:globalvar, b:bufvar, w:winvar, t:tabvar] in [['global', 'buf', 'win', 'tab'], ['1', '2', '3', '4']] slist->add(g:globalvar) slist->add(b:bufvar) slist->add(w:winvar) slist->add(t:tabvar) endfor assert_equal(['global', 'buf', 'win', 'tab', '1', '2', '3', '4'], slist) unlet! g:globalvar b:bufvar w:winvar t:tabvar var res = [] for [_, n, _] in [[1, 2, 3], [4, 5, 6]] res->add(n) endfor assert_equal([2, 5], res) var text: list<string> = ["hello there", "goodbye now"] var splitted = '' for [first; next] in mapnew(text, (i, v) => split(v)) splitted ..= string(first) .. string(next) .. '/' endfor assert_equal("'hello'['there']/'goodbye'['now']/", splitted) END v9.CheckDefAndScriptSuccess(lines) lines =<< trim END for [v1, v2] in [[1, 2, 3], [3, 4]] echo v1 v2 endfor END v9.CheckDefExecFailure(lines, 'E710:', 1) lines =<< trim END for [v1, v2] in [[1], [3, 4]] echo v1 v2 endfor END v9.CheckDefExecFailure(lines, 'E711:', 1) lines =<< trim END for [v1, v1] in [[1, 2], [3, 4]] echo v1 endfor END v9.CheckDefExecFailure(lines, 'E1017:', 1) lines =<< trim END for [a, b] in g:listlist echo a endfor END g:listlist = [1, 2, 3] v9.CheckDefExecFailure(lines, 'E1140:', 1) enddef def Test_for_loop_with_try_continue() var lines =<< trim END var looped = 0 var cleanup = 0 for i in range(3) looped += 1 try eval [][0] catch continue finally cleanup += 1 endtry endfor assert_equal(3, looped) assert_equal(3, cleanup) END v9.CheckDefAndScriptSuccess(lines) enddef def Test_while_skipped_block() # test skipped blocks at outside of function var lines =<< trim END var result = [] var n = 0 if true n = 1 while n < 3 result += [n] n += 1 endwhile else n = 3 while n < 5 result += [n] n += 1 endwhile endif assert_equal([1, 2], result) result = [] if false n = 1 while n < 3 result += [n] n += 1 endwhile else n = 3 while n < 5 result += [n] n += 1 endwhile endif assert_equal([3, 4], result) END v9.CheckDefAndScriptSuccess(lines) # test skipped blocks at inside of function lines =<< trim END def DefTrue() var result = [] var n = 0 if true n = 1 while n < 3 result += [n] n += 1 endwhile else n = 3 while n < 5 result += [n] n += 1 endwhile endif assert_equal([1, 2], result) enddef DefTrue() def DefFalse() var result = [] var n = 0 if false n = 1 while n < 3 result += [n] n += 1 endwhile else n = 3 while n < 5 result += [n] n += 1 endwhile endif assert_equal([3, 4], result) enddef DefFalse() END v9.CheckDefAndScriptSuccess(lines) enddef def Test_while_loop() var result = '' var cnt = 0 while cnt < 555 if cnt == 3 break endif cnt += 1 if cnt == 2 continue endif result ..= cnt .. '_' endwhile assert_equal('1_3_', result) var s = '' while s == 'x' # {comment} endwhile enddef def Test_while_loop_in_script() var lines =<< trim END vim9script var result = '' var cnt = 0 while cnt < 3 var s = 'v' .. cnt result ..= s cnt += 1 endwhile assert_equal('v0v1v2', result) END v9.CheckScriptSuccess(lines) enddef def Test_while_loop_fails() v9.CheckDefFailure(['while xxx'], 'E1001:') v9.CheckDefFailure(['endwhile'], 'E588:') v9.CheckDefFailure(['continue'], 'E586:') v9.CheckDefFailure(['if true', 'continue'], 'E586:') v9.CheckDefFailure(['break'], 'E587:') v9.CheckDefFailure(['if true', 'break'], 'E587:') v9.CheckDefFailure(['while 1', 'echo 3'], 'E170:') var lines =<< trim END var s = '' while s = '' endwhile END v9.CheckDefFailure(lines, 'E488:') enddef def Test_interrupt_loop() var caught = false var x = 0 try while 1 x += 1 if x == 100 feedkeys("\<C-C>", 'Lt') endif endwhile catch caught = true assert_equal(100, x) endtry assert_true(caught, 'should have caught an exception') # consume the CTRL-C getchar(0) enddef def Test_automatic_line_continuation() var mylist = [ 'one', 'two', 'three', ] # comment assert_equal(['one', 'two', 'three'], mylist) var mydict = { ['one']: 1, ['two']: 2, ['three']: 3, } # comment assert_equal({one: 1, two: 2, three: 3}, mydict) mydict = { one: 1, # comment two: # comment 2, # comment three: 3 # comment } assert_equal({one: 1, two: 2, three: 3}, mydict) mydict = { one: 1, two: 2, three: 3 } assert_equal({one: 1, two: 2, three: 3}, mydict) assert_equal( ['one', 'two', 'three'], split('one two three') ) enddef def Test_vim9_comment() v9.CheckScriptSuccess([ 'vim9script', '# something', '#something', '#{{something', ]) v9.CheckScriptFailure([ 'vim9script', '#{something', ], 'E1170:') split Xv9cfile v9.CheckScriptSuccess([ 'vim9script', 'edit #something', ]) v9.CheckScriptSuccess([ 'vim9script', 'edit #{something', ]) close v9.CheckScriptFailure([ 'vim9script', ':# something', ], 'E488:') v9.CheckScriptFailure([ '# something', ], 'E488:') v9.CheckScriptFailure([ ':# something', ], 'E488:') { # block start } # block end v9.CheckDefFailure([ '{# comment', ], 'E488:') v9.CheckDefFailure([ '{', '}# comment', ], 'E488:') echo "yes" # comment v9.CheckDefFailure([ 'echo "yes"# comment', ], 'E488:') v9.CheckScriptSuccess([ 'vim9script', 'echo "yes" # something', ]) v9.CheckScriptFailure([ 'vim9script', 'echo "yes"# something', ], 'E121:') v9.CheckScriptFailure([ 'vim9script', 'echo# something', ], 'E1144:') v9.CheckScriptFailure([ 'echo "yes" # something', ], 'E121:') exe "echo" # comment v9.CheckDefFailure([ 'exe "echo"# comment', ], 'E488:') v9.CheckScriptSuccess([ 'vim9script', 'exe "echo" # something', ]) v9.CheckScriptFailure([ 'vim9script', 'exe "echo"# something', ], 'E121:') v9.CheckScriptFailure([ 'vim9script', 'exe# something', ], 'E1144:') v9.CheckScriptFailure([ 'exe "echo" # something', ], 'E121:') v9.CheckDefFailure([ 'try# comment', ' echo "yes"', 'catch', 'endtry', ], 'E1144:') v9.CheckScriptFailure([ 'vim9script', 'try# comment', 'echo "yes"', ], 'E1144:') v9.CheckDefFailure([ 'try', ' throw#comment', 'catch', 'endtry', ], 'E1144:') v9.CheckDefFailure([ 'try', ' throw "yes"#comment', 'catch', 'endtry', ], 'E488:') v9.CheckDefFailure([ 'try', ' echo "yes"', 'catch# comment', 'endtry', ], 'E1144:') v9.CheckScriptFailure([ 'vim9script', 'try', ' echo "yes"', 'catch# comment', 'endtry', ], 'E1144:') v9.CheckDefFailure([ 'try', ' echo "yes"', 'catch /pat/# comment', 'endtry', ], 'E488:') v9.CheckDefFailure([ 'try', 'echo "yes"', 'catch', 'endtry# comment', ], 'E1144:') v9.CheckScriptFailure([ 'vim9script', 'try', ' echo "yes"', 'catch', 'endtry# comment', ], 'E1144:') v9.CheckScriptSuccess([ 'vim9script', 'hi # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'hi# comment', ], 'E1144:') v9.CheckScriptSuccess([ 'vim9script', 'hi Search # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'hi Search# comment', ], 'E416:') v9.CheckScriptSuccess([ 'vim9script', 'hi link This Search # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'hi link This That# comment', ], 'E413:') v9.CheckScriptSuccess([ 'vim9script', 'hi clear This # comment', 'hi clear # comment', ]) # not tested, because it doesn't give an error but a warning: # hi clear This# comment', v9.CheckScriptFailure([ 'vim9script', 'hi clear# comment', ], 'E416:') v9.CheckScriptSuccess([ 'vim9script', 'hi Group term=bold', 'match Group /todo/ # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'hi Group term=bold', 'match Group /todo/# comment', ], 'E488:') v9.CheckScriptSuccess([ 'vim9script', 'match # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'match# comment', ], 'E1144:') v9.CheckScriptSuccess([ 'vim9script', 'match none # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'match none# comment', ], 'E475:') v9.CheckScriptSuccess([ 'vim9script', 'menutrans clear # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'menutrans clear# comment text', ], 'E474:') v9.CheckScriptSuccess([ 'vim9script', 'syntax clear # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax clear# comment text', ], 'E28:') v9.CheckScriptSuccess([ 'vim9script', 'syntax keyword Word some', 'syntax clear Word # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax keyword Word some', 'syntax clear Word# comment text', ], 'E28:') v9.CheckScriptSuccess([ 'vim9script', 'syntax list # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax list# comment text', ], 'E28:') v9.CheckScriptSuccess([ 'vim9script', 'syntax match Word /pat/ oneline # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax match Word /pat/ oneline# comment', ], 'E475:') v9.CheckScriptSuccess([ 'vim9script', 'syntax keyword Word word # comm[ent', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax keyword Word word# comm[ent', ], 'E789:') v9.CheckScriptSuccess([ 'vim9script', 'syntax match Word /pat/ # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax match Word /pat/# comment', ], 'E402:') v9.CheckScriptSuccess([ 'vim9script', 'syntax match Word /pat/ contains=Something # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax match Word /pat/ contains=Something# comment', ], 'E475:') v9.CheckScriptFailure([ 'vim9script', 'syntax match Word /pat/ contains= # comment', ], 'E406:') v9.CheckScriptFailure([ 'vim9script', 'syntax match Word /pat/ contains=# comment', ], 'E475:') v9.CheckScriptSuccess([ 'vim9script', 'syntax region Word start=/pat/ end=/pat/ # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax region Word start=/pat/ end=/pat/# comment', ], 'E402:') v9.CheckScriptSuccess([ 'vim9script', 'syntax sync # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax sync# comment', ], 'E404:') v9.CheckScriptSuccess([ 'vim9script', 'syntax sync ccomment # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax sync ccomment# comment', ], 'E404:') v9.CheckScriptSuccess([ 'vim9script', 'syntax cluster Some contains=Word # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'syntax cluster Some contains=Word# comment', ], 'E475:') v9.CheckScriptSuccess([ 'vim9script', 'command Echo echo # comment', 'command Echo # comment', 'delcommand Echo', ]) v9.CheckScriptFailure([ 'vim9script', 'command Echo echo# comment', 'Echo', ], 'E1144:') delcommand Echo var curdir = getcwd() v9.CheckScriptSuccess([ 'command Echo cd " comment', 'Echo', 'delcommand Echo', ]) v9.CheckScriptSuccess([ 'vim9script', 'command Echo cd # comment', 'Echo', 'delcommand Echo', ]) v9.CheckScriptFailure([ 'vim9script', 'command Echo cd " comment', 'Echo', ], 'E344:') delcommand Echo chdir(curdir) v9.CheckScriptFailure([ 'vim9script', 'command Echo# comment', ], 'E182:') v9.CheckScriptFailure([ 'vim9script', 'command Echo echo', 'command Echo# comment', ], 'E182:') delcommand Echo v9.CheckScriptSuccess([ 'vim9script', 'function # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'function " comment', ], 'E129:') v9.CheckScriptFailure([ 'vim9script', 'function# comment', ], 'E1144:') v9.CheckScriptSuccess([ 'vim9script', 'import "./vim9.vim" as v9', 'function v9.CheckScriptSuccess # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'import "./vim9.vim" as v9', 'function v9.CheckScriptSuccess# comment', ], 'E1048: Item not found in script: CheckScriptSuccess#') v9.CheckScriptSuccess([ 'vim9script', 'func g:DeleteMeA()', 'endfunc', 'delfunction g:DeleteMeA # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'func g:DeleteMeB()', 'endfunc', 'delfunction g:DeleteMeB# comment', ], 'E488:') v9.CheckScriptSuccess([ 'vim9script', 'call execute("ls") # comment', ]) v9.CheckScriptFailure([ 'vim9script', 'call execute("ls")# comment', ], 'E488:') v9.CheckScriptFailure([ 'def Test() " comment', 'enddef', ], 'E488:') v9.CheckScriptFailure([ 'vim9script', 'def Test() " comment', 'enddef', ], 'E488:') v9.CheckScriptSuccess([ 'func Test() " comment', 'endfunc', 'delfunc Test', ]) v9.CheckScriptSuccess([ 'vim9script', 'func Test() " comment', 'endfunc', ]) v9.CheckScriptSuccess([ 'def Test() # comment', 'enddef', ]) v9.CheckScriptFailure([ 'func Test() # comment', 'endfunc', ], 'E488:') var lines =<< trim END vim9script syn region Text \ start='foo' #\ comment \ end='bar' syn region Text start='foo' #\ comment \ end='bar' END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script syn region Text \ start='foo' "\ comment \ end='bar' END v9.CheckScriptFailure(lines, 'E399:') enddef def Test_vim9_comment_gui() CheckCanRunGui v9.CheckScriptFailure([ 'vim9script', 'gui#comment' ], 'E1144:') v9.CheckScriptFailure([ 'vim9script', 'gui -f#comment' ], 'E194:') enddef def Test_vim9_comment_not_compiled() au TabEnter *.vim g:entered = 1 au TabEnter *.x g:entered = 2 edit test.vim doautocmd TabEnter #comment assert_equal(1, g:entered) doautocmd TabEnter f.x assert_equal(2, g:entered) g:entered = 0 doautocmd TabEnter f.x #comment assert_equal(2, g:entered) assert_fails('doautocmd Syntax#comment', 'E216:') au! TabEnter unlet g:entered v9.CheckScriptSuccess([ 'vim9script', 'g:var = 123', 'b:var = 456', 'w:var = 777', 't:var = 888', 'unlet g:var w:var # something', ]) v9.CheckScriptFailure([ 'vim9script', 'let var = 123', ], 'E1126: Cannot use :let in Vim9 script') v9.CheckScriptFailure([ 'vim9script', 'var g:var = 123', ], 'E1016: Cannot declare a global variable:') v9.CheckScriptFailure([ 'vim9script', 'var b:var = 123', ], 'E1016: Cannot declare a buffer variable:') v9.CheckScriptFailure([ 'vim9script', 'var w:var = 123', ], 'E1016: Cannot declare a window variable:') v9.CheckScriptFailure([ 'vim9script', 'var t:var = 123', ], 'E1016: Cannot declare a tab variable:') v9.CheckScriptFailure([ 'vim9script', 'var v:version = 123', ], 'E1016: Cannot declare a v: variable:') v9.CheckScriptFailure([ 'vim9script', 'var $VARIABLE = "text"', ], 'E1016: Cannot declare an environment variable:') v9.CheckScriptFailure([ 'vim9script', 'g:var = 123', 'unlet g:var# comment1', ], 'E108:') v9.CheckScriptFailure([ 'let g:var = 123', 'unlet g:var # something', ], 'E488:') v9.CheckScriptSuccess([ 'vim9script', 'if 1 # comment2', ' echo "yes"', 'elseif 2 #comment', ' echo "no"', 'endif', ]) v9.CheckScriptFailure([ 'vim9script', 'if 1# comment3', ' echo "yes"', 'endif', ], 'E488:') v9.CheckScriptFailure([ 'vim9script', 'if 0 # comment4', ' echo "yes"', 'elseif 2#comment', ' echo "no"', 'endif', ], 'E488:') v9.CheckScriptSuccess([ 'vim9script', 'var v = 1 # comment5', ]) v9.CheckScriptFailure([ 'vim9script', 'var v = 1# comment6', ], 'E488:') v9.CheckScriptSuccess([ 'vim9script', 'new', 'setline(1, ["# define pat", "last"])', ':$', 'dsearch /pat/ #comment', 'bwipe!', ]) v9.CheckScriptFailure([ 'vim9script', 'new', 'setline(1, ["# define pat", "last"])', ':$', 'dsearch /pat/#comment', 'bwipe!', ], 'E488:') v9.CheckScriptFailure([ 'vim9script', 'func! SomeFunc()', ], 'E477:') enddef def Test_finish() var lines =<< trim END vim9script g:res = 'one' if v:false | finish | endif g:res = 'two' finish g:res = 'three' END writefile(lines, 'Xfinished', 'D') source Xfinished assert_equal('two', g:res) unlet g:res enddef def Test_forward_declaration() var lines =<< trim END vim9script def GetValue(): string return theVal enddef var theVal = 'something' g:initVal = GetValue() theVal = 'else' g:laterVal = GetValue() END writefile(lines, 'Xforward', 'D') source Xforward assert_equal('something', g:initVal) assert_equal('else', g:laterVal) unlet g:initVal unlet g:laterVal enddef def Test_declare_script_var_in_func() var lines =<< trim END vim9script func Declare() let s:local = 123 endfunc Declare() END v9.CheckScriptFailure(lines, 'E1269:') enddef def Test_lock_script_var() var lines =<< trim END vim9script var local = 123 assert_equal(123, local) var error: string try local = 'asdf' catch error = v:exception endtry assert_match('E1012: Type mismatch; expected number but got string', error) lockvar local try local = 999 catch error = v:exception endtry assert_match('E741: Value is locked: local', error) END v9.CheckScriptSuccess(lines) enddef func Test_vim9script_not_global() " check that items defined in Vim9 script are script-local, not global let vim9lines =<< trim END vim9script var name = 'local' func TheFunc() echo 'local' endfunc def DefFunc() echo 'local' enddef END call writefile(vim9lines, 'Xvim9script.vim', 'D') source Xvim9script.vim try echo g:var assert_report('did not fail') catch /E121:/ " caught endtry try call TheFunc() assert_report('did not fail') catch /E117:/ " caught endtry try call DefFunc() assert_report('did not fail') catch /E117:/ " caught endtry endfunc def Test_vim9_copen() # this was giving an error for setting w:quickfix_title copen quit enddef def Test_script_var_in_autocmd() # using a script variable from an autocommand, defined in a :def function in a # legacy Vim script, cannot check the variable type. var lines =<< trim END let s:counter = 1 def s:Func() au! CursorHold au CursorHold * s:counter += 1 enddef call s:Func() doau CursorHold call assert_equal(2, s:counter) au! CursorHold END v9.CheckScriptSuccess(lines) enddef def Test_error_in_autoload_script() var save_rtp = &rtp var dir = getcwd() .. '/Xruntime' &rtp = dir mkdir(dir .. '/autoload', 'pR') var lines =<< trim END vim9script noclear export def Autoloaded() enddef def Broken() var x: any = '' eval x != 0 enddef Broken() END writefile(lines, dir .. '/autoload/script.vim') lines =<< trim END vim9script def CallAutoloaded() script#Autoloaded() enddef function Legacy() try call s:CallAutoloaded() catch call assert_match('E1030: Using a String as a Number', v:exception) endtry endfunction Legacy() END v9.CheckScriptSuccess(lines) &rtp = save_rtp enddef def Test_error_in_autoload_script_foldexpr() var save_rtp = &rtp mkdir('Xvim/autoload', 'pR') &runtimepath = 'Xvim' var lines =<< trim END vim9script eval [][0] echomsg 'no error' END lines->writefile('Xvim/autoload/script.vim') lines =<< trim END vim9script import autoload 'script.vim' &foldmethod = 'expr' &foldexpr = 'script.Func()' redraw END v9.CheckScriptFailure(lines, 'E684: List index out of range: 0') enddef def Test_invalid_sid() assert_fails('func <SNR>1234_func', 'E123:') if g:RunVim([], ['wq! Xdidit'], '+"func <SNR>1_func"') assert_equal([], readfile('Xdidit')) endif delete('Xdidit') enddef def Test_restoring_cpo() writefile(['vim9script', 'set nocp'], 'Xsourced', 'D') writefile(['call writefile(["done"], "Xdone")', 'quit!'], 'Xclose', 'D') if g:RunVim([], [], '-u NONE +"set cpo+=a" -S Xsourced -S Xclose') assert_equal(['done'], readfile('Xdone')) endif delete('Xdone') writefile(['vim9script', 'g:cpoval = &cpo'], 'XanotherScript', 'D') set cpo=aABceFsMny> edit XanotherScript so % assert_equal('aABceFsMny>', &cpo) assert_equal('aABceFs', g:cpoval) :1del setline(1, 'let g:cpoval = &cpo') w so % assert_equal('aABceFsMny>', &cpo) assert_equal('aABceFsMny>', g:cpoval) set cpo&vim unlet g:cpoval if has('unix') # 'cpo' is not restored in main vimrc var save_HOME = $HOME $HOME = getcwd() .. '/Xhome' mkdir('Xhome', 'R') var lines =<< trim END vim9script writefile(['before: ' .. &cpo], 'Xrporesult') set cpo+=M writefile(['after: ' .. &cpo], 'Xrporesult', 'a') END writefile(lines, 'Xhome/.vimrc') lines =<< trim END call writefile(['later: ' .. &cpo], 'Xrporesult', 'a') END writefile(lines, 'Xlegacy', 'D') lines =<< trim END vim9script call writefile(['vim9: ' .. &cpo], 'Xrporesult', 'a') qa END writefile(lines, 'Xvim9', 'D') var cmd = g:GetVimCommand() .. " -S Xlegacy -S Xvim9" cmd = substitute(cmd, '-u NONE', '', '') exe "silent !" .. cmd assert_equal([ 'before: aABceFs', 'after: aABceFsM', 'later: aABceFsM', 'vim9: aABceFs'], readfile('Xrporesult')) $HOME = save_HOME delete('Xrporesult') endif enddef " Use :function so we can use Check commands func Test_no_redraw_when_restoring_cpo() CheckScreendump CheckFeature timers call Run_test_no_redraw_when_restoring_cpo() endfunc def Run_test_no_redraw_when_restoring_cpo() var lines =<< trim END vim9script export def Func() enddef END mkdir('Xnordir/autoload', 'pR') writefile(lines, 'Xnordir/autoload/script.vim') lines =<< trim END vim9script set cpo+=M exe 'set rtp^=' .. getcwd() .. '/Xnordir' au CmdlineEnter : ++once timer_start(0, (_) => script#Func()) setline(1, 'some text') END writefile(lines, 'XTest_redraw_cpo', 'D') var buf = g:RunVimInTerminal('-S XTest_redraw_cpo', {'rows': 6}) term_sendkeys(buf, "V:") g:VerifyScreenDump(buf, 'Test_vim9_no_redraw', {}) # clean up term_sendkeys(buf, "\<Esc>u") g:StopVimInTerminal(buf) enddef func Test_reject_declaration() CheckScreendump call Run_test_reject_declaration() endfunc def Run_test_reject_declaration() var buf = g:RunVimInTerminal('', {'rows': 6}) term_sendkeys(buf, ":vim9cmd var x: number\<CR>") g:VerifyScreenDump(buf, 'Test_vim9_reject_declaration_1', {}) term_sendkeys(buf, ":\<CR>") term_sendkeys(buf, ":vim9cmd g:foo = 123 | echo g:foo\<CR>") g:VerifyScreenDump(buf, 'Test_vim9_reject_declaration_2', {}) # clean up g:StopVimInTerminal(buf) enddef def Test_minimal_command_name_length() var names = [ 'cons', 'brea', 'cat', 'catc', 'con', 'cont', 'conti', 'contin', 'continu', 'el', 'els', 'elsei', 'endfo', 'en', 'end', 'endi', 'endw', 'endt', 'endtr', 'exp', 'expo', 'expor', 'fina', 'finall', 'fini', 'finis', 'imp', 'impo', 'impor', 'retu', 'retur', 'th', 'thr', 'thro', 'wh', 'whi', 'whil', ] for name in names v9.CheckDefAndScriptFailure([name .. ' '], 'E1065:') endfor var lines =<< trim END vim9script def SomeFunc() endd END v9.CheckScriptFailure(lines, 'E1065:') lines =<< trim END vim9script def SomeFunc() endde END v9.CheckScriptFailure(lines, 'E1065:') enddef def Test_unset_any_variable() var lines =<< trim END var name: any assert_equal(0, name) END v9.CheckDefAndScriptSuccess(lines) enddef func Test_define_func_at_command_line() CheckRunVimInTerminal " call indirectly to avoid compilation error for missing functions call Run_Test_define_func_at_command_line() endfunc def Run_Test_define_func_at_command_line() # run in a separate Vim instance to avoid the script context var lines =<< trim END func CheckAndQuit() call assert_fails('call Afunc()', 'E117: Unknown function: Bfunc') call writefile(['errors: ' .. string(v:errors)], 'Xdidcmd') endfunc END writefile([''], 'Xdidcmd', 'D') writefile(lines, 'XcallFunc', 'D') var buf = g:RunVimInTerminal('-S XcallFunc', {rows: 6}) # define Afunc() on the command line term_sendkeys(buf, ":def Afunc()\<CR>Bfunc()\<CR>enddef\<CR>") term_sendkeys(buf, ":call CheckAndQuit()\<CR>") g:WaitForAssert(() => assert_equal(['errors: []'], readfile('Xdidcmd'))) call g:StopVimInTerminal(buf) enddef def Test_script_var_scope() var lines =<< trim END vim9script if true if true var one = 'one' echo one endif echo one endif END v9.CheckScriptFailure(lines, 'E121:', 7) lines =<< trim END vim9script if true if false var one = 'one' echo one else var one = 'one' echo one endif echo one endif END v9.CheckScriptFailure(lines, 'E121:', 10) lines =<< trim END vim9script while true var one = 'one' echo one break endwhile echo one END v9.CheckScriptFailure(lines, 'E121:', 7) lines =<< trim END vim9script for i in range(1) var one = 'one' echo one endfor echo one END v9.CheckScriptFailure(lines, 'E121:', 6) lines =<< trim END vim9script { var one = 'one' assert_equal('one', one) } assert_false(exists('one')) assert_false(exists('s:one')) END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script { var one = 'one' echo one } echo one END v9.CheckScriptFailure(lines, 'E121:', 6) enddef def Test_catch_exception_in_callback() var lines =<< trim END vim9script def Callback(...l: list<any>) try var x: string var y: string # this error should be caught with CHECKLEN var sl = [''] [x, y] = sl catch g:caught = 'yes' endtry enddef popup_menu('popup', {callback: Callback}) feedkeys("\r", 'xt') END v9.CheckScriptSuccess(lines) unlet g:caught enddef def Test_no_unknown_error_after_error() if !has('unix') || !has('job') throw 'Skipped: not unix of missing +job feature' endif # FIXME: this check should not be needed if has('win32') throw 'Skipped: does not work on MS-Windows' endif var lines =<< trim END vim9script var source: list<number> def Out_cb(...l: list<any>) eval [][0] enddef def Exit_cb(...l: list<any>) sleep 1m g:did_call_exit_cb = true source += l enddef var myjob = job_start('echo burp', {out_cb: Out_cb, exit_cb: Exit_cb, mode: 'raw'}) while job_status(myjob) == 'run' sleep 10m endwhile # wait for Exit_cb() to be called for x in range(100) if exists('g:did_call_exit_cb') unlet g:did_call_exit_cb break endif sleep 10m endfor END writefile(lines, 'Xdef', 'D') # Either the exit or out callback is called first, accept them in any order assert_fails('so Xdef', ['E684:\|E1012:', 'E1012:\|E684:']) enddef def InvokeNormal() exe "norm! :m+1\r" enddef def Test_invoke_normal_in_visual_mode() xnoremap <F3> <Cmd>call <SID>InvokeNormal()<CR> new setline(1, ['aaa', 'bbb']) feedkeys("V\<F3>", 'xt') assert_equal(['bbb', 'aaa'], getline(1, 2)) xunmap <F3> enddef def Test_white_space_after_command() var lines =<< trim END exit_cb: Func}) END v9.CheckDefAndScriptFailure(lines, 'E1144:', 1) lines =<< trim END e# END v9.CheckDefAndScriptFailure(lines, 'E1144:', 1) enddef def Test_script_var_gone_when_sourced_twice() var lines =<< trim END vim9script if exists('g:guard') finish endif g:guard = 1 var name = 'thename' def g:GetName(): string return name enddef def g:SetName(arg: string) name = arg enddef END writefile(lines, 'XscriptTwice.vim', 'D') so XscriptTwice.vim assert_equal('thename', g:GetName()) g:SetName('newname') assert_equal('newname', g:GetName()) so XscriptTwice.vim assert_fails('call g:GetName()', 'E1149:') assert_fails('call g:SetName("x")', 'E1149:') delfunc g:GetName delfunc g:SetName unlet g:guard enddef def Test_unsupported_commands() var lines =<< trim END ka END v9.CheckDefAndScriptFailure(lines, ['E476:', 'E492:']) lines =<< trim END :1ka END v9.CheckDefAndScriptFailure(lines, ['E476:', 'E492:']) lines =<< trim END :k a END v9.CheckDefAndScriptFailure(lines, 'E1100:') lines =<< trim END :1k a END v9.CheckDefAndScriptFailure(lines, 'E481:') lines =<< trim END t END v9.CheckDefAndScriptFailure(lines, 'E1100:') lines =<< trim END x END v9.CheckDefAndScriptFailure(lines, 'E1100:') lines =<< trim END xit END v9.CheckDefAndScriptFailure(lines, 'E1100:') lines =<< trim END Print END v9.CheckDefAndScriptFailure(lines, ['E476: Invalid command: Print', 'E492: Not an editor command: Print']) lines =<< trim END mode 4 END v9.CheckDefAndScriptFailure(lines, ['E476: Invalid command: mode 4', 'E492: Not an editor command: mode 4']) enddef def Test_mapping_line_number() var lines =<< trim END vim9script def g:FuncA() # Some comment FuncB(0) enddef # Some comment def FuncB( # Some comment n: number ) exe 'nno ' # Some comment .. '<F3> a' .. 'b' .. 'c' enddef END v9.CheckScriptSuccess(lines) var res = execute('verbose nmap <F3>') assert_match('No mapping found', res) g:FuncA() res = execute('verbose nmap <F3>') assert_match(' <F3> .* abc.*Last set from .*XScriptSuccess\d\+ line 11', res) nunmap <F3> delfunc g:FuncA enddef def Test_option_set() # legacy script allows for white space var lines =<< trim END set foldlevel =11 call assert_equal(11, &foldlevel) END v9.CheckScriptSuccess(lines) set foldlevel set foldlevel=12 assert_equal(12, &foldlevel) set foldlevel+=2 assert_equal(14, &foldlevel) set foldlevel-=3 assert_equal(11, &foldlevel) lines =<< trim END set foldlevel =1 END v9.CheckDefExecAndScriptFailure(lines, 'E1205: No white space allowed between option and: =1') lines =<< trim END set foldlevel +=1 END v9.CheckDefExecAndScriptFailure(lines, 'E1205: No white space allowed between option and: +=1') lines =<< trim END set foldlevel ^=1 END v9.CheckDefExecAndScriptFailure(lines, 'E1205: No white space allowed between option and: ^=1') lines =<< trim END set foldlevel -=1 END v9.CheckDefExecAndScriptFailure(lines, 'E1205: No white space allowed between option and: -=1') set foldlevel& enddef def Test_option_set_line_number() var lines =<< trim END vim9script # line2 # line3 def F() # line5 &foldlevel = -128 enddef F() END v9.CheckScriptSuccess(lines) var res = execute('verbose set foldlevel') assert_match(' foldlevel.*Last set from .*XScriptSuccess\d\+ line 6', res) enddef def Test_option_modifier() # legacy script allows for white space var lines =<< trim END set hlsearch & hlsearch ! call assert_equal(1, &hlsearch) END v9.CheckScriptSuccess(lines) set hlsearch set hlsearch! assert_equal(false, &hlsearch) set hlsearch set hlsearch& assert_equal(false, &hlsearch) lines =<< trim END set hlsearch & END v9.CheckDefExecAndScriptFailure(lines, 'E1205: No white space allowed between option and: &') lines =<< trim END set hlsearch ! END v9.CheckDefExecAndScriptFailure(lines, 'E1205: No white space allowed between option and: !') set hlsearch& enddef " This must be called last, it may cause following :def functions to fail def Test_xxx_echoerr_line_number() var lines =<< trim END echoerr 'some' .. ' error' .. ' continued' END v9.CheckDefExecAndScriptFailure(lines, 'some error continued', 1) enddef func Test_debug_with_lambda() CheckRunVimInTerminal " call indirectly to avoid compilation error for missing functions call Run_Test_debug_with_lambda() endfunc def Run_Test_debug_with_lambda() var lines =<< trim END vim9script def Func() var n = 0 echo [0]->filter((_, v) => v == n) enddef breakadd func Func Func() END writefile(lines, 'XdebugFunc', 'D') var buf = g:RunVimInTerminal('-S XdebugFunc', {rows: 6, wait_for_ruler: 0}) g:WaitForAssert(() => assert_match('^>', term_getline(buf, 6))) term_sendkeys(buf, "cont\<CR>") g:WaitForAssert(() => assert_match('\[0\]', term_getline(buf, 5))) g:StopVimInTerminal(buf) enddef func Test_debug_running_out_of_lines() CheckRunVimInTerminal " call indirectly to avoid compilation error for missing functions call Run_Test_debug_running_out_of_lines() endfunc def Run_Test_debug_running_out_of_lines() var lines =<< trim END vim9script def Crash() # # # # # # # if true # endif enddef breakadd func Crash Crash() END writefile(lines, 'XdebugFunc', 'D') var buf = g:RunVimInTerminal('-S XdebugFunc', {rows: 6, wait_for_ruler: 0}) g:WaitForAssert(() => assert_match('^>', term_getline(buf, 6))) term_sendkeys(buf, "next\<CR>") g:TermWait(buf) g:WaitForAssert(() => assert_match('^>', term_getline(buf, 6))) term_sendkeys(buf, "cont\<CR>") g:TermWait(buf) g:StopVimInTerminal(buf) enddef def Test_ambiguous_command_error() var lines =<< trim END vim9script command CmdA echomsg 'CmdA' command CmdB echomsg 'CmdB' Cmd END v9.CheckScriptFailure(lines, 'E464: Ambiguous use of user-defined command: Cmd', 4) lines =<< trim END vim9script def Func() Cmd enddef Func() END v9.CheckScriptFailure(lines, 'E464: Ambiguous use of user-defined command: Cmd', 1) lines =<< trim END vim9script nnoremap <F3> <ScriptCmd>Cmd<CR> feedkeys("\<F3>", 'xt') END v9.CheckScriptFailure(lines, 'E464: Ambiguous use of user-defined command: Cmd', 3) delcommand CmdA delcommand CmdB nunmap <F3> enddef " Execute this near the end, profiling doesn't stop until Vim exits. " This only tests that it works, not the profiling output. def Test_profile_with_lambda() CheckFeature profile var lines =<< trim END vim9script def ProfiledWithLambda() var n = 3 echo [[1, 2], [3, 4]]->filter((_, l) => l[0] == n) enddef def ProfiledNested() var x = 0 def Nested(): any return x enddef Nested() enddef def g:ProfiledNestedProfiled() var x = 0 def Nested(): any return x enddef Nested() enddef def Profile() ProfiledWithLambda() ProfiledNested() # Also profile the nested function. Use a different function, although # the contents is the same, to make sure it was not already compiled. profile func * g:ProfiledNestedProfiled() profdel func * profile pause enddef var result = 'done' try # mark functions for profiling now to avoid E1271 profile start Xprofile.log profile func ProfiledWithLambda profile func ProfiledNested Profile() catch result = 'failed: ' .. v:exception finally writefile([result], 'Xdidprofile') endtry END writefile(lines, 'Xprofile.vim', 'D') call system(g:GetVimCommand() .. ' --clean' .. ' -c "so Xprofile.vim"' .. ' -c "qall!"') call assert_equal(0, v:shell_error) assert_equal(['done'], readfile('Xdidprofile')) assert_true(filereadable('Xprofile.log')) delete('Xdidprofile') delete('Xprofile.log') enddef func Test_misplaced_type() CheckRunVimInTerminal call Run_Test_misplaced_type() endfunc def Run_Test_misplaced_type() writefile(['let g:somevar = "asdf"'], 'XTest_misplaced_type', 'D') var buf = g:RunVimInTerminal('-S XTest_misplaced_type', {'rows': 6}) term_sendkeys(buf, ":vim9cmd echo islocked('somevar: string')\<CR>") g:VerifyScreenDump(buf, 'Test_misplaced_type', {}) g:StopVimInTerminal(buf) enddef " Ensure echo doesn't crash when stringifying empty variables. def Test_echo_uninit_variables() var res: string var var_bool: bool var var_num: number var var_float: float var Var_func: func var var_string: string var var_blob: blob var var_list: list<any> var var_dict: dict<any> redir => res echo var_bool echo var_num echo var_float echo Var_func echo var_string echo var_blob echo var_list echo var_dict redir END assert_equal(['false', '0', '0.0', 'function()', '', '0z', '[]', '{}'], res->split('\n')) if has('job') var var_job: job var var_channel: channel redir => res echo var_job echo var_channel redir END assert_equal(['no process', 'channel fail'], res->split('\n')) endif enddef def Test_free_type_before_use() # this rather complicated script was freeing a type before using it var lines =<< trim END vim9script def Scan(rel: list<dict<any>>): func(func(dict<any>)) return (Emit: func(dict<any>)) => { for t in rel Emit(t) endfor } enddef def Build(Cont: func(func(dict<any>))): list<dict<any>> var rel: list<dict<any>> = [] Cont((t) => { add(rel, t) }) return rel enddef var R = [{A: 0}] var result = Scan(R)->Build() result = Scan(R)->Build() assert_equal(R, result) END v9.CheckScriptSuccess(lines) enddef " The following complicated script used to cause an internal error (E340) " because the funcref instruction memory was referenced after the instruction " memory was reallocated (Github issue #13178) def Test_refer_funcref_instr_after_realloc() var lines =<< trim END vim9script def A(d: bool) var e = abs(0) var f = &emoji &emoji = true if ['', '', '']->index('xxx') == 0 eval 0 + 0 endif if &filetype == 'xxx' var g = abs(0) while g > 0 if getline(g) == '' break endif --g endwhile if g == 0 return endif if d feedkeys($'{g}G') g = abs(0) endif var h = abs(0) var i = abs(0) var j = abs(0) while j < 0 if abs(0) < h && getline(j) != '' break endif ++j endwhile feedkeys($'{g}G{j}G') return endif def B() enddef def C() enddef enddef A(false) END v9.CheckScriptSuccess(lines) enddef " Test for calling a deferred function after an exception def Test_defer_after_exception() var lines =<< trim END vim9script var callTrace: list<number> = [] def Bar() callTrace += [1] throw 'InnerException' enddef def Defer() callTrace += [2] callTrace += [3] try Bar() catch /InnerException/ callTrace += [4] endtry callTrace += [5] callTrace += [6] enddef def Foo() defer Defer() throw "TestException" enddef try Foo() catch /TestException/ callTrace += [7] endtry assert_equal([2, 3, 1, 4, 5, 6, 7], callTrace) END v9.CheckSourceSuccess(lines) enddef " Test for multiple deferred function which throw exceptions. " Exceptions thrown by deferred functions should result in error messages but " not propagated into the calling functions. def Test_multidefer_with_exception() var lines =<< trim END vim9script var callTrace: list<number> = [] def Except() callTrace += [1] throw 'InnerException' callTrace += [2] enddef def FirstDefer() callTrace += [3] callTrace += [4] enddef def SecondDeferWithExcept() callTrace += [5] Except() callTrace += [6] enddef def ThirdDefer() callTrace += [7] callTrace += [8] enddef def Foo() callTrace += [9] defer FirstDefer() defer SecondDeferWithExcept() defer ThirdDefer() callTrace += [10] enddef v:errmsg = '' try callTrace += [11] Foo() callTrace += [12] catch /TestException/ callTrace += [13] catch callTrace += [14] finally callTrace += [15] endtry callTrace += [16] assert_equal('E605: Exception not caught: InnerException', v:errmsg) assert_equal([11, 9, 10, 7, 8, 5, 1, 3, 4, 12, 15, 16], callTrace) END v9.CheckSourceSuccess(lines) enddef " Test for using ":defer" inside an if statement with a false condition def Test_defer_skipped() var lines =<< trim END def Foo() if false defer execute('echow "hello"', "") endif enddef defcompile END v9.CheckSourceSuccess(lines) enddef " Test for using defer without parenthesis for the function name def Test_defer_func_without_paren() var lines =<< trim END vim9script def Foo() defer Bar enddef defcompile END v9.CheckScriptFailure(lines, 'E107: Missing parentheses: Bar', 1) enddef " Test for using defer without parenthesis for the function name def Test_defer_non_existing_func() var lines =<< trim END vim9script def Foo() defer Bar() enddef defcompile END v9.CheckScriptFailure(lines, 'E1001: Variable not found: Bar', 1) enddef " Test for using defer with an invalid function name def Test_defer_invalid_func() var lines =<< trim END vim9script def Foo() var Abc = 10 defer Abc() enddef defcompile END v9.CheckScriptFailure(lines, 'E129: Function name required', 2) enddef " Test for using defer with an invalid argument to a function def Test_defer_invalid_func_arg() var lines =<< trim END vim9script def Bar(x: number) enddef def Foo() defer Bar(a) enddef defcompile END v9.CheckScriptFailure(lines, 'E1001: Variable not found: a', 1) enddef " Test for using an non-existing type in a "for" statement. def Test_invalid_type_in_for() var lines =<< trim END vim9script def Foo() for b: x in range(10) endfor enddef defcompile END v9.CheckSourceFailure(lines, 'E1010: Type not recognized: x in range(10)', 1) enddef " Test for using a line break between the variable name and the type in a for " statement. def Test_for_stmt_space_before_type() var lines =<< trim END vim9script def Foo() for a :number in range(10) endfor enddef defcompile END v9.CheckSourceFailure(lines, 'E1059: No white space allowed before colon: :number in range(10)', 2) enddef " This test used to cause an use-after-free memory access def Test_for_empty_line_after_lambda() var lines =<< trim END vim9script echomsg range(0, 2)->map((_, v) => { return 1 }) assert_equal('[1, 1, 1]', v:statusmsg) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script echomsg range(0, 1)->map((_, v) => { return 1 }) range(0, 1)->map((_, v) => { return 2 }) # comment assert_equal('[1, 1] [2, 2]', v:statusmsg) END v9.CheckSourceSuccess(lines) enddef " Keep this last, it messes up highlighting. def Test_substitute_cmd() new setline(1, 'something') :substitute(some(other( assert_equal('otherthing', getline(1)) bwipe! # also when the context is Vim9 script var lines =<< trim END vim9script new setline(1, 'something') :substitute(some(other( assert_equal('otherthing', getline(1)) bwipe! END writefile(lines, 'Xvim9lines', 'D') source Xvim9lines enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker