input_spec.lua (16316B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local eq = t.eq 6 local feed = n.feed 7 local api = n.api 8 local clear = n.clear 9 local source = n.source 10 local command = n.command 11 local exc_exec = n.exc_exec 12 local async_meths = n.async_meths 13 local NIL = vim.NIL 14 15 local screen 16 17 before_each(function() 18 clear() 19 screen = Screen.new(25, 5) 20 source([[ 21 hi Test ctermfg=Red guifg=Red term=bold 22 function CustomCompl(...) 23 return 'TEST' 24 endfunction 25 function CustomListCompl(...) 26 return ['FOO'] 27 endfunction 28 29 highlight RBP1 guibg=Red 30 highlight RBP2 guibg=Yellow 31 highlight RBP3 guibg=Green 32 highlight RBP4 guibg=Blue 33 let g:NUM_LVLS = 4 34 function Redraw() 35 redraw! 36 return '' 37 endfunction 38 cnoremap <expr> {REDRAW} Redraw() 39 function RainBowParens(cmdline) 40 let ret = [] 41 let i = 0 42 let lvl = 0 43 while i < len(a:cmdline) 44 if a:cmdline[i] is# '(' 45 call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) 46 let lvl += 1 47 elseif a:cmdline[i] is# ')' 48 let lvl -= 1 49 call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) 50 endif 51 let i += 1 52 endwhile 53 return ret 54 endfunction 55 ]]) 56 screen:set_default_attr_ids({ 57 EOB = { bold = true, foreground = Screen.colors.Blue1 }, 58 T = { foreground = Screen.colors.Red }, 59 RBP1 = { background = Screen.colors.Red }, 60 RBP2 = { background = Screen.colors.Yellow }, 61 RBP3 = { background = Screen.colors.Green }, 62 RBP4 = { background = Screen.colors.Blue }, 63 SEP = { bold = true, reverse = true }, 64 CONFIRM = { bold = true, foreground = Screen.colors.SeaGreen4 }, 65 }) 66 end) 67 68 describe('input()', function() 69 it('works with multiline prompts', function() 70 feed([[:call input("Test\nFoo")<CR>]]) 71 screen:expect([[ 72 | 73 {EOB:~ }| 74 {SEP: }| 75 Test | 76 Foo^ | 77 ]]) 78 end) 79 it('works with multiline prompts and :echohl', function() 80 feed([[:echohl Test | call input("Test\nFoo")<CR>]]) 81 screen:expect([[ 82 | 83 {EOB:~ }| 84 {SEP: }| 85 {T:Test} | 86 {T:Foo}^ | 87 ]]) 88 command('redraw!') 89 screen:expect([[ 90 | 91 {EOB:~ }|*3 92 {T:Foo}^ | 93 ]]) 94 end) 95 it('allows unequal numeric arguments when using multiple args', function() 96 command('echohl Test') 97 feed([[:call input(1, 2)<CR>]]) 98 screen:expect([[ 99 | 100 {EOB:~ }|*3 101 {T:1}2^ | 102 ]]) 103 feed('<BS>') 104 screen:expect([[ 105 | 106 {EOB:~ }|*3 107 {T:1}^ | 108 ]]) 109 end) 110 it('allows unequal numeric values when using {opts} dict', function() 111 command('echohl Test') 112 api.nvim_set_var('opts', { prompt = 1, default = 2, cancelreturn = 3 }) 113 feed([[:echo input(opts)<CR>]]) 114 screen:expect([[ 115 | 116 {EOB:~ }|*3 117 {T:1}2^ | 118 ]]) 119 feed('<BS>') 120 screen:expect([[ 121 | 122 {EOB:~ }|*3 123 {T:1}^ | 124 ]]) 125 feed('<Esc>') 126 screen:expect([[ 127 ^ | 128 {EOB:~ }|*3 129 {T:3} | 130 ]]) 131 end) 132 it('works with redraw', function() 133 command('echohl Test') 134 api.nvim_set_var('opts', { prompt = 'Foo>', default = 'Bar' }) 135 feed([[:echo inputdialog(opts)<CR>]]) 136 screen:expect([[ 137 | 138 {EOB:~ }|*3 139 {T:Foo>}Bar^ | 140 ]]) 141 command('mode') 142 screen:expect { 143 grid = [[ 144 | 145 {EOB:~ }|*3 146 {T:Foo>}Bar^ | 147 ]], 148 reset = true, 149 } 150 feed('<BS>') 151 screen:expect([[ 152 | 153 {EOB:~ }|*3 154 {T:Foo>}Ba^ | 155 ]]) 156 command('mode') 157 screen:expect { 158 grid = [[ 159 | 160 {EOB:~ }|*3 161 {T:Foo>}Ba^ | 162 ]], 163 reset = true, 164 } 165 end) 166 it('allows omitting everything with dict argument', function() 167 command('echohl Test') 168 feed([[:call input({})<CR>]]) 169 screen:expect([[ 170 | 171 {EOB:~ }|*3 172 ^ | 173 ]]) 174 end) 175 it('supports completion', function() 176 feed(':let var = input("", "", "custom,CustomCompl")<CR>') 177 feed('<Tab><CR>') 178 eq('TEST', api.nvim_get_var('var')) 179 180 feed(':let var = input({"completion": "customlist,CustomListCompl"})<CR>') 181 feed('<Tab><CR>') 182 eq('FOO', api.nvim_get_var('var')) 183 end) 184 it('supports cancelreturn', function() 185 feed(':let var = input({"cancelreturn": "BAR"})<CR>') 186 feed('<Esc>') 187 eq('BAR', api.nvim_get_var('var')) 188 feed(':let var = input({"cancelreturn": []})<CR>') 189 feed('<Esc>') 190 eq({}, api.nvim_get_var('var')) 191 feed(':let var = input({"cancelreturn": v:false})<CR>') 192 feed('<Esc>') 193 eq(false, api.nvim_get_var('var')) 194 feed(':let var = input({"cancelreturn": v:null})<CR>') 195 feed('<Esc>') 196 eq(NIL, api.nvim_get_var('var')) 197 end) 198 it('supports default string', function() 199 feed(':let var = input("", "DEF1")<CR>') 200 feed('<CR>') 201 eq('DEF1', api.nvim_get_var('var')) 202 203 feed(':let var = input({"default": "DEF2"})<CR>') 204 feed('<CR>') 205 eq('DEF2', api.nvim_get_var('var')) 206 end) 207 it('errors out on invalid inputs', function() 208 eq('Vim(call):E730: Using a List as a String', exc_exec('call input([])')) 209 eq('Vim(call):E730: Using a List as a String', exc_exec('call input("", [])')) 210 eq('Vim(call):E730: Using a List as a String', exc_exec('call input("", "", [])')) 211 eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"prompt": []})')) 212 eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"default": []})')) 213 eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"completion": []})')) 214 eq('Vim(call):E5050: {opts} must be the only argument', exc_exec('call input({}, "default")')) 215 eq( 216 'Vim(call):E118: Too many arguments for function: input', 217 exc_exec('call input("prompt> ", "default", "file", "extra")') 218 ) 219 end) 220 it('supports highlighting', function() 221 command('nnoremap <expr> X input({"highlight": "RainBowParens"})[-1]') 222 feed([[X]]) 223 feed('(())') 224 screen:expect([[ 225 | 226 {EOB:~ }|*3 227 {RBP1:(}{RBP2:()}{RBP1:)}^ | 228 ]]) 229 end) 230 it('is not hidden by :silent', function() 231 feed([[:silent call input('Foo: ')<CR>]]) 232 screen:expect([[ 233 | 234 {EOB:~ }| 235 {SEP: }| 236 Foo: ^ | 237 | 238 ]]) 239 feed('Bar') 240 screen:expect([[ 241 | 242 {EOB:~ }| 243 {SEP: }| 244 Foo: Bar^ | 245 | 246 ]]) 247 feed('<CR>') 248 end) 249 end) 250 describe('inputdialog()', function() 251 it('works with multiline prompts', function() 252 feed([[:call inputdialog("Test\nFoo")<CR>]]) 253 screen:expect([[ 254 | 255 {EOB:~ }| 256 {SEP: }| 257 Test | 258 Foo^ | 259 ]]) 260 end) 261 it('works with multiline prompts and :echohl', function() 262 feed([[:echohl Test | call inputdialog("Test\nFoo")<CR>]]) 263 screen:expect([[ 264 | 265 {EOB:~ }| 266 {SEP: }| 267 {T:Test} | 268 {T:Foo}^ | 269 ]]) 270 command('redraw!') 271 screen:expect([[ 272 | 273 {EOB:~ }|*3 274 {T:Foo}^ | 275 ]]) 276 end) 277 it('allows unequal numeric arguments when using multiple args', function() 278 command('echohl Test') 279 feed([[:call inputdialog(1, 2)<CR>]]) 280 screen:expect([[ 281 | 282 {EOB:~ }|*3 283 {T:1}2^ | 284 ]]) 285 feed('<BS>') 286 screen:expect([[ 287 | 288 {EOB:~ }|*3 289 {T:1}^ | 290 ]]) 291 end) 292 it('allows unequal numeric values when using {opts} dict', function() 293 command('echohl Test') 294 api.nvim_set_var('opts', { prompt = 1, default = 2, cancelreturn = 3 }) 295 feed([[:echo input(opts)<CR>]]) 296 screen:expect([[ 297 | 298 {EOB:~ }|*3 299 {T:1}2^ | 300 ]]) 301 feed('<BS>') 302 screen:expect([[ 303 | 304 {EOB:~ }|*3 305 {T:1}^ | 306 ]]) 307 feed('<Esc>') 308 screen:expect([[ 309 ^ | 310 {EOB:~ }|*3 311 {T:3} | 312 ]]) 313 end) 314 it('works with redraw', function() 315 command('echohl Test') 316 api.nvim_set_var('opts', { prompt = 'Foo>', default = 'Bar' }) 317 feed([[:echo input(opts)<CR>]]) 318 screen:expect([[ 319 | 320 {EOB:~ }|*3 321 {T:Foo>}Bar^ | 322 ]]) 323 command('mode') 324 screen:expect { 325 grid = [[ 326 | 327 {EOB:~ }|*3 328 {T:Foo>}Bar^ | 329 ]], 330 reset = true, 331 } 332 feed('<BS>') 333 screen:expect([[ 334 | 335 {EOB:~ }|*3 336 {T:Foo>}Ba^ | 337 ]]) 338 command('mode') 339 screen:expect { 340 grid = [[ 341 | 342 {EOB:~ }|*3 343 {T:Foo>}Ba^ | 344 ]], 345 reset = true, 346 } 347 end) 348 it('allows omitting everything with dict argument', function() 349 command('echohl Test') 350 feed(':echo inputdialog({})<CR>') 351 screen:expect([[ 352 | 353 {EOB:~ }|*3 354 ^ | 355 ]]) 356 end) 357 it('supports completion', function() 358 feed(':let var = inputdialog({"completion": "customlist,CustomListCompl"})<CR>') 359 feed('<Tab><CR>') 360 eq('FOO', api.nvim_get_var('var')) 361 end) 362 it('supports cancelreturn', function() 363 feed(':let var = inputdialog("", "", "CR1")<CR>') 364 feed('<Esc>') 365 eq('CR1', api.nvim_get_var('var')) 366 367 feed(':let var = inputdialog({"cancelreturn": "BAR"})<CR>') 368 feed('<Esc>') 369 eq('BAR', api.nvim_get_var('var')) 370 end) 371 it('supports default string', function() 372 feed(':let var = inputdialog("", "DEF1")<CR>') 373 feed('<CR>') 374 eq('DEF1', api.nvim_get_var('var')) 375 376 feed(':let var = inputdialog({"default": "DEF2"})<CR>') 377 feed('<CR>') 378 eq('DEF2', api.nvim_get_var('var')) 379 end) 380 it('errors out on invalid inputs', function() 381 eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog([])')) 382 eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog("", [])')) 383 eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog("", "", [])')) 384 eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"prompt": []})')) 385 eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"default": []})')) 386 eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"completion": []})')) 387 eq( 388 'Vim(call):E5050: {opts} must be the only argument', 389 exc_exec('call inputdialog({}, "default")') 390 ) 391 eq( 392 'Vim(call):E118: Too many arguments for function: inputdialog', 393 exc_exec('call inputdialog("prompt> ", "default", "file", "extra")') 394 ) 395 end) 396 it('supports highlighting', function() 397 command('nnoremap <expr> X inputdialog({"highlight": "RainBowParens"})[-1]') 398 feed([[X]]) 399 feed('(())') 400 screen:expect([[ 401 | 402 {EOB:~ }|*3 403 {RBP1:(}{RBP2:()}{RBP1:)}^ | 404 ]]) 405 end) 406 end) 407 408 describe('confirm()', function() 409 it('works', function() 410 api.nvim_set_option_value('more', false, {}) -- Avoid hit-enter prompt 411 api.nvim_set_option_value('laststatus', 2, {}) 412 -- screen:expect() calls are needed to avoid feeding input too early 413 screen:expect({ any = '%[No Name%]' }) 414 415 async_meths.nvim_command([[let a = confirm('Press O to proceed')]]) 416 screen:expect({ any = '{CONFIRM:.+: }' }) 417 feed('o') 418 screen:expect({ any = '%[No Name%]' }) 419 eq(1, api.nvim_get_var('a')) 420 421 async_meths.nvim_command([[let a = 'Are you sure?'->confirm("&Yes\n&No")]]) 422 screen:expect({ any = '{CONFIRM:.+: }' }) 423 feed('y') 424 screen:expect({ any = '%[No Name%]' }) 425 eq(1, api.nvim_get_var('a')) 426 427 async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) 428 screen:expect({ any = '{CONFIRM:.+: }' }) 429 feed('n') 430 screen:expect({ any = '%[No Name%]' }) 431 eq(2, api.nvim_get_var('a')) 432 433 -- Not possible to match Vim's CTRL-C test here as CTRL-C always sets got_int in Nvim. 434 435 -- confirm() should return 0 when pressing ESC. 436 async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) 437 screen:expect({ any = '{CONFIRM:.+: }' }) 438 feed('<Esc>') 439 screen:expect({ any = '%[No Name%]' }) 440 eq(0, api.nvim_get_var('a')) 441 442 -- Default choice is returned when pressing <CR>. 443 async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) 444 screen:expect({ any = '{CONFIRM:.+: }' }) 445 feed('<CR>') 446 screen:expect({ any = '%[No Name%]' }) 447 eq(1, api.nvim_get_var('a')) 448 449 async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No", 2)]]) 450 screen:expect({ any = '{CONFIRM:.+: }' }) 451 feed('<CR>') 452 screen:expect({ any = '%[No Name%]' }) 453 eq(2, api.nvim_get_var('a')) 454 455 async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No", 0)]]) 456 screen:expect({ any = '{CONFIRM:.+: }' }) 457 feed('<CR>') 458 screen:expect({ any = '%[No Name%]' }) 459 eq(0, api.nvim_get_var('a')) 460 461 -- Test with the {type} 4th argument 462 for _, type in ipairs({ 'Error', 'Question', 'Info', 'Warning', 'Generic' }) do 463 async_meths.nvim_command( 464 ([[let a = confirm('Are you sure?', "&Yes\n&No", 1, '%s')]]):format(type) 465 ) 466 screen:expect({ any = '{CONFIRM:.+: }' }) 467 feed('y') 468 screen:expect({ any = '%[No Name%]' }) 469 eq(1, api.nvim_get_var('a')) 470 end 471 end) 472 473 it('shows dialog even if :silent #8788', function() 474 command("autocmd BufNewFile * call confirm('test')") 475 476 local function check_and_clear(edit_line) 477 screen:expect([[ 478 | 479 {SEP: }| 480 ]] .. edit_line .. [[ 481 {CONFIRM:test} | 482 {CONFIRM:[O]k: }^ | 483 ]]) 484 feed('<cr>') 485 command('redraw') 486 command('bdelete!') 487 end 488 489 -- With shortmess-=F 490 command('set shortmess-=F') 491 feed(':edit foo<cr>') 492 check_and_clear('"foo" [New] |\n') 493 494 -- With shortmess+=F 495 command('set shortmess+=F') 496 feed(':edit foo<cr>') 497 check_and_clear(':edit foo |\n') 498 499 -- With :silent 500 feed(':silent edit foo<cr>') 501 check_and_clear(':silent edit foo |\n') 502 503 -- With API (via eval/Vimscript) call and shortmess+=F 504 feed(':call nvim_command("edit x")<cr>') 505 check_and_clear(':call nvim_command("edit |\n') 506 507 async_meths.nvim_command('edit x') 508 check_and_clear(' |\n') 509 end) 510 end)