forked from aniani/vim
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>
5158 lines
108 KiB
VimL
5158 lines
108 KiB
VimL
" 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
|
|
|
|
func g:NoSuchFunc()
|
|
echo 'none'
|
|
endfunc
|
|
|
|
def Test_try_catch_throw()
|
|
var l = []
|
|
try # comment
|
|
add(l, '1')
|
|
throw 'wrong'
|
|
add(l, '2') # "unreachable code"
|
|
catch # comment
|
|
add(l, v:exception)
|
|
finally # comment
|
|
add(l, '3')
|
|
endtry # comment
|
|
assert_equal(['1', 'wrong', '3'], l)
|
|
|
|
l = []
|
|
try
|
|
try
|
|
add(l, '1')
|
|
throw 'wrong'
|
|
add(l, '2') # "unreachable code"
|
|
catch /right/
|
|
add(l, v:exception)
|
|
endtry
|
|
catch /wrong/
|
|
add(l, 'caught')
|
|
finally
|
|
add(l, 'finally')
|
|
endtry
|
|
assert_equal(['1', 'caught', 'finally'], l)
|
|
|
|
var n: number
|
|
try
|
|
n = l[3]
|
|
catch /E684:/
|
|
n = 99
|
|
endtry
|
|
assert_equal(99, n)
|
|
|
|
var done = 'no'
|
|
if 0
|
|
try | catch | endtry
|
|
else
|
|
done = 'yes'
|
|
endif
|
|
assert_equal('yes', done)
|
|
|
|
done = 'no'
|
|
if 1
|
|
done = 'yes'
|
|
else
|
|
try | catch | endtry
|
|
done = 'never'
|
|
endif
|
|
assert_equal('yes', done)
|
|
|
|
if 1
|
|
else
|
|
try | catch /pat/ | endtry
|
|
try | catch /pat/
|
|
endtry
|
|
try
|
|
catch /pat/ | endtry
|
|
try
|
|
catch /pat/
|
|
endtry
|
|
endif
|
|
|
|
try
|
|
# string slice returns a string, not a number
|
|
n = g:astring[3]
|
|
catch /E1012:/
|
|
n = 77
|
|
endtry
|
|
assert_equal(77, n)
|
|
|
|
try
|
|
n = l[g:astring]
|
|
catch /E1012:/
|
|
n = 88
|
|
endtry
|
|
assert_equal(88, n)
|
|
|
|
try
|
|
n = s:does_not_exist
|
|
catch /E121:/
|
|
n = 111
|
|
endtry
|
|
assert_equal(111, n)
|
|
|
|
try
|
|
n = g:does_not_exist
|
|
catch /E121:/
|
|
n = 121
|
|
endtry
|
|
assert_equal(121, n)
|
|
|
|
var d = {one: 1}
|
|
try
|
|
n = d[g:astring]
|
|
catch /E716:/
|
|
n = 222
|
|
endtry
|
|
assert_equal(222, n)
|
|
|
|
try
|
|
n = -g:astring
|
|
catch /E1012:/
|
|
n = 233
|
|
endtry
|
|
assert_equal(233, n)
|
|
|
|
try
|
|
n = +g:astring
|
|
catch /E1012:/
|
|
n = 244
|
|
endtry
|
|
assert_equal(244, n)
|
|
|
|
try
|
|
n = +g:alist
|
|
catch /E1012:/
|
|
n = 255
|
|
endtry
|
|
assert_equal(255, n)
|
|
|
|
var nd: dict<any>
|
|
try
|
|
nd = {[g:alist]: 1}
|
|
catch /E1105:/
|
|
n = 266
|
|
endtry
|
|
assert_equal(266, n)
|
|
|
|
l = [1, 2, 3]
|
|
try
|
|
[n] = l
|
|
catch /E1093:/
|
|
n = 277
|
|
endtry
|
|
assert_equal(277, n)
|
|
|
|
try
|
|
&ts = g:astring
|
|
catch /E1012:/
|
|
n = 288
|
|
endtry
|
|
assert_equal(288, n)
|
|
|
|
try
|
|
&backspace = 'asdf'
|
|
catch /E474:/
|
|
n = 299
|
|
endtry
|
|
assert_equal(299, n)
|
|
|
|
l = [1]
|
|
try
|
|
l[3] = 3
|
|
catch /E684:/
|
|
n = 300
|
|
endtry
|
|
assert_equal(300, n)
|
|
|
|
try
|
|
unlet g:does_not_exist
|
|
catch /E108:/
|
|
n = 322
|
|
endtry
|
|
assert_equal(322, n)
|
|
|
|
try
|
|
d = {text: 1, [g:astring]: 2}
|
|
catch /E721:/
|
|
n = 333
|
|
endtry
|
|
assert_equal(333, n)
|
|
|
|
try
|
|
l = g:DeletedFunc()
|
|
catch /E933:/
|
|
n = 344
|
|
endtry
|
|
assert_equal(344, n)
|
|
|
|
try
|
|
echo range(1, 2, 0)
|
|
catch /E726:/
|
|
n = 355
|
|
endtry
|
|
assert_equal(355, n)
|
|
|
|
var P = function('g:NoSuchFunc')
|
|
delfunc g:NoSuchFunc
|
|
try
|
|
echo P()
|
|
catch /E117:/
|
|
n = 366
|
|
endtry
|
|
assert_equal(366, n)
|
|
|
|
try
|
|
echo g:NoSuchFunc()
|
|
catch /E117:/
|
|
n = 377
|
|
endtry
|
|
assert_equal(377, n)
|
|
|
|
try
|
|
echo g:alist + 4
|
|
catch /E745:/
|
|
n = 388
|
|
endtry
|
|
assert_equal(388, n)
|
|
|
|
try
|
|
echo 4 + g:alist
|
|
catch /E745:/
|
|
n = 399
|
|
endtry
|
|
assert_equal(399, n)
|
|
|
|
try
|
|
echo g:alist.member
|
|
catch /E715:/
|
|
n = 400
|
|
endtry
|
|
assert_equal(400, n)
|
|
|
|
try
|
|
echo d.member
|
|
catch /E716:/
|
|
n = 411
|
|
endtry
|
|
assert_equal(411, n)
|
|
|
|
var counter = 0
|
|
for i in range(4)
|
|
try
|
|
eval [][0]
|
|
catch
|
|
endtry
|
|
counter += 1
|
|
endfor
|
|
assert_equal(4, counter)
|
|
|
|
# no requirement for spaces before |
|
|
try|echo 0|catch|endtry
|
|
|
|
# return in try with finally
|
|
def ReturnInTry(): number
|
|
var ret = 4
|
|
try
|
|
return ret
|
|
catch /this/
|
|
return -1
|
|
catch /that/
|
|
return -1
|
|
finally
|
|
# changing ret has no effect
|
|
ret = 7
|
|
endtry
|
|
return -2
|
|
enddef
|
|
assert_equal(4, ReturnInTry())
|
|
|
|
# return in catch with finally
|
|
def ReturnInCatch(): number
|
|
var ret = 5
|
|
try
|
|
throw 'getout'
|
|
return -1 # "unreachable code"
|
|
catch /getout/
|
|
# ret is evaluated here
|
|
return ret
|
|
finally
|
|
# changing ret later has no effect
|
|
ret = -3
|
|
endtry
|
|
return -2
|
|
enddef
|
|
assert_equal(5, ReturnInCatch())
|
|
|
|
# return in finally after empty catch
|
|
def ReturnInFinally(): number
|
|
try
|
|
finally
|
|
return 6
|
|
endtry
|
|
enddef
|
|
assert_equal(6, ReturnInFinally())
|
|
|
|
var lines =<< trim END
|
|
vim9script
|
|
try
|
|
acos('0.5')
|
|
->setline(1)
|
|
catch
|
|
g:caught = v:exception
|
|
endtry
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
assert_match('E1219: Float or Number required for argument 1', g:caught)
|
|
unlet g:caught
|
|
|
|
# missing catch and/or finally
|
|
lines =<< trim END
|
|
vim9script
|
|
try
|
|
echo 'something'
|
|
endtry
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1032:')
|
|
|
|
# skipping try-finally-endtry when try-finally-endtry is used in another block
|
|
lines =<< trim END
|
|
if v:true
|
|
try
|
|
finally
|
|
endtry
|
|
else
|
|
try
|
|
finally
|
|
endtry
|
|
endif
|
|
END
|
|
v9.CheckDefAndScriptSuccess(lines)
|
|
enddef
|
|
|
|
def Test_unreachable_after()
|
|
var lines =<< trim END
|
|
try
|
|
throw 'Error'
|
|
echo 'not reached'
|
|
catch /Error/
|
|
endtry
|
|
END
|
|
v9.CheckDefFailure(lines, 'E1095: Unreachable code after :throw')
|
|
|
|
lines =<< trim END
|
|
def SomeFunc(): number
|
|
try
|
|
return 3
|
|
echo 'not reached'
|
|
catch /Error/
|
|
endtry
|
|
return 4
|
|
enddef
|
|
defcompile
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1095: Unreachable code after :return')
|
|
enddef
|
|
|
|
def Test_throw_in_nested_try()
|
|
var lines =<< trim END
|
|
vim9script
|
|
|
|
def Try(F: func(): void)
|
|
try
|
|
F()
|
|
catch
|
|
endtry
|
|
enddef
|
|
|
|
class X
|
|
def F()
|
|
try
|
|
throw 'Foobar'
|
|
catch
|
|
throw v:exception
|
|
endtry
|
|
enddef
|
|
endclass
|
|
|
|
def Test_TryMethod()
|
|
var x = X.new()
|
|
Try(() => x.F())
|
|
enddef
|
|
|
|
|
|
try
|
|
Test_TryMethod()
|
|
catch
|
|
endtry
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
enddef
|
|
|
|
def Test_try_var_decl()
|
|
var lines =<< trim END
|
|
vim9script
|
|
try
|
|
var in_try = 1
|
|
assert_equal(1, get(s:, 'in_try', -1))
|
|
throw "getout"
|
|
catch
|
|
var in_catch = 2
|
|
assert_equal(-1, get(s:, 'in_try', -1))
|
|
assert_equal(2, get(s:, 'in_catch', -1))
|
|
finally
|
|
var in_finally = 3
|
|
assert_equal(-1, get(s:, 'in_try', -1))
|
|
assert_equal(-1, get(s:, 'in_catch', -1))
|
|
assert_equal(3, get(s:, 'in_finally', -1))
|
|
endtry
|
|
assert_equal(-1, get(s:, 'in_try', -1))
|
|
assert_equal(-1, get(s:, 'in_catch', -1))
|
|
assert_equal(-1, get(s:, 'in_finally', -1))
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
enddef
|
|
|
|
def Test_try_ends_in_return()
|
|
var lines =<< trim END
|
|
vim9script
|
|
def Foo(): string
|
|
try
|
|
return 'foo'
|
|
catch
|
|
return 'caught'
|
|
endtry
|
|
enddef
|
|
assert_equal('foo', Foo())
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
|
|
lines =<< trim END
|
|
vim9script
|
|
def Foo(): string
|
|
try
|
|
return 'foo'
|
|
catch
|
|
return 'caught'
|
|
endtry
|
|
echo 'notreached'
|
|
enddef
|
|
assert_equal('foo', Foo())
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1095:')
|
|
|
|
lines =<< trim END
|
|
vim9script
|
|
def Foo(): string
|
|
try
|
|
return 'foo'
|
|
catch /x/
|
|
return 'caught'
|
|
endtry
|
|
enddef
|
|
assert_equal('foo', Foo())
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1027:')
|
|
|
|
lines =<< trim END
|
|
vim9script
|
|
def Foo(): string
|
|
try
|
|
echo 'foo'
|
|
catch
|
|
echo 'caught'
|
|
finally
|
|
return 'done'
|
|
endtry
|
|
enddef
|
|
assert_equal('done', Foo())
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
|
|
enddef
|
|
|
|
def Test_try_in_catch()
|
|
var lines =<< trim END
|
|
vim9script
|
|
var seq = []
|
|
def DoIt()
|
|
try
|
|
seq->add('throw 1')
|
|
eval [][0]
|
|
seq->add('notreached')
|
|
catch
|
|
seq->add('catch')
|
|
try
|
|
seq->add('throw 2')
|
|
eval [][0]
|
|
seq->add('notreached')
|
|
catch /nothing/
|
|
seq->add('notreached')
|
|
endtry
|
|
seq->add('done')
|
|
endtry
|
|
enddef
|
|
DoIt()
|
|
assert_equal(['throw 1', 'catch', 'throw 2', 'done'], seq)
|
|
END
|
|
enddef
|
|
|
|
def Test_error_in_catch()
|
|
var lines =<< trim END
|
|
try
|
|
eval [][0]
|
|
catch /E684:/
|
|
eval [][0]
|
|
endtry
|
|
END
|
|
v9.CheckDefExecFailure(lines, 'E684:', 4)
|
|
enddef
|
|
|
|
" :while at the very start of a function that :continue jumps to
|
|
def s:TryContinueFunc()
|
|
while g:Count < 2
|
|
g:sequence ..= 't'
|
|
try
|
|
echoerr 'Test'
|
|
catch
|
|
g:Count += 1
|
|
g:sequence ..= 'c'
|
|
continue
|
|
endtry
|
|
g:sequence ..= 'e'
|
|
g:Count += 1
|
|
endwhile
|
|
enddef
|
|
|
|
def Test_continue_in_try_in_while()
|
|
g:Count = 0
|
|
g:sequence = ''
|
|
TryContinueFunc()
|
|
assert_equal('tctc', g:sequence)
|
|
unlet g:Count
|
|
unlet g:sequence
|
|
enddef
|
|
|
|
def Test_break_in_try_in_for()
|
|
var lines =<< trim END
|
|
vim9script
|
|
def Ls(): list<string>
|
|
var ls: list<string>
|
|
for s in ['abc', 'def']
|
|
for _ in [123, 456]
|
|
try
|
|
eval [][0]
|
|
catch
|
|
break
|
|
endtry
|
|
endfor
|
|
ls += [s]
|
|
endfor
|
|
return ls
|
|
enddef
|
|
assert_equal(['abc', 'def'], Ls())
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
enddef
|
|
|
|
def Test_nocatch_return_in_try()
|
|
# return in try block returns normally
|
|
def ReturnInTry(): string
|
|
try
|
|
return '"some message"'
|
|
catch
|
|
endtry
|
|
return 'not reached'
|
|
enddef
|
|
exe 'echoerr ' .. ReturnInTry()
|
|
enddef
|
|
|
|
def Test_cnext_works_in_catch()
|
|
var lines =<< trim END
|
|
vim9script
|
|
au BufEnter * eval 1 + 2
|
|
writefile(['text'], 'Xcncfile1')
|
|
writefile(['text'], 'Xcncfile2')
|
|
var items = [
|
|
{lnum: 1, filename: 'Xcncfile1', valid: true},
|
|
{lnum: 1, filename: 'Xcncfile2', valid: true}
|
|
]
|
|
setqflist([], ' ', {items: items})
|
|
cwindow
|
|
|
|
def CnextOrCfirst()
|
|
# if cnext fails, cfirst is used
|
|
try
|
|
cnext
|
|
catch
|
|
cfirst
|
|
endtry
|
|
enddef
|
|
|
|
CnextOrCfirst()
|
|
CnextOrCfirst()
|
|
writefile([getqflist({idx: 0}).idx], 'Xcncresult')
|
|
qall
|
|
END
|
|
writefile(lines, 'XCatchCnext', 'D')
|
|
g:RunVim([], [], '--clean -S XCatchCnext')
|
|
assert_equal(['1'], readfile('Xcncresult'))
|
|
|
|
delete('Xcncfile1')
|
|
delete('Xcncfile2')
|
|
delete('Xcncresult')
|
|
enddef
|
|
|
|
def Test_throw_skipped()
|
|
if 0
|
|
throw dontgethere
|
|
endif
|
|
enddef
|
|
|
|
def Test_nocatch_throw_silenced()
|
|
var lines =<< trim END
|
|
vim9script
|
|
def Func()
|
|
throw 'error'
|
|
enddef
|
|
silent! Func()
|
|
END
|
|
writefile(lines, 'XthrowSilenced', 'D')
|
|
source XthrowSilenced
|
|
enddef
|
|
|
|
" g:DeletedFunc() is found when compiling Test_try_catch_throw() and then
|
|
" deleted, this should give a runtime error.
|
|
def DeletedFunc(): list<any>
|
|
return ['delete me']
|
|
enddef
|
|
defcompile DeletedFunc
|
|
|
|
call test_override('unreachable', 1)
|
|
defcompile Test_try_catch_throw
|
|
call test_override('unreachable', 0)
|
|
|
|
delfunc DeletedFunc
|
|
|
|
def s:ThrowFromDef()
|
|
throw "getout" # comment
|
|
enddef
|
|
|
|
func s:CatchInFunc()
|
|
try
|
|
call s:ThrowFromDef()
|
|
catch
|
|
let g:thrown_func = v:exception
|
|
endtry
|
|
endfunc
|
|
|
|
def s:CatchInDef()
|
|
try
|
|
ThrowFromDef()
|
|
catch
|
|
g:thrown_def = v:exception
|
|
endtry
|
|
enddef
|
|
|
|
def s:ReturnFinally(): string
|
|
try
|
|
return 'intry'
|
|
finally
|
|
g:in_finally = 'finally'
|
|
endtry
|
|
return 'end'
|
|
enddef
|
|
|
|
def Test_try_catch_nested()
|
|
CatchInFunc()
|
|
assert_equal('getout', g:thrown_func)
|
|
|
|
CatchInDef()
|
|
assert_equal('getout', g:thrown_def)
|
|
|
|
assert_equal('intry', ReturnFinally())
|
|
assert_equal('finally', g:in_finally)
|
|
|
|
var l = []
|
|
try
|
|
l->add('1')
|
|
throw 'bad'
|
|
l->add('x') # "unreachable code"
|
|
catch /bad/
|
|
l->add('2')
|
|
try
|
|
l->add('3')
|
|
throw 'one'
|
|
l->add('x')
|
|
catch /one/
|
|
l->add('4')
|
|
try
|
|
l->add('5')
|
|
throw 'more'
|
|
l->add('x')
|
|
catch /more/
|
|
l->add('6')
|
|
endtry
|
|
endtry
|
|
endtry
|
|
assert_equal(['1', '2', '3', '4', '5', '6'], l)
|
|
|
|
l = []
|
|
try
|
|
try
|
|
l->add('1')
|
|
throw 'foo'
|
|
l->add('x')
|
|
catch
|
|
l->add('2')
|
|
throw 'bar'
|
|
l->add('x')
|
|
finally
|
|
l->add('3')
|
|
endtry
|
|
l->add('x')
|
|
catch /bar/
|
|
l->add('4')
|
|
endtry
|
|
assert_equal(['1', '2', '3', '4'], l)
|
|
enddef
|
|
|
|
call test_override('unreachable', 1)
|
|
defcompile Test_try_catch_nested
|
|
call test_override('unreachable', 0)
|
|
|
|
def s:TryOne(): number
|
|
try
|
|
return 0
|
|
catch
|
|
endtry
|
|
return 0
|
|
enddef
|
|
|
|
def s:TryTwo(n: number): string
|
|
try
|
|
var x = {}
|
|
catch
|
|
endtry
|
|
return 'text'
|
|
enddef
|
|
|
|
def Test_try_catch_twice()
|
|
assert_equal('text', TryOne()->TryTwo())
|
|
enddef
|
|
|
|
def Test_try_catch_match()
|
|
var seq = 'a'
|
|
try
|
|
throw 'something'
|
|
catch /nothing/
|
|
seq ..= 'x'
|
|
catch /some/
|
|
seq ..= 'b'
|
|
catch /asdf/
|
|
seq ..= 'x'
|
|
catch ?a\?sdf?
|
|
seq ..= 'y'
|
|
finally
|
|
seq ..= 'c'
|
|
endtry
|
|
assert_equal('abc', seq)
|
|
enddef
|
|
|
|
def Test_try_catch_fails()
|
|
v9.CheckDefFailure(['catch'], 'E603:')
|
|
v9.CheckDefFailure(['try', 'echo 0', 'catch', 'catch'], 'E1033:')
|
|
v9.CheckDefFailure(['try', 'echo 0', 'catch /pat'], 'E1067:')
|
|
v9.CheckDefFailure(['finally'], 'E606:')
|
|
v9.CheckDefFailure(['try', 'echo 0', 'finally', 'echo 1', 'finally'], 'E607:')
|
|
v9.CheckDefFailure(['endtry'], 'E602:')
|
|
v9.CheckDefFailure(['while 1', 'endtry'], 'E170:')
|
|
v9.CheckDefFailure(['for i in range(5)', 'endtry'], 'E170:')
|
|
v9.CheckDefFailure(['if 1', 'endtry'], 'E171:')
|
|
v9.CheckDefFailure(['try', 'echo 1', 'endtry'], 'E1032:')
|
|
|
|
v9.CheckDefFailure(['throw'], 'E1143:')
|
|
v9.CheckDefFailure(['throw xxx'], 'E1001:')
|
|
enddef
|
|
|
|
def Try_catch_skipped()
|
|
var l = []
|
|
try
|
|
finally
|
|
endtry
|
|
|
|
if 1
|
|
else
|
|
try
|
|
endtry
|
|
endif
|
|
enddef
|
|
|
|
" The skipped try/endtry was updating the wrong instruction.
|
|
def Test_try_catch_skipped()
|
|
var instr = execute('disassemble Try_catch_skipped')
|
|
assert_match("NEWLIST size 0\n", instr)
|
|
enddef
|
|
|
|
def Test_throw_line_number()
|
|
def Func()
|
|
eval 1 + 1
|
|
eval 2 + 2
|
|
throw 'exception'
|
|
enddef
|
|
try
|
|
Func()
|
|
catch /exception/
|
|
assert_match('line 3', v:throwpoint)
|
|
endtry
|
|
enddef
|
|
|
|
|
|
def Test_throw_vimscript()
|
|
# only checks line continuation
|
|
var lines =<< trim END
|
|
vim9script
|
|
try
|
|
throw 'one'
|
|
.. 'two'
|
|
catch
|
|
assert_equal('onetwo', v:exception)
|
|
endtry
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
|
|
lines =<< trim END
|
|
vim9script
|
|
@r = ''
|
|
def Func()
|
|
throw @r
|
|
enddef
|
|
var result = ''
|
|
try
|
|
Func()
|
|
catch /E1129:/
|
|
result = 'caught'
|
|
endtry
|
|
assert_equal('caught', result)
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
enddef
|
|
|
|
def Test_error_in_nested_function()
|
|
# an error in a nested :function aborts executing in the calling :def function
|
|
var lines =<< trim END
|
|
vim9script
|
|
def Func()
|
|
Error()
|
|
g:test_var = 1
|
|
enddef
|
|
func Error() abort
|
|
eval [][0]
|
|
endfunc
|
|
Func()
|
|
END
|
|
g:test_var = 0
|
|
v9.CheckScriptFailure(lines, 'E684:')
|
|
assert_equal(0, g:test_var)
|
|
enddef
|
|
|
|
def Test_abort_after_error()
|
|
var lines =<< trim END
|
|
vim9script
|
|
while true
|
|
echo notfound
|
|
endwhile
|
|
g:gotthere = true
|
|
END
|
|
g:gotthere = false
|
|
v9.CheckScriptFailure(lines, 'E121:')
|
|
assert_false(g:gotthere)
|
|
unlet g:gotthere
|
|
enddef
|
|
|
|
def Test_cexpr_vimscript()
|
|
# only checks line continuation
|
|
set errorformat=File\ %f\ line\ %l
|
|
var lines =<< trim END
|
|
vim9script
|
|
cexpr 'File'
|
|
.. ' someFile' ..
|
|
' line 19'
|
|
assert_equal(19, getqflist()[0].lnum)
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
|
|
lines =<< trim END
|
|
vim9script
|
|
def CexprFail()
|
|
au QuickfixCmdPre * echo g:doesnotexist
|
|
cexpr 'File otherFile line 99'
|
|
g:didContinue = 'yes'
|
|
enddef
|
|
CexprFail()
|
|
g:didContinue = 'also'
|
|
END
|
|
g:didContinue = 'no'
|
|
v9.CheckScriptFailure(lines, 'E121: Undefined variable: g:doesnotexist')
|
|
assert_equal('no', g:didContinue)
|
|
au! QuickfixCmdPre
|
|
|
|
lines =<< trim END
|
|
vim9script
|
|
def CexprFail()
|
|
cexpr g:aNumber
|
|
g:didContinue = 'yes'
|
|
enddef
|
|
CexprFail()
|
|
g:didContinue = 'also'
|
|
END
|
|
g:aNumber = 123
|
|
g:didContinue = 'no'
|
|
v9.CheckScriptFailure(lines, 'E777: String or List expected')
|
|
assert_equal('no', g:didContinue)
|
|
unlet g:didContinue
|
|
|
|
set errorformat&
|
|
enddef
|
|
|
|
def Test_statusline_syntax()
|
|
# legacy syntax is used for 'statusline'
|
|
var lines =<< trim END
|
|
vim9script
|
|
func g:Status()
|
|
return '%{"x" is# "x"}'
|
|
endfunc
|
|
set laststatus=2 statusline=%!Status()
|
|
redrawstatus
|
|
set laststatus statusline=
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
enddef
|
|
|
|
def Test_list_vimscript()
|
|
# checks line continuation and comments
|
|
var lines =<< trim END
|
|
vim9script
|
|
var mylist = [
|
|
'one',
|
|
# comment
|
|
'two', # empty line follows
|
|
|
|
'three',
|
|
]
|
|
assert_equal(['one', 'two', 'three'], mylist)
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
|
|
# check all lines from heredoc are kept
|
|
lines =<< trim END
|
|
# comment 1
|
|
two
|
|
# comment 3
|
|
|
|
five
|
|
# comment 6
|
|
END
|
|
assert_equal(['# comment 1', 'two', '# comment 3', '', 'five', '# comment 6'], lines)
|
|
|
|
lines =<< trim END
|
|
[{
|
|
a: 0}]->string()->assert_equal("[{'a': 0}]")
|
|
END
|
|
v9.CheckDefAndScriptSuccess(lines)
|
|
enddef
|
|
|
|
if has('channel')
|
|
let someJob = test_null_job()
|
|
|
|
def FuncWithError()
|
|
echomsg g:someJob
|
|
enddef
|
|
|
|
func Test_convert_emsg_to_exception()
|
|
try
|
|
call FuncWithError()
|
|
catch
|
|
call assert_match('Vim:E908:', v:exception)
|
|
endtry
|
|
endfunc
|
|
endif
|
|
|
|
def Test_vim9script_mix()
|
|
var lines =<< trim END
|
|
if has(g:feature)
|
|
" legacy script
|
|
let g:legacy = 1
|
|
finish
|
|
endif
|
|
vim9script
|
|
g:legacy = 0
|
|
END
|
|
g:feature = 'eval'
|
|
g:legacy = -1
|
|
v9.CheckScriptSuccess(lines)
|
|
assert_equal(1, g:legacy)
|
|
|
|
g:feature = 'noteval'
|
|
g:legacy = -1
|
|
v9.CheckScriptSuccess(lines)
|
|
assert_equal(0, g:legacy)
|
|
enddef
|
|
|
|
def Test_vim9script_fails()
|
|
v9.CheckScriptFailure(['scriptversion 2', 'vim9script'], 'E1039:')
|
|
v9.CheckScriptFailure(['vim9script', 'scriptversion 2'], 'E1040:')
|
|
|
|
v9.CheckScriptFailure(['vim9script', 'var str: string', 'str = 1234'], 'E1012:')
|
|
v9.CheckScriptFailure(['vim9script', 'const str = "asdf"', 'str = "xxx"'], 'E46:')
|
|
|
|
assert_fails('vim9script', 'E1038:')
|
|
v9.CheckDefFailure(['vim9script'], 'E1038:')
|
|
|
|
# no error when skipping
|
|
if has('nothing')
|
|
vim9script
|
|
endif
|
|
enddef
|
|
|
|
def Test_script_var_shadows_function()
|
|
var lines =<< trim END
|
|
vim9script
|
|
def Func(): number
|
|
return 123
|
|
enddef
|
|
var Func = 1
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1041:', 5)
|
|
enddef
|
|
|
|
def Test_function_shadows_script_var()
|
|
var lines =<< trim END
|
|
vim9script
|
|
var Func = 1
|
|
def Func(): number
|
|
return 123
|
|
enddef
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1041:', 3)
|
|
enddef
|
|
|
|
def Test_script_var_shadows_command()
|
|
var lines =<< trim END
|
|
var undo = 1
|
|
undo = 2
|
|
assert_equal(2, undo)
|
|
END
|
|
v9.CheckDefAndScriptSuccess(lines)
|
|
|
|
lines =<< trim END
|
|
var undo = 1
|
|
undo
|
|
END
|
|
v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)
|
|
enddef
|
|
|
|
def Test_vim9script_call_wrong_type()
|
|
var lines =<< trim END
|
|
vim9script
|
|
var Time = 'localtime'
|
|
Time()
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1085:')
|
|
enddef
|
|
|
|
def Test_vim9script_reload_delfunc()
|
|
var first_lines =<< trim END
|
|
vim9script
|
|
def FuncYes(): string
|
|
return 'yes'
|
|
enddef
|
|
END
|
|
var withno_lines =<< trim END
|
|
def FuncNo(): string
|
|
return 'no'
|
|
enddef
|
|
def g:DoCheck(no_exists: bool)
|
|
assert_equal('yes', FuncYes())
|
|
assert_equal('no', FuncNo())
|
|
enddef
|
|
END
|
|
var nono_lines =<< trim END
|
|
def g:DoCheck(no_exists: bool)
|
|
assert_equal('yes', FuncYes())
|
|
assert_fails('FuncNo()', 'E117:', '', 2, 'DoCheck')
|
|
enddef
|
|
END
|
|
|
|
# FuncNo() is defined
|
|
writefile(first_lines + withno_lines, 'Xreloaded.vim', 'D')
|
|
source Xreloaded.vim
|
|
g:DoCheck(true)
|
|
|
|
# FuncNo() is not redefined
|
|
writefile(first_lines + nono_lines, 'Xreloaded.vim')
|
|
source Xreloaded.vim
|
|
g:DoCheck(false)
|
|
|
|
# FuncNo() is back
|
|
writefile(first_lines + withno_lines, 'Xreloaded.vim')
|
|
source Xreloaded.vim
|
|
g:DoCheck(false)
|
|
enddef
|
|
|
|
def Test_vim9script_reload_delvar()
|
|
# write the script with a script-local variable
|
|
var lines =<< trim END
|
|
vim9script
|
|
var name = 'string'
|
|
END
|
|
writefile(lines, 'XreloadVar.vim', 'D')
|
|
source XreloadVar.vim
|
|
|
|
# now write the script using the same variable locally - works
|
|
lines =<< trim END
|
|
vim9script
|
|
def Func()
|
|
var name = 'string'
|
|
enddef
|
|
END
|
|
writefile(lines, 'XreloadVar.vim')
|
|
source XreloadVar.vim
|
|
enddef
|
|
|
|
def Test_func_redefine_error()
|
|
var lines = [
|
|
'vim9script',
|
|
'def Func()',
|
|
' eval [][0]',
|
|
'enddef',
|
|
'Func()',
|
|
]
|
|
writefile(lines, 'Xtestscript.vim', 'D')
|
|
|
|
for count in range(3)
|
|
try
|
|
source Xtestscript.vim
|
|
catch /E684/
|
|
# function name should contain <SNR> every time
|
|
assert_match('E684: List index out of range', v:exception)
|
|
assert_match('function <SNR>\d\+_Func, line 1', v:throwpoint)
|
|
endtry
|
|
endfor
|
|
enddef
|
|
|
|
def Test_func_redefine_fails()
|
|
var lines =<< trim END
|
|
vim9script
|
|
def Func()
|
|
echo 'one'
|
|
enddef
|
|
def Func()
|
|
echo 'two'
|
|
enddef
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1073:')
|
|
|
|
lines =<< trim END
|
|
vim9script
|
|
def Foo(): string
|
|
return 'foo'
|
|
enddef
|
|
def Func()
|
|
var Foo = {-> 'lambda'}
|
|
enddef
|
|
defcompile
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1073:')
|
|
enddef
|
|
|
|
def Test_lambda_split()
|
|
# this was using freed memory, because of the split expression
|
|
var lines =<< trim END
|
|
vim9script
|
|
try
|
|
0
|
|
0->(0
|
|
->a.0(
|
|
->u
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1050:')
|
|
enddef
|
|
|
|
def Test_fixed_size_list()
|
|
# will be allocated as one piece of memory, check that changes work
|
|
var l = [1, 2, 3, 4]
|
|
l->remove(0)
|
|
l->add(5)
|
|
l->insert(99, 1)
|
|
assert_equal([2, 99, 3, 4, 5], l)
|
|
enddef
|
|
|
|
def Test_no_insert_xit()
|
|
v9.CheckDefExecFailure(['a = 1'], 'E1100:')
|
|
v9.CheckDefExecFailure(['c = 1'], 'E1100:')
|
|
v9.CheckDefExecFailure(['i = 1'], 'E1100:')
|
|
v9.CheckDefExecFailure(['t = 1'], 'E1100:')
|
|
v9.CheckDefExecFailure(['x = 1'], 'E1100:')
|
|
|
|
v9.CheckScriptFailure(['vim9script', 'a = 1'], 'E488:')
|
|
v9.CheckScriptFailure(['vim9script', 'a'], 'E1100:')
|
|
v9.CheckScriptFailure(['vim9script', 'c = 1'], 'E488:')
|
|
v9.CheckScriptFailure(['vim9script', 'c'], 'E1100:')
|
|
v9.CheckScriptFailure(['vim9script', 'i = 1'], 'E488:')
|
|
v9.CheckScriptFailure(['vim9script', 'i'], 'E1100:')
|
|
v9.CheckScriptFailure(['vim9script', 'o = 1'], 'E1100:')
|
|
v9.CheckScriptFailure(['vim9script', 'o'], 'E1100:')
|
|
v9.CheckScriptFailure(['vim9script', 't'], 'E1100:')
|
|
v9.CheckScriptFailure(['vim9script', 't = 1'], 'E1100:')
|
|
v9.CheckScriptFailure(['vim9script', 'x = 1'], 'E1100:')
|
|
enddef
|
|
|
|
def s:IfElse(what: number): string
|
|
var res = ''
|
|
if what == 1
|
|
res = "one"
|
|
elseif what == 2
|
|
res = "two"
|
|
else
|
|
res = "three"
|
|
endif
|
|
return res
|
|
enddef
|
|
|
|
def Test_if_elseif_else()
|
|
assert_equal('one', IfElse(1))
|
|
assert_equal('two', IfElse(2))
|
|
assert_equal('three', IfElse(3))
|
|
enddef
|
|
|
|
def Test_if_elseif_else_fails()
|
|
v9.CheckDefFailure(['elseif true'], 'E582:')
|
|
v9.CheckDefFailure(['else'], 'E581:')
|
|
v9.CheckDefFailure(['endif'], 'E580:')
|
|
v9.CheckDefFailure(['if g:abool', 'elseif xxx'], 'E1001:')
|
|
v9.CheckDefFailure(['if true', 'echo 1'], 'E171:')
|
|
|
|
var lines =<< trim END
|
|
var s = ''
|
|
if s = ''
|
|
endif
|
|
END
|
|
v9.CheckDefFailure(lines, 'E488:')
|
|
|
|
lines =<< trim END
|
|
var s = ''
|
|
if s == ''
|
|
elseif s = ''
|
|
endif
|
|
END
|
|
v9.CheckDefFailure(lines, 'E488:')
|
|
|
|
lines =<< trim END
|
|
var cond = true
|
|
if cond
|
|
echo 'true'
|
|
elseif
|
|
echo 'false'
|
|
endif
|
|
END
|
|
v9.CheckDefAndScriptFailure(lines, ['E1143:', 'E15:'], 4)
|
|
enddef
|
|
|
|
def Test_if_else_func_using_var()
|
|
var lines =<< trim END
|
|
vim9script
|
|
|
|
const debug = true
|
|
if debug
|
|
var mode_chars = 'something'
|
|
def Bits2Ascii()
|
|
var x = mode_chars
|
|
g:where = 'in true'
|
|
enddef
|
|
else
|
|
def Bits2Ascii()
|
|
g:where = 'in false'
|
|
enddef
|
|
endif
|
|
|
|
Bits2Ascii()
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
assert_equal('in true', g:where)
|
|
unlet g:where
|
|
|
|
lines =<< trim END
|
|
vim9script
|
|
|
|
const debug = false
|
|
if debug
|
|
var mode_chars = 'something'
|
|
def Bits2Ascii()
|
|
g:where = 'in true'
|
|
enddef
|
|
else
|
|
def Bits2Ascii()
|
|
var x = mode_chars
|
|
g:where = 'in false'
|
|
enddef
|
|
endif
|
|
|
|
Bits2Ascii()
|
|
END
|
|
v9.CheckScriptFailure(lines, 'E1001: Variable not found: mode_chars')
|
|
enddef
|
|
|
|
let g:bool_true = v:true
|
|
let g:bool_false = v:false
|
|
|
|
def Test_if_const_expr()
|
|
var res = false
|
|
if true ? true : false
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
g:glob = 2
|
|
if false
|
|
execute('g:glob = 3')
|
|
endif
|
|
assert_equal(2, g:glob)
|
|
if true
|
|
execute('g:glob = 3')
|
|
endif
|
|
assert_equal(3, g:glob)
|
|
|
|
res = false
|
|
if g:bool_true ? true : false
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
res = false
|
|
if true ? g:bool_true : false
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
res = false
|
|
if true ? true : g:bool_false
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
res = false
|
|
if true ? false : true
|
|
res = true
|
|
endif
|
|
assert_equal(false, res)
|
|
|
|
res = false
|
|
if false ? false : true
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
res = false
|
|
if false ? true : false
|
|
res = true
|
|
endif
|
|
assert_equal(false, res)
|
|
|
|
res = false
|
|
if has('xyz') ? true : false
|
|
res = true
|
|
endif
|
|
assert_equal(false, res)
|
|
|
|
res = false
|
|
if true && true
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
res = false
|
|
if true && false
|
|
res = true
|
|
endif
|
|
assert_equal(false, res)
|
|
|
|
res = false
|
|
if g:bool_true && false
|
|
res = true
|
|
endif
|
|
assert_equal(false, res)
|
|
|
|
res = false
|
|
if true && g:bool_false
|
|
res = true
|
|
endif
|
|
assert_equal(false, res)
|
|
|
|
res = false
|
|
if false && false
|
|
res = true
|
|
endif
|
|
assert_equal(false, res)
|
|
|
|
res = false
|
|
if true || false
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
res = false
|
|
if g:bool_true || false
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
res = false
|
|
if true || g:bool_false
|
|
res = true
|
|
endif
|
|
assert_equal(true, res)
|
|
|
|
res = false
|
|
if false || false
|
|
res = true
|
|
endif
|
|
assert_equal(false, res)
|
|
|
|
# with constant "false" expression may be invalid so long as the syntax is OK
|
|
if false | eval 1 + 2 | endif
|
|
if false | eval burp + 234 | endif
|
|
if false | echo burp 234 'asd' | endif
|
|
if false
|
|
burp
|
|
endif
|
|
|
|
if 0
|
|
if 1
|
|
echo nothing
|
|
elseif 1
|
|
echo still nothing
|
|
endif
|
|
endif
|
|
|
|
# expression with line breaks skipped
|
|
if false
|
|
('aaa'
|
|
.. 'bbb'
|
|
.. 'ccc'
|
|
)->setline(1)
|
|
endif
|
|
enddef
|
|
|
|
def Test_if_const_expr_fails()
|
|
v9.CheckDefFailure(['if "aaa" == "bbb'], 'E114:')
|
|
v9.CheckDefFailure(["if 'aaa' == 'bbb"], 'E115:')
|
|
v9.CheckDefFailure(["if has('aaa'"], 'E110:')
|
|
v9.CheckDefFailure(["if has('aaa') ? true false"], 'E109:')
|
|
enddef
|
|
|
|
def s:RunNested(i: number): number
|
|
var x: number = 0
|
|
if i % 2
|
|
if 1
|
|
# comment
|
|
else
|
|
# comment
|
|
endif
|
|
x += 1
|
|
else
|
|
x += 1000
|
|
endif
|
|
return x
|
|
enddef
|
|
|
|
def Test_nested_if()
|
|
assert_equal(1, RunNested(1))
|
|
assert_equal(1000, RunNested(2))
|
|
enddef
|
|
|
|
def Test_execute_cmd()
|
|
# missing argument is ignored
|
|
execute
|
|
execute # comment
|
|
|
|
new
|
|
setline(1, 'default')
|
|
execute 'setline(1, "execute-string")'
|
|
assert_equal('execute-string', getline(1))
|
|
|
|
execute "setline(1, 'execute-string')"
|
|
assert_equal('execute-string', getline(1))
|
|
|
|
var cmd1 = 'setline(1,'
|
|
var cmd2 = '"execute-var")'
|
|
execute cmd1 cmd2 # comment
|
|
assert_equal('execute-var', getline(1))
|
|
|
|
execute cmd1 cmd2 '|setline(1, "execute-var-string")'
|
|
assert_equal('execute-var-string', getline(1))
|
|
|
|
var cmd_first = 'call '
|
|
var cmd_last = 'setline(1, "execute-var-var")'
|
|
execute cmd_first .. cmd_last
|
|
assert_equal('execute-var-var', getline(1))
|
|
bwipe!
|
|
|
|
var n = true
|
|
execute 'echomsg' (n ? '"true"' : '"no"')
|
|
assert_match('^true$', g:Screenline(&lines))
|
|
|
|
echomsg [1, 2, 3] {a: 1, b: 2}
|
|
assert_match('^\[1, 2, 3\] {''a'': 1, ''b'': 2}$', g:Screenline(&lines))
|
|
|
|
v9.CheckDefFailure(['execute xxx'], 'E1001:', 1)
|
|
v9.CheckDefExecFailure(['execute "tabnext " .. 8'], 'E475:', 1)
|
|
v9.CheckDefFailure(['execute "cmd"# comment'], 'E488:', 1)
|
|
if has('channel')
|
|
v9.CheckDefExecFailure(['execute test_null_channel()'], 'E908:', 1)
|
|
endif
|
|
enddef
|
|
|
|
def Test_execute_cmd_vimscript()
|
|
# only checks line continuation
|
|
var lines =<< trim END
|
|
vim9script
|
|
execute 'g:someVar'
|
|
.. ' = ' ..
|
|
'28'
|
|
assert_equal(28, g:someVar)
|
|
unlet g:someVar
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
enddef
|
|
|
|
def Test_execute_finish()
|
|
# the empty lines are relevant here
|
|
var lines =<< trim END
|
|
vim9script
|
|
|
|
var vname = "g:hello"
|
|
|
|
if exists(vname) | finish | endif | execute vname '= "world"'
|
|
|
|
assert_equal('world', g:hello)
|
|
|
|
if exists(vname) | finish | endif | execute vname '= "world"'
|
|
|
|
assert_report('should not be reached')
|
|
END
|
|
v9.CheckScriptSuccess(lines)
|
|
enddef
|
|
|
|
def Test_echo_cmd()
|
|
echo 'some' # comment
|
|
echon 'thing'
|
|
assert_match('^something$', g:Screenline(&lines))
|
|
|
|
echo "some" # comment
|
|
echon "thing"
|
|
assert_match('^something$', g:Screenline(&lines))
|
|
|
|
var str1 = 'some'
|
|
var str2 = 'more'
|
|
echo str1 str2
|
|
assert_match('^some more$', g:Screenline(&lines))
|
|
|
|
echo "one\ntwo"
|
|
assert_match('^one$', g:Screenline(&lines - 1))
|
|
assert_match('^two$', g:Screenline(&lines))
|
|
|
|
v9.CheckDefFailure(['echo "xxx"# comment'], 'E488:')
|
|
|
|
# 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()
|
|
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)
|
|
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([
|
|
'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
|
|
|
|
" 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
|