1
0
forked from aniani/vim
Files
vim/src/testdir/test_vim9_script.vim

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

5270 lines
111 KiB
VimL
Raw Normal View History

" 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_command_block()
com SomeCommand {
g:someVar = 'some'
}
InvokeSomeCommand()
assert_equal('some', g:someVar)
delcommand SomeCommand
unlet g:someVar
enddef
" Test for using heredoc in a :command command block
def Test_command_block_heredoc()
var lines =<< trim CODE
vim9script
com SomeCommand {
g:someVar =<< trim END
aaa
bbb
END
}
SomeCommand
assert_equal(['aaa', 'bbb'], g:someVar)
def Foo()
g:someVar = []
SomeCommand
assert_equal(['aaa', 'bbb'], g:someVar)
enddef
Foo()
delcommand SomeCommand
unlet g:someVar
CODE
v9.CheckSourceSuccess( lines)
# Execute a command with heredoc in a block
lines =<< trim CODE
vim9script
com SomeCommand {
g:someVar =<< trim END
aaa
bbb
END
}
execute('SomeCommand')
assert_equal(['aaa', 'bbb'], g:someVar)
delcommand SomeCommand
unlet g:someVar
CODE
v9.CheckSourceSuccess(lines)
# Heredoc with comment
lines =<< trim CODE
vim9script
com SomeCommand {
g:someVar =<< trim END # comment
aaa
bbb
END
}
execute('SomeCommand')
assert_equal(['aaa', 'bbb'], g:someVar)
delcommand SomeCommand
unlet g:someVar
CODE
v9.CheckSourceSuccess(lines)
# heredoc evaluation
lines =<< trim CODE
vim9script
com SomeCommand {
var suffix = '---'
g:someVar =<< trim eval END
ccc{suffix}
ddd
END
}
SomeCommand
assert_equal(['ccc---', 'ddd'], g:someVar)
def Foo()
g:someVar = []
SomeCommand
assert_equal(['ccc---', 'ddd'], g:someVar)
enddef
Foo()
delcommand SomeCommand
unlet g:someVar
CODE
v9.CheckSourceSuccess(lines)
# command following heredoc
lines =<< trim CODE
vim9script
com SomeCommand {
var l =<< trim END
eee
fff
END
g:someVar = l
}
SomeCommand
assert_equal(['eee', 'fff'], g:someVar)
delcommand SomeCommand
unlet g:someVar
CODE
v9.CheckSourceSuccess(lines)
# Error in heredoc
lines =<< trim CODE
vim9script
com SomeCommand {
g:someVar =<< trim END
eee
fff
}
try
SomeCommand
catch
assert_match("E990: Missing end marker 'END'", v:exception)
endtry
delcommand SomeCommand
unlet g:someVar
CODE
v9.CheckSourceSuccess(lines)
enddef
def Test_autocommand_block()
au BufNew *.xml {
g:otherVar = 'other'
}
split other.xml
assert_equal('other', g:otherVar)
bwipe!
au! BufNew *.xml
unlet g:otherVar
enddef
def Test_block_in_a_string()
var lines =<< trim END
vim9script
def Foo(): string
var x = ' => { # abc'
return x
enddef
assert_equal(' => { # abc', Foo())
def Bar(): string
var x = " => { # abc"
return x
enddef
assert_equal(" => { # abc", Bar())
END
v9.CheckSourceSuccess(lines)
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)
patch 9.1.0999: Vim9: leaking finished exception Problem: leaking finished exception (after v9.1.0984) Solution: use finish_exception to clean up caught exceptions (Yee Cheng Chin) In Vimscript, v:exception/throwpoint/stacktrace are supposed to reflect the currently caught exception, and be popped after the exception is finished (via endtry, finally, or a thrown exception inside catch). Vim9script does not handle this properly, and leaks them instead. This is clearly visible when launching GVim with menu enabled. A caught exception inside the s:BMShow() in menu.vim would show up when querying `v:stacktrace` even though the exception was already caught and handled. To fix this, just use the same functionality as Vimscript by calling `finish_exception` to properly restore the states. Note that this assumes `current_exception` is always the same as `caught_stack` which believe should be the case. Added tests for this. Also fix up test_stacktrace to properly test the stack restore behavior where we have nested exceptions in catch blocks and to also test the vim9script functionality properly. - Also, remove its dependency on explicitly checking a line number in runtest.vim which is a very fragile way to write tests as any minor change in runtest.vim (shared among all tests) would require changing test_stacktrace.vim. We don't actually need such granularity in the test. closes: #16413 Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2025-01-09 22:14:34 +01:00
# test that the v:exception stacks are correctly restored
try
try
throw 101
catch
assert_equal('101', v:exception)
try
catch
finally
assert_equal('101', v:exception) # finally shouldn't clear if it doesn't own it
endtry
assert_equal('101', v:exception)
throw 102 # Re-throw inside catch block
endtry
catch
assert_equal('102', v:exception)
try
throw 103 # throw inside nested exception stack
catch
assert_equal('103', v:exception)
endtry
assert_equal('102', v:exception) # restored stack
finally
assert_equal('', v:exception) # finally should clear if it owns the exception
endtry
try
try
throw 104
catch
try
exec 'nonexistent_cmd' # normal exception inside nested exception stack
catch
assert_match('E492:', v:exception)
endtry
eval [][0] # normal exception inside catch block
endtry
catch
assert_match('E684:', v:exception)
endtry
assert_equal('', v:exception) # All exceptions properly popped
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
patch 9.1.0999: Vim9: leaking finished exception Problem: leaking finished exception (after v9.1.0984) Solution: use finish_exception to clean up caught exceptions (Yee Cheng Chin) In Vimscript, v:exception/throwpoint/stacktrace are supposed to reflect the currently caught exception, and be popped after the exception is finished (via endtry, finally, or a thrown exception inside catch). Vim9script does not handle this properly, and leaks them instead. This is clearly visible when launching GVim with menu enabled. A caught exception inside the s:BMShow() in menu.vim would show up when querying `v:stacktrace` even though the exception was already caught and handled. To fix this, just use the same functionality as Vimscript by calling `finish_exception` to properly restore the states. Note that this assumes `current_exception` is always the same as `caught_stack` which believe should be the case. Added tests for this. Also fix up test_stacktrace to properly test the stack restore behavior where we have nested exceptions in catch blocks and to also test the vim9script functionality properly. - Also, remove its dependency on explicitly checking a line number in runtest.vim which is a very fragile way to write tests as any minor change in runtest.vim (shared among all tests) would require changing test_stacktrace.vim. We don't actually need such granularity in the test. closes: #16413 Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2025-01-09 22:14:34 +01:00
def Func2()
eval 1 + 1
eval 2 + 2
eval 3 + 3
throw 'exception'
enddef
try
Func()
catch /exception/
patch 9.1.0999: Vim9: leaking finished exception Problem: leaking finished exception (after v9.1.0984) Solution: use finish_exception to clean up caught exceptions (Yee Cheng Chin) In Vimscript, v:exception/throwpoint/stacktrace are supposed to reflect the currently caught exception, and be popped after the exception is finished (via endtry, finally, or a thrown exception inside catch). Vim9script does not handle this properly, and leaks them instead. This is clearly visible when launching GVim with menu enabled. A caught exception inside the s:BMShow() in menu.vim would show up when querying `v:stacktrace` even though the exception was already caught and handled. To fix this, just use the same functionality as Vimscript by calling `finish_exception` to properly restore the states. Note that this assumes `current_exception` is always the same as `caught_stack` which believe should be the case. Added tests for this. Also fix up test_stacktrace to properly test the stack restore behavior where we have nested exceptions in catch blocks and to also test the vim9script functionality properly. - Also, remove its dependency on explicitly checking a line number in runtest.vim which is a very fragile way to write tests as any minor change in runtest.vim (shared among all tests) would require changing test_stacktrace.vim. We don't actually need such granularity in the test. closes: #16413 Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2025-01-09 22:14:34 +01:00
try
Func2()
catch /exception/
assert_match('line 4', v:throwpoint)
endtry
assert_match('line 3', v:throwpoint)
endtry
patch 9.1.0999: Vim9: leaking finished exception Problem: leaking finished exception (after v9.1.0984) Solution: use finish_exception to clean up caught exceptions (Yee Cheng Chin) In Vimscript, v:exception/throwpoint/stacktrace are supposed to reflect the currently caught exception, and be popped after the exception is finished (via endtry, finally, or a thrown exception inside catch). Vim9script does not handle this properly, and leaks them instead. This is clearly visible when launching GVim with menu enabled. A caught exception inside the s:BMShow() in menu.vim would show up when querying `v:stacktrace` even though the exception was already caught and handled. To fix this, just use the same functionality as Vimscript by calling `finish_exception` to properly restore the states. Note that this assumes `current_exception` is always the same as `caught_stack` which believe should be the case. Added tests for this. Also fix up test_stacktrace to properly test the stack restore behavior where we have nested exceptions in catch blocks and to also test the vim9script functionality properly. - Also, remove its dependency on explicitly checking a line number in runtest.vim which is a very fragile way to write tests as any minor change in runtest.vim (shared among all tests) would require changing test_stacktrace.vim. We don't actually need such granularity in the test. closes: #16413 Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2025-01-09 22:14:34 +01:00
assert_match('', v:throwpoint)
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
if 1
# do nothing
else
var [a] = [10]
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:')
# Test for echoing a script local function name
var lines =<< trim END
vim9script
def ScriptLocalEcho()
enddef
echo ScriptLocalEcho
END
new
setline(1, lines)
assert_match('<SNR>\d\+_ScriptLocalEcho', execute('source')->split("\n")[0])
bw!
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()
# Invalid range
var lines =<< trim END
def Foo()
:$echowindow "foo"
enddef
defcompile
END
v9.CheckDefAndScriptFailure(lines, 'E16: Invalid range')
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)
# Test for trying to use the loop variable "_" inside the loop
for _ in "a"
assert_fails('echo _', 'E1181: Cannot use an underscore here')
endfor
END
v9.CheckDefAndScriptSuccess(lines)
lines =<< trim END
for i : number : [1, 2]
echo i
endfor
END
v9.CheckSourceDefAndScriptFailure(lines, 'E1059: No white space allowed before colon: : [1, 2]', 1)
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)
patch 9.1.0589: vi: d{motion} and cw work differently than expected Problem: vi: d{motion} and cw command work differently than expected Solution: add new cpo-z flag to make the behaviour configurable There are two special vi compatible behaviours (or should I say bugs?): 1): cw behaves differently than dw. That is, because cw is special cased by Vim and is effectively aliased to ce. POSIX behaviour is documented here: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html#tag_20_152_13_81 2): d{motion} may make the whole delete operation linewise, if the start and end of the motion are on different lines and there are only blanks before the start and after the end of the motion. Did not find a related POSIX link that requires this behaviour. Both behaviours can be considered inconsistent, but we cannot easily change it, because it would be a backward incompatible change and also incompatible to how classic vi behaved. So let's add the new cpo flag "z", which when not included fixes both behaviours and make them more consistent to what users would expect. This has been requested several times: https://groups.google.com/d/msg/vim_use/aaBqT6ECkA4/ALf4odKzEDgJ https://groups.google.com/d/msg/vim_dev/Dpn3xtUF16I/T6JcOPKN6usJ http://www.reddit.com/r/vim/comments/26nut8/why_does_cw_work_like_ce/ https://groups.google.com/d/msg/vim_use/vunNWLFWfQg/MmJh_ZGaAgAJ https://github.com/vim/vim/issues/4390 So in summary, if you want to have the w motion work more consistent, remove the 'z' from the cpo settings. related: #4390 closes: #15263 Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-07-15 20:51:11 +02:00
assert_equal('aABceFsz', 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([
patch 9.1.0589: vi: d{motion} and cw work differently than expected Problem: vi: d{motion} and cw command work differently than expected Solution: add new cpo-z flag to make the behaviour configurable There are two special vi compatible behaviours (or should I say bugs?): 1): cw behaves differently than dw. That is, because cw is special cased by Vim and is effectively aliased to ce. POSIX behaviour is documented here: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html#tag_20_152_13_81 2): d{motion} may make the whole delete operation linewise, if the start and end of the motion are on different lines and there are only blanks before the start and after the end of the motion. Did not find a related POSIX link that requires this behaviour. Both behaviours can be considered inconsistent, but we cannot easily change it, because it would be a backward incompatible change and also incompatible to how classic vi behaved. So let's add the new cpo flag "z", which when not included fixes both behaviours and make them more consistent to what users would expect. This has been requested several times: https://groups.google.com/d/msg/vim_use/aaBqT6ECkA4/ALf4odKzEDgJ https://groups.google.com/d/msg/vim_dev/Dpn3xtUF16I/T6JcOPKN6usJ http://www.reddit.com/r/vim/comments/26nut8/why_does_cw_work_like_ce/ https://groups.google.com/d/msg/vim_use/vunNWLFWfQg/MmJh_ZGaAgAJ https://github.com/vim/vim/issues/4390 So in summary, if you want to have the w motion work more consistent, remove the 'z' from the cpo settings. related: #4390 closes: #15263 Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-07-15 20:51:11 +02:00
'before: aABceFsz',
'after: aABceFszM',
'later: aABceFszM',
'vim9: aABceFsz'], 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: 10, wait_for_ruler: 0})
g:WaitForAssert(() => assert_match('^>', term_getline(buf, 10)))
term_sendkeys(buf, "cont\<CR>")
g:WaitForAssert(() => assert_match('\[0\]', term_getline(buf, 9)))
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: 10, wait_for_ruler: 0})
g:WaitForAssert(() => assert_match('^>', term_getline(buf, 10)))
term_sendkeys(buf, "next\<CR>")
g:TermWait(buf)
g:WaitForAssert(() => assert_match('^>', term_getline(buf, 10)))
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', 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 a 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
" Test for evaluating a lambda block from a string
def Test_eval_lambda_block()
var lines =<< trim END
vim9script
var Fn = eval("(x: number): number => {\nreturn x * 2\n}")
assert_equal(6, Fn(3))
END
v9.CheckSourceSuccess(lines)
enddef
" Test for using various null values
def Test_null_values()
var lines =<< trim END
var nullValues = [
[null, 1, 'null', 7, 'special'],
[null_blob, 1, '0z', 10, 'blob'],
[null_dict, 1, '{}', 4, 'dict<any>'],
[null_function, 1, "function('')", 2, 'func(...): unknown'],
[null_list, 1, '[]', 3, 'list<any>'],
[null_object, 1, 'object of [unknown]', 13, 'object<Unknown>'],
[null_partial, 1, "function('')", 2, 'func(...): unknown'],
[null_string, 1, "''", 1, 'string']
]
if has('channel')
nullValues->add([null_channel, 1, 'channel fail', 9, 'channel'])
endif
if has('job')
nullValues->add([null_job, 1, 'no process', 8, 'job'])
endif
for [Val, emptyExp, stringExp, typeExp, typenameExp] in nullValues
assert_equal(emptyExp, empty(Val))
assert_equal(stringExp, string(Val))
assert_equal(typeExp, type(Val))
assert_equal(typenameExp, typename(Val))
assert_equal(Val, copy(Val))
assert_equal(-1, test_refcount(Val))
endfor
END
v9.CheckSourceDefAndScriptSuccess(lines)
enddef
" Test for using an unknown type in a typecast
def Test_unknown_type_in_typecast()
var lines =<< trim END
vim9script
var a = <MyType>b
END
v9.CheckSourceFailure(lines, 'E1010: Type not recognized: MyType', 2)
lines =<< trim END
vim9script
var Fn = <funcx(number, number): number>b
END
v9.CheckSourceFailure(lines, 'E1010: Type not recognized: funcx(number, number): number', 2)
# Wrong type in a type cast
lines =<< trim END
vim9script
var i: number = <number>true
END
v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got bool', 2)
enddef
" Test for calling a function as a method with a list argument
" This exercises some conditions in the assignment statement parsing code.
def Test_method_call_with_list_arg()
var lines =<< trim END
vim9script
def Foo(l: list<number>)
g:save_list = l
enddef
def Bar()
var a = 10
var b = 20
[a, b]->Foo()
enddef
g:save_list = []
Bar()
assert_equal([10, 20], g:save_list)
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