luaeval_spec.lua (20620B)
1 -- Test suite for testing luaeval() function 2 local t = require('test.testutil') 3 local n = require('test.functional.testnvim')() 4 local Screen = require('test.functional.ui.screen') 5 6 local pcall_err = t.pcall_err 7 local exc_exec = n.exc_exec 8 local remove_trace = t.remove_trace 9 local exec_lua = n.exec_lua 10 local command = n.command 11 local api = n.api 12 local fn = n.fn 13 local clear = n.clear 14 local eval = n.eval 15 local feed = n.feed 16 local assert_alive = n.assert_alive 17 local NIL = vim.NIL 18 local eq = t.eq 19 local matches = t.matches 20 21 before_each(clear) 22 23 local function startswith(expected, actual) 24 eq(expected, actual:sub(1, #expected)) 25 end 26 27 describe('luaeval()', function() 28 local nested_by_level = {} 29 local nested = {} 30 local nested_s = '{}' 31 for i = 1, 100 do 32 if i % 2 == 0 then 33 nested = { nested } 34 nested_s = '{' .. nested_s .. '}' 35 else 36 nested = { nested = nested } 37 nested_s = '{nested=' .. nested_s .. '}' 38 end 39 nested_by_level[i] = { o = nested, s = nested_s } 40 end 41 42 describe('second argument', function() 43 it('is successfully received', function() 44 local q = { 45 t = true, 46 f = false, --[[n=NIL,]] 47 d = { l = { 'string', 42, 0.42 } }, 48 } 49 eq(q, fn.luaeval('_A', q)) 50 -- Not tested: nil, funcrefs, returned object identity: behaviour will 51 -- most likely change. 52 end) 53 end) 54 describe('lua values', function() 55 it('are successfully transformed', function() 56 eq( 57 { n = 1, f = 1.5, s = 'string', l = { 4, 2 } }, 58 fn.luaeval('{n=1, f=1.5, s="string", l={4, 2}}') 59 ) 60 -- Not tested: nil inside containers: behaviour will most likely change. 61 eq(NIL, fn.luaeval('nil')) 62 eq({ [''] = 1 }, fn.luaeval('{[""]=1}')) 63 end) 64 end) 65 describe('recursive lua values', function() 66 it('are successfully transformed', function() 67 command('lua rawset(_G, "d", {})') 68 command('lua rawset(d, "d", d)') 69 eq("\n{'d': {...@0}}", fn.execute('echo luaeval("d")')) 70 71 command('lua rawset(_G, "l", {})') 72 command('lua table.insert(l, l)') 73 eq('\n[[...@0]]', fn.execute('echo luaeval("l")')) 74 end) 75 end) 76 describe('strings with NULs', function() 77 it('are successfully converted to blobs', function() 78 command([[let s = luaeval('"\0"')]]) 79 eq('\000', api.nvim_get_var('s')) 80 end) 81 it('are successfully converted to special dictionaries in table keys', function() 82 command([[let d = luaeval('{["\0"]=1}')]]) 83 eq({ _TYPE = {}, _VAL = { { '\000', 1 } } }, api.nvim_get_var('d')) 84 eq(1, fn.eval('d._TYPE is v:msgpack_types.map')) 85 eq(eval('v:t_blob'), fn.eval('type(d._VAL[0][0])')) 86 end) 87 it('are successfully converted to blobs from a list', function() 88 command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]]) 89 eq({ 'abc', 'a\000b', 'c\000d', 'def' }, api.nvim_get_var('l')) 90 end) 91 end) 92 93 -- Not checked: funcrefs converted to NIL. To be altered to something more 94 -- meaningful later. 95 96 it('scalars', function() 97 -- Also test method call (->) syntax 98 eq(1, fn.luaeval('1')) 99 eq(0, eval('"1"->luaeval()->type()')) 100 101 eq(1.5, fn.luaeval('1.5')) 102 eq(5, eval('"1.5"->luaeval()->type()')) 103 104 eq('test', fn.luaeval('"test"')) 105 eq(1, eval('"\'test\'"->luaeval()->type()')) 106 107 eq('', fn.luaeval('""')) 108 eq('\000', fn.luaeval([['\0']])) 109 eq('\000\n\000', fn.luaeval([['\0\n\0']])) 110 eq(10, eval([[type(luaeval("'\\0\\n\\0'"))]])) 111 112 eq(true, fn.luaeval('true')) 113 eq(false, fn.luaeval('false')) 114 eq(NIL, fn.luaeval('nil')) 115 end) 116 117 it('containers', function() 118 eq({}, fn.luaeval('{}')) 119 eq(3, eval('type(luaeval("{}"))')) 120 121 eq({ test = 1, foo = 2 }, fn.luaeval('{test=1, foo=2}')) 122 eq(4, eval('type(luaeval("{test=1, foo=2}"))')) 123 124 eq({ 4, 2 }, fn.luaeval('{4, 2}')) 125 eq(3, eval('type(luaeval("{4, 2}"))')) 126 127 eq({ NIL, 20 }, fn.luaeval('{[2] = 20}')) 128 eq(3, eval('type(luaeval("{[2] = 20}"))')) 129 130 eq({ 10, NIL, 30 }, fn.luaeval('{[1] = 10, [3] = 30}')) 131 eq(3, eval('type(luaeval("{[1] = 10, [3] = 30}"))')) 132 133 local level = 30 134 eq(nested_by_level[level].o, fn.luaeval(nested_by_level[level].s)) 135 136 eq( 137 { _TYPE = {}, _VAL = { { '\000\n\000', '\000\n\000\000' } } }, 138 fn.luaeval([[{['\0\n\0']='\0\n\0\0'}]]) 139 ) 140 eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]])) 141 eq(eval('v:t_blob'), eval([[type(luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0])]])) 142 eq( 143 { nested = { { _TYPE = {}, _VAL = { { '\000\n\000', '\000\n\000\000' } } } } }, 144 fn.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]]) 145 ) 146 end) 147 148 it('passes scalars as argument', function() 149 eq(1, fn.luaeval('_A', 1)) 150 eq(1.5, fn.luaeval('_A', 1.5)) 151 eq('', fn.luaeval('_A', '')) 152 eq('test', fn.luaeval('_A', 'test')) 153 eq(NIL, fn.luaeval('_A', NIL)) 154 eq(true, fn.luaeval('_A', true)) 155 eq(false, fn.luaeval('_A', false)) 156 end) 157 158 it('passes containers as argument', function() 159 eq({}, fn.luaeval('_A', {})) 160 eq({ test = 1 }, fn.luaeval('_A', { test = 1 })) 161 eq({ 4, 2 }, fn.luaeval('_A', { 4, 2 })) 162 local level = 28 163 eq(nested_by_level[level].o, fn.luaeval('_A', nested_by_level[level].o)) 164 end) 165 166 local function sp(typ, val) 167 return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) 168 end 169 local function mapsp(...) 170 local val = '' 171 for i = 1, (select('#', ...) / 2) do 172 val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...), select(i * 2, ...)) 173 end 174 return sp('map', '[' .. val .. ']') 175 end 176 local function luaevalarg(argexpr, expr) 177 return eval((([=[ 178 [ 179 extend(g:, {'_ret': luaeval(%s, %s)})._ret, 180 type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE') 181 ? [ 182 get(keys(filter(copy(v:msgpack_types), 'v:val is g:_ret._TYPE')), 0, 183 g:_ret._TYPE), 184 get(g:_ret, '_VAL', g:_ret) 185 ] 186 : [0, g:_ret]][1] 187 ]=]):format(expr or '"_A"', argexpr):gsub('\n', ''))) 188 end 189 190 it('passes special dictionaries', function() 191 eq({ 0, '\000\n\000' }, luaevalarg(sp('string', '["\\n", "\\n"]'))) 192 eq({ 0, true }, luaevalarg(sp('boolean', 1))) 193 eq({ 0, false }, luaevalarg(sp('boolean', 0))) 194 eq({ 0, NIL }, luaevalarg(sp('nil', 0))) 195 eq({ 0, { [''] = '' } }, luaevalarg(mapsp(sp('string', '[""]'), '""'))) 196 end) 197 198 it('failure modes', function() 199 eq( 200 'Vim(call):E5100: Cannot convert given Lua table: table should contain either only integer keys or only string keys', 201 exc_exec('call luaeval("{1, foo=2}")') 202 ) 203 204 startswith('Vim(call):E5107: Lua: [string "luaeval()"]:', exc_exec('call luaeval("1, 2, 3")')) 205 startswith('Vim(call):E5108: Lua: [string "luaeval()"]:', exc_exec('call luaeval("(nil)()")')) 206 end) 207 208 it('handles sending lua functions to viml', function() 209 eq( 210 true, 211 exec_lua [[ 212 can_pass_lua_callback_to_vim_from_lua_result = nil 213 214 vim.fn.call(function() 215 can_pass_lua_callback_to_vim_from_lua_result = true 216 end, {}) 217 218 return can_pass_lua_callback_to_vim_from_lua_result 219 ]] 220 ) 221 end) 222 223 it('run functions even in timers', function() 224 eq( 225 true, 226 exec_lua [[ 227 can_pass_lua_callback_to_vim_from_lua_result = nil 228 229 vim.fn.timer_start(50, function() 230 can_pass_lua_callback_to_vim_from_lua_result = true 231 end) 232 233 vim.wait(1000, function() 234 return can_pass_lua_callback_to_vim_from_lua_result 235 end) 236 237 return can_pass_lua_callback_to_vim_from_lua_result 238 ]] 239 ) 240 end) 241 242 it('can run named functions more than once', function() 243 eq( 244 5, 245 exec_lua [[ 246 count_of_vals = 0 247 248 vim.fn.timer_start(5, function() 249 count_of_vals = count_of_vals + 1 250 end, {['repeat'] = 5}) 251 252 vim.fn.wait(1000, function() 253 return count_of_vals >= 5 254 end) 255 256 return count_of_vals 257 ]] 258 ) 259 end) 260 261 it('can handle clashing names', function() 262 eq( 263 1, 264 exec_lua [[ 265 local f_loc = function() return 1 end 266 267 local result = nil 268 vim.fn.timer_start(100, function() 269 result = f_loc() 270 end) 271 272 local f_loc = function() return 2 end 273 vim.wait(1000, function() return result ~= nil end) 274 275 return result 276 ]] 277 ) 278 end) 279 280 it('can handle functions with errors', function() 281 eq( 282 true, 283 exec_lua [[ 284 vim.fn.timer_start(10, function() 285 error("dead function") 286 end) 287 288 vim.wait(1000, function() return false end) 289 290 return true 291 ]] 292 ) 293 -- v:errmsg is set properly #35554 294 matches(': dead function\n', api.nvim_get_vvar('errmsg')) 295 end) 296 297 it('can pass functions around', function() 298 command [[ 299 function VimCanCallLuaCallbacks(Concat, Cb) 300 let message = a:Concat("Hello Vim", "I'm Lua") 301 call a:Cb(message) 302 endfunction 303 ]] 304 305 eq( 306 "Hello Vim I'm Lua", 307 exec_lua [[ 308 can_pass_lua_callback_to_vim_from_lua_result = "" 309 310 vim.fn.VimCanCallLuaCallbacks( 311 function(greeting, message) return greeting .. " " .. message end, 312 function(message) can_pass_lua_callback_to_vim_from_lua_result = message end 313 ) 314 315 return can_pass_lua_callback_to_vim_from_lua_result 316 ]] 317 ) 318 end) 319 320 it('handles funcrefs', function() 321 command [[ 322 function VimCanCallLuaCallbacks(Concat, Cb) 323 let message = a:Concat("Hello Vim", "I'm Lua") 324 call a:Cb(message) 325 endfunction 326 ]] 327 328 eq( 329 "Hello Vim I'm Lua", 330 exec_lua [[ 331 can_pass_lua_callback_to_vim_from_lua_result = "" 332 333 vim.funcref('VimCanCallLuaCallbacks')( 334 function(greeting, message) return greeting .. " " .. message end, 335 function(message) can_pass_lua_callback_to_vim_from_lua_result = message end 336 ) 337 338 return can_pass_lua_callback_to_vim_from_lua_result 339 ]] 340 ) 341 end) 342 343 it('works with metatables using __call', function() 344 eq( 345 1, 346 exec_lua [[ 347 local this_is_local_variable = false 348 local callable_table = setmetatable({x = 1}, { 349 __call = function(t, ...) 350 this_is_local_variable = t.x 351 end 352 }) 353 354 vim.fn.timer_start(5, callable_table) 355 356 vim.wait(1000, function() 357 return this_is_local_variable 358 end) 359 360 return this_is_local_variable 361 ]] 362 ) 363 end) 364 365 it('handles being called from a timer once.', function() 366 eq( 367 3, 368 exec_lua [[ 369 local this_is_local_variable = false 370 local callable_table = setmetatable({5, 4, 3, 2, 1}, { 371 __call = function(t, ...) this_is_local_variable = t[3] end 372 }) 373 374 vim.fn.timer_start(5, callable_table) 375 vim.wait(1000, function() 376 return this_is_local_variable 377 end) 378 379 return this_is_local_variable 380 ]] 381 ) 382 end) 383 384 it('calls functions once with __call metamethod', function() 385 eq( 386 true, 387 exec_lua [[ 388 local this_is_local_variable = false 389 local callable_table = setmetatable({a = true, b = false}, { 390 __call = function(t, ...) this_is_local_variable = t.a end 391 }) 392 393 assert(getmetatable(callable_table).__call) 394 vim.fn.call(callable_table, {}) 395 396 return this_is_local_variable 397 ]] 398 ) 399 end) 400 401 it('works with lists using __call', function() 402 eq( 403 3, 404 exec_lua [[ 405 local this_is_local_variable = false 406 local mt = { 407 __call = function(t, ...) 408 this_is_local_variable = t[3] 409 end 410 } 411 local callable_table = setmetatable({5, 4, 3, 2, 1}, mt) 412 413 -- Call it once... 414 vim.fn.timer_start(5, callable_table) 415 vim.wait(1000, function() 416 return this_is_local_variable 417 end) 418 419 assert(this_is_local_variable) 420 this_is_local_variable = false 421 422 vim.fn.timer_start(5, callable_table) 423 vim.wait(1000, function() 424 return this_is_local_variable 425 end) 426 427 return this_is_local_variable 428 ]] 429 ) 430 end) 431 432 it('fails with tables not using __call', function() 433 eq( 434 { false, 'Vim:E921: Invalid callback argument' }, 435 exec_lua [[ 436 local this_is_local_variable = false 437 local callable_table = setmetatable({x = 1}, {}) 438 439 return {pcall(function() vim.fn.timer_start(5, callable_table) end)} 440 ]] 441 ) 442 end) 443 444 it('converts containers with type_idx', function() 445 eq(5, eval('type(luaeval("{[vim.type_idx]=vim.types.float, [vim.val_idx]=0}"))')) 446 eq(4, eval([[type(luaeval('{[vim.type_idx]=vim.types.dictionary}'))]])) 447 eq(3, eval([[type(luaeval('{[vim.type_idx]=vim.types.array}'))]])) 448 449 eq({}, fn.luaeval('{[vim.type_idx]=vim.types.array}')) 450 451 -- Presence of type_idx makes Vim ignore some keys 452 eq( 453 { 42 }, 454 fn.luaeval('{[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}') 455 ) 456 eq( 457 { foo = 2 }, 458 fn.luaeval('{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}') 459 ) 460 eq(10, fn.luaeval('{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) 461 462 -- The following should not crash 463 eq({}, fn.luaeval('{[vim.type_idx]=vim.types.dictionary}')) 464 end) 465 466 it('converts self-containing containers', function() 467 api.nvim_set_var('l', {}) 468 eval('add(l, l)') 469 eq(true, eval('luaeval("_A == _A[1]", l)')) 470 eq(true, eval('luaeval("_A[1] == _A[1][1]", [l])')) 471 eq(true, eval('luaeval("_A.d == _A.d[1]", {"d": l})')) 472 eq(true, eval('luaeval("_A ~= _A[1]", [l])')) 473 474 api.nvim_set_var('d', { foo = 42 }) 475 eval('extend(d, {"d": d})') 476 eq(true, eval('luaeval("_A == _A.d", d)')) 477 eq(true, eval('luaeval("_A[1] == _A[1].d", [d])')) 478 eq(true, eval('luaeval("_A.d == _A.d.d", {"d": d})')) 479 eq(true, eval('luaeval("_A ~= _A.d", {"d": d})')) 480 end) 481 482 it('fails when doing incorrect things in lua', function() 483 -- Conversion errors 484 eq( 485 'Vim(call):E5108: Lua: [string "luaeval()"]:1: attempt to call field \'xxx_nonexistent_key_xxx\' (a nil value)', 486 remove_trace(exc_exec([[call luaeval("vim.xxx_nonexistent_key_xxx()")]])) 487 ) 488 eq( 489 'Vim(call):E5108: Lua: [string "luaeval()"]:1: ERROR', 490 remove_trace(exc_exec([[call luaeval("error('ERROR')")]])) 491 ) 492 eq('Vim(call):E5108: Lua: [NULL]', remove_trace(exc_exec([[call luaeval("error(nil)")]]))) 493 end) 494 495 it('does not leak memory when called with too long line', function() 496 local s = ('x'):rep(65536) 497 eq( 498 'Vim(call):E5107: Lua: [string "luaeval()"]:1: unexpected symbol near \')\'', 499 exc_exec([[call luaeval("(']] .. s .. [[' + )")]]) 500 ) 501 eq(s, fn.luaeval('"' .. s .. '"')) 502 end) 503 end) 504 505 describe('v:lua', function() 506 before_each(function() 507 exec_lua([[ 508 function _G.foo(a,b,n) 509 _G.val = n 510 return a+b 511 end 512 mymod = {} 513 function mymod.noisy(name) 514 vim.api.nvim_set_current_line("hey "..name) 515 end 516 function mymod.crashy() 517 nonexistent() 518 end 519 function mymod.whatis(value) 520 return type(value) .. ": " .. tostring(value) 521 end 522 function mymod.omni(findstart, base) 523 if findstart == 1 then 524 return 5 525 else 526 if base == 'st' then 527 return {'stuff', 'steam', 'strange things'} 528 end 529 end 530 end 531 ]]) 532 end) 533 534 it('in expressions', function() 535 eq(7, eval('v:lua.foo(3,4,v:null)')) 536 eq(true, exec_lua([[return _G.val == vim.NIL]])) 537 eq(NIL, eval('v:lua.mymod.noisy("eval")')) 538 eq('hey eval', api.nvim_get_current_line()) 539 eq('string: abc', eval('v:lua.mymod.whatis(0z616263)')) 540 eq('string: ', eval('v:lua.mymod.whatis(v:_null_blob)')) 541 542 eq( 543 'Vim:E5108: Lua: [string "<nvim>"]:0: attempt to call global \'nonexistent\' (a nil value)', 544 pcall_err(eval, 'v:lua.mymod.crashy()') 545 ) 546 end) 547 548 it('when called as a method', function() 549 eq(123, eval('110->v:lua.foo(13)')) 550 eq(true, exec_lua([[return _G.val == nil]])) 551 552 eq(321, eval('300->v:lua.foo(21, "boop")')) 553 eq('boop', exec_lua([[return _G.val]])) 554 555 eq(NIL, eval('"there"->v:lua.mymod.noisy()')) 556 eq('hey there', api.nvim_get_current_line()) 557 eq({ 5, 10, 15, 20 }, eval('[[1], [2, 3], [4]]->v:lua.vim.tbl_flatten()->map({_, v -> v * 5})')) 558 559 eq( 560 'Vim:E5108: Lua: [string "<nvim>"]:0: attempt to call global \'nonexistent\' (a nil value)', 561 pcall_err(eval, '"huh?"->v:lua.mymod.crashy()') 562 ) 563 end) 564 565 it('in :call', function() 566 command(":call v:lua.mymod.noisy('command')") 567 eq('hey command', api.nvim_get_current_line()) 568 eq( 569 'Vim(call):E5108: Lua: [string "<nvim>"]:0: attempt to call global \'nonexistent\' (a nil value)', 570 pcall_err(command, 'call v:lua.mymod.crashy()') 571 ) 572 end) 573 574 it('in func options', function() 575 local screen = Screen.new(60, 8) 576 api.nvim_set_option_value('omnifunc', 'v:lua.mymod.omni', {}) 577 feed('isome st<c-x><c-o>') 578 screen:expect { 579 grid = [[ 580 some stuff^ | 581 {1:~ }{12: stuff }{1: }| 582 {1:~ }{4: steam }{1: }| 583 {1:~ }{4: strange things }{1: }| 584 {1:~ }|*3 585 {5:-- Omni completion (^O^N^P) }{6:match 1 of 3} | 586 ]], 587 } 588 api.nvim_set_option_value('operatorfunc', 'v:lua.mymod.noisy', {}) 589 feed('<Esc>g@g@') 590 eq('hey line', api.nvim_get_current_line()) 591 end) 592 593 it('supports packages', function() 594 command('set pp+=test/functional/fixtures') 595 eq('\tbadval', eval("v:lua.require'leftpad'('badval')")) 596 eq(9003, eval("v:lua.require'bar'.doit()")) 597 eq(9004, eval("v:lua.require'baz-quux'.doit()")) 598 eq(9003, eval("1 ? v:lua.require'bar'.doit() : v:lua.require'baz-quux'.doit()")) 599 eq(9004, eval("0 ? v:lua.require'bar'.doit() : v:lua.require'baz-quux'.doit()")) 600 end) 601 602 it('fails for invalid usage', function() 603 eq( 604 [[Vim(let):E15: Invalid expression: "v:lua.func"]], 605 pcall_err(command, 'let g:Func = v:lua.func') 606 ) 607 eq([[Vim(let):E15: Invalid expression: "v:lua"]], pcall_err(command, 'let g:Func = v:lua')) 608 eq( 609 [[Vim(let):E15: Invalid expression: "v:['lua']"]], 610 pcall_err(command, "let g:Func = v:['lua']") 611 ) 612 613 eq([[Vim:E15: Invalid expression: "v:['lua'].foo()"]], pcall_err(eval, "v:['lua'].foo()")) 614 eq( 615 "Vim(call):E461: Illegal variable name: v:['lua']", 616 pcall_err(command, "call v:['lua'].baar()") 617 ) 618 eq('Vim:E1085: Not a callable type: v:lua', pcall_err(eval, 'v:lua()')) 619 620 eq( 621 'Vim(let):E46: Cannot change read-only variable "v:[\'lua\']"', 622 pcall_err(command, "let v:['lua'] = 'xx'") 623 ) 624 eq( 625 'Vim(let):E46: Cannot change read-only variable "v:lua"', 626 pcall_err(command, "let v:lua = 'xx'") 627 ) 628 629 eq('Vim:E107: Missing parentheses: v:lua.func', pcall_err(eval, "'bad'->v:lua.func")) 630 eq( 631 'Vim:E274: No white space allowed before parenthesis', 632 pcall_err(eval, "'bad'->v:lua.func ()") 633 ) 634 eq('Vim:E107: Missing parentheses: v:lua', pcall_err(eval, "'bad'->v:lua")) 635 eq('Vim:E1085: Not a callable type: v:lua', pcall_err(eval, "'bad'->v:lua()")) 636 eq([[Vim:E15: Invalid expression: "v:lua.()"]], pcall_err(eval, "'bad'->v:lua.()")) 637 638 eq('Vim:E1085: Not a callable type: v:lua', pcall_err(eval, 'v:lua()')) 639 eq([[Vim:E15: Invalid expression: "v:lua.()"]], pcall_err(eval, 'v:lua.()')) 640 end) 641 642 describe('invalid usage in fold text', function() 643 before_each(function() 644 feed('ifoo<CR>bar<Esc>') 645 command('1,2fold') 646 end) 647 648 it('with missing function name when used as simple function', function() 649 api.nvim_set_option_value('debug', 'throw', {}) 650 eq( 651 [[Vim(eval):E15: Invalid expression: "v:lua.()"]], 652 pcall_err(command, 'set foldtext=v:lua.() | eval foldtextresult(1)') 653 ) 654 end) 655 656 it('with missing function name when used in expression', function() 657 api.nvim_set_option_value('debug', 'throw', {}) 658 eq( 659 [[Vim(eval):E15: Invalid expression: "+v:lua.()"]], 660 pcall_err(command, 'set foldtext=+v:lua.() | eval foldtextresult(1)') 661 ) 662 end) 663 664 it('with non-existent function when used as simple function', function() 665 command('set foldtext=v:lua.NoSuchFunc() | eval foldtextresult(1)') 666 assert_alive() 667 end) 668 669 it('with non-existent function when used in expression', function() 670 command('set foldtext=+v:lua.NoSuchFunc() | eval foldtextresult(1)') 671 assert_alive() 672 end) 673 end) 674 end)