vim_spec.lua (95861B)
1 -- Test suite for testing interactions with API bindings 2 local t = require('test.testutil') 3 local n = require('test.functional.testnvim')() 4 local Screen = require('test.functional.ui.screen') 5 6 local nvim_prog = n.nvim_prog 7 local fn = n.fn 8 local api = n.api 9 local command = n.command 10 local dedent = t.dedent 11 local insert = n.insert 12 local clear = n.clear 13 local eq = t.eq 14 local ok = t.ok 15 local pesc = vim.pesc 16 local eval = n.eval 17 local feed = n.feed 18 local pcall_err = t.pcall_err 19 local exec_lua = n.exec_lua 20 local matches = t.matches 21 local exec = n.exec 22 local NIL = vim.NIL 23 local retry = t.retry 24 local next_msg = n.next_msg 25 local remove_trace = t.remove_trace 26 local poke_eventloop = n.poke_eventloop 27 local assert_alive = n.assert_alive 28 local expect = n.expect 29 30 describe('lua stdlib', function() 31 before_each(clear) 32 -- İ: `tolower("İ")` is `i` which has length 1 while `İ` itself has 33 -- length 2 (in bytes). 34 -- Ⱥ: `tolower("Ⱥ")` is `ⱥ` which has length 2 while `Ⱥ` itself has 35 -- length 3 (in bytes). 36 -- 37 -- Note: 'i' !=? 'İ' and 'ⱥ' !=? 'Ⱥ' on some systems. 38 -- Note: Built-in Nvim comparison (on systems lacking `strcasecmp`) works 39 -- only on ASCII characters. 40 it('vim.stricmp', function() 41 eq(0, fn.luaeval('vim.stricmp("a", "A")')) 42 eq(0, fn.luaeval('vim.stricmp("A", "a")')) 43 eq(0, fn.luaeval('vim.stricmp("a", "a")')) 44 eq(0, fn.luaeval('vim.stricmp("A", "A")')) 45 46 eq(0, fn.luaeval('vim.stricmp("", "")')) 47 eq(0, fn.luaeval('vim.stricmp("\\0", "\\0")')) 48 eq(0, fn.luaeval('vim.stricmp("\\0\\0", "\\0\\0")')) 49 eq(0, fn.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0\\0")')) 50 eq(0, fn.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0a")')) 51 eq(0, fn.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0A")')) 52 eq(0, fn.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0a")')) 53 54 eq(0, fn.luaeval('vim.stricmp("a\\0", "A\\0")')) 55 eq(0, fn.luaeval('vim.stricmp("A\\0", "a\\0")')) 56 eq(0, fn.luaeval('vim.stricmp("a\\0", "a\\0")')) 57 eq(0, fn.luaeval('vim.stricmp("A\\0", "A\\0")')) 58 59 eq(0, fn.luaeval('vim.stricmp("\\0a", "\\0A")')) 60 eq(0, fn.luaeval('vim.stricmp("\\0A", "\\0a")')) 61 eq(0, fn.luaeval('vim.stricmp("\\0a", "\\0a")')) 62 eq(0, fn.luaeval('vim.stricmp("\\0A", "\\0A")')) 63 64 eq(0, fn.luaeval('vim.stricmp("\\0a\\0", "\\0A\\0")')) 65 eq(0, fn.luaeval('vim.stricmp("\\0A\\0", "\\0a\\0")')) 66 eq(0, fn.luaeval('vim.stricmp("\\0a\\0", "\\0a\\0")')) 67 eq(0, fn.luaeval('vim.stricmp("\\0A\\0", "\\0A\\0")')) 68 69 eq(-1, fn.luaeval('vim.stricmp("a", "B")')) 70 eq(-1, fn.luaeval('vim.stricmp("A", "b")')) 71 eq(-1, fn.luaeval('vim.stricmp("a", "b")')) 72 eq(-1, fn.luaeval('vim.stricmp("A", "B")')) 73 74 eq(-1, fn.luaeval('vim.stricmp("", "\\0")')) 75 eq(-1, fn.luaeval('vim.stricmp("\\0", "\\0\\0")')) 76 eq(-1, fn.luaeval('vim.stricmp("\\0\\0", "\\0\\0\\0")')) 77 eq(-1, fn.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0b")')) 78 eq(-1, fn.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0B")')) 79 eq(-1, fn.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0b")')) 80 81 eq(-1, fn.luaeval('vim.stricmp("a\\0", "B\\0")')) 82 eq(-1, fn.luaeval('vim.stricmp("A\\0", "b\\0")')) 83 eq(-1, fn.luaeval('vim.stricmp("a\\0", "b\\0")')) 84 eq(-1, fn.luaeval('vim.stricmp("A\\0", "B\\0")')) 85 86 eq(-1, fn.luaeval('vim.stricmp("\\0a", "\\0B")')) 87 eq(-1, fn.luaeval('vim.stricmp("\\0A", "\\0b")')) 88 eq(-1, fn.luaeval('vim.stricmp("\\0a", "\\0b")')) 89 eq(-1, fn.luaeval('vim.stricmp("\\0A", "\\0B")')) 90 91 eq(-1, fn.luaeval('vim.stricmp("\\0a\\0", "\\0B\\0")')) 92 eq(-1, fn.luaeval('vim.stricmp("\\0A\\0", "\\0b\\0")')) 93 eq(-1, fn.luaeval('vim.stricmp("\\0a\\0", "\\0b\\0")')) 94 eq(-1, fn.luaeval('vim.stricmp("\\0A\\0", "\\0B\\0")')) 95 96 eq(1, fn.luaeval('vim.stricmp("c", "B")')) 97 eq(1, fn.luaeval('vim.stricmp("C", "b")')) 98 eq(1, fn.luaeval('vim.stricmp("c", "b")')) 99 eq(1, fn.luaeval('vim.stricmp("C", "B")')) 100 101 eq(1, fn.luaeval('vim.stricmp("\\0", "")')) 102 eq(1, fn.luaeval('vim.stricmp("\\0\\0", "\\0")')) 103 eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0")')) 104 eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0\\0", "\\0\\0\\0")')) 105 eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0C", "\\0\\0\\0b")')) 106 eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0B")')) 107 eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0b")')) 108 109 eq(1, fn.luaeval('vim.stricmp("c\\0", "B\\0")')) 110 eq(1, fn.luaeval('vim.stricmp("C\\0", "b\\0")')) 111 eq(1, fn.luaeval('vim.stricmp("c\\0", "b\\0")')) 112 eq(1, fn.luaeval('vim.stricmp("C\\0", "B\\0")')) 113 114 eq(1, fn.luaeval('vim.stricmp("c\\0", "B")')) 115 eq(1, fn.luaeval('vim.stricmp("C\\0", "b")')) 116 eq(1, fn.luaeval('vim.stricmp("c\\0", "b")')) 117 eq(1, fn.luaeval('vim.stricmp("C\\0", "B")')) 118 119 eq(1, fn.luaeval('vim.stricmp("\\0c", "\\0B")')) 120 eq(1, fn.luaeval('vim.stricmp("\\0C", "\\0b")')) 121 eq(1, fn.luaeval('vim.stricmp("\\0c", "\\0b")')) 122 eq(1, fn.luaeval('vim.stricmp("\\0C", "\\0B")')) 123 124 eq(1, fn.luaeval('vim.stricmp("\\0c\\0", "\\0B\\0")')) 125 eq(1, fn.luaeval('vim.stricmp("\\0C\\0", "\\0b\\0")')) 126 eq(1, fn.luaeval('vim.stricmp("\\0c\\0", "\\0b\\0")')) 127 eq(1, fn.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")')) 128 end) 129 130 --- @param prerel string | nil 131 local function test_vim_deprecate(prerel) 132 -- vim.deprecate(name, alternative, version, plugin, backtrace) 133 -- See MAINTAIN.md for the soft/hard deprecation policy 134 135 describe(('vim.deprecate prerel=%s,'):format(prerel or 'nil'), function() 136 local curver --- @type {major:number, minor:number} 137 138 before_each(function() 139 curver = exec_lua('return vim.version()') 140 end) 141 142 it('plugin=nil, same message skipped', function() 143 -- "0.10" or "0.10-dev+xxx" 144 local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '') 145 eq( 146 ([[foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]]):format( 147 curstr 148 ), 149 exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr) 150 ) 151 -- Same message as above; skipped this time. 152 eq(vim.NIL, exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr)) 153 end) 154 155 it('plugin=nil, no error if soft-deprecated', function() 156 eq(vim.NIL, exec_lua [[return vim.deprecate('old1', 'new1', '0.99.0')]]) 157 -- Major version > current Nvim major is always "soft-deprecated". 158 -- XXX: This is also a reminder to update the hardcoded `nvim_major`, when Nvim reaches 1.0. 159 eq(vim.NIL, exec_lua [[return vim.deprecate('old2', 'new2', '1.0.0')]]) 160 end) 161 162 it('plugin=nil, show error if hard-deprecated', function() 163 -- "0.10" or "0.11" 164 local nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1)) 165 166 local was_removed = prerel and 'was removed' or 'will be removed' 167 eq( 168 dedent( 169 [[ 170 foo.hard_dep() is deprecated. Run ":checkhealth vim.deprecated" for more information]] 171 ):format(was_removed, nextver), 172 exec_lua('return vim.deprecate(...)', 'foo.hard_dep()', 'vim.new_api()', nextver) 173 ) 174 end) 175 176 it('plugin specified', function() 177 -- When `plugin` is specified, don't show ":help deprecated". #22235 178 eq( 179 dedent [[ 180 foo.bar() is deprecated, use zub.wooo{ok=yay} instead. 181 Feature will be removed in my-plugin.nvim 0.3.0]], 182 exec_lua( 183 'return vim.deprecate(...)', 184 'foo.bar()', 185 'zub.wooo{ok=yay}', 186 '0.3.0', 187 'my-plugin.nvim', 188 false 189 ) 190 ) 191 192 -- plugins: no soft deprecation period 193 eq( 194 dedent [[ 195 foo.bar() is deprecated, use zub.wooo{ok=yay} instead. 196 Feature will be removed in my-plugin.nvim 0.11.0]], 197 exec_lua( 198 'return vim.deprecate(...)', 199 'foo.bar()', 200 'zub.wooo{ok=yay}', 201 '0.11.0', 202 'my-plugin.nvim', 203 false 204 ) 205 ) 206 end) 207 end) 208 end 209 210 test_vim_deprecate() 211 test_vim_deprecate('-dev+g0000000') 212 213 it('vim.startswith', function() 214 eq(true, fn.luaeval('vim.startswith("123", "1")')) 215 eq(true, fn.luaeval('vim.startswith("123", "")')) 216 eq(true, fn.luaeval('vim.startswith("123", "123")')) 217 eq(true, fn.luaeval('vim.startswith("", "")')) 218 219 eq(false, fn.luaeval('vim.startswith("123", " ")')) 220 eq(false, fn.luaeval('vim.startswith("123", "2")')) 221 eq(false, fn.luaeval('vim.startswith("123", "1234")')) 222 223 matches( 224 'prefix: expected string, got nil', 225 pcall_err(exec_lua, 'return vim.startswith("123", nil)') 226 ) 227 matches('s: expected string, got nil', pcall_err(exec_lua, 'return vim.startswith(nil, "123")')) 228 end) 229 230 it('vim.endswith', function() 231 eq(true, fn.luaeval('vim.endswith("123", "3")')) 232 eq(true, fn.luaeval('vim.endswith("123", "")')) 233 eq(true, fn.luaeval('vim.endswith("123", "123")')) 234 eq(true, fn.luaeval('vim.endswith("", "")')) 235 236 eq(false, fn.luaeval('vim.endswith("123", " ")')) 237 eq(false, fn.luaeval('vim.endswith("123", "2")')) 238 eq(false, fn.luaeval('vim.endswith("123", "1234")')) 239 240 matches( 241 'suffix: expected string, got nil', 242 pcall_err(exec_lua, 'return vim.endswith("123", nil)') 243 ) 244 matches('s: expected string, got nil', pcall_err(exec_lua, 'return vim.endswith(nil, "123")')) 245 end) 246 247 it('vim.str_utfindex/str_byteindex', function() 248 exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ\000ъ"]]) 249 local indices32 = { 250 [0] = 0, 251 1, 252 2, 253 3, 254 5, 255 7, 256 9, 257 10, 258 12, 259 13, 260 16, 261 19, 262 20, 263 23, 264 24, 265 28, 266 29, 267 33, 268 34, 269 35, 270 37, 271 38, 272 40, 273 42, 274 44, 275 46, 276 48, 277 49, 278 51, 279 } 280 local indices16 = { 281 [0] = 0, 282 1, 283 2, 284 3, 285 5, 286 7, 287 9, 288 10, 289 12, 290 13, 291 16, 292 19, 293 20, 294 23, 295 24, 296 28, 297 28, 298 29, 299 33, 300 33, 301 34, 302 35, 303 37, 304 38, 305 40, 306 42, 307 44, 308 46, 309 48, 310 49, 311 51, 312 } 313 local indices8 = { 314 [0] = 0, 315 1, 316 2, 317 3, 318 4, 319 5, 320 6, 321 7, 322 8, 323 9, 324 10, 325 11, 326 12, 327 13, 328 14, 329 15, 330 16, 331 17, 332 18, 333 19, 334 20, 335 21, 336 22, 337 23, 338 24, 339 25, 340 26, 341 27, 342 28, 343 29, 344 30, 345 31, 346 32, 347 33, 348 34, 349 35, 350 36, 351 37, 352 38, 353 39, 354 40, 355 41, 356 42, 357 43, 358 44, 359 45, 360 46, 361 47, 362 48, 363 49, 364 50, 365 51, 366 } 367 for i, k in pairs(indices32) do 368 eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ...)', i), i) 369 eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ..., false)', i), i) 370 eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-32", ...)', i), i) 371 end 372 for i, k in pairs(indices16) do 373 eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ..., true)', i), i) 374 eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-16", ...)', i), i) 375 end 376 for i, k in pairs(indices8) do 377 eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-8", ...)', i), i) 378 end 379 matches( 380 'index out of range', 381 pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, ...)', #indices32 + 1) 382 ) 383 matches( 384 'index out of range', 385 pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, ..., true)', #indices16 + 1) 386 ) 387 matches( 388 'index out of range', 389 pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, "utf-16", ...)', #indices16 + 1) 390 ) 391 matches( 392 'index out of range', 393 pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, "utf-32", ...)', #indices32 + 1) 394 ) 395 matches( 396 'invalid encoding', 397 pcall_err(exec_lua, 'return vim.str_byteindex("hello", "madeupencoding", 1)') 398 ) 399 eq( 400 indices32[#indices32], 401 exec_lua('return vim.str_byteindex(_G.test_text, "utf-32", 99999, false)') 402 ) 403 eq( 404 indices16[#indices16], 405 exec_lua('return vim.str_byteindex(_G.test_text, "utf-16", 99999, false)') 406 ) 407 eq( 408 indices8[#indices8], 409 exec_lua('return vim.str_byteindex(_G.test_text, "utf-8", 99999, false)') 410 ) 411 eq(2, exec_lua('return vim.str_byteindex("é", "utf-16", 2, false)')) 412 local i32, i16, i8 = 0, 0, 0 413 local len = 51 414 for k = 0, len do 415 if indices32[i32] < k then 416 i32 = i32 + 1 417 end 418 if indices16[i16] < k then 419 i16 = i16 + 1 420 if indices16[i16 + 1] == indices16[i16] then 421 i16 = i16 + 1 422 end 423 end 424 if indices8[i8] < k then 425 i8 = i8 + 1 426 end 427 eq({ i32, i16 }, exec_lua('return {vim.str_utfindex(_G.test_text, ...)}', k), k) 428 eq({ i32 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-32", ...)}', k), k) 429 eq({ i16 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-16", ...)}', k), k) 430 eq({ i8 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-8", ...)}', k), k) 431 end 432 433 eq({ #indices32, #indices16 }, exec_lua('return {vim.str_utfindex(_G.test_text)}')) 434 435 eq(#indices32, exec_lua('return vim.str_utfindex(_G.test_text, "utf-32", math.huge, false)')) 436 eq(#indices16, exec_lua('return vim.str_utfindex(_G.test_text, "utf-16", math.huge, false)')) 437 eq(#indices8, exec_lua('return vim.str_utfindex(_G.test_text, "utf-8", math.huge, false)')) 438 439 eq(#indices32, exec_lua('return vim.str_utfindex(_G.test_text, "utf-32")')) 440 eq(#indices16, exec_lua('return vim.str_utfindex(_G.test_text, "utf-16")')) 441 eq(#indices8, exec_lua('return vim.str_utfindex(_G.test_text, "utf-8")')) 442 matches( 443 'invalid encoding', 444 pcall_err(exec_lua, 'return vim.str_utfindex(_G.test_text, "madeupencoding", ...)', 1) 445 ) 446 matches( 447 'index out of range', 448 pcall_err(exec_lua, 'return vim.str_utfindex(_G.test_text, ...)', len + 1) 449 ) 450 end) 451 452 it('vim.str_utf_start', function() 453 exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]]) 454 local expected_positions = { 455 0, 456 0, 457 0, 458 0, 459 -1, 460 0, 461 -1, 462 0, 463 -1, 464 0, 465 0, 466 -1, 467 0, 468 0, 469 -1, 470 -2, 471 0, 472 -1, 473 -2, 474 0, 475 0, 476 -1, 477 -2, 478 0, 479 0, 480 -1, 481 -2, 482 -3, 483 0, 484 0, 485 -1, 486 -2, 487 -3, 488 0, 489 0, 490 0, 491 -1, 492 0, 493 0, 494 -1, 495 0, 496 -1, 497 0, 498 -1, 499 0, 500 -1, 501 0, 502 -1, 503 } 504 eq( 505 expected_positions, 506 exec_lua([[ 507 local start_codepoint_positions = {} 508 for idx = 1, #_G.test_text do 509 table.insert(start_codepoint_positions, vim.str_utf_start(_G.test_text, idx)) 510 end 511 return start_codepoint_positions 512 ]]) 513 ) 514 end) 515 516 it('vim.str_utf_end', function() 517 exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]]) 518 local expected_positions = { 519 0, 520 0, 521 0, 522 1, 523 0, 524 1, 525 0, 526 1, 527 0, 528 0, 529 1, 530 0, 531 0, 532 2, 533 1, 534 0, 535 2, 536 1, 537 0, 538 0, 539 2, 540 1, 541 0, 542 0, 543 3, 544 2, 545 1, 546 0, 547 0, 548 3, 549 2, 550 1, 551 0, 552 0, 553 0, 554 1, 555 0, 556 0, 557 1, 558 0, 559 1, 560 0, 561 1, 562 0, 563 1, 564 0, 565 1, 566 0, 567 } 568 eq( 569 expected_positions, 570 exec_lua([[ 571 local end_codepoint_positions = {} 572 for idx = 1, #_G.test_text do 573 table.insert(end_codepoint_positions, vim.str_utf_end(_G.test_text, idx)) 574 end 575 return end_codepoint_positions 576 ]]) 577 ) 578 end) 579 580 it('vim.str_utf_pos', function() 581 exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]]) 582 local expected_positions = { 583 1, 584 2, 585 3, 586 4, 587 6, 588 8, 589 10, 590 11, 591 13, 592 14, 593 17, 594 20, 595 21, 596 24, 597 25, 598 29, 599 30, 600 34, 601 35, 602 36, 603 38, 604 39, 605 41, 606 43, 607 45, 608 47, 609 } 610 eq(expected_positions, exec_lua('return vim.str_utf_pos(_G.test_text)')) 611 end) 612 613 it('vim.schedule', function() 614 exec_lua([[ 615 test_table = {} 616 vim.schedule(function() 617 table.insert(test_table, "xx") 618 end) 619 table.insert(test_table, "yy") 620 ]]) 621 eq({ 'yy', 'xx' }, exec_lua('return test_table')) 622 623 -- Validates args. 624 matches('vim.schedule: expected function', pcall_err(exec_lua, "vim.schedule('stringly')")) 625 matches('vim.schedule: expected function', pcall_err(exec_lua, 'vim.schedule()')) 626 627 exec_lua([[ 628 vim.schedule(function() 629 error("big failure\nvery async") 630 end) 631 ]]) 632 633 feed('<cr>') 634 matches('big failure\nvery async', remove_trace(eval('v:errmsg'))) 635 636 local screen = Screen.new(60, 5) 637 screen:expect { 638 grid = [[ 639 ^ | 640 {1:~ }|*3 641 | 642 ]], 643 } 644 645 -- nvim_command causes a Vimscript exception, check that it is properly caught 646 -- and propagated as an error message in async contexts.. #10809 647 exec_lua([[ 648 vim.schedule(function() 649 vim.api.nvim_command(":echo 'err") 650 end) 651 ]]) 652 screen:expect { 653 grid = [[ 654 {9:stack traceback:} | 655 {9: [C]: in function 'nvim_command'} | 656 {9: [string "<nvim>"]:2: in function <[string "<nvim>"]:}| 657 {9:1>} | 658 {6:Press ENTER or type command to continue}^ | 659 ]], 660 } 661 end) 662 663 it('vim.gsplit, vim.split', function() 664 local tests = { 665 -- plain trimempty 666 { 'a,b', ',', false, false, { 'a', 'b' } }, 667 { ':aa::::bb:', ':', false, false, { '', 'aa', '', '', '', 'bb', '' } }, 668 { ':aa::::bb:', ':', false, true, { 'aa', '', '', '', 'bb' } }, 669 { 'aa::::bb:', ':', false, true, { 'aa', '', '', '', 'bb' } }, 670 { ':aa::bb:', ':', false, true, { 'aa', '', 'bb' } }, 671 { '/a/b:/b/\n', '[:\n]', false, true, { '/a/b', '/b/' } }, 672 { '::ee::ff:', ':', false, false, { '', '', 'ee', '', 'ff', '' } }, 673 { '::ee::ff::', ':', false, true, { 'ee', '', 'ff' } }, 674 { 'ab', '.', false, false, { '', '', '' } }, 675 { 'a1b2c', '[0-9]', false, false, { 'a', 'b', 'c' } }, 676 { 'xy', '', false, false, { 'x', 'y' } }, 677 { 'here be dragons', ' ', false, false, { 'here', 'be', 'dragons' } }, 678 { 'axaby', 'ab?', false, false, { '', 'x', 'y' } }, 679 { 'f v2v v3v w2w ', '([vw])2%1', false, false, { 'f ', ' v3v ', ' ' } }, 680 { '', '', false, false, {} }, 681 { '', '', false, true, {} }, 682 { '\n', '[:\n]', false, true, {} }, 683 { '', 'a', false, false, { '' } }, 684 { 'x*yz*oo*l', '*', true, false, { 'x', 'yz', 'oo', 'l' } }, 685 } 686 687 for _, q in ipairs(tests) do 688 eq(q[5], vim.split(q[1], q[2], { plain = q[3], trimempty = q[4] }), q[1]) 689 end 690 691 -- Test old signature 692 eq({ 'x', 'yz', 'oo', 'l' }, vim.split('x*yz*oo*l', '*', true)) 693 694 local loops = { 695 { 'abc', '.-' }, 696 } 697 698 for _, q in ipairs(loops) do 699 matches('Infinite loop detected', pcall_err(vim.split, q[1], q[2])) 700 end 701 702 -- Validates args. 703 eq(true, pcall(vim.split, 'string', 'string')) 704 matches('s: expected string, got number', pcall_err(vim.split, 1, 'string')) 705 matches('sep: expected string, got number', pcall_err(vim.split, 'string', 1)) 706 matches('opts: expected table, got number', pcall_err(vim.split, 'string', 'string', 1)) 707 end) 708 709 it('vim.trim', function() 710 local trim = function(s) 711 return exec_lua('return vim.trim(...)', s) 712 end 713 714 local trims = { 715 { ' a', 'a' }, 716 { ' b ', 'b' }, 717 { '\tc', 'c' }, 718 { 'r\n', 'r' }, 719 { '', '' }, 720 { ' \t \n', '' }, 721 } 722 723 for _, q in ipairs(trims) do 724 eq(q[2], trim(q[1])) 725 end 726 727 -- Validates args. 728 matches('s: expected string, got number', pcall_err(trim, 2)) 729 end) 730 731 it('vim.inspect', function() 732 -- just make sure it basically works, it has its own test suite 733 local inspect = function(q, opts) 734 return exec_lua('return vim.inspect(...)', q, opts) 735 end 736 737 eq('2', inspect(2)) 738 eq('{+a = {+b = 1+}+}', inspect({ a = { b = 1 } }, { newline = '+', indent = '' })) 739 740 -- special value vim.inspect.KEY works 741 eq( 742 '{ KEY_a = "x", KEY_b = "y"}', 743 exec_lua([[ 744 return vim.inspect({a="x", b="y"}, {newline = '', process = function(item, path) 745 if path[#path] == vim.inspect.KEY then 746 return 'KEY_'..item 747 end 748 return item 749 end}) 750 ]]) 751 ) 752 end) 753 754 it('vim.deepcopy', function() 755 ok(exec_lua([[ 756 local a = { x = { 1, 2 }, y = 5} 757 local b = vim.deepcopy(a) 758 759 return b.x[1] == 1 and b.x[2] == 2 and b.y == 5 and vim.tbl_count(b) == 2 760 and tostring(a) ~= tostring(b) 761 ]])) 762 763 ok(exec_lua([[ 764 local a = {} 765 local b = vim.deepcopy(a) 766 767 return vim.islist(b) and vim.tbl_count(b) == 0 and tostring(a) ~= tostring(b) 768 ]])) 769 770 ok(exec_lua([[ 771 local a = vim.empty_dict() 772 local b = vim.deepcopy(a) 773 774 return not vim.islist(b) and vim.tbl_count(b) == 0 775 ]])) 776 777 ok(exec_lua([[ 778 local a = {x = vim.empty_dict(), y = {}} 779 local b = vim.deepcopy(a) 780 781 return not vim.islist(b.x) and vim.islist(b.y) 782 and vim.tbl_count(b) == 2 783 and tostring(a) ~= tostring(b) 784 ]])) 785 786 ok(exec_lua([[ 787 local f1 = function() return 1 end 788 local f2 = function() return 2 end 789 local t1 = {f = f1} 790 local t2 = vim.deepcopy(t1) 791 t1.f = f2 792 return t1.f() ~= t2.f() 793 ]])) 794 795 ok(exec_lua([[ 796 local t1 = {a = 5} 797 t1.self = t1 798 local t2 = vim.deepcopy(t1) 799 return t2.self == t2 and t2.self ~= t1 800 ]])) 801 802 ok(exec_lua([[ 803 local mt = {mt=true} 804 local t1 = setmetatable({a = 5}, mt) 805 local t2 = vim.deepcopy(t1) 806 return getmetatable(t2) == mt 807 ]])) 808 809 ok(exec_lua([[ 810 local t1 = {a = vim.NIL} 811 local t2 = vim.deepcopy(t1) 812 return t2.a == vim.NIL 813 ]])) 814 815 matches( 816 'Cannot deepcopy object of type thread', 817 pcall_err( 818 exec_lua, 819 [[ 820 local thread = coroutine.create(function () return 0 end) 821 local t = {thr = thread} 822 vim.deepcopy(t) 823 ]] 824 ) 825 ) 826 end) 827 828 it('vim.pesc', function() 829 eq('foo%-bar', exec_lua([[return vim.pesc('foo-bar')]])) 830 eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) 831 -- pesc() returns one result. #20751 832 eq({ 'x' }, exec_lua([[return {vim.pesc('x')}]])) 833 834 -- Validates args. 835 matches('s: expected string, got number', pcall_err(exec_lua, [[return vim.pesc(2)]])) 836 end) 837 838 it('vim.list_contains', function() 839 eq(true, exec_lua("return vim.list_contains({'a','b','c'}, 'c')")) 840 eq(false, exec_lua("return vim.list_contains({'a','b','c'}, 'd')")) 841 end) 842 843 it('vim.tbl_contains', function() 844 eq(true, exec_lua("return vim.tbl_contains({'a','b','c'}, 'c')")) 845 eq(false, exec_lua("return vim.tbl_contains({'a','b','c'}, 'd')")) 846 eq(true, exec_lua("return vim.tbl_contains({[2]='a',foo='b',[5] = 'c'}, 'c')")) 847 eq( 848 true, 849 exec_lua([[ 850 return vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v) 851 return vim.deep_equal(v, { 'b', 'c' }) 852 end, { predicate = true }) 853 ]]) 854 ) 855 end) 856 857 it('vim.tbl_keys', function() 858 eq({}, exec_lua('return vim.tbl_keys({})')) 859 for _, v in pairs(exec_lua("return vim.tbl_keys({'a', 'b', 'c'})")) do 860 eq(true, exec_lua('return vim.tbl_contains({ 1, 2, 3 }, ...)', v)) 861 end 862 for _, v in pairs(exec_lua('return vim.tbl_keys({a=1, b=2, c=3})')) do 863 eq(true, exec_lua("return vim.tbl_contains({ 'a', 'b', 'c' }, ...)", v)) 864 end 865 end) 866 867 it('vim.tbl_values', function() 868 eq({}, exec_lua('return vim.tbl_values({})')) 869 for _, v in pairs(exec_lua("return vim.tbl_values({'a', 'b', 'c'})")) do 870 eq(true, exec_lua("return vim.tbl_contains({ 'a', 'b', 'c' }, ...)", v)) 871 end 872 for _, v in pairs(exec_lua('return vim.tbl_values({a=1, b=2, c=3})')) do 873 eq(true, exec_lua('return vim.tbl_contains({ 1, 2, 3 }, ...)', v)) 874 end 875 end) 876 877 it('vim.tbl_map', function() 878 eq( 879 {}, 880 exec_lua([[ 881 return vim.tbl_map(function(v) return v * 2 end, {}) 882 ]]) 883 ) 884 eq( 885 { 2, 4, 6 }, 886 exec_lua([[ 887 return vim.tbl_map(function(v) return v * 2 end, {1, 2, 3}) 888 ]]) 889 ) 890 eq( 891 { { i = 2 }, { i = 4 }, { i = 6 } }, 892 exec_lua([[ 893 return vim.tbl_map(function(v) return { i = v.i * 2 } end, {{i=1}, {i=2}, {i=3}}) 894 ]]) 895 ) 896 end) 897 898 it('vim.tbl_filter', function() 899 eq( 900 {}, 901 exec_lua([[ 902 return vim.tbl_filter(function(v) return (v % 2) == 0 end, {}) 903 ]]) 904 ) 905 eq( 906 { 2 }, 907 exec_lua([[ 908 return vim.tbl_filter(function(v) return (v % 2) == 0 end, {1, 2, 3}) 909 ]]) 910 ) 911 eq( 912 { { i = 2 } }, 913 exec_lua([[ 914 return vim.tbl_filter(function(v) return (v.i % 2) == 0 end, {{i=1}, {i=2}, {i=3}}) 915 ]]) 916 ) 917 end) 918 919 it('vim.isarray', function() 920 eq(true, exec_lua('return vim.isarray({})')) 921 eq(false, exec_lua('return vim.isarray(vim.empty_dict())')) 922 eq(true, exec_lua("return vim.isarray({'a', 'b', 'c'})")) 923 eq(false, exec_lua("return vim.isarray({'a', '32', a='hello', b='baz'})")) 924 eq(false, exec_lua("return vim.isarray({1, a='hello', b='baz'})")) 925 eq(false, exec_lua("return vim.isarray({a='hello', b='baz', 1})")) 926 eq(false, exec_lua("return vim.isarray({1, 2, nil, a='hello'})")) 927 eq(true, exec_lua('return vim.isarray({1, 2, nil, 4})')) 928 eq(true, exec_lua('return vim.isarray({nil, 2, 3, 4})')) 929 eq(false, exec_lua('return vim.isarray({1, [1.5]=2, [3]=3})')) 930 end) 931 932 it('vim.islist', function() 933 eq(true, exec_lua('return vim.islist({})')) 934 eq(false, exec_lua('return vim.islist(vim.empty_dict())')) 935 eq(true, exec_lua("return vim.islist({'a', 'b', 'c'})")) 936 eq(false, exec_lua("return vim.islist({'a', '32', a='hello', b='baz'})")) 937 eq(false, exec_lua("return vim.islist({1, a='hello', b='baz'})")) 938 eq(false, exec_lua("return vim.islist({a='hello', b='baz', 1})")) 939 eq(false, exec_lua("return vim.islist({1, 2, nil, a='hello'})")) 940 eq(false, exec_lua('return vim.islist({1, 2, nil, 4})')) 941 eq(false, exec_lua('return vim.islist({nil, 2, 3, 4})')) 942 eq(false, exec_lua('return vim.islist({1, [1.5]=2, [3]=3})')) 943 end) 944 945 it('vim.tbl_isempty', function() 946 eq(true, exec_lua('return vim.tbl_isempty({})')) 947 eq(false, exec_lua('return vim.tbl_isempty({ 1, 2, 3 })')) 948 eq(false, exec_lua('return vim.tbl_isempty({a=1, b=2, c=3})')) 949 end) 950 951 it('vim.tbl_get', function() 952 eq( 953 true, 954 exec_lua("return vim.tbl_get({ test = { nested_test = true }}, 'test', 'nested_test')") 955 ) 956 eq(NIL, exec_lua("return vim.tbl_get({ unindexable = true }, 'unindexable', 'missing_key')")) 957 eq(NIL, exec_lua("return vim.tbl_get({ unindexable = 1 }, 'unindexable', 'missing_key')")) 958 eq( 959 NIL, 960 exec_lua( 961 "return vim.tbl_get({ unindexable = coroutine.create(function () end) }, 'unindexable', 'missing_key')" 962 ) 963 ) 964 eq( 965 NIL, 966 exec_lua( 967 "return vim.tbl_get({ unindexable = function () end }, 'unindexable', 'missing_key')" 968 ) 969 ) 970 eq(NIL, exec_lua("return vim.tbl_get({}, 'missing_key')")) 971 eq(NIL, exec_lua('return vim.tbl_get({})')) 972 eq(NIL, exec_lua("return vim.tbl_get({}, nil, 'key')")) 973 eq(1, exec_lua("return select('#', vim.tbl_get({}))")) 974 eq(1, exec_lua("return select('#', vim.tbl_get({ nested = {} }, 'nested', 'missing_key'))")) 975 end) 976 977 it('vim.tbl_extend', function() 978 ok(exec_lua([[ 979 local a = {x = 1} 980 local b = {y = 2} 981 local c = vim.tbl_extend("keep", a, b) 982 983 return c.x == 1 and b.y == 2 and vim.tbl_count(c) == 2 984 ]])) 985 986 ok(exec_lua([[ 987 local a = {x = 1} 988 local b = {y = 2} 989 local c = {z = 3} 990 local d = vim.tbl_extend("keep", a, b, c) 991 992 return d.x == 1 and d.y == 2 and d.z == 3 and vim.tbl_count(d) == 3 993 ]])) 994 995 ok(exec_lua([[ 996 local a = {x = 1} 997 local b = {x = 3} 998 local c = vim.tbl_extend("keep", a, b) 999 1000 return c.x == 1 and vim.tbl_count(c) == 1 1001 ]])) 1002 1003 ok(exec_lua([[ 1004 local a = {x = 1} 1005 local b = {x = 3} 1006 local c = vim.tbl_extend("force", a, b) 1007 1008 return c.x == 3 and vim.tbl_count(c) == 1 1009 ]])) 1010 1011 ok(exec_lua([[ 1012 local a = vim.empty_dict() 1013 local b = {} 1014 local c = vim.tbl_extend("keep", a, b) 1015 1016 return not vim.islist(c) and vim.tbl_count(c) == 0 1017 ]])) 1018 1019 ok(exec_lua([[ 1020 local a = {} 1021 local b = vim.empty_dict() 1022 local c = vim.tbl_extend("keep", a, b) 1023 1024 return vim.islist(c) and vim.tbl_count(c) == 0 1025 ]])) 1026 1027 ok(exec_lua([[ 1028 local a = {x = {a = 1, b = 2}} 1029 local b = {x = {a = 2, c = {y = 3}}} 1030 local c = vim.tbl_extend("keep", a, b) 1031 1032 local count = 0 1033 for _ in pairs(c) do count = count + 1 end 1034 1035 return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1 1036 ]])) 1037 1038 ok(exec_lua([[ 1039 local a = { a = 1, b = 2, c = 1 } 1040 local b = { a = -1, b = 5, c = 3, d = 4 } 1041 -- Return the maximum value for each key. 1042 local c = vim.tbl_extend(function(k, prev_v, v) 1043 if prev_v then 1044 return v > prev_v and v or prev_v 1045 else 1046 return v 1047 end 1048 end, a, b) 1049 return vim.deep_equal(c, { a = 1, b = 5, c = 3, d = 4 }) 1050 ]])) 1051 1052 matches( 1053 'invalid "behavior": nil', 1054 pcall_err( 1055 exec_lua, 1056 [[ 1057 return vim.tbl_extend() 1058 ]] 1059 ) 1060 ) 1061 1062 matches( 1063 'wrong number of arguments %(given 1, expected at least 3%)', 1064 pcall_err( 1065 exec_lua, 1066 [[ 1067 return vim.tbl_extend("keep") 1068 ]] 1069 ) 1070 ) 1071 1072 matches( 1073 'wrong number of arguments %(given 2, expected at least 3%)', 1074 pcall_err( 1075 exec_lua, 1076 [[ 1077 return vim.tbl_extend("keep", {}) 1078 ]] 1079 ) 1080 ) 1081 end) 1082 1083 it('vim.tbl_deep_extend', function() 1084 ok(exec_lua([[ 1085 local a = {x = {a = 1, b = 2}} 1086 local b = {x = {a = 2, c = {y = 3}}} 1087 local c = vim.tbl_deep_extend("keep", a, b) 1088 1089 local count = 0 1090 for _ in pairs(c) do count = count + 1 end 1091 1092 return c.x.a == 1 and c.x.b == 2 and c.x.c.y == 3 and count == 1 1093 ]])) 1094 1095 ok(exec_lua([[ 1096 local a = {x = {a = 1, b = 2}} 1097 local b = {x = {a = 2, c = {y = 3}}} 1098 local c = vim.tbl_deep_extend("force", a, b) 1099 1100 local count = 0 1101 for _ in pairs(c) do count = count + 1 end 1102 1103 return c.x.a == 2 and c.x.b == 2 and c.x.c.y == 3 and count == 1 1104 ]])) 1105 1106 ok(exec_lua([[ 1107 local a = {x = {a = 1, b = 2}} 1108 local b = {x = {a = 2, c = {y = 3}}} 1109 local c = {x = {c = 4, d = {y = 4}}} 1110 local d = vim.tbl_deep_extend("keep", a, b, c) 1111 1112 local count = 0 1113 for _ in pairs(c) do count = count + 1 end 1114 1115 return d.x.a == 1 and d.x.b == 2 and d.x.c.y == 3 and d.x.d.y == 4 and count == 1 1116 ]])) 1117 1118 ok(exec_lua([[ 1119 local a = {x = {a = 1, b = 2}} 1120 local b = {x = {a = 2, c = {y = 3}}} 1121 local c = {x = {c = 4, d = {y = 4}}} 1122 local d = vim.tbl_deep_extend("force", a, b, c) 1123 1124 local count = 0 1125 for _ in pairs(c) do count = count + 1 end 1126 1127 return d.x.a == 2 and d.x.b == 2 and d.x.c == 4 and d.x.d.y == 4 and count == 1 1128 ]])) 1129 1130 ok(exec_lua([[ 1131 local a = vim.empty_dict() 1132 local b = {} 1133 local c = vim.tbl_deep_extend("keep", a, b) 1134 1135 local count = 0 1136 for _ in pairs(c) do count = count + 1 end 1137 1138 return not vim.islist(c) and count == 0 1139 ]])) 1140 1141 ok(exec_lua([[ 1142 local a = {} 1143 local b = vim.empty_dict() 1144 local c = vim.tbl_deep_extend("keep", a, b) 1145 1146 local count = 0 1147 for _ in pairs(c) do count = count + 1 end 1148 1149 return vim.islist(c) and count == 0 1150 ]])) 1151 1152 eq( 1153 { a = { b = 1 } }, 1154 exec_lua([[ 1155 local a = { a = { b = 1 } } 1156 local b = { a = {} } 1157 return vim.tbl_deep_extend("force", a, b) 1158 ]]) 1159 ) 1160 1161 eq( 1162 { a = { b = 1 } }, 1163 exec_lua([[ 1164 local a = { a = 123 } 1165 local b = { a = { b = 1} } 1166 return vim.tbl_deep_extend("force", a, b) 1167 ]]) 1168 ) 1169 1170 ok(exec_lua([[ 1171 local a = { a = {[2] = 3} } 1172 local b = { a = {[3] = 3} } 1173 local c = vim.tbl_deep_extend("force", a, b) 1174 return vim.deep_equal(c, {a = {[2] = 3, [3] = 3}}) 1175 ]])) 1176 1177 eq( 1178 { a = 123 }, 1179 exec_lua([[ 1180 local a = { a = { b = 1} } 1181 local b = { a = 123 } 1182 return vim.tbl_deep_extend("force", a, b) 1183 ]]) 1184 ) 1185 1186 ok(exec_lua([[ 1187 local a = { sub = { 'a', 'b' } } 1188 local b = { sub = { 'b', 'c' } } 1189 local c = vim.tbl_deep_extend('force', a, b) 1190 return vim.deep_equal(c, { sub = { 'b', 'c' } }) 1191 ]])) 1192 1193 ok(exec_lua([[ 1194 local a = { a = 1, b = 2, c = { d = 1, e = -2} } 1195 local b = { a = -1, b = 5, c = { d = 6 } } 1196 -- Return the maximum value for each key. 1197 local c = vim.tbl_deep_extend(function(k, prev_v, v) 1198 if prev_v then 1199 return v > prev_v and v or prev_v 1200 else 1201 return v 1202 end 1203 end, a, b) 1204 return vim.deep_equal(c, { a = 1, b = 5, c = { d = 6, e = -2 } }) 1205 ]])) 1206 1207 matches('invalid "behavior": nil', pcall_err(exec_lua, [[return vim.tbl_deep_extend()]])) 1208 1209 matches( 1210 'wrong number of arguments %(given 1, expected at least 3%)', 1211 pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep")]]) 1212 ) 1213 1214 matches( 1215 'wrong number of arguments %(given 2, expected at least 3%)', 1216 pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep", {})]]) 1217 ) 1218 1219 matches( 1220 'after the second argument%: expected table, got number', 1221 pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep", {}, 42)]]) 1222 ) 1223 end) 1224 1225 it('vim.tbl_count', function() 1226 eq(0, exec_lua [[ return vim.tbl_count({}) ]]) 1227 eq(0, exec_lua [[ return vim.tbl_count(vim.empty_dict()) ]]) 1228 eq(0, exec_lua [[ return vim.tbl_count({nil}) ]]) 1229 eq(0, exec_lua [[ return vim.tbl_count({a=nil}) ]]) 1230 eq(1, exec_lua [[ return vim.tbl_count({1}) ]]) 1231 eq(2, exec_lua [[ return vim.tbl_count({1, 2}) ]]) 1232 eq(2, exec_lua [[ return vim.tbl_count({1, nil, 3}) ]]) 1233 eq(1, exec_lua [[ return vim.tbl_count({a=1}) ]]) 1234 eq(2, exec_lua [[ return vim.tbl_count({a=1, b=2}) ]]) 1235 eq(2, exec_lua [[ return vim.tbl_count({a=1, b=nil, c=3}) ]]) 1236 end) 1237 1238 it('vim.deep_equal', function() 1239 eq(true, exec_lua [[ return vim.deep_equal({a=1}, {a=1}) ]]) 1240 eq(true, exec_lua [[ return vim.deep_equal({a={b=1}}, {a={b=1}}) ]]) 1241 eq(true, exec_lua [[ return vim.deep_equal({a={b={nil}}}, {a={b={}}}) ]]) 1242 eq(true, exec_lua [[ return vim.deep_equal({a=1, [5]=5}, {nil,nil,nil,nil,5,a=1}) ]]) 1243 eq(false, exec_lua [[ return vim.deep_equal(1, {nil,nil,nil,nil,5,a=1}) ]]) 1244 eq(false, exec_lua [[ return vim.deep_equal(1, 3) ]]) 1245 eq(false, exec_lua [[ return vim.deep_equal(nil, 3) ]]) 1246 eq(false, exec_lua [[ return vim.deep_equal({a=1}, {a=2}) ]]) 1247 end) 1248 1249 it('vim.list_extend', function() 1250 eq({ 1, 2, 3 }, exec_lua [[ return vim.list_extend({1}, {2,3}) ]]) 1251 matches( 1252 'src: expected table, got nil', 1253 pcall_err(exec_lua, [[ return vim.list_extend({1}, nil) ]]) 1254 ) 1255 eq({ 1, 2 }, exec_lua [[ return vim.list_extend({1}, {2;a=1}) ]]) 1256 eq(true, exec_lua [[ local a = {1} return vim.list_extend(a, {2;a=1}) == a ]]) 1257 eq({ 2 }, exec_lua [[ return vim.list_extend({}, {2;a=1}, 1) ]]) 1258 eq({}, exec_lua [[ return vim.list_extend({}, {2;a=1}, 2) ]]) 1259 eq({}, exec_lua [[ return vim.list_extend({}, {2;a=1}, 1, -1) ]]) 1260 eq({ 2 }, exec_lua [[ return vim.list_extend({}, {2;a=1}, -1, 2) ]]) 1261 end) 1262 1263 it('vim.tbl_add_reverse_lookup', function() 1264 eq( 1265 true, 1266 exec_lua [[ 1267 local a = { A = 1 } 1268 vim.tbl_add_reverse_lookup(a) 1269 return vim.deep_equal(a, { A = 1; [1] = 'A'; }) 1270 ]] 1271 ) 1272 -- Throw an error for trying to do it twice (run into an existing key) 1273 local code = [[ 1274 local res = {} 1275 local a = { A = 1 } 1276 vim.tbl_add_reverse_lookup(a) 1277 assert(vim.deep_equal(a, { A = 1; [1] = 'A'; })) 1278 vim.tbl_add_reverse_lookup(a) 1279 ]] 1280 matches( 1281 'The reverse lookup found an existing value for "[1A]" while processing key "[1A]"$', 1282 pcall_err(exec_lua, code) 1283 ) 1284 end) 1285 1286 it('vim.spairs', function() 1287 local res = '' 1288 local table = { 1289 ccc = 1, 1290 bbb = 2, 1291 ddd = 3, 1292 aaa = 4, 1293 } 1294 for key, _ in vim.spairs(table) do 1295 res = res .. key 1296 end 1297 matches('aaabbbcccddd', res) 1298 end) 1299 1300 it('vim.call, vim.fn', function() 1301 eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]])) 1302 eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]])) 1303 -- compat: nvim_call_function uses "special" value for Vimscript float 1304 eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]])) 1305 1306 exec([[ 1307 func! FooFunc(test) 1308 let g:test = a:test 1309 return {} 1310 endfunc 1311 func! VarArg(...) 1312 return a:000 1313 endfunc 1314 func! Nilly() 1315 return [v:null, v:null] 1316 endfunc 1317 ]]) 1318 eq(true, exec_lua([[return next(vim.fn.FooFunc(3)) == nil ]])) 1319 eq(3, eval('g:test')) 1320 eq(true, exec_lua([[return vim.tbl_isempty(vim.api.nvim_call_function("FooFunc", {5}))]])) 1321 eq(5, eval('g:test')) 1322 1323 eq({ 2, 'foo', true }, exec_lua([[return vim.fn.VarArg(2, "foo", true)]])) 1324 1325 eq( 1326 true, 1327 exec_lua([[ 1328 local x = vim.fn.Nilly() 1329 return #x == 2 and x[1] == vim.NIL and x[2] == vim.NIL 1330 ]]) 1331 ) 1332 eq({ NIL, NIL }, exec_lua([[return vim.fn.Nilly()]])) 1333 1334 -- error handling 1335 eq( 1336 { false, 'Vim:E897: List or Blob required' }, 1337 exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]]) 1338 ) 1339 1340 -- conversion between LuaRef and Vim Funcref 1341 eq( 1342 true, 1343 exec_lua([[ 1344 local x = vim.fn.VarArg(function() return 'foo' end, function() return 'bar' end) 1345 return #x == 2 and x[1]() == 'foo' and x[2]() == 'bar' 1346 ]]) 1347 ) 1348 1349 -- Test for #20211 1350 eq( 1351 'a (b) c', 1352 exec_lua([[ 1353 return vim.fn.substitute('a b c', 'b', function(m) return '(' .. m[1] .. ')' end, 'g') 1354 ]]) 1355 ) 1356 end) 1357 1358 it('vim.call fails in fast context', function() 1359 local screen = Screen.new(120, 10) 1360 exec_lua([[ 1361 local timer = vim.uv.new_timer() 1362 timer:start(0, 0, function() 1363 timer:close() 1364 vim.call('sin', 0.0) 1365 end) 1366 ]]) 1367 screen:expect({ 1368 any = pesc('E5560: Vimscript function "sin" must not be called in a fast event context'), 1369 }) 1370 feed('<CR>') 1371 assert_alive() 1372 end) 1373 1374 it('vim.fn errors when calling API function', function() 1375 matches( 1376 'Tried to call API function with vim.fn: use vim.api.nvim_get_current_line instead', 1377 pcall_err(exec_lua, 'vim.fn.nvim_get_current_line()') 1378 ) 1379 end) 1380 1381 it('vim.fn is allowed in "fast" context by some functions #18306', function() 1382 exec_lua([[ 1383 local timer = vim.uv.new_timer() 1384 timer:start(0, 0, function() 1385 timer:close() 1386 assert(vim.in_fast_event()) 1387 vim.g.fnres = vim.fn.iconv('hello', 'utf-8', 'utf-8') 1388 end) 1389 ]]) 1390 1391 poke_eventloop() 1392 eq('hello', exec_lua [[return vim.g.fnres]]) 1393 end) 1394 1395 it('vim.rpcrequest and vim.rpcnotify', function() 1396 exec_lua([[ 1397 chan = vim.fn.jobstart({'cat'}, {rpc=true}) 1398 vim.rpcrequest(chan, 'nvim_set_current_line', 'meow') 1399 ]]) 1400 eq('meow', api.nvim_get_current_line()) 1401 command("let x = [3, 'aa', v:true, v:null]") 1402 eq( 1403 true, 1404 exec_lua([[ 1405 ret = vim.rpcrequest(chan, 'nvim_get_var', 'x') 1406 return #ret == 4 and ret[1] == 3 and ret[2] == 'aa' and ret[3] == true and ret[4] == vim.NIL 1407 ]]) 1408 ) 1409 eq({ 3, 'aa', true, NIL }, exec_lua([[return ret]])) 1410 1411 eq( 1412 { {}, {}, false, true }, 1413 exec_lua([[ 1414 vim.rpcrequest(chan, 'nvim_exec', 'let xx = {}\nlet yy = []', false) 1415 local dict = vim.rpcrequest(chan, 'nvim_eval', 'xx') 1416 local list = vim.rpcrequest(chan, 'nvim_eval', 'yy') 1417 return {dict, list, vim.islist(dict), vim.islist(list)} 1418 ]]) 1419 ) 1420 1421 exec_lua([[ 1422 vim.rpcrequest(chan, 'nvim_set_var', 'aa', {}) 1423 vim.rpcrequest(chan, 'nvim_set_var', 'bb', vim.empty_dict()) 1424 ]]) 1425 eq({ 1, 1 }, eval('[type(g:aa) == type([]), type(g:bb) == type({})]')) 1426 1427 -- error handling 1428 eq({ false, 'Invalid channel: 23' }, exec_lua([[return {pcall(vim.rpcrequest, 23, 'foo')}]])) 1429 eq({ false, 'Invalid channel: 23' }, exec_lua([[return {pcall(vim.rpcnotify, 23, 'foo')}]])) 1430 1431 eq( 1432 { false, 'Vim:E121: Undefined variable: foobar' }, 1433 exec_lua([[return {pcall(vim.rpcrequest, chan, 'nvim_eval', "foobar")}]]) 1434 ) 1435 1436 -- rpcnotify doesn't wait on request 1437 eq( 1438 'meow', 1439 exec_lua([[ 1440 vim.rpcnotify(chan, 'nvim_set_current_line', 'foo') 1441 return vim.api.nvim_get_current_line() 1442 ]]) 1443 ) 1444 retry(10, nil, function() 1445 eq('foo', api.nvim_get_current_line()) 1446 end) 1447 1448 local screen = Screen.new(50, 7) 1449 exec_lua([[ 1450 timer = vim.uv.new_timer() 1451 timer:start(20, 0, function () 1452 -- notify ok (executed later when safe) 1453 vim.rpcnotify(chan, 'nvim_set_var', 'yy', {3, vim.NIL}) 1454 -- rpcrequest an error 1455 vim.rpcrequest(chan, 'nvim_set_current_line', 'bork') 1456 end) 1457 ]]) 1458 screen:expect { 1459 grid = [[ 1460 {9:[string "<nvim>"]:6: E5560: rpcrequest must not be}| 1461 {9: called in a fast event context} | 1462 {9:stack traceback:} | 1463 {9: [C]: in function 'rpcrequest'} | 1464 {9: [string "<nvim>"]:6: in function <[string }| 1465 {9:"<nvim>"]:2>} | 1466 {6:Press ENTER or type command to continue}^ | 1467 ]], 1468 } 1469 feed('<cr>') 1470 retry(10, nil, function() 1471 eq({ 3, NIL }, api.nvim_get_var('yy')) 1472 end) 1473 1474 exec_lua([[timer:close()]]) 1475 end) 1476 1477 it('vim.empty_dict()', function() 1478 eq( 1479 { true, false, true, true }, 1480 exec_lua([[ 1481 vim.api.nvim_set_var('listy', {}) 1482 vim.api.nvim_set_var('dicty', vim.empty_dict()) 1483 local listy = vim.fn.eval("listy") 1484 local dicty = vim.fn.eval("dicty") 1485 return {vim.islist(listy), vim.islist(dicty), next(listy) == nil, next(dicty) == nil} 1486 ]]) 1487 ) 1488 1489 -- vim.empty_dict() gives new value each time 1490 -- equality is not overridden (still by ref) 1491 -- non-empty table uses the usual heuristics (ignores the tag) 1492 eq( 1493 { false, { 'foo' }, { namey = 'bar' } }, 1494 exec_lua([[ 1495 local aa = vim.empty_dict() 1496 local bb = vim.empty_dict() 1497 local equally = (aa == bb) 1498 aa[1] = "foo" 1499 bb["namey"] = "bar" 1500 return {equally, aa, bb} 1501 ]]) 1502 ) 1503 1504 eq('{ {}, vim.empty_dict() }', exec_lua('return vim.inspect({{}, vim.empty_dict()})')) 1505 eq('{}', exec_lua([[ return vim.fn.json_encode(vim.empty_dict()) ]])) 1506 eq('{"a": {}, "b": []}', exec_lua([[ return vim.fn.json_encode({a=vim.empty_dict(), b={}}) ]])) 1507 end) 1508 1509 it('vim.validate (fast form)', function() 1510 exec_lua("vim.validate('arg1', {}, 'table')") 1511 exec_lua("vim.validate('arg1', nil, 'table', true)") 1512 exec_lua("vim.validate('arg1', { foo='foo' }, 'table')") 1513 exec_lua("vim.validate('arg1', { 'foo' }, 'table')") 1514 exec_lua("vim.validate('arg1', 'foo', 'string')") 1515 exec_lua("vim.validate('arg1', nil, 'string', true)") 1516 exec_lua("vim.validate('arg1', 1, 'number')") 1517 exec_lua("vim.validate('arg1', 0, 'number')") 1518 exec_lua("vim.validate('arg1', 0.1, 'number')") 1519 exec_lua("vim.validate('arg1', nil, 'number', true)") 1520 exec_lua("vim.validate('arg1', true, 'boolean')") 1521 exec_lua("vim.validate('arg1', false, 'boolean')") 1522 exec_lua("vim.validate('arg1', nil, 'boolean', true)") 1523 exec_lua("vim.validate('arg1', function()end, 'function')") 1524 exec_lua("vim.validate('arg1', nil, 'function', true)") 1525 exec_lua("vim.validate('arg1', nil, 'nil')") 1526 exec_lua("vim.validate('arg1', nil, 'nil', true)") 1527 exec_lua("vim.validate('arg1', coroutine.create(function()end), 'thread')") 1528 exec_lua("vim.validate('arg1', nil, 'thread', true)") 1529 exec_lua("vim.validate('arg1', 2, function(a) return (a % 2) == 0 end, 'even number')") 1530 exec_lua("vim.validate('arg1', 5, {'number', 'string'})") 1531 exec_lua("vim.validate('arg2', 'foo', {'number', 'string'})") 1532 1533 matches('arg1: expected number, got nil', pcall_err(vim.validate, 'arg1', nil, 'number')) 1534 matches('arg1: expected string, got nil', pcall_err(vim.validate, 'arg1', nil, 'string')) 1535 matches('arg1: expected table, got nil', pcall_err(vim.validate, 'arg1', nil, 'table')) 1536 matches('arg1: expected function, got nil', pcall_err(vim.validate, 'arg1', nil, 'function')) 1537 matches('arg1: expected string, got number', pcall_err(vim.validate, 'arg1', 5, 'string')) 1538 matches('arg1: expected table, got number', pcall_err(vim.validate, 'arg1', 5, 'table')) 1539 matches('arg1: expected function, got number', pcall_err(vim.validate, 'arg1', 5, 'function')) 1540 matches('arg1: expected number, got string', pcall_err(vim.validate, 'arg1', '5', 'number')) 1541 matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate('arg1', 1, 'x')")) 1542 matches('invalid validator: 1', pcall_err(exec_lua, "vim.validate('arg1', 1, 1)")) 1543 matches('invalid arguments', pcall_err(exec_lua, "vim.validate('arg1', { 1 })")) 1544 1545 -- Validated parameters are required by default. 1546 matches( 1547 'arg1: expected string, got nil', 1548 pcall_err(exec_lua, "vim.validate('arg1', nil, 'string')") 1549 ) 1550 -- Explicitly required. 1551 matches( 1552 'arg1: expected string, got nil', 1553 pcall_err(exec_lua, "vim.validate('arg1', nil, 'string', false)") 1554 ) 1555 1556 matches( 1557 'arg1: expected table, got number', 1558 pcall_err(exec_lua, "vim.validate('arg1', 1, 'table')") 1559 ) 1560 1561 matches( 1562 'arg1: expected even number, got 3', 1563 pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end, 'even number')") 1564 ) 1565 matches( 1566 'arg1: expected %?, got 3', 1567 pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end)") 1568 ) 1569 matches( 1570 'arg1: expected number|string, got nil', 1571 pcall_err(exec_lua, "vim.validate('arg1', nil, {'number', 'string'})") 1572 ) 1573 1574 -- Validator func can return an extra "Info" message. 1575 matches( 1576 'arg1: expected %?, got 3. Info: TEST_MSG', 1577 pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1, 'TEST_MSG' end)") 1578 ) 1579 -- Caller can override the "expected" message. 1580 eq( 1581 'arg1: expected TEST_MSG, got nil', 1582 pcall_err(exec_lua, "vim.validate('arg1', nil, 'table', 'TEST_MSG')") 1583 ) 1584 end) 1585 1586 it('vim.validate (spec form)', function() 1587 exec_lua("vim.validate{arg1={{}, 'table' }}") 1588 exec_lua("vim.validate{arg1={{}, 't' }}") 1589 exec_lua("vim.validate{arg1={nil, 't', true }}") 1590 exec_lua("vim.validate{arg1={{ foo='foo' }, 't' }}") 1591 exec_lua("vim.validate{arg1={{ 'foo' }, 't' }}") 1592 exec_lua("vim.validate{arg1={'foo', 'string' }}") 1593 exec_lua("vim.validate{arg1={'foo', 's' }}") 1594 exec_lua("vim.validate{arg1={'', 's' }}") 1595 exec_lua("vim.validate{arg1={nil, 's', true }}") 1596 exec_lua("vim.validate{arg1={1, 'number' }}") 1597 exec_lua("vim.validate{arg1={1, 'n' }}") 1598 exec_lua("vim.validate{arg1={0, 'n' }}") 1599 exec_lua("vim.validate{arg1={0.1, 'n' }}") 1600 exec_lua("vim.validate{arg1={nil, 'n', true }}") 1601 exec_lua("vim.validate{arg1={true, 'boolean' }}") 1602 exec_lua("vim.validate{arg1={true, 'b' }}") 1603 exec_lua("vim.validate{arg1={false, 'b' }}") 1604 exec_lua("vim.validate{arg1={nil, 'b', true }}") 1605 exec_lua("vim.validate{arg1={function()end, 'function' }}") 1606 exec_lua("vim.validate{arg1={function()end, 'f' }}") 1607 exec_lua("vim.validate{arg1={nil, 'f', true }}") 1608 exec_lua("vim.validate{arg1={nil, 'nil' }}") 1609 exec_lua("vim.validate{arg1={nil, 'nil', true }}") 1610 exec_lua("vim.validate{arg1={coroutine.create(function()end), 'thread' }}") 1611 exec_lua("vim.validate{arg1={nil, 'thread', true }}") 1612 exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") 1613 exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}") 1614 exec_lua("vim.validate{arg1={5, {'n', 's'} }, arg2={ 'foo', {'n', 's'} }}") 1615 1616 matches('expected table, got number', pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) 1617 matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) 1618 matches('invalid validator: 1', pcall_err(exec_lua, 'vim.validate{ arg1={ 1, 1 }}')) 1619 matches('invalid validator: nil', pcall_err(exec_lua, 'vim.validate{ arg1={ 1 }}')) 1620 1621 -- Validated parameters are required by default. 1622 matches( 1623 'arg1: expected string, got nil', 1624 pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}") 1625 ) 1626 -- Explicitly required. 1627 matches( 1628 'arg1: expected string, got nil', 1629 pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}") 1630 ) 1631 1632 matches('arg1: expected table, got number', pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}")) 1633 matches( 1634 'arg2: expected string, got number', 1635 pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}") 1636 ) 1637 matches( 1638 'arg2: expected string, got nil', 1639 pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}") 1640 ) 1641 matches( 1642 'arg2: expected string, got nil', 1643 pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}") 1644 ) 1645 matches( 1646 'arg1: expected even number, got 3', 1647 pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}") 1648 ) 1649 matches( 1650 'arg1: expected %?, got 3', 1651 pcall_err(exec_lua, 'vim.validate{arg1={3, function(a) return a == 1 end}}') 1652 ) 1653 matches( 1654 'arg1: expected number|string, got nil', 1655 pcall_err(exec_lua, "vim.validate{ arg1={ nil, {'n', 's'} }}") 1656 ) 1657 1658 -- Pass an additional message back. 1659 matches( 1660 'arg1: expected %?, got 3. Info: TEST_MSG', 1661 pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1, 'TEST_MSG' end}}") 1662 ) 1663 end) 1664 1665 it('vim.is_callable', function() 1666 eq(true, exec_lua('return vim.is_callable(function()end)')) 1667 eq( 1668 true, 1669 exec_lua([[ 1670 local meta = { __call = function()end } 1671 local function new_callable() 1672 return setmetatable({}, meta) 1673 end 1674 local callable = new_callable() 1675 return vim.is_callable(callable) 1676 ]]) 1677 ) 1678 1679 eq( 1680 { false, false }, 1681 exec_lua([[ 1682 local meta = { __call = {} } 1683 assert(meta.__call) 1684 local function new() 1685 return setmetatable({}, meta) 1686 end 1687 local not_callable = new() 1688 return { pcall(function() not_callable() end), vim.is_callable(not_callable) } 1689 ]]) 1690 ) 1691 eq( 1692 { false, false }, 1693 exec_lua([[ 1694 local function new() 1695 return { __call = function()end } 1696 end 1697 local not_callable = new() 1698 assert(not_callable.__call) 1699 return { pcall(function() not_callable() end), vim.is_callable(not_callable) } 1700 ]]) 1701 ) 1702 eq( 1703 { false, false }, 1704 exec_lua([[ 1705 local meta = setmetatable( 1706 { __index = { __call = function() end } }, 1707 { __index = { __call = function() end } } 1708 ) 1709 assert(meta.__call) 1710 local not_callable = setmetatable({}, meta) 1711 assert(not_callable.__call) 1712 return { pcall(function() not_callable() end), vim.is_callable(not_callable) } 1713 ]]) 1714 ) 1715 eq( 1716 { false, false }, 1717 exec_lua([[ 1718 local meta = setmetatable({ 1719 __index = function() 1720 return function() end 1721 end, 1722 }, { 1723 __index = function() 1724 return function() end 1725 end, 1726 }) 1727 assert(meta.__call) 1728 local not_callable = setmetatable({}, meta) 1729 assert(not_callable.__call) 1730 return { pcall(function() not_callable() end), vim.is_callable(not_callable) } 1731 ]]) 1732 ) 1733 eq(false, exec_lua('return vim.is_callable(1)')) 1734 eq(false, exec_lua("return vim.is_callable('foo')")) 1735 eq(false, exec_lua('return vim.is_callable({})')) 1736 end) 1737 1738 it('vim.cmd', function() 1739 exec_lua [[ 1740 vim.cmd "autocmd BufNew * ++once lua BUF = vim.fn.expand('<abuf>')" 1741 vim.cmd "new" 1742 ]] 1743 eq('2', fn.luaeval 'BUF') 1744 eq(2, fn.luaeval '#vim.api.nvim_list_bufs()') 1745 1746 -- vim.cmd can be indexed with a command name 1747 exec_lua [[ 1748 vim.cmd.let 'g:var = 2' 1749 ]] 1750 1751 eq(2, fn.luaeval 'vim.g.var') 1752 end) 1753 1754 it('vim.regex', function() 1755 exec_lua [[ 1756 re1 = vim.regex"ab\\+c" 1757 vim.cmd "set nomagic ignorecase" 1758 re2 = vim.regex"xYz" 1759 ]] 1760 eq({}, exec_lua [[return {re1:match_str("x ac")}]]) 1761 eq({ 3, 7 }, exec_lua [[return {re1:match_str("ac abbc")}]]) 1762 1763 api.nvim_buf_set_lines(0, 0, -1, true, { 'yy', 'abc abbc' }) 1764 eq({}, exec_lua [[return {re1:match_line(0, 0)}]]) 1765 eq({ 0, 3 }, exec_lua [[return {re1:match_line(0, 1)}]]) 1766 eq({ 3, 7 }, exec_lua [[return {re1:match_line(0, 1, 1)}]]) 1767 eq({ 3, 7 }, exec_lua [[return {re1:match_line(0, 1, 1, 8)}]]) 1768 eq({}, exec_lua [[return {re1:match_line(0, 1, 1, 7)}]]) 1769 eq({ 0, 3 }, exec_lua [[return {re1:match_line(0, 1, 0, 7)}]]) 1770 1771 -- vim.regex() error inside :silent! should not crash. #20546 1772 command([[silent! lua vim.regex('\\z')]]) 1773 assert_alive() 1774 end) 1775 1776 it('vim.defer_fn', function() 1777 eq( 1778 false, 1779 exec_lua [[ 1780 vim.g.test = false 1781 vim.defer_fn(function() vim.g.test = true end, 150) 1782 return vim.g.test 1783 ]] 1784 ) 1785 exec_lua [[vim.wait(1000, function() return vim.g.test end)]] 1786 eq(true, exec_lua [[return vim.g.test]]) 1787 end) 1788 1789 it('nested vim.defer_fn does not leak handles on exit #19727', function() 1790 n.expect_exit(exec_lua, function() 1791 vim.defer_fn(function() 1792 vim.defer_fn(function() 1793 vim.defer_fn(function() end, 0) 1794 end, 0) 1795 end, 0) 1796 vim.cmd('qall') 1797 end) 1798 end) 1799 1800 it('vim.defer_fn with timeout does not leak handles on exit', function() 1801 n.expect_exit(exec_lua, function() 1802 vim.defer_fn(function() end, 50) 1803 vim.cmd('qall') 1804 end) 1805 end) 1806 1807 describe('vim.region', function() 1808 it('charwise', function() 1809 insert(dedent([[ 1810 text tααt tααt text 1811 text tαxt txtα tex 1812 text tαxt tαxt 1813 ]])) 1814 eq({ 5, 13 }, exec_lua [[ return vim.region(0,{0,5},{0,13},'v',false)[0] ]]) 1815 eq({ 5, 15 }, exec_lua [[ return vim.region(0,{0,5},{0,13},'v',true)[0] ]]) 1816 eq({ 5, 15 }, exec_lua [[ return vim.region(0,{0,5},{0,14},'v',true)[0] ]]) 1817 eq({ 5, 15 }, exec_lua [[ return vim.region(0,{0,5},{0,15},'v',false)[0] ]]) 1818 eq({ 5, 17 }, exec_lua [[ return vim.region(0,{0,5},{0,15},'v',true)[0] ]]) 1819 eq({ 5, 17 }, exec_lua [[ return vim.region(0,{0,5},{0,16},'v',true)[0] ]]) 1820 eq({ 5, 17 }, exec_lua [[ return vim.region(0,{0,5},{0,17},'v',false)[0] ]]) 1821 eq({ 5, 18 }, exec_lua [[ return vim.region(0,{0,5},{0,17},'v',true)[0] ]]) 1822 end) 1823 it('blockwise', function() 1824 insert([[αα]]) 1825 eq({ 0, 5 }, exec_lua [[ return vim.region(0,{0,0},{0,4},'3',true)[0] ]]) 1826 end) 1827 it('linewise', function() 1828 insert(dedent([[ 1829 text tααt tααt text 1830 text tαxt txtα tex 1831 text tαxt tαxt 1832 ]])) 1833 eq({ 0, -1 }, exec_lua [[ return vim.region(0,{1,5},{1,14},'V',true)[1] ]]) 1834 end) 1835 it('getpos() input', function() 1836 insert('getpos') 1837 eq({ 0, 6 }, exec_lua [[ return vim.region(0,{0,0},'.','v',true)[0] ]]) 1838 end) 1839 end) 1840 1841 describe('vim.on_key', function() 1842 it('tracks Unicode input', function() 1843 insert([[hello world ]]) 1844 1845 exec_lua [[ 1846 keys = {} 1847 typed = {} 1848 1849 vim.on_key(function(buf, typed_buf) 1850 if buf:byte() == 27 then 1851 buf = "<ESC>" 1852 end 1853 if typed_buf:byte() == 27 then 1854 typed_buf = "<ESC>" 1855 end 1856 1857 table.insert(keys, buf) 1858 table.insert(typed, typed_buf) 1859 end) 1860 ]] 1861 1862 insert([[next 🤦 lines å …]]) 1863 1864 -- It has escape in the keys pressed 1865 eq('inext 🤦 lines å …<ESC>', exec_lua [[return table.concat(keys, '')]]) 1866 eq('inext 🤦 lines å …<ESC>', exec_lua [[return table.concat(typed, '')]]) 1867 end) 1868 1869 it('tracks input with modifiers', function() 1870 exec_lua [[ 1871 keys = {} 1872 typed = {} 1873 1874 vim.on_key(function(buf, typed_buf) 1875 table.insert(keys, vim.fn.keytrans(buf)) 1876 table.insert(typed, vim.fn.keytrans(typed_buf)) 1877 end) 1878 ]] 1879 1880 feed([[i<C-V><C-;><C-V><C-…><Esc>]]) 1881 1882 eq('i<C-V><C-;><C-V><C-…><Esc>', exec_lua [[return table.concat(keys, '')]]) 1883 eq('i<C-V><C-;><C-V><C-…><Esc>', exec_lua [[return table.concat(typed, '')]]) 1884 end) 1885 1886 it('works with character find and Select mode', function() 1887 insert('12345') 1888 1889 exec_lua [[ 1890 typed = {} 1891 1892 vim.cmd('snoremap # @') 1893 1894 vim.on_key(function(buf, typed_buf) 1895 table.insert(typed, vim.fn.keytrans(typed_buf)) 1896 end) 1897 ]] 1898 1899 feed('F3gHβγδεζ<Esc>gH…<Esc>gH#$%^') 1900 eq('F3gHβγδεζ<Esc>gH…<Esc>gH#$%^', exec_lua [[return table.concat(typed, '')]]) 1901 end) 1902 1903 it('allows removing on_key listeners', function() 1904 -- Create some unused namespaces 1905 api.nvim_create_namespace('unused1') 1906 api.nvim_create_namespace('unused2') 1907 api.nvim_create_namespace('unused3') 1908 api.nvim_create_namespace('unused4') 1909 1910 insert([[hello world]]) 1911 1912 exec_lua [[ 1913 keys = {} 1914 1915 return vim.on_key(function(buf) 1916 if buf:byte() == 27 then 1917 buf = "<ESC>" 1918 end 1919 1920 table.insert(keys, buf) 1921 end, vim.api.nvim_create_namespace("logger")) 1922 ]] 1923 1924 insert([[next lines]]) 1925 1926 eq(1, exec_lua('return vim.on_key()')) 1927 exec_lua("vim.on_key(nil, vim.api.nvim_create_namespace('logger'))") 1928 eq(0, exec_lua('return vim.on_key()')) 1929 1930 insert([[more lines]]) 1931 1932 -- It has escape in the keys pressed 1933 eq('inext lines<ESC>', exec_lua [[return table.concat(keys, '')]]) 1934 end) 1935 1936 it('skips any function that caused an error and shows stacktrace', function() 1937 insert([[hello world]]) 1938 1939 exec_lua [[ 1940 local function ErrF2() 1941 error("Dumb Error") 1942 end 1943 local function ErrF1() 1944 ErrF2() 1945 end 1946 1947 keys = {} 1948 1949 return vim.on_key(function(buf) 1950 if buf:byte() == 27 then 1951 buf = "<ESC>" 1952 end 1953 1954 table.insert(keys, buf) 1955 1956 if buf == 'l' then 1957 ErrF1() 1958 end 1959 end) 1960 ]] 1961 1962 insert([[next lines]]) 1963 insert([[more lines]]) 1964 1965 -- Only the first letter gets added. After that we remove the callback 1966 eq('inext l', exec_lua [[ return table.concat(keys, '') ]]) 1967 1968 local errmsg = api.nvim_get_vvar('errmsg') 1969 matches( 1970 [[ 1971 ^vim%.on%_key%(%) callbacks:.* 1972 With ns%_id %d+: .*: Dumb Error 1973 stack traceback: 1974 .*: in function 'error' 1975 .*: in function 'ErrF2' 1976 .*: in function 'ErrF1' 1977 .*]], 1978 errmsg 1979 ) 1980 end) 1981 1982 it('argument 1 is keys after mapping, argument 2 is typed keys', function() 1983 exec_lua [[ 1984 keys = {} 1985 typed = {} 1986 1987 vim.cmd("inoremap hello world") 1988 1989 vim.on_key(function(buf, typed_buf) 1990 if buf:byte() == 27 then 1991 buf = "<ESC>" 1992 end 1993 if typed_buf:byte() == 27 then 1994 typed_buf = "<ESC>" 1995 end 1996 1997 table.insert(keys, buf) 1998 table.insert(typed, typed_buf) 1999 end) 2000 ]] 2001 insert('hello') 2002 2003 eq('iworld<ESC>', exec_lua [[return table.concat(keys, '')]]) 2004 eq('ihello<ESC>', exec_lua [[return table.concat(typed, '')]]) 2005 end) 2006 2007 it('can call vim.fn functions on Ctrl-C #17273', function() 2008 exec_lua([[ 2009 _G.ctrl_c_cmdtype = '' 2010 2011 vim.on_key(function(c) 2012 if c == '\3' then 2013 _G.ctrl_c_cmdtype = vim.fn.getcmdtype() 2014 end 2015 end) 2016 ]]) 2017 feed('/') 2018 poke_eventloop() -- This is needed because Ctrl-C flushes input 2019 feed('<C-C>') 2020 eq('/', exec_lua([[return _G.ctrl_c_cmdtype]])) 2021 end) 2022 2023 it('callback is not invoked recursively #30752', function() 2024 local screen = Screen.new(60, 10) 2025 exec_lua([[ 2026 vim.on_key(function(key, typed) 2027 vim.api.nvim_echo({ 2028 { 'key_cb\n' }, 2029 { ("KEYCB: key '%s', typed '%s'\n"):format(key, typed) }, 2030 }, false, {}) 2031 end) 2032 ]]) 2033 feed('^') 2034 screen:expect([[ 2035 | 2036 {1:~ }|*5 2037 {3: }| 2038 key_cb | 2039 KEYCB: key '^', typed '^' | 2040 {6:Press ENTER or type command to continue}^ | 2041 ]]) 2042 feed('<C-C>') 2043 screen:expect([[ 2044 | 2045 {1:~ }|*3 2046 {3: }| 2047 key_cb | 2048 KEYCB: key '^', typed '^' | 2049 key_cb | 2050 KEYCB: key '{18:^C}', typed '{18:^C}' | 2051 {6:Press ENTER or type command to continue}^ | 2052 ]]) 2053 feed('<C-C>') 2054 screen:expect([[ 2055 ^ | 2056 {1:~ }|*8 2057 | 2058 ]]) 2059 end) 2060 2061 it('can discard input', function() 2062 -- discard the first key produced by every other 'x' key typed 2063 exec_lua [[ 2064 n_key = 0 2065 2066 vim.on_key(function(buf, typed_buf) 2067 if typed_buf == 'x' then 2068 n_key = n_key + 1 2069 return (n_key % 2 == 0) and '' or nil 2070 end 2071 end) 2072 ]] 2073 2074 api.nvim_buf_set_lines(0, 0, -1, true, { '54321' }) 2075 2076 feed('x') -- 'x' not discarded 2077 expect('4321') 2078 feed('x') -- 'x' discarded 2079 expect('4321') 2080 feed('x') -- 'x' not discarded 2081 expect('321') 2082 feed('x') -- 'x' discarded 2083 expect('321') 2084 2085 api.nvim_buf_set_lines(0, 0, -1, true, { '54321' }) 2086 2087 -- only the first key from the mapping is discarded 2088 command('nnoremap x $x') 2089 feed('0x') -- '$' not discarded 2090 expect('5432') 2091 feed('0x') -- '$' discarded 2092 expect('432') 2093 feed('0x') -- '$' not discarded 2094 expect('43') 2095 feed('0x') -- '$' discarded 2096 expect('3') 2097 2098 feed('i') 2099 -- when discarding <Cmd>, the following command is also discarded. 2100 command([[inoremap x <Cmd>call append('$', 'foo')<CR>]]) 2101 feed('x') -- not discarded 2102 expect('3\nfoo') 2103 feed('x') -- discarded 2104 expect('3\nfoo') 2105 feed('x') -- not discarded 2106 expect('3\nfoo\nfoo') 2107 feed('x') -- discarded 2108 expect('3\nfoo\nfoo') 2109 2110 -- K_LUA is handled similarly to <Cmd> 2111 exec_lua([[vim.keymap.set('i', 'x', function() vim.fn.append('$', 'bar') end)]]) 2112 feed('x') -- not discarded 2113 expect('3\nfoo\nfoo\nbar') 2114 feed('x') -- discarded 2115 expect('3\nfoo\nfoo\nbar') 2116 feed('x') -- not discarded 2117 expect('3\nfoo\nfoo\nbar\nbar') 2118 feed('x') -- discarded 2119 expect('3\nfoo\nfoo\nbar\nbar') 2120 end) 2121 2122 it('behaves consistently with <Cmd>, K_LUA, nvim_paste', function() 2123 exec_lua([[ 2124 vim.keymap.set('i', '<F2>', "<Cmd>call append('$', 'FOO')<CR>") 2125 vim.keymap.set('i', '<F3>', function() vim.fn.append('$', 'BAR') end) 2126 ]]) 2127 2128 feed('qrafoo<F2><F3>') 2129 api.nvim_paste('bar', false, -1) 2130 feed('<Esc>q') 2131 expect('foobar\nFOO\nBAR') 2132 2133 exec_lua([[ 2134 keys = {} 2135 typed = {} 2136 2137 vim.on_key(function(buf, typed_buf) 2138 table.insert(keys, buf) 2139 table.insert(typed, typed_buf) 2140 end) 2141 ]]) 2142 2143 feed('@r') 2144 local keys = exec_lua('return keys') 2145 eq('@r', exec_lua([[return table.concat(typed, '')]])) 2146 expect('foobarfoobar\nFOO\nBAR\nFOO\nBAR') 2147 2148 -- Add a new callback that discards most special keys as well as 'f'. 2149 -- The old callback is still active. 2150 exec_lua([[ 2151 vim.on_key(function(buf, _) 2152 if not buf:find('^[@rao\27]$') then 2153 return '' 2154 end 2155 end) 2156 2157 keys = {} 2158 typed = {} 2159 ]]) 2160 2161 feed('@r') 2162 eq(keys, exec_lua('return keys')) 2163 eq('@r', exec_lua([[return table.concat(typed, '')]])) 2164 -- The "bar" paste is discarded as a whole. 2165 expect('foobarfoobaroo\nFOO\nBAR\nFOO\nBAR') 2166 end) 2167 2168 it('callback invalid return', function() 2169 -- second key produces an error which removes the callback 2170 exec_lua [[ 2171 n_call = 0 2172 2173 vim.on_key(function(buf, typed_buf) 2174 if typed_buf == 'x' then 2175 n_call = n_call + 1 2176 end 2177 return n_call >= 2 and '!' or nil 2178 end) 2179 ]] 2180 2181 api.nvim_buf_set_lines(0, 0, -1, true, { '54321' }) 2182 2183 feed('x') 2184 eq(1, exec_lua [[ return n_call ]]) 2185 eq(1, exec_lua [[ return vim.on_key(nil, nil) ]]) 2186 eq('', eval('v:errmsg')) 2187 feed('x') 2188 eq(2, exec_lua [[ return n_call ]]) 2189 matches('return string must be empty', eval('v:errmsg')) 2190 command('let v:errmsg = ""') 2191 2192 eq(0, exec_lua [[ return vim.on_key(nil, nil) ]]) 2193 2194 feed('x') 2195 eq(2, exec_lua [[ return n_call ]]) 2196 expect('21') 2197 eq('', eval('v:errmsg')) 2198 end) 2199 end) 2200 2201 describe('vim.wait', function() 2202 before_each(function() 2203 exec_lua [[ 2204 -- high precision timer 2205 get_time = function() 2206 return vim.fn.reltimefloat(vim.fn.reltime()) 2207 end 2208 ]] 2209 end) 2210 2211 it('runs from lua', function() 2212 exec_lua [[vim.wait(100, function() return true end)]] 2213 end) 2214 2215 it('returns all (multiple) callback results', function() 2216 eq({ true, false }, exec_lua [[return { vim.wait(200, function() return true, false end) }]]) 2217 eq( 2218 { true, 'a', 42, { ok = { 'yes' } } }, 2219 exec_lua [[ 2220 local ok, rv1, rv2, rv3 = vim.wait(200, function() 2221 return true, 'a', 42, { ok = { 'yes' } } 2222 end) 2223 2224 return { ok, rv1, rv2, rv3 } 2225 ]] 2226 ) 2227 end) 2228 2229 it('does not return callback results on timeout', function() 2230 eq( 2231 { false, -1 }, 2232 exec_lua [[ 2233 return { vim.wait(1, function() 2234 return false, 'a', 42, { ok = { 'yes' } } 2235 end) } 2236 ]] 2237 ) 2238 end) 2239 2240 it('waits the expected time if false', function() 2241 eq( 2242 { time = true, wait_result = { false, -1 } }, 2243 exec_lua [[ 2244 start_time = get_time() 2245 wait_succeed, wait_fail_val = vim.wait(200, function() return false end) 2246 2247 return { 2248 -- 150ms waiting or more results in true. Flaky tests are bad. 2249 time = (start_time + 0.15) < get_time(), 2250 wait_result = {wait_succeed, wait_fail_val} 2251 } 2252 ]] 2253 ) 2254 end) 2255 2256 it('does not block other events', function() 2257 eq( 2258 { time = true, wait_result = true }, 2259 exec_lua [[ 2260 start_time = get_time() 2261 2262 vim.g.timer_result = false 2263 timer = vim.uv.new_timer() 2264 timer:start(100, 0, vim.schedule_wrap(function() 2265 vim.g.timer_result = true 2266 end)) 2267 2268 -- Would wait ten seconds if results blocked. 2269 wait_result = vim.wait(10000, function() return vim.g.timer_result end) 2270 2271 timer:close() 2272 2273 return { 2274 time = (start_time + 5) > get_time(), 2275 wait_result = wait_result, 2276 } 2277 ]] 2278 ) 2279 end) 2280 2281 it('does not process non-fast events when commanded', function() 2282 eq( 2283 { wait_result = false }, 2284 exec_lua [[ 2285 start_time = get_time() 2286 2287 vim.g.timer_result = false 2288 timer = vim.uv.new_timer() 2289 timer:start(100, 0, vim.schedule_wrap(function() 2290 vim.g.timer_result = true 2291 end)) 2292 2293 wait_result = vim.wait(300, function() return vim.g.timer_result end, nil, true) 2294 2295 timer:close() 2296 2297 return { 2298 wait_result = wait_result, 2299 } 2300 ]] 2301 ) 2302 end) 2303 2304 it('works with vim.defer_fn', function() 2305 eq( 2306 { time = true, wait_result = true }, 2307 exec_lua [[ 2308 start_time = get_time() 2309 2310 vim.defer_fn(function() vim.g.timer_result = true end, 100) 2311 wait_result = vim.wait(10000, function() return vim.g.timer_result end) 2312 2313 return { 2314 time = (start_time + 5) > get_time(), 2315 wait_result = wait_result, 2316 } 2317 ]] 2318 ) 2319 end) 2320 2321 it('does not crash when callback errors', function() 2322 local result = exec_lua [[ 2323 return {pcall(function() vim.wait(1000, function() error("As Expected") end) end)} 2324 ]] 2325 eq({ false, '[string "<nvim>"]:1: As Expected' }, { result[1], remove_trace(result[2]) }) 2326 end) 2327 2328 it('callback must be a function', function() 2329 eq( 2330 { false, 'vim.wait: callback must be callable' }, 2331 exec_lua [[return {pcall(function() vim.wait(1000, 13) end)}]] 2332 ) 2333 end) 2334 2335 it('waits if callback arg is nil', function() 2336 eq( 2337 true, 2338 exec_lua [[ 2339 local start_time = vim.uv.hrtime() 2340 vim.wait(50.1, nil) -- select('#', ...) == 1 2341 return vim.uv.hrtime() - start_time > 25000 2342 ]] 2343 ) 2344 end) 2345 2346 it('waits if callback arg is omitted', function() 2347 eq( 2348 true, 2349 exec_lua [[ 2350 local start_time = vim.uv.hrtime() 2351 vim.wait(50) -- select('#', ...) == 0 2352 return vim.uv.hrtime() - start_time > 25000 2353 ]] 2354 ) 2355 end) 2356 2357 it('invokes callback exactly once if it returns true immediately', function() 2358 eq( 2359 true, 2360 exec_lua [[ 2361 vim.g.wait_count = 0 2362 vim.wait(1000, function() 2363 vim.g.wait_count = vim.g.wait_count + 1 2364 return true 2365 end, 20) 2366 return vim.g.wait_count == 1 2367 ]] 2368 ) 2369 end) 2370 2371 it('calls callbacks few times with large `interval`', function() 2372 eq( 2373 true, 2374 exec_lua [[ 2375 vim.g.wait_count = 0 2376 vim.wait(50, function() vim.g.wait_count = vim.g.wait_count + 1 end, 200) 2377 return vim.g.wait_count < 5 2378 ]] 2379 ) 2380 end) 2381 2382 it('does not leak when Nvim exits while waiting', function() 2383 n.expect_exit(500, exec_lua, function() 2384 vim.defer_fn(function() 2385 vim.cmd('qall!') 2386 end, 10) 2387 vim.wait(10000) 2388 end) 2389 end) 2390 2391 it('plays nice with `not` when fails', function() 2392 eq( 2393 true, 2394 exec_lua [[ 2395 if not vim.wait(50, function() end) then 2396 return true 2397 end 2398 2399 return false 2400 ]] 2401 ) 2402 end) 2403 2404 it('plays nice with `if` when success', function() 2405 eq( 2406 true, 2407 exec_lua [[ 2408 if vim.wait(50, function() return true end) then 2409 return true 2410 end 2411 2412 return false 2413 ]] 2414 ) 2415 end) 2416 2417 it('returns immediately with false if timeout is 0', function() 2418 eq( 2419 { false, -1 }, 2420 exec_lua [[ 2421 return { 2422 vim.wait(0, function() return false end) 2423 } 2424 ]] 2425 ) 2426 end) 2427 2428 it('works with tables with __call', function() 2429 eq( 2430 true, 2431 exec_lua [[ 2432 local t = setmetatable({}, {__call = function(...) return true end}) 2433 return vim.wait(100, t, 10) 2434 ]] 2435 ) 2436 end) 2437 2438 it('works with tables with __call that change', function() 2439 eq( 2440 true, 2441 exec_lua [[ 2442 local t = {count = 0} 2443 setmetatable(t, { 2444 __call = function() 2445 t.count = t.count + 1 2446 return t.count > 3 2447 end 2448 }) 2449 2450 return vim.wait(1000, t, 10) 2451 ]] 2452 ) 2453 end) 2454 2455 it('fails with negative intervals', function() 2456 local pcall_result = exec_lua [[ 2457 return pcall(function() vim.wait(1000, function() return false end, -1) end) 2458 ]] 2459 2460 eq(false, pcall_result) 2461 end) 2462 2463 it('fails with weird intervals', function() 2464 local pcall_result = exec_lua [[ 2465 return pcall(function() vim.wait(1000, function() return false end, 'a string value') end) 2466 ]] 2467 2468 eq(false, pcall_result) 2469 end) 2470 2471 describe('returns -2 when interrupted', function() 2472 before_each(function() 2473 local channel = api.nvim_get_chan_info(0).id 2474 api.nvim_set_var('channel', channel) 2475 end) 2476 2477 it('without callback', function() 2478 exec_lua([[ 2479 function _G.Wait() 2480 vim.rpcnotify(vim.g.channel, 'ready') 2481 -- handles math.huge #36854 2482 local _, interrupted = vim.wait(math.huge) 2483 vim.rpcnotify(vim.g.channel, 'wait', interrupted) 2484 end 2485 ]]) 2486 feed(':lua _G.Wait()<CR>') 2487 eq({ 'notification', 'ready', {} }, next_msg(500)) 2488 feed('<C-C>') 2489 eq({ 'notification', 'wait', { -2 } }, next_msg(500)) 2490 end) 2491 2492 it('with callback', function() 2493 exec_lua([[ 2494 function _G.Wait() 2495 vim.rpcnotify(vim.g.channel, 'ready') 2496 local _, interrupted = vim.wait(math.huge, function() end) 2497 vim.rpcnotify(vim.g.channel, 'wait', interrupted) 2498 end 2499 ]]) 2500 feed(':lua _G.Wait()<CR>') 2501 eq({ 'notification', 'ready', {} }, next_msg(500)) 2502 feed('<C-C>') 2503 eq({ 'notification', 'wait', { -2 } }, next_msg(500)) 2504 end) 2505 end) 2506 2507 it('fails in fast callbacks #26122', function() 2508 local screen = Screen.new(80, 10) 2509 exec_lua([[ 2510 local timer = vim.uv.new_timer() 2511 timer:start(0, 0, function() 2512 timer:close() 2513 vim.wait(100, function() end) 2514 end) 2515 ]]) 2516 screen:expect({ 2517 any = pesc('E5560: vim.wait must not be called in a fast event context'), 2518 }) 2519 feed('<CR>') 2520 assert_alive() 2521 end) 2522 end) 2523 2524 it('vim.notify_once', function() 2525 local screen = Screen.new(60, 5) 2526 screen:expect { 2527 grid = [[ 2528 ^ | 2529 {1:~ }|*3 2530 | 2531 ]], 2532 } 2533 exec_lua [[vim.notify_once("I'll only tell you this once...", vim.log.levels.WARN)]] 2534 screen:expect { 2535 grid = [[ 2536 ^ | 2537 {1:~ }|*3 2538 {19:I'll only tell you this once...} | 2539 ]], 2540 } 2541 feed('<C-l>') 2542 screen:expect { 2543 grid = [[ 2544 ^ | 2545 {1:~ }|*3 2546 | 2547 ]], 2548 } 2549 exec_lua [[vim.notify_once("I'll only tell you this once...")]] 2550 screen:expect_unchanged() 2551 end) 2552 2553 describe('vim.schedule_wrap', function() 2554 it('preserves argument lists', function() 2555 exec_lua [[ 2556 local fun = vim.schedule_wrap(function(kling, klang, klonk) 2557 vim.rpcnotify(1, 'mayday_mayday', {a=kling, b=klang, c=klonk}) 2558 end) 2559 fun("BOB", nil, "MIKE") 2560 ]] 2561 eq({ 'notification', 'mayday_mayday', { { a = 'BOB', c = 'MIKE' } } }, next_msg()) 2562 2563 -- let's gooooo 2564 exec_lua [[ 2565 vim.schedule_wrap(function(...) vim.rpcnotify(1, 'boogalo', select('#', ...)) end)(nil,nil,nil,nil) 2566 ]] 2567 eq({ 'notification', 'boogalo', { 4 } }, next_msg()) 2568 end) 2569 end) 2570 2571 describe('vim.api.nvim_buf_call', function() 2572 it('can access buf options', function() 2573 local buf1 = api.nvim_get_current_buf() 2574 local buf2 = exec_lua [[ 2575 buf2 = vim.api.nvim_create_buf(false, true) 2576 return buf2 2577 ]] 2578 2579 eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 })) 2580 eq(false, api.nvim_get_option_value('autoindent', { buf = buf2 })) 2581 2582 local val = exec_lua [[ 2583 return vim.api.nvim_buf_call(buf2, function() 2584 vim.cmd "set autoindent" 2585 return vim.api.nvim_get_current_buf() 2586 end) 2587 ]] 2588 2589 eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 })) 2590 eq(true, api.nvim_get_option_value('autoindent', { buf = buf2 })) 2591 eq(buf1, api.nvim_get_current_buf()) 2592 eq(buf2, val) 2593 end) 2594 2595 it('does not cause ml_get errors with invalid visual selection', function() 2596 -- Should be fixed by vim-patch:8.2.4028. 2597 exec_lua [[ 2598 local api = vim.api 2599 local t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end 2600 api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"}) 2601 api.nvim_feedkeys(t "G<C-V>", "txn", false) 2602 api.nvim_buf_call(api.nvim_create_buf(false, true), function() vim.cmd "redraw" end) 2603 ]] 2604 end) 2605 2606 it('can be nested crazily with hidden buffers', function() 2607 eq( 2608 true, 2609 exec_lua([[ 2610 local function scratch_buf_call(fn) 2611 local buf = vim.api.nvim_create_buf(false, true) 2612 vim.api.nvim_set_option_value('cindent', true, {buf = buf}) 2613 return vim.api.nvim_buf_call(buf, function() 2614 return vim.api.nvim_get_current_buf() == buf 2615 and vim.api.nvim_get_option_value('cindent', {buf = buf}) 2616 and fn() 2617 end) and vim.api.nvim_buf_delete(buf, {}) == nil 2618 end 2619 2620 return scratch_buf_call(function() 2621 return scratch_buf_call(function() 2622 return scratch_buf_call(function() 2623 return scratch_buf_call(function() 2624 return scratch_buf_call(function() 2625 return scratch_buf_call(function() 2626 return scratch_buf_call(function() 2627 return scratch_buf_call(function() 2628 return scratch_buf_call(function() 2629 return scratch_buf_call(function() 2630 return scratch_buf_call(function() 2631 return scratch_buf_call(function() 2632 return true 2633 end) 2634 end) 2635 end) 2636 end) 2637 end) 2638 end) 2639 end) 2640 end) 2641 end) 2642 end) 2643 end) 2644 end) 2645 ]]) 2646 ) 2647 end) 2648 2649 it('can return values by reference', function() 2650 eq( 2651 { 4, 7 }, 2652 exec_lua [[ 2653 local val = {4, 10} 2654 local ref = vim.api.nvim_buf_call(0, function() return val end) 2655 ref[2] = 7 2656 return val 2657 ]] 2658 ) 2659 end) 2660 2661 it('can get Visual selection in current buffer #34162', function() 2662 insert('foo bar baz') 2663 feed('gg0fbvtb') 2664 local text = exec_lua([[ 2665 return vim.api.nvim_buf_call(0, function() 2666 return vim.fn.getregion(vim.fn.getpos('.'), vim.fn.getpos('v')) 2667 end) 2668 ]]) 2669 eq({ 'bar ' }, text) 2670 end) 2671 end) 2672 2673 describe('vim.api.nvim_win_call', function() 2674 it('can access window options', function() 2675 command('vsplit') 2676 local win1 = api.nvim_get_current_win() 2677 command('wincmd w') 2678 local win2 = exec_lua [[ 2679 win2 = vim.api.nvim_get_current_win() 2680 return win2 2681 ]] 2682 command('wincmd p') 2683 2684 eq('', api.nvim_get_option_value('winhighlight', { win = win1 })) 2685 eq('', api.nvim_get_option_value('winhighlight', { win = win2 })) 2686 2687 local val = exec_lua [[ 2688 return vim.api.nvim_win_call(win2, function() 2689 vim.cmd "setlocal winhighlight=Normal:Normal" 2690 return vim.api.nvim_get_current_win() 2691 end) 2692 ]] 2693 2694 eq('', api.nvim_get_option_value('winhighlight', { win = win1 })) 2695 eq('Normal:Normal', api.nvim_get_option_value('winhighlight', { win = win2 })) 2696 eq(win1, api.nvim_get_current_win()) 2697 eq(win2, val) 2698 end) 2699 2700 it('failure modes', function() 2701 matches( 2702 'nvim_exec2%(%), line 1: Vim:E492: Not an editor command: fooooo', 2703 pcall_err(exec_lua, [[vim.api.nvim_win_call(0, function() vim.cmd 'fooooo' end)]]) 2704 ) 2705 eq( 2706 'Lua: [string "<nvim>"]:0: fooooo', 2707 pcall_err(exec_lua, [[vim.api.nvim_win_call(0, function() error('fooooo') end)]]) 2708 ) 2709 end) 2710 2711 it('does not cause ml_get errors with invalid visual selection', function() 2712 -- Add lines to the current buffer and make another window looking into an empty buffer. 2713 exec_lua [[ 2714 _G.api = vim.api 2715 _G.t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end 2716 _G.win_lines = api.nvim_get_current_win() 2717 vim.cmd "new" 2718 _G.win_empty = api.nvim_get_current_win() 2719 api.nvim_set_current_win(win_lines) 2720 api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"}) 2721 ]] 2722 2723 -- Start Visual in current window, redraw in other window with fewer lines. 2724 -- Should be fixed by vim-patch:8.2.4018. 2725 exec_lua [[ 2726 api.nvim_feedkeys(t "G<C-V>", "txn", false) 2727 api.nvim_win_call(win_empty, function() vim.cmd "redraw" end) 2728 ]] 2729 2730 -- Start Visual in current window, extend it in other window with more lines. 2731 -- Fixed for win_execute by vim-patch:8.2.4026, but nvim_win_call should also not be affected. 2732 exec_lua [[ 2733 api.nvim_feedkeys(t "<Esc>gg", "txn", false) 2734 api.nvim_set_current_win(win_empty) 2735 api.nvim_feedkeys(t "gg<C-V>", "txn", false) 2736 api.nvim_win_call(win_lines, function() api.nvim_feedkeys(t "G<C-V>", "txn", false) end) 2737 vim.cmd "redraw" 2738 ]] 2739 end) 2740 2741 it('updates ruler if cursor moved', function() 2742 -- Fixed for win_execute in vim-patch:8.1.2124, but should've applied to nvim_win_call too! 2743 local screen = Screen.new(30, 5) 2744 exec_lua [[ 2745 _G.api = vim.api 2746 vim.opt.ruler = true 2747 local lines = {} 2748 for i = 0, 499 do lines[#lines + 1] = tostring(i) end 2749 api.nvim_buf_set_lines(0, 0, -1, true, lines) 2750 api.nvim_win_set_cursor(0, {20, 0}) 2751 vim.cmd "split" 2752 _G.win = api.nvim_get_current_win() 2753 vim.cmd "wincmd w | redraw" 2754 ]] 2755 screen:expect [[ 2756 19 | 2757 {2:< Name] [+] 20,1 3%}| 2758 ^19 | 2759 {3:< Name] [+] 20,1 3%}| 2760 | 2761 ]] 2762 exec_lua [[ 2763 api.nvim_win_call(win, function() api.nvim_win_set_cursor(0, {100, 0}) end) 2764 vim.cmd "redraw" 2765 ]] 2766 screen:expect [[ 2767 99 | 2768 {2:< Name] [+] 100,1 19%}| 2769 ^19 | 2770 {3:< Name] [+] 20,1 3%}| 2771 | 2772 ]] 2773 end) 2774 2775 it('can return values by reference', function() 2776 eq( 2777 { 7, 10 }, 2778 exec_lua [[ 2779 local val = {4, 10} 2780 local ref = vim.api.nvim_win_call(0, function() return val end) 2781 ref[1] = 7 2782 return val 2783 ]] 2784 ) 2785 end) 2786 2787 it('layout in current tabpage does not affect windows in others', function() 2788 command('tab split') 2789 local t2_move_win = api.nvim_get_current_win() 2790 command('vsplit') 2791 local t2_other_win = api.nvim_get_current_win() 2792 command('tabprevious') 2793 matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)')) 2794 command('vsplit') 2795 2796 -- Without vim-patch:8.2.3862, this gives E36, despite just the 1st tabpage being full. 2797 exec_lua('vim.api.nvim_win_call(..., function() vim.cmd.wincmd "J" end)', t2_move_win) 2798 eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2)) 2799 end) 2800 end) 2801 2802 describe('vim.iconv', function() 2803 it('can convert strings', function() 2804 eq( 2805 'hello', 2806 exec_lua [[ 2807 return vim.iconv('hello', 'latin1', 'utf-8') 2808 ]] 2809 ) 2810 end) 2811 2812 it('can validate arguments', function() 2813 eq( 2814 { false, 'Expected at least 3 arguments' }, 2815 exec_lua [[ 2816 return {pcall(vim.iconv, 'hello')} 2817 ]] 2818 ) 2819 2820 eq( 2821 { false, "bad argument #3 to '?' (expected string)" }, 2822 exec_lua [[ 2823 return {pcall(vim.iconv, 'hello', 'utf-8', true)} 2824 ]] 2825 ) 2826 end) 2827 2828 it('can handle bad encodings', function() 2829 eq( 2830 NIL, 2831 exec_lua [[ 2832 return vim.iconv('hello', 'foo', 'bar') 2833 ]] 2834 ) 2835 end) 2836 2837 it('can handle strings with NUL bytes', function() 2838 eq( 2839 7, 2840 exec_lua [[ 2841 local a = string.char(97, 98, 99, 0, 100, 101, 102) -- abc\0def 2842 return string.len(vim.iconv(a, 'latin1', 'utf-8')) 2843 ]] 2844 ) 2845 end) 2846 end) 2847 2848 describe('vim.defaulttable', function() 2849 it('creates nested table by default', function() 2850 eq( 2851 { b = { c = 1 } }, 2852 exec_lua [[ 2853 local a = vim.defaulttable() 2854 a.b.c = 1 2855 return a 2856 ]] 2857 ) 2858 end) 2859 2860 it('allows to create default objects', function() 2861 eq( 2862 { b = 1 }, 2863 exec_lua [[ 2864 local a = vim.defaulttable(function() return 0 end) 2865 a.b = a.b + 1 2866 return a 2867 ]] 2868 ) 2869 end) 2870 2871 it('accepts the key name', function() 2872 eq( 2873 { b = 'b', c = 'c' }, 2874 exec_lua [[ 2875 local a = vim.defaulttable(function(k) return k end) 2876 local _ = a.b 2877 local _ = a.c 2878 return a 2879 ]] 2880 ) 2881 end) 2882 end) 2883 2884 it('vim.lua_omnifunc', function() 2885 local screen = Screen.new(60, 5) 2886 command [[ set omnifunc=v:lua.vim.lua_omnifunc ]] 2887 2888 -- Note: the implementation is shared with lua command line completion. 2889 -- More tests for completion in lua/command_line_completion_spec.lua 2890 feed [[ivim.insp<c-x><c-o>]] 2891 screen:expect { 2892 grid = [[ 2893 vim.inspect^ | 2894 {1:~ }{12: inspect }{1: }| 2895 {1:~ }{4: inspect_pos }{1: }| 2896 {1:~ }| 2897 {5:-- Omni completion (^O^N^P) }{6:match 1 of 2} | 2898 ]], 2899 } 2900 end) 2901 2902 it('vim.print', function() 2903 -- vim.print() returns its args. 2904 eq( 2905 { 42, 'abc', { a = { b = 77 } } }, 2906 exec_lua [[return {vim.print(42, 'abc', { a = { b = 77 }})}]] 2907 ) 2908 2909 -- vim.print() pretty-prints the args. 2910 eq( 2911 dedent [[ 2912 2913 42 2914 abc 2915 { 2916 a = { 2917 b = 77 2918 } 2919 }]], 2920 eval [[execute('lua vim.print(42, "abc", { a = { b = 77 }})')]] 2921 ) 2922 end) 2923 2924 it('vim.F.if_nil', function() 2925 local function if_nil(...) 2926 return exec_lua( 2927 [[ 2928 local args = {...} 2929 local nargs = select('#', ...) 2930 for i = 1, nargs do 2931 if args[i] == vim.NIL then 2932 args[i] = nil 2933 end 2934 end 2935 return vim.F.if_nil(unpack(args, 1, nargs)) 2936 ]], 2937 ... 2938 ) 2939 end 2940 2941 local a = NIL 2942 local b = NIL 2943 local c = 42 2944 local d = false 2945 eq(42, if_nil(a, c)) 2946 eq(false, if_nil(d, b)) 2947 eq(42, if_nil(a, b, c, d)) 2948 eq(false, if_nil(d)) 2949 eq(false, if_nil(d, c)) 2950 eq(NIL, if_nil(a)) 2951 end) 2952 2953 it('lpeg', function() 2954 eq( 2955 5, 2956 exec_lua [[ 2957 local m = vim.lpeg 2958 return m.match(m.R'09'^1, '4504ab') 2959 ]] 2960 ) 2961 2962 eq(4, exec_lua [[ return vim.re.match("abcde", '[a-c]+') ]]) 2963 end) 2964 2965 it('vim.ringbuf', function() 2966 local results = exec_lua([[ 2967 local ringbuf = vim.ringbuf(3) 2968 ringbuf:push("a") -- idx: 0 2969 local peeka1 = ringbuf:peek() 2970 local peeka2 = ringbuf:peek() 2971 local popa = ringbuf:pop() 2972 local popnil = ringbuf:pop() 2973 ringbuf:push("a") -- idx: 1 2974 ringbuf:push("b") -- idx: 2 2975 2976 -- doesn't read last added item, but uses separate read index 2977 local pop_after_add_b = ringbuf:pop() 2978 2979 ringbuf:push("c") -- idx: 3 wraps around, overrides idx: 0 "a" 2980 ringbuf:push("d") -- idx: 4 wraps around, overrides idx: 1 "a" 2981 return { 2982 peeka1 = peeka1, 2983 peeka2 = peeka2, 2984 pop1 = popa, 2985 pop2 = popnil, 2986 pop3 = ringbuf:pop(), 2987 pop4 = ringbuf:pop(), 2988 pop5 = ringbuf:pop(), 2989 pop_after_add_b = pop_after_add_b, 2990 } 2991 ]]) 2992 local expected = { 2993 peeka1 = 'a', 2994 peeka2 = 'a', 2995 pop1 = 'a', 2996 pop2 = nil, 2997 pop3 = 'b', 2998 pop4 = 'c', 2999 pop5 = 'd', 3000 pop_after_add_b = 'a', 3001 } 3002 eq(expected, results) 3003 end) 3004 end) 3005 3006 describe('lua: builtin modules', function() 3007 local function do_tests() 3008 eq(2, exec_lua [[return vim.tbl_count {x=1,y=2}]]) 3009 eq('{ 10, "spam" }', exec_lua [[return vim.inspect {10, 'spam'}]]) 3010 end 3011 3012 it('works', function() 3013 clear() 3014 do_tests() 3015 end) 3016 3017 it('works when disabled', function() 3018 clear('--luamod-dev') 3019 do_tests() 3020 end) 3021 3022 it('works without runtime', function() 3023 clear { env = { VIMRUNTIME = 'fixtures/a' } } 3024 do_tests() 3025 end) 3026 3027 it('fails when disabled without runtime', function() 3028 clear() 3029 command("let $VIMRUNTIME='fixtures/a'") 3030 -- Use system([nvim,…]) instead of clear() to avoid stderr noise. #21844 3031 local out = fn.system({ 3032 nvim_prog, 3033 '--clean', 3034 '--luamod-dev', 3035 [[+call nvim_exec_lua('return vim.tbl_count {x=1,y=2}')]], 3036 '+qa!', 3037 }):gsub('\r\n', '\n') 3038 eq(1, eval('v:shell_error')) 3039 matches("'vim%._core.shared' not found", out) 3040 end) 3041 end) 3042 3043 describe('lua: require("mod") from packages', function() 3044 before_each(function() 3045 clear('--cmd', 'set rtp+=test/functional/fixtures pp+=test/functional/fixtures') 3046 end) 3047 3048 it('propagates syntax error', function() 3049 local syntax_error_msg = exec_lua [[ 3050 local _, err = pcall(require, "syntax_error") 3051 return err 3052 ]] 3053 3054 matches('unexpected symbol', syntax_error_msg) 3055 end) 3056 3057 it('uses the right order of mod.lua vs mod/init.lua', function() 3058 -- lua/fancy_x.lua takes precedence over lua/fancy_x/init.lua 3059 eq('I am fancy_x.lua', exec_lua [[ return require'fancy_x' ]]) 3060 -- but lua/fancy_y/init.lua takes precedence over after/lua/fancy_y.lua 3061 eq('I am init.lua of fancy_y!', exec_lua [[ return require'fancy_y' ]]) 3062 -- safety check: after/lua/fancy_z.lua is still loaded 3063 eq('I am fancy_z.lua', exec_lua [[ return require'fancy_z' ]]) 3064 end) 3065 end) 3066 3067 describe('vim.keymap', function() 3068 before_each(clear) 3069 3070 it('validates', function() 3071 matches( 3072 'modes: expected string|table, got number', 3073 pcall_err(exec_lua, [[vim.keymap.set(42, 'x', print)]]) 3074 ) 3075 3076 matches( 3077 'rhs: expected string|function, got nil', 3078 pcall_err(exec_lua, [[vim.keymap.set('n', 'x')]]) 3079 ) 3080 3081 matches( 3082 'lhs: expected string, got table', 3083 pcall_err(exec_lua, [[vim.keymap.set('n', {}, print)]]) 3084 ) 3085 3086 matches( 3087 'rhs: expected string|function, got number', 3088 pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 42, function() end)]]) 3089 ) 3090 3091 matches( 3092 'opts: expected table, got function', 3093 pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 'x', function() end)]]) 3094 ) 3095 3096 matches( 3097 'rhs: expected string|function, got number', 3098 pcall_err(exec_lua, [[vim.keymap.set('z', 'x', 42)]]) 3099 ) 3100 3101 matches('Invalid mode shortname: "z"', pcall_err(exec_lua, [[vim.keymap.set('z', 'x', 'y')]])) 3102 end) 3103 3104 it('mapping', function() 3105 eq( 3106 0, 3107 exec_lua [[ 3108 GlobalCount = 0 3109 vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) 3110 return GlobalCount 3111 ]] 3112 ) 3113 3114 feed('asdf\n') 3115 3116 eq(1, exec_lua [[return GlobalCount]]) 3117 end) 3118 3119 it('expr mapping', function() 3120 exec_lua [[ 3121 vim.keymap.set('n', 'aa', function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, {expr = true}) 3122 ]] 3123 3124 feed('aa') 3125 3126 eq({ 'π<M-π>foo<' }, api.nvim_buf_get_lines(0, 0, -1, false)) 3127 end) 3128 3129 it('overwrite a mapping', function() 3130 eq( 3131 0, 3132 exec_lua [[ 3133 GlobalCount = 0 3134 vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) 3135 return GlobalCount 3136 ]] 3137 ) 3138 3139 feed('asdf\n') 3140 3141 eq(1, exec_lua [[return GlobalCount]]) 3142 3143 exec_lua [[ 3144 vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount - 1 end) 3145 ]] 3146 3147 feed('asdf\n') 3148 3149 eq(0, exec_lua [[return GlobalCount]]) 3150 end) 3151 3152 it('unmap', function() 3153 eq( 3154 0, 3155 exec_lua [[ 3156 GlobalCount = 0 3157 vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) 3158 return GlobalCount 3159 ]] 3160 ) 3161 3162 feed('asdf\n') 3163 3164 eq(1, exec_lua [[return GlobalCount]]) 3165 3166 exec_lua [[ 3167 vim.keymap.del('n', 'asdf') 3168 ]] 3169 3170 feed('asdf\n') 3171 3172 eq(1, exec_lua [[return GlobalCount]]) 3173 eq('\nNo mapping found', n.exec_capture('nmap asdf')) 3174 end) 3175 3176 it('buffer-local mappings', function() 3177 eq( 3178 0, 3179 exec_lua [[ 3180 GlobalCount = 0 3181 vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end, {buffer=true}) 3182 return GlobalCount 3183 ]] 3184 ) 3185 3186 feed('asdf\n') 3187 3188 eq(1, exec_lua [[return GlobalCount]]) 3189 3190 exec_lua [[ 3191 vim.keymap.del('n', 'asdf', {buffer=true}) 3192 ]] 3193 3194 feed('asdf\n') 3195 3196 eq(1, exec_lua [[return GlobalCount]]) 3197 eq('\nNo mapping found', n.exec_capture('nmap asdf')) 3198 end) 3199 3200 it('does not mutate the opts parameter', function() 3201 eq( 3202 true, 3203 exec_lua [[ 3204 opts = {buffer=true} 3205 vim.keymap.set('n', 'asdf', function() end, opts) 3206 return opts.buffer 3207 ]] 3208 ) 3209 eq( 3210 true, 3211 exec_lua [[ 3212 vim.keymap.del('n', 'asdf', opts) 3213 return opts.buffer 3214 ]] 3215 ) 3216 end) 3217 3218 it('<Plug> mappings', function() 3219 eq( 3220 0, 3221 exec_lua [[ 3222 GlobalCount = 0 3223 vim.keymap.set('n', '<plug>(asdf)', function() GlobalCount = GlobalCount + 1 end) 3224 vim.keymap.set('n', 'ww', '<plug>(asdf)') 3225 return GlobalCount 3226 ]] 3227 ) 3228 3229 feed('ww\n') 3230 3231 eq(1, exec_lua [[return GlobalCount]]) 3232 end) 3233 end) 3234 3235 describe('Vimscript function exists()', function() 3236 it('can check a lua function', function() 3237 eq( 3238 1, 3239 exec_lua [[ 3240 _G.test = function() print("hello") end 3241 return vim.fn.exists('*v:lua.test') 3242 ]] 3243 ) 3244 3245 eq(1, fn.exists('*v:lua.require("mpack").decode')) 3246 eq(1, fn.exists("*v:lua.require('mpack').decode")) 3247 eq(1, fn.exists('*v:lua.require"mpack".decode')) 3248 eq(1, fn.exists("*v:lua.require'mpack'.decode")) 3249 eq(1, fn.exists("*v:lua.require('vim.lsp').start")) 3250 eq(1, fn.exists('*v:lua.require"vim.lsp".start')) 3251 eq(1, fn.exists("*v:lua.require'vim.lsp'.start")) 3252 eq(0, fn.exists("*v:lua.require'vim.lsp'.unknown")) 3253 eq(0, fn.exists('*v:lua.?')) 3254 end) 3255 end)