input_spec.lua 16 KB

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