" Test Vim9 assignments source check.vim source vim9.vim let s:appendToMe = 'xxx' let s:addToMe = 111 let g:existing = 'yes' let g:inc_counter = 1 let $SOME_ENV_VAR = 'some' let g:alist = [7] let g:adict = #{a: 1} let g:astring = 'text' def Test_assignment_bool() var bool1: bool = true assert_equal(v:true, bool1) var bool2: bool = false assert_equal(v:false, bool2) var bool3: bool = 0 assert_equal(false, bool3) var bool4: bool = 1 assert_equal(true, bool4) var bool5: bool = 1 && true assert_equal(true, bool5) var bool6: bool = 0 && 1 assert_equal(false, bool6) var bool7: bool = 0 || 1 && true assert_equal(true, bool7) var lines =<< trim END vim9script def GetFlag(): bool var flag: bool = 1 return flag enddef var flag: bool = GetFlag() assert_equal(true, flag) flag = 0 assert_equal(false, flag) flag = 1 assert_equal(true, flag) flag = 1 || true assert_equal(true, flag) flag = 1 && false assert_equal(false, flag) var cp: bool = &cp var fen: bool = &l:fen END CheckScriptSuccess(lines) CheckDefAndScriptFailure(['var x: bool = 2'], 'E1012:') CheckDefAndScriptFailure(['var x: bool = -1'], 'E1012:') CheckDefAndScriptFailure(['var x: bool = [1]'], 'E1012:') CheckDefAndScriptFailure(['var x: bool = {}'], 'E1012:') CheckDefAndScriptFailure(['var x: bool = "x"'], 'E1012:') CheckDefAndScriptFailure(['var x: bool = "x"', '', 'eval 0'], 'E1012:', 1) enddef def Test_syntax() var name = 234 var other: list = ['asdf'] enddef def Test_assignment() CheckDefFailure(['var x:string'], 'E1069:') CheckDefFailure(['var x:string = "x"'], 'E1069:') CheckDefFailure(['var a:string = "x"'], 'E1069:') CheckDefFailure(['var lambda = () => "lambda"'], 'E704:') CheckScriptFailure(['var x = "x"'], 'E1124:') # lower case name is OK for a list var lambdaLines =<< trim END var lambdaList: list = [Test_syntax] lambdaList[0] = () => "lambda" END CheckDefAndScriptSuccess(lambdaLines) var nr: number = 1234 CheckDefFailure(['var nr: number = "asdf"'], 'E1012:') var a: number = 6 #comment assert_equal(6, a) if has('channel') var chan1: channel assert_equal('fail', ch_status(chan1)) var job1: job assert_equal('fail', job_status(job1)) # calling job_start() is in test_vim9_fails.vim, it causes leak reports endif if has('float') var float1: float = 3.4 endif var Funky1: func var Funky2: func = function('len') var Party2: func = funcref('g:Test_syntax') g:newvar = 'new' #comment assert_equal('new', g:newvar) assert_equal('yes', g:existing) g:existing = 'no' assert_equal('no', g:existing) v:char = 'abc' assert_equal('abc', v:char) $ENVVAR = 'foobar' assert_equal('foobar', $ENVVAR) $ENVVAR = '' var lines =<< trim END vim9script $ENVVAR = 'barfoo' assert_equal('barfoo', $ENVVAR) $ENVVAR = '' END CheckScriptSuccess(lines) s:appendToMe ..= 'yyy' assert_equal('xxxyyy', s:appendToMe) s:addToMe += 222 assert_equal(333, s:addToMe) s:newVar = 'new' assert_equal('new', s:newVar) set ts=7 var ts: number = &ts assert_equal(7, ts) &ts += 1 assert_equal(8, &ts) &ts -= 3 assert_equal(5, &ts) &ts *= 2 assert_equal(10, &ts) &ts /= 3 assert_equal(3, &ts) set ts=10 &ts %= 4 assert_equal(2, &ts) if has('float') var f100: float = 100.0 f100 /= 5 assert_equal(20.0, f100) var f200: float = 200.0 f200 /= 5.0 assert_equal(40.0, f200) CheckDefFailure(['var nr: number = 200', 'nr /= 5.0'], 'E1012:') endif lines =<< trim END &ts = 6 &ts += 3 assert_equal(9, &ts) &l:ts = 6 assert_equal(6, &ts) &l:ts += 2 assert_equal(8, &ts) &g:ts = 6 assert_equal(6, &g:ts) &g:ts += 2 assert_equal(8, &g:ts) &number = true assert_equal(true, &number) &number = 0 assert_equal(false, &number) &number = 1 assert_equal(true, &number) &number = false assert_equal(false, &number) END CheckDefAndScriptSuccess(lines) CheckDefFailure(['¬ex += 3'], 'E113:') CheckDefFailure(['&ts ..= "xxx"'], 'E1019:') CheckDefFailure(['&ts = [7]'], 'E1012:') CheckDefExecFailure(['&ts = g:alist'], 'E1012: Type mismatch; expected number but got list') CheckDefFailure(['&ts = "xx"'], 'E1012:') CheckDefExecFailure(['&ts = g:astring'], 'E1012: Type mismatch; expected number but got string') CheckDefFailure(['&path += 3'], 'E1012:') CheckDefExecFailure(['&bs = "asdf"'], 'E474:') # test freeing ISN_STOREOPT CheckDefFailure(['&ts = 3', 'var asdf'], 'E1022:') &ts = 8 lines =<< trim END var save_TI = &t_TI &t_TI = '' assert_equal('', &t_TI) &t_TI = 'xxx' assert_equal('xxx', &t_TI) &t_TI = save_TI END CheckDefAndScriptSuccess(lines) CheckDefFailure(['&t_TI = 123'], 'E1012:') CheckScriptFailure(['vim9script', '&t_TI = 123'], 'E928:') CheckDefFailure(['var s:var = 123'], 'E1101:') CheckDefFailure(['var s:var: number'], 'E1101:') lines =<< trim END vim9script def SomeFunc() s:var = 123 enddef defcompile END CheckScriptFailure(lines, 'E1089:') g:inc_counter += 1 assert_equal(2, g:inc_counter) $SOME_ENV_VAR ..= 'more' assert_equal('somemore', $SOME_ENV_VAR) CheckDefFailure(['$SOME_ENV_VAR += "more"'], 'E1051:') CheckDefFailure(['$SOME_ENV_VAR += 123'], 'E1012:') v:errmsg = 'none' v:errmsg ..= 'again' assert_equal('noneagain', v:errmsg) CheckDefFailure(['v:errmsg += "more"'], 'E1051:') CheckDefFailure(['v:errmsg += 123'], 'E1012:') var text =<< trim END some text END enddef def Test_assign_register() var lines =<< trim END @c = 'areg' @c ..= 'add' assert_equal('aregadd', @c) @@ = 'some text' assert_equal('some text', getreg('"')) END CheckDefAndScriptSuccess(lines) CheckDefFailure(['@a += "more"'], 'E1051:') CheckDefFailure(['@a += 123'], 'E1012:') enddef def Test_reserved_name() for name in ['true', 'false', 'null'] CheckDefExecAndScriptFailure(['var ' .. name .. ' = 0'], 'E1034:') CheckDefExecAndScriptFailure(['var ' .. name .. ': bool'], 'E1034:') endfor enddef def Test_skipped_assignment() var lines =<< trim END for x in [] var i: number = 1 while false i += 1 endwhile endfor END CheckDefAndScriptSuccess(lines) enddef def Test_assign_unpack() var lines =<< trim END var v1: number var v2: number [v1, v2] = [1, 2] assert_equal(1, v1) assert_equal(2, v2) [v1, _, v2, _] = [1, 99, 2, 77] assert_equal(1, v1) assert_equal(2, v2) [v1, v2; _] = [1, 2, 3, 4, 5] assert_equal(1, v1) assert_equal(2, v2) var reslist = [] for text in ['aaa {bbb} ccc', 'ddd {eee} fff'] var before: string var middle: string var after: string [_, before, middle, after; _] = text->matchlist('\(.\{-\}\){\(.\{-\}\)}\(.*\)') reslist->add(before)->add(middle)->add(after) endfor assert_equal(['aaa ', 'bbb', ' ccc', 'ddd ', 'eee', ' fff'], reslist) var a = 1 var b = 3 [a, b] += [2, 4] assert_equal(3, a) assert_equal(7, b) [a, b] -= [1, 2] assert_equal(2, a) assert_equal(5, b) [a, b] *= [3, 2] assert_equal(6, a) assert_equal(10, b) [a, b] /= [2, 4] assert_equal(3, a) assert_equal(2, b) [a, b] = [17, 15] [a, b] %= [5, 3] assert_equal(2, a) assert_equal(0, b) END CheckDefAndScriptSuccess(lines) lines =<< trim END var v1: number var v2: number [v1, v2] = END CheckDefFailure(lines, 'E1097:', 5) lines =<< trim END var v1: number var v2: number [v1, v2] = xxx END CheckDefFailure(lines, 'E1001:', 3) lines =<< trim END var v1: number var v2: number [v1, v2] = popup_clear() END CheckDefFailure(lines, 'E1031:', 3) lines =<< trim END [v1, v2] = [1, 2] END CheckDefFailure(lines, 'E1089', 1) CheckScriptFailure(['vim9script'] + lines, 'E1089', 2) lines =<< trim END var v1: number var v2: number [v1, v2] = '' END CheckDefFailure(lines, 'E1012: Type mismatch; expected list but got string', 3) lines =<< trim END g:values = [false, 0] var x: bool var y: string [x, y] = g:values END CheckDefExecAndScriptFailure(lines, 'E1163: Variable 2: type mismatch, expected string but got number') enddef def Test_assign_linebreak() var nr: number nr = 123 assert_equal(123, nr) var n2: number [nr, n2] = [12, 34] assert_equal(12, nr) assert_equal(34, n2) CheckDefFailure(["var x = #"], 'E1097:', 3) var lines =<< trim END var x: list = ['a'] var y: list = x ->copy() ->copy() END CheckDefFailure(lines, 'E1012:', 2) lines =<< trim END var x: any x.key = 1 + 2 + 3 + 4 + 5 END CheckDefExecAndScriptFailure2(lines, 'E1148:', 'E1203:', 2) enddef def Test_assign_index() # list of list var l1: list l1[0] = 123 assert_equal([123], l1) var l2: list> l2[0] = [] l2[0][0] = 123 assert_equal([[123]], l2) var l3: list>> l3[0] = [] l3[0][0] = [] l3[0][0][0] = 123 assert_equal([[[123]]], l3) var lines =<< trim END var l3: list> l3[0] = [] l3[0][0] = [] END CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got list', 3) # dict of dict var d1: dict d1.one = 1 assert_equal({one: 1}, d1) var d2: dict> d2.one = {} d2.one.two = 123 assert_equal({one: {two: 123}}, d2) var d3: dict>> d3.one = {} d3.one.two = {} d3.one.two.three = 123 assert_equal({one: {two: {three: 123}}}, d3) lines =<< trim END var d3: dict> d3.one = {} d3.one.two = {} END CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got dict', 3) lines =<< trim END var lines: list lines['a'] = 'asdf' END CheckDefFailure(lines, 'E1012:', 2) lines =<< trim END var lines: string lines[9] = 'asdf' END CheckDefFailure(lines, 'E1141:', 2) # list of dict var ld: list> ld[0] = {} ld[0].one = 123 assert_equal([{one: 123}], ld) lines =<< trim END var ld: list> ld[0] = [] END CheckDefFailure(lines, 'E1012: Type mismatch; expected dict but got list', 2) # dict of list var dl: dict> dl.one = [] dl.one[0] = 123 assert_equal({one: [123]}, dl) lines =<< trim END var dl: dict> dl.one = {} END CheckDefFailure(lines, 'E1012: Type mismatch; expected list but got dict', 2) enddef def Test_extend_list() var lines =<< trim END vim9script var l: list l += [123] assert_equal([123], l) END CheckScriptSuccess(lines) lines =<< trim END vim9script var list: list extend(list, ['x']) assert_equal(['x'], list) END CheckScriptSuccess(lines) # appending to NULL list from a function lines =<< trim END vim9script var list: list def Func() list += ['a', 'b'] enddef Func() assert_equal(['a', 'b'], list) END CheckScriptSuccess(lines) lines =<< trim END vim9script var list: list def Func() extend(list, ['x', 'b']) enddef Func() assert_equal(['x', 'b'], list) END CheckScriptSuccess(lines) lines =<< trim END vim9script var l: list = test_null_list() extend(l, ['x']) assert_equal(['x'], l) END CheckScriptSuccess(lines) lines =<< trim END vim9script extend(test_null_list(), ['x']) END CheckScriptFailure(lines, 'E1134:', 2) enddef def Test_extend_dict() var lines =<< trim END vim9script var d: dict extend(d, {a: 1}) assert_equal({a: 1}, d) var d2: dict d2['one'] = 1 assert_equal({one: 1}, d2) END CheckScriptSuccess(lines) lines =<< trim END vim9script var d: dict = test_null_dict() extend(d, {a: 'x'}) assert_equal({a: 'x'}, d) END CheckScriptSuccess(lines) lines =<< trim END vim9script extend(test_null_dict(), {a: 'x'}) END CheckScriptFailure(lines, 'E1133:', 2) enddef def Test_single_letter_vars() # single letter variables var a: number = 123 a = 123 assert_equal(123, a) var b: number b = 123 assert_equal(123, b) var g: number g = 123 assert_equal(123, g) var s: number s = 123 assert_equal(123, s) var t: number t = 123 assert_equal(123, t) var v: number v = 123 assert_equal(123, v) var w: number w = 123 assert_equal(123, w) enddef def Test_vim9_single_char_vars() var lines =<< trim END vim9script # single character variable declarations work var a: string var b: number var l: list var s: string var t: number var v: number var w: number # script-local variables can be used without s: prefix a = 'script-a' b = 111 l = [1, 2, 3] s = 'script-s' t = 222 v = 333 w = 444 assert_equal('script-a', a) assert_equal(111, b) assert_equal([1, 2, 3], l) assert_equal('script-s', s) assert_equal(222, t) assert_equal(333, v) assert_equal(444, w) END writefile(lines, 'Xsinglechar') source Xsinglechar delete('Xsinglechar') enddef def Test_assignment_list() var list1: list = [false, true, false] var list2: list = [1, 2, 3] var list3: list = ['sdf', 'asdf'] var list4: list = ['yes', true, 1234] var list5: list = [0z01, 0z02] var listS: list = [] var listN: list = [] assert_equal([1, 2, 3], list2) list2[-1] = 99 assert_equal([1, 2, 99], list2) list2[-2] = 88 assert_equal([1, 88, 99], list2) list2[-3] = 77 assert_equal([77, 88, 99], list2) list2 += [100] assert_equal([77, 88, 99, 100], list2) list3 += ['end'] assert_equal(['sdf', 'asdf', 'end'], list3) CheckDefExecFailure(['var ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:') CheckDefExecFailure(['var [v1, v2] = [1, 2]'], 'E1092:') # type becomes list var somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c'] var lines =<< trim END var d = {dd: test_null_list()} d.dd[0] = 0 END CheckDefExecFailure(lines, 'E1147:', 2) lines =<< trim END def OneArg(x: bool) enddef def TwoArgs(x: bool, y: bool) enddef var fl: list = [OneArg, TwoArgs] END CheckDefExecAndScriptFailure(lines, 'E1012:', 5) enddef def PartFuncBool(b: bool): string return 'done' enddef def Test_assignment_partial() var lines =<< trim END var Partial: func(): string = function(PartFuncBool, [true]) assert_equal('done', Partial()) END CheckDefAndScriptSuccess(lines) lines =<< trim END vim9script def Func(b: bool) enddef var Ref: func = function(Func, [true]) assert_equal('func()', typename(Ref)) Ref() END CheckScriptSuccess(lines) enddef def Test_assignment_list_any_index() var l: list = [1, 2] for [x, y, _] in [[0, 1, ''], [1, 3, '']] l[x] = l[x] + y endfor assert_equal([2, 5], l) enddef def Test_assignment_list_vim9script() var lines =<< trim END vim9script var v1: number var v2: number var v3: number [v1, v2, v3] = [1, 2, 3] assert_equal([1, 2, 3], [v1, v2, v3]) END CheckScriptSuccess(lines) enddef def Test_assignment_dict() var dict1: dict = {one: false, two: true} var dict2: dict = {one: 1, two: 2} var dict3: dict = {key: 'value'} var dict4: dict = {one: 1, two: '2'} var dict5: dict = {one: 0z01, two: 0z02} # overwrite dict3['key'] = 'another' assert_equal(dict3, {key: 'another'}) dict3.key = 'yet another' assert_equal(dict3, {key: 'yet another'}) # member "any" can also be a dict and assigned to var anydict: dict = {nest: {}, nr: 0} anydict.nest['this'] = 123 anydict.nest.that = 456 assert_equal({nest: {this: 123, that: 456}, nr: 0}, anydict) var lines =<< trim END var dd = {} dd.two = 2 assert_equal({two: 2}, dd) END CheckDefAndScriptSuccess(lines) lines =<< trim END var d = {dd: {}} d.dd[0] = 2 d.dd['x'] = 3 d.dd.y = 4 assert_equal({dd: {0: 2, x: 3, y: 4}}, d) END CheckDefAndScriptSuccess(lines) lines =<< trim END var dd = {one: 1} dd.one) = 2 END CheckDefFailure(lines, 'E488:', 2) lines =<< trim END var dd = {one: 1} var dd.one = 2 END CheckDefAndScriptFailure(lines, 'E1017:', 2) # empty key can be used var dd = {} dd[""] = 6 assert_equal({['']: 6}, dd) # type becomes dict var somedict = rand() > 0 ? {a: 1, b: 2} : {a: 'a', b: 'b'} # assignment to script-local dict lines =<< trim END vim9script var test: dict = {} def FillDict(): dict test['a'] = 43 return test enddef assert_equal({a: 43}, FillDict()) END CheckScriptSuccess(lines) lines =<< trim END vim9script var test: dict def FillDict(): dict test['a'] = 43 return test enddef FillDict() assert_equal({a: 43}, test) END CheckScriptSuccess(lines) # assignment to global dict lines =<< trim END vim9script g:test = {} def FillDict(): dict g:test['a'] = 43 return g:test enddef assert_equal({a: 43}, FillDict()) END CheckScriptSuccess(lines) # assignment to buffer dict lines =<< trim END vim9script b:test = {} def FillDict(): dict b:test['a'] = 43 return b:test enddef assert_equal({a: 43}, FillDict()) END CheckScriptSuccess(lines) lines =<< trim END var d = {dd: test_null_dict()} d.dd[0] = 0 END CheckDefExecFailure(lines, 'E1103:', 2) lines =<< trim END var d = {dd: 'string'} d.dd[0] = 0 END CheckDefExecFailure(lines, 'E1148:', 2) lines =<< trim END var n: any n.key = 5 END CheckDefExecAndScriptFailure2(lines, 'E1148:', 'E1203: Dot can only be used on a dictionary: n.key = 5', 2) enddef def Test_assignment_local() # Test in a separated file in order not to the current buffer/window/tab is # changed. var script_lines: list =<< trim END let b:existing = 'yes' let w:existing = 'yes' let t:existing = 'yes' def Test_assignment_local_internal() b:newvar = 'new' assert_equal('new', b:newvar) assert_equal('yes', b:existing) b:existing = 'no' assert_equal('no', b:existing) b:existing ..= 'NO' assert_equal('noNO', b:existing) w:newvar = 'new' assert_equal('new', w:newvar) assert_equal('yes', w:existing) w:existing = 'no' assert_equal('no', w:existing) w:existing ..= 'NO' assert_equal('noNO', w:existing) t:newvar = 'new' assert_equal('new', t:newvar) assert_equal('yes', t:existing) t:existing = 'no' assert_equal('no', t:existing) t:existing ..= 'NO' assert_equal('noNO', t:existing) enddef call Test_assignment_local_internal() END CheckScriptSuccess(script_lines) enddef def Test_assignment_default() # Test default values. var thebool: bool assert_equal(v:false, thebool) var thenumber: number assert_equal(0, thenumber) if has('float') var thefloat: float assert_equal(0.0, thefloat) endif var thestring: string assert_equal('', thestring) var theblob: blob assert_equal(0z, theblob) var Thefunc: func assert_equal(test_null_function(), Thefunc) var thelist: list assert_equal([], thelist) var thedict: dict assert_equal({}, thedict) if has('channel') var thejob: job assert_equal(test_null_job(), thejob) var thechannel: channel assert_equal(test_null_channel(), thechannel) if has('unix') && executable('cat') # check with non-null job and channel, types must match thejob = job_start("cat ", {}) thechannel = job_getchannel(thejob) job_stop(thejob, 'kill') endif endif var nr = 1234 | nr = 5678 assert_equal(5678, nr) enddef let scriptvar = 'init' def Test_assignment_var_list() var lines =<< trim END var v1: string var v2: string var vrem: list [v1] = ['aaa'] assert_equal('aaa', v1) [v1, v2] = ['one', 'two'] assert_equal('one', v1) assert_equal('two', v2) [v1, v2; vrem] = ['one', 'two'] assert_equal('one', v1) assert_equal('two', v2) assert_equal([], vrem) [v1, v2; vrem] = ['one', 'two', 'three'] assert_equal('one', v1) assert_equal('two', v2) assert_equal(['three'], vrem) [&ts, &sw] = [3, 4] assert_equal(3, &ts) assert_equal(4, &sw) set ts=8 sw=4 [@a, @z] = ['aa', 'zz'] assert_equal('aa', @a) assert_equal('zz', @z) [$SOME_VAR, $OTHER_VAR] = ['some', 'other'] assert_equal('some', $SOME_VAR) assert_equal('other', $OTHER_VAR) [g:globalvar, b:bufvar, w:winvar, t:tabvar, v:errmsg] = ['global', 'buf', 'win', 'tab', 'error'] assert_equal('global', g:globalvar) assert_equal('buf', b:bufvar) assert_equal('win', w:winvar) assert_equal('tab', t:tabvar) assert_equal('error', v:errmsg) unlet g:globalvar END CheckDefAndScriptSuccess(lines) [g:globalvar, s:scriptvar, b:bufvar] = ['global', 'script', 'buf'] assert_equal('global', g:globalvar) assert_equal('script', s:scriptvar) assert_equal('buf', b:bufvar) lines =<< trim END vim9script var s:scriptvar = 'init' [g:globalvar, s:scriptvar, w:winvar] = ['global', 'script', 'win'] assert_equal('global', g:globalvar) assert_equal('script', s:scriptvar) assert_equal('win', w:winvar) END CheckScriptSuccess(lines) enddef def Test_assignment_vim9script() var lines =<< trim END vim9script def Func(): list return [1, 2] enddef var name1: number var name2: number [name1, name2] = Func() assert_equal(1, name1) assert_equal(2, name2) var ll = Func() assert_equal([1, 2], ll) @/ = 'text' assert_equal('text', @/) @0 = 'zero' assert_equal('zero', @0) @1 = 'one' assert_equal('one', @1) @9 = 'nine' assert_equal('nine', @9) @- = 'minus' assert_equal('minus', @-) if has('clipboard_working') @* = 'star' assert_equal('star', @*) @+ = 'plus' assert_equal('plus', @+) endif var a: number = 123 assert_equal(123, a) var s: string = 'yes' assert_equal('yes', s) var b: number = 42 assert_equal(42, b) var w: number = 43 assert_equal(43, w) var t: number = 44 assert_equal(44, t) var to_var = 0 to_var = 3 assert_equal(3, to_var) END CheckScriptSuccess(lines) lines =<< trim END vim9script var n: number def Func() n = 'string' enddef defcompile END CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string') enddef def Mess(): string v:foldstart = 123 return 'xxx' enddef def Test_assignment_failure() CheckDefFailure(['var name=234'], 'E1004:') CheckDefFailure(['var name =234'], 'E1004:') CheckDefFailure(['var name= 234'], 'E1004:') CheckScriptFailure(['vim9script', 'var name=234'], 'E1004:') CheckScriptFailure(['vim9script', 'var name=234'], "before and after '='") CheckScriptFailure(['vim9script', 'var name =234'], 'E1004:') CheckScriptFailure(['vim9script', 'var name= 234'], 'E1004:') CheckScriptFailure(['vim9script', 'var name = 234', 'name+=234'], 'E1004:') CheckScriptFailure(['vim9script', 'var name = 234', 'name+=234'], "before and after '+='") CheckScriptFailure(['vim9script', 'var name = "x"', 'name..="y"'], 'E1004:') CheckScriptFailure(['vim9script', 'var name = "x"', 'name..="y"'], "before and after '..='") CheckDefFailure(['var true = 1'], 'E1034:') CheckDefFailure(['var false = 1'], 'E1034:') CheckDefFailure(['var null = 1'], 'E1034:') CheckDefFailure(['var this = 1'], 'E1034:') CheckDefFailure(['[a; b; c] = g:list'], 'E452:') CheckDefExecFailure(['var a: number', '[a] = test_null_list()'], 'E1093:') CheckDefExecFailure(['var a: number', '[a] = []'], 'E1093:') CheckDefExecFailure(['var x: number', 'var y: number', '[x, y] = [1]'], 'E1093:') CheckDefExecFailure(['var x: string', 'var y: string', '[x, y] = ["x"]'], 'E1093:') CheckDefExecFailure(['var x: number', 'var y: number', 'var z: list', '[x, y; z] = [1]'], 'E1093:') CheckDefFailure(['var somevar'], "E1022:") CheckDefFailure(['var &tabstop = 4'], 'E1052:') CheckDefFailure(['&g:option = 5'], 'E113:') CheckScriptFailure(['vim9script', 'var &tabstop = 4'], 'E1052:') CheckDefFailure(['var $VAR = 5'], 'E1016: Cannot declare an environment variable:') CheckScriptFailure(['vim9script', 'var $ENV = "xxx"'], 'E1016:') if has('dnd') CheckDefFailure(['var @~ = 5'], 'E1066:') else CheckDefFailure(['var @~ = 5'], 'E354:') CheckDefFailure(['@~ = 5'], 'E354:') endif CheckDefFailure(['var @a = 5'], 'E1066:') CheckDefFailure(['var @/ = "x"'], 'E1066:') CheckScriptFailure(['vim9script', 'var @a = "abc"'], 'E1066:') CheckDefFailure(['var g:var = 5'], 'E1016: Cannot declare a global variable:') CheckDefFailure(['var w:var = 5'], 'E1016: Cannot declare a window variable:') CheckDefFailure(['var b:var = 5'], 'E1016: Cannot declare a buffer variable:') CheckDefFailure(['var t:var = 5'], 'E1016: Cannot declare a tab variable:') CheckDefFailure(['var anr = 4', 'anr ..= "text"'], 'E1019:') CheckDefFailure(['var xnr += 4'], 'E1020:', 1) CheckScriptFailure(['vim9script', 'var xnr += 4'], 'E1020:') CheckDefFailure(["var xnr = xnr + 1"], 'E1001:', 1) CheckScriptFailure(['vim9script', 'var xnr = xnr + 4'], 'E121:') CheckScriptFailure(['vim9script', 'def Func()', 'var dummy = s:notfound', 'enddef', 'defcompile'], 'E1108:') CheckDefFailure(['var name: list = [123]'], 'expected list but got list') CheckDefFailure(['var name: list = ["xx"]'], 'expected list but got list') CheckDefFailure(['var name: dict = {key: 123}'], 'expected dict but got dict') CheckDefFailure(['var name: dict = {key: "xx"}'], 'expected dict but got dict') CheckDefFailure(['var name = feedkeys("0")'], 'E1031:') CheckDefFailure(['var name: number = feedkeys("0")'], 'expected number but got void') CheckDefFailure(['var name: dict '], 'E1068:') CheckDefFailure(['var name: dict = [] l[0] = 'value' assert_equal('value', l[0]) l[1] = 'asdf' assert_equal('value', l[0]) assert_equal('asdf', l[1]) assert_equal('asdf', l[-1]) assert_equal('value', l[-2]) var nrl: list = [] for i in range(5) nrl[i] = i endfor assert_equal([0, 1, 2, 3, 4], nrl) var ul: list ul[0] = 1 ul[1] = 2 ul[2] = 3 assert_equal([1, 2, 3], ul) END CheckDefAndScriptSuccess(lines) CheckDefFailure(["var l: list = ['', true]"], 'E1012: Type mismatch; expected list but got list', 1) CheckDefFailure(["var l: list> = [['', true]]"], 'E1012: Type mismatch; expected list> but got list>', 1) enddef def Test_assign_dict() var lines =<< trim END var d: dict = {} d['key'] = 'value' assert_equal('value', d['key']) d[123] = 'qwerty' assert_equal('qwerty', d[123]) assert_equal('qwerty', d['123']) var nrd: dict = {} for i in range(3) nrd[i] = i endfor assert_equal({0: 0, 1: 1, 2: 2}, nrd) d.somekey = 'someval' assert_equal({key: 'value', '123': 'qwerty', somekey: 'someval'}, d) # unlet d.somekey # assert_equal({key: 'value', '123': 'qwerty'}, d) END CheckDefAndScriptSuccess(lines) # TODO: move to above once "unlet d.somekey" in :def is implemented lines =<< trim END vim9script var d: dict = {} d['key'] = 'value' d.somekey = 'someval' assert_equal({key: 'value', somekey: 'someval'}, d) unlet d.somekey assert_equal({key: 'value'}, d) END CheckScriptSuccess(lines) CheckDefFailure(["var d: dict = {a: '', b: true}"], 'E1012: Type mismatch; expected dict but got dict', 1) CheckDefFailure(["var d: dict> = {x: {a: '', b: true}}"], 'E1012: Type mismatch; expected dict> but got dict>', 1) enddef def Test_assign_dict_unknown_type() var lines =<< trim END vim9script var mylist = [] mylist += [{one: 'one'}] def Func() var dd = mylist[0] assert_equal('one', dd.one) enddef Func() END CheckScriptSuccess(lines) lines =<< trim END vim9script var mylist = [[]] mylist[0] += [{one: 'one'}] def Func() var dd = mylist[0][0] assert_equal('one', dd.one) enddef Func() END CheckScriptSuccess(lines) enddef def Test_assign_dict_with_op() var lines =<< trim END var ds: dict = {a: 'x'} ds['a'] ..= 'y' ds.a ..= 'z' assert_equal('xyz', ds.a) var dn: dict = {a: 9} dn['a'] += 2 assert_equal(11, dn.a) dn.a += 2 assert_equal(13, dn.a) dn['a'] -= 3 assert_equal(10, dn.a) dn.a -= 2 assert_equal(8, dn.a) dn['a'] *= 2 assert_equal(16, dn.a) dn.a *= 2 assert_equal(32, dn.a) dn['a'] /= 3 assert_equal(10, dn.a) dn.a /= 2 assert_equal(5, dn.a) dn['a'] %= 3 assert_equal(2, dn.a) dn.a %= 6 assert_equal(2, dn.a) var dd: dict>> dd.a = {} dd.a.b = [0] dd.a.b += [1] assert_equal({a: {b: [0, 1]}}, dd) var dab = {a: ['b']} dab.a[0] ..= 'c' assert_equal({a: ['bc']}, dab) END CheckDefAndScriptSuccess(lines) enddef def Test_assign_list_with_op() var lines =<< trim END var ls: list = ['x'] ls[0] ..= 'y' assert_equal('xy', ls[0]) var ln: list = [9] ln[0] += 2 assert_equal(11, ln[0]) ln[0] -= 3 assert_equal(8, ln[0]) ln[0] *= 2 assert_equal(16, ln[0]) ln[0] /= 3 assert_equal(5, ln[0]) ln[0] %= 3 assert_equal(2, ln[0]) END CheckDefAndScriptSuccess(lines) enddef def Test_assign_with_op_fails() var lines =<< trim END var s = 'abc' s[1] += 'x' END CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2) lines =<< trim END var s = 'abc' s[1] ..= 'x' END CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2) lines =<< trim END var dd: dict>> dd.a = {} dd.a.b += [1] END CheckDefExecAndScriptFailure(lines, 'E716:', 3) enddef def Test_assign_lambda() # check if assign a lambda to a variable which type is func or any. var lines =<< trim END vim9script var FuncRef = () => 123 assert_equal(123, FuncRef()) var FuncRef_Func: func = () => 123 assert_equal(123, FuncRef_Func()) var FuncRef_Any: any = () => 123 assert_equal(123, FuncRef_Any()) var FuncRef_Number: func(): number = () => 321 assert_equal(321, FuncRef_Number()) END CheckScriptSuccess(lines) lines =<< trim END var Ref: func(number) Ref = (j) => !j END CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected func(number) but got func(any): bool') lines =<< trim END echo filter([1, 2, 3], (_, v: string) => v + 1) END CheckDefAndScriptFailure(lines, 'E1051:') enddef def Test_heredoc() var lines =<< trim END # comment text END assert_equal(['text'], lines) CheckDefFailure(['var lines =<< trim END X', 'END'], 'E488:') CheckDefFailure(['var lines =<< trim END " comment', 'END'], 'E488:') lines =<< trim [END] def Func() var&lines =<< trim END x x enddef defcompile [END] CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: END') delfunc! g:Func lines =<< trim [END] def Func() var lines =<< trim END x x x x x x x x enddef call Func() [END] CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: END') delfunc! g:Func enddef def Test_var_func_call() var lines =<< trim END vim9script func GetValue() if exists('g:count') let g:count += 1 else let g:count = 1 endif return 'this' endfunc var val: string = GetValue() # env var is always a string var env = $TERM END writefile(lines, 'Xfinished') source Xfinished # GetValue() is not called during discovery phase assert_equal(1, g:count) unlet g:count delete('Xfinished') enddef def Test_var_missing_type() var lines =<< trim END vim9script var name = g:unknown END CheckScriptFailure(lines, 'E121:') lines =<< trim END vim9script var nr: number = 123 var name = nr END CheckScriptSuccess(lines) enddef def Test_var_declaration() var lines =<< trim END vim9script var name: string g:var_uninit = name name = 'text' g:var_test = name # prefixing s: is optional s:name = 'prefixed' g:var_prefixed = s:name const FOO: number = 123 assert_equal(123, FOO) const FOOS = 'foos' assert_equal('foos', FOOS) final FLIST = [1] assert_equal([1], FLIST) FLIST[0] = 11 assert_equal([11], FLIST) const g:FOO: number = 321 assert_equal(321, g:FOO) const g:FOOS = 'gfoos' assert_equal('gfoos', g:FOOS) final g:FLIST = [2] assert_equal([2], g:FLIST) g:FLIST[0] = 22 assert_equal([22], g:FLIST) def SetGlobalConst() const g:globConst = 123 enddef SetGlobalConst() assert_equal(123, g:globConst) assert_true(islocked('g:globConst')) const w:FOO: number = 46 assert_equal(46, w:FOO) const w:FOOS = 'wfoos' assert_equal('wfoos', w:FOOS) final w:FLIST = [3] assert_equal([3], w:FLIST) w:FLIST[0] = 33 assert_equal([33], w:FLIST) var s:other: number other = 1234 g:other_var = other var xyz: string # comment # type is inferred var s:dict = {['a']: 222} def GetDictVal(key: any) g:dict_val = s:dict[key] enddef GetDictVal('a') final adict: dict = {} def ChangeAdict() adict.foo = 'foo' enddef ChangeAdict() END CheckScriptSuccess(lines) assert_equal('', g:var_uninit) assert_equal('text', g:var_test) assert_equal('prefixed', g:var_prefixed) assert_equal(1234, g:other_var) assert_equal(222, g:dict_val) unlet g:var_uninit unlet g:var_test unlet g:var_prefixed unlet g:other_var unlet g:globConst unlet g:FOO unlet g:FOOS unlet g:FLIST unlet w:FOO unlet w:FOOS unlet w:FLIST enddef def Test_var_declaration_fails() var lines =<< trim END vim9script final var: string END CheckScriptFailure(lines, 'E1125:') lines =<< trim END vim9script const g:constvar = 'string' g:constvar = 'xx' END CheckScriptFailure(lines, 'E741:') unlet g:constvar lines =<< trim END vim9script var name = 'one' lockvar name def SetLocked() name = 'two' enddef SetLocked() END CheckScriptFailure(lines, 'E741: Value is locked: name', 1) lines =<< trim END let s:legacy = 'one' lockvar s:legacy def SetLocked() s:legacy = 'two' enddef call SetLocked() END CheckScriptFailure(lines, 'E741: Value is locked: s:legacy', 1) lines =<< trim END vim9script def SetGlobalConst() const g:globConst = 123 enddef SetGlobalConst() g:globConst = 234 END CheckScriptFailure(lines, 'E741: Value is locked: g:globConst', 6) unlet g:globConst lines =<< trim END vim9script const cdict: dict = {} def Change() cdict.foo = 'foo' enddef defcompile END CheckScriptFailure(lines, 'E46:') lines =<< trim END vim9script final w:finalvar = [9] w:finalvar = [8] END CheckScriptFailure(lines, 'E1122:') unlet w:finalvar lines =<< trim END vim9script const var: string END CheckScriptFailure(lines, 'E1021:') lines =<< trim END vim9script var 9var: string END CheckScriptFailure(lines, 'E488:') CheckDefFailure(['var foo.bar = 2'], 'E1087:') CheckDefFailure(['var foo[3] = 2'], 'E1087:') CheckDefFailure(['const foo: number'], 'E1021:') enddef def Test_script_local_in_legacy() # OK to define script-local later when prefixed with s: var lines =<< trim END def SetLater() s:legvar = 'two' enddef defcompile let s:legvar = 'one' call SetLater() call assert_equal('two', s:legvar) END CheckScriptSuccess(lines) # OK to leave out s: prefix when script-local already defined lines =<< trim END let s:legvar = 'one' def SetNoPrefix() legvar = 'two' enddef call SetNoPrefix() call assert_equal('two', s:legvar) END CheckScriptSuccess(lines) # Not OK to leave out s: prefix when script-local defined later lines =<< trim END def SetLaterNoPrefix() legvar = 'two' enddef defcompile let s:legvar = 'one' END CheckScriptFailure(lines, 'E476:', 1) edit! Xfile lines =<< trim END var edit: bool legacy edit END CheckDefAndScriptSuccess(lines) enddef def Test_var_type_check() var lines =<< trim END vim9script var name: string name = 1234 END CheckScriptFailure(lines, 'E1012:') lines =<< trim END vim9script var name:string END CheckScriptFailure(lines, 'E1069:') lines =<< trim END vim9script var name: asdf END CheckScriptFailure(lines, 'E1010:') lines =<< trim END vim9script var s:l: list s:l = [] END CheckScriptSuccess(lines) lines =<< trim END vim9script var s:d: dict s:d = {} END CheckScriptSuccess(lines) lines =<< trim END vim9script var d = {a: 1, b: [2]} def Func(b: bool) var l: list = b ? d.b : [3] enddef defcompile END CheckScriptSuccess(lines) enddef let g:dict_number = #{one: 1, two: 2} def Test_var_list_dict_type() var ll: list ll = [1, 2, 2, 3, 3, 3]->uniq() ll->assert_equal([1, 2, 3]) var dd: dict dd = g:dict_number dd->assert_equal(g:dict_number) var lines =<< trim END var ll: list ll = [1, 2, 3]->map('"one"') END CheckDefExecFailure(lines, 'E1012: Type mismatch; expected number but got string') enddef def Test_cannot_use_let() CheckDefAndScriptFailure(['let a = 34'], 'E1126:', 1) enddef def Test_unlet() g:somevar = 'yes' assert_true(exists('g:somevar')) unlet g:somevar assert_false(exists('g:somevar')) unlet! g:somevar # also works for script-local variable in legacy Vim script s:somevar = 'legacy' assert_true(exists('s:somevar')) unlet s:somevar assert_false(exists('s:somevar')) unlet! s:somevar CheckDefExecFailure([ 'var dd = 111', 'unlet dd', ], 'E1081:', 2) # dict unlet var dd = {a: 1, b: 2, c: 3} unlet dd['a'] unlet dd.c assert_equal({b: 2}, dd) # list unlet var ll = [1, 2, 3, 4] unlet ll[1] unlet ll[-1] assert_equal([1, 3], ll) ll = [1, 2, 3, 4] unlet ll[0 : 1] assert_equal([3, 4], ll) ll = [1, 2, 3, 4] unlet ll[2 : 8] assert_equal([1, 2], ll) ll = [1, 2, 3, 4] unlet ll[-2 : -1] assert_equal([1, 2], ll) CheckDefFailure([ 'var ll = [1, 2]', 'll[1 : 2] = 7', ], 'E1165:', 2) CheckDefFailure([ 'var dd = {a: 1}', 'unlet dd["a" : "a"]', ], 'E1166:', 2) CheckDefExecFailure([ 'unlet g:adict[0 : 1]', ], 'E1148:', 1) CheckDefFailure([ 'var ll = [1, 2]', 'unlet ll[0:1]', ], 'E1004:', 2) CheckDefFailure([ 'var ll = [1, 2]', 'unlet ll[0 :1]', ], 'E1004:', 2) CheckDefFailure([ 'var ll = [1, 2]', 'unlet ll[0: 1]', ], 'E1004:', 2) CheckDefFailure([ 'var ll = [1, 2]', 'unlet ll["x" : 1]', ], 'E1012:', 2) CheckDefFailure([ 'var ll = [1, 2]', 'unlet ll[0 : "x"]', ], 'E1012:', 2) # list of dict unlet var dl = [{a: 1, b: 2}, {c: 3}] unlet dl[0]['b'] assert_equal([{a: 1}, {c: 3}], dl) CheckDefExecFailure([ 'var ll = test_null_list()', 'unlet ll[0]', ], 'E684:', 2) CheckDefExecFailure([ 'var ll = [1]', 'unlet ll[2]', ], 'E684:', 2) CheckDefExecFailure([ 'var ll = [1]', 'unlet ll[g:astring]', ], 'E1012:', 2) CheckDefExecFailure([ 'var dd = test_null_dict()', 'unlet dd["a"]', ], 'E716:', 2) CheckDefExecFailure([ 'var dd = {a: 1}', 'unlet dd["b"]', ], 'E716:', 2) CheckDefExecFailure([ 'var dd = {a: 1}', 'unlet dd[g:alist]', ], 'E1105:', 2) # can compile unlet before variable exists g:someDict = {key: 'val'} var k = 'key' unlet g:someDict[k] assert_equal({}, g:someDict) unlet g:someDict assert_false(exists('g:someDict')) CheckScriptFailure([ 'vim9script', 'var svar = 123', 'unlet svar', ], 'E1081:') CheckScriptFailure([ 'vim9script', 'var svar = 123', 'unlet s:svar', ], 'E1081:') CheckScriptFailure([ 'vim9script', 'var svar = 123', 'def Func()', ' unlet svar', 'enddef', 'defcompile', ], 'E1081:') CheckScriptFailure([ 'vim9script', 'var svar = 123', 'func Func()', ' unlet s:svar', 'endfunc', 'Func()', ], 'E1081:') CheckScriptFailure([ 'vim9script', 'var svar = 123', 'def Func()', ' unlet s:svar', 'enddef', 'defcompile', ], 'E1081:') writefile(['vim9script', 'export var svar = 1234'], 'XunletExport.vim') var lines =<< trim END vim9script import svar from './XunletExport.vim' def UnletSvar() unlet svar enddef defcompile END CheckScriptFailure(lines, 'E1081:', 1) delete('XunletExport.vim') $ENVVAR = 'foobar' assert_equal('foobar', $ENVVAR) unlet $ENVVAR assert_equal('', $ENVVAR) enddef def Test_expr_error_no_assign() var lines =<< trim END vim9script var x = invalid echo x END CheckScriptFailureList(lines, ['E121:', 'E121:']) lines =<< trim END vim9script var x = 1 / 0 echo x END CheckScriptFailure(lines, 'E1154:') lines =<< trim END vim9script var x = 1 % 0 echo x END CheckScriptFailure(lines, 'E1154:') lines =<< trim END var x: string 'string' END CheckDefAndScriptFailure(lines, 'E488:') enddef def Test_assign_command_modifier() var lines =<< trim END var verbose = 0 verbose = 1 assert_equal(1, verbose) silent verbose = 2 assert_equal(2, verbose) silent verbose += 2 assert_equal(4, verbose) silent verbose -= 1 assert_equal(3, verbose) var topleft = {one: 1} sandbox topleft.one = 3 assert_equal({one: 3}, topleft) leftabove topleft[' '] = 4 assert_equal({one: 3, ' ': 4}, topleft) var x: number var y: number silent [x, y] = [1, 2] assert_equal(1, x) assert_equal(2, y) END CheckDefAndScriptSuccess(lines) enddef def Test_assign_alt_buf_register() var lines =<< trim END edit 'file_b1' var b1 = bufnr() edit 'file_b2' var b2 = bufnr() assert_equal(b1, bufnr('#')) @# = b2 assert_equal(b2, bufnr('#')) END CheckDefAndScriptSuccess(lines) enddef def Test_script_funcref_case() var lines =<< trim END var Len = (s: string): number => len(s) + 1 assert_equal(5, Len('asdf')) END CheckDefAndScriptSuccess(lines) lines =<< trim END var len = (s: string): number => len(s) + 1 END CheckDefAndScriptFailure(lines, 'E704:') lines =<< trim END vim9script var s:Len = (s: string): number => len(s) + 2 assert_equal(6, Len('asdf')) END CheckScriptSuccess(lines) lines =<< trim END vim9script var s:len = (s: string): number => len(s) + 1 END CheckScriptFailure(lines, 'E704:') enddef def Test_inc_dec() var lines =<< trim END var nr = 7 ++nr assert_equal(8, nr) --nr assert_equal(7, nr) ++nr | ++nr assert_equal(9, nr) ++nr # comment assert_equal(10, nr) var ll = [1, 2] --ll[0] ++ll[1] assert_equal([0, 3], ll) g:count = 1 ++g:count --g:count assert_equal(1, g:count) unlet g:count END CheckDefAndScriptSuccess(lines) lines =<< trim END var nr = 7 ++ nr END CheckDefAndScriptFailure(lines, "E1202: No white space allowed after '++': ++ nr") enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker