buffer_spec.lua 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local Screen = require('test.functional.ui.screen')
  4. local tt = require('test.functional.testterm')
  5. local assert_alive = n.assert_alive
  6. local feed, clear = n.feed, n.clear
  7. local poke_eventloop = n.poke_eventloop
  8. local nvim_prog = n.nvim_prog
  9. local eval, feed_command, source = n.eval, n.feed_command, n.source
  10. local pcall_err = t.pcall_err
  11. local eq, neq = t.eq, t.neq
  12. local api = n.api
  13. local retry = t.retry
  14. local testprg = n.testprg
  15. local write_file = t.write_file
  16. local command = n.command
  17. local exc_exec = n.exc_exec
  18. local matches = t.matches
  19. local exec_lua = n.exec_lua
  20. local sleep = vim.uv.sleep
  21. local fn = n.fn
  22. local is_os = t.is_os
  23. local skip = t.skip
  24. describe(':terminal buffer', function()
  25. local screen
  26. before_each(function()
  27. clear()
  28. command('set modifiable swapfile undolevels=20')
  29. screen = tt.setup_screen()
  30. end)
  31. it('terminal-mode forces various options', function()
  32. feed([[<C-\><C-N>]])
  33. command('setlocal cursorline cursorlineopt=both cursorcolumn scrolloff=4 sidescrolloff=7')
  34. eq(
  35. { 'both', 1, 1, 4, 7 },
  36. eval('[&l:cursorlineopt, &l:cursorline, &l:cursorcolumn, &l:scrolloff, &l:sidescrolloff]')
  37. )
  38. eq('nt', eval('mode(1)'))
  39. -- Enter terminal-mode ("insert" mode in :terminal).
  40. feed('i')
  41. eq('t', eval('mode(1)'))
  42. eq(
  43. { 'number', 1, 0, 0, 0 },
  44. eval('[&l:cursorlineopt, &l:cursorline, &l:cursorcolumn, &l:scrolloff, &l:sidescrolloff]')
  45. )
  46. end)
  47. it('terminal-mode does not change cursorlineopt if cursorline is disabled', function()
  48. feed([[<C-\><C-N>]])
  49. command('setlocal nocursorline cursorlineopt=both')
  50. feed('i')
  51. eq({ 0, 'both' }, eval('[&l:cursorline, &l:cursorlineopt]'))
  52. end)
  53. it('terminal-mode disables cursorline when cursorlineopt is only set to "line"', function()
  54. feed([[<C-\><C-N>]])
  55. command('setlocal cursorline cursorlineopt=line')
  56. feed('i')
  57. eq({ 0, 'line' }, eval('[&l:cursorline, &l:cursorlineopt]'))
  58. end)
  59. describe('when a new file is edited', function()
  60. before_each(function()
  61. feed('<c-\\><c-n>:set bufhidden=wipe<cr>:enew<cr>')
  62. screen:expect([[
  63. ^ |
  64. {4:~ }|*5
  65. :enew |
  66. ]])
  67. end)
  68. it('will hide the buffer, ignoring the bufhidden option', function()
  69. feed(':bnext:l<esc>')
  70. screen:expect([[
  71. ^ |
  72. {4:~ }|*5
  73. |
  74. ]])
  75. end)
  76. end)
  77. describe('swap and undo', function()
  78. before_each(function()
  79. feed('<c-\\><c-n>')
  80. screen:expect([[
  81. tty ready |
  82. ^ |
  83. |*5
  84. ]])
  85. end)
  86. it('does not create swap files', function()
  87. local swapfile = api.nvim_exec('swapname', true):gsub('\n', '')
  88. eq(nil, io.open(swapfile))
  89. end)
  90. it('does not create undofiles files', function()
  91. local undofile = api.nvim_eval('undofile(bufname("%"))')
  92. eq(nil, io.open(undofile))
  93. end)
  94. end)
  95. it('cannot be modified directly', function()
  96. feed('<c-\\><c-n>dd')
  97. screen:expect([[
  98. tty ready |
  99. ^ |
  100. |*4
  101. {8:E21: Cannot make changes, 'modifiable' is off} |
  102. ]])
  103. end)
  104. it('sends data to the terminal when the "put" operator is used', function()
  105. feed('<c-\\><c-n>gg"ayj')
  106. feed_command('let @a = "appended " . @a')
  107. feed('"ap"ap')
  108. screen:expect([[
  109. ^tty ready |
  110. appended tty ready |*2
  111. |
  112. |*2
  113. :let @a = "appended " . @a |
  114. ]])
  115. -- operator count is also taken into consideration
  116. feed('3"ap')
  117. screen:expect([[
  118. ^tty ready |
  119. appended tty ready |*5
  120. :let @a = "appended " . @a |
  121. ]])
  122. end)
  123. it('sends data to the terminal when the ":put" command is used', function()
  124. feed('<c-\\><c-n>gg"ayj')
  125. feed_command('let @a = "appended " . @a')
  126. feed_command('put a')
  127. screen:expect([[
  128. ^tty ready |
  129. appended tty ready |
  130. |
  131. |*3
  132. :put a |
  133. ]])
  134. -- line argument is only used to move the cursor
  135. feed_command('6put a')
  136. screen:expect([[
  137. tty ready |
  138. appended tty ready |*2
  139. |
  140. |
  141. ^ |
  142. :6put a |
  143. ]])
  144. end)
  145. it('can be deleted', function()
  146. feed('<c-\\><c-n>:bd!<cr>')
  147. screen:expect([[
  148. ^ |
  149. {4:~ }|*5
  150. :bd! |
  151. ]])
  152. feed_command('bnext')
  153. screen:expect([[
  154. ^ |
  155. {4:~ }|*5
  156. :bnext |
  157. ]])
  158. end)
  159. it('handles loss of focus gracefully', function()
  160. -- Change the statusline to avoid printing the file name, which varies.
  161. api.nvim_set_option_value('statusline', '==========', {})
  162. -- Save the buffer number of the terminal for later testing.
  163. local tbuf = eval('bufnr("%")')
  164. local exitcmd = is_os('win') and "['cmd', '/c', 'exit']" or "['sh', '-c', 'exit']"
  165. source([[
  166. function! SplitWindow(id, data, event)
  167. new
  168. call feedkeys("iabc\<Esc>")
  169. endfunction
  170. startinsert
  171. call jobstart(]] .. exitcmd .. [[, {'on_exit': function("SplitWindow")})
  172. call feedkeys("\<C-\>", 't') " vim will expect <C-n>, but be exited out of
  173. " the terminal before it can be entered.
  174. ]])
  175. -- We should be in a new buffer now.
  176. screen:expect([[
  177. ab^c |
  178. {4:~ }|
  179. {5:========== }|
  180. rows: 2, cols: 50 |
  181. |
  182. {18:========== }|
  183. |
  184. ]])
  185. neq(tbuf, eval('bufnr("%")'))
  186. feed_command('quit!') -- Should exit the new window, not the terminal.
  187. eq(tbuf, eval('bufnr("%")'))
  188. end)
  189. describe('handles confirmations', function()
  190. it('with :confirm', function()
  191. feed('<c-\\><c-n>')
  192. feed_command('confirm bdelete')
  193. screen:expect { any = 'Close "term://' }
  194. end)
  195. it('with &confirm', function()
  196. feed('<c-\\><c-n>')
  197. feed_command('bdelete')
  198. screen:expect { any = 'E89' }
  199. feed('<cr>')
  200. eq('terminal', eval('&buftype'))
  201. feed_command('set confirm | bdelete')
  202. screen:expect { any = 'Close "term://' }
  203. feed('y')
  204. neq('terminal', eval('&buftype'))
  205. end)
  206. end)
  207. it('it works with set rightleft #11438', function()
  208. local columns = eval('&columns')
  209. feed(string.rep('a', columns))
  210. command('set rightleft')
  211. screen:expect([[
  212. ydaer ytt|
  213. ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
  214. |*4
  215. {3:-- TERMINAL --} |
  216. ]])
  217. command('bdelete!')
  218. end)
  219. it('requires bang (!) to close a running job #15402', function()
  220. skip(is_os('win'), 'Test freezes the CI and makes it time out')
  221. eq('Vim(wqall):E948: Job still running', exc_exec('wqall'))
  222. for _, cmd in ipairs({ 'bdelete', '%bdelete', 'bwipeout', 'bunload' }) do
  223. matches(
  224. '^Vim%('
  225. .. cmd:gsub('%%', '')
  226. .. '%):E89: term://.*tty%-test.* will be killed %(add %! to override%)$',
  227. exc_exec(cmd)
  228. )
  229. end
  230. command('call jobstop(&channel)')
  231. assert(0 >= eval('jobwait([&channel], 1000)[0]'))
  232. command('bdelete')
  233. end)
  234. it('stops running jobs with :quit', function()
  235. -- Open in a new window to avoid terminating the nvim instance
  236. command('split')
  237. command('terminal')
  238. command('set nohidden')
  239. command('quit')
  240. end)
  241. it('does not segfault when pasting empty register #13955', function()
  242. feed('<c-\\><c-n>')
  243. feed_command('put a') -- register a is empty
  244. n.assert_alive()
  245. end)
  246. it([[can use temporary normal mode <c-\><c-o>]], function()
  247. eq('t', fn.mode(1))
  248. feed [[<c-\><c-o>]]
  249. screen:expect {
  250. grid = [[
  251. tty ready |
  252. ^ |
  253. |*4
  254. {3:-- (terminal) --} |
  255. ]],
  256. }
  257. eq('ntT', fn.mode(1))
  258. feed [[:let g:x = 17]]
  259. screen:expect {
  260. grid = [[
  261. tty ready |
  262. |
  263. |*4
  264. :let g:x = 17^ |
  265. ]],
  266. }
  267. feed [[<cr>]]
  268. screen:expect {
  269. grid = [[
  270. tty ready |
  271. ^ |
  272. |*4
  273. {3:-- TERMINAL --} |
  274. ]],
  275. }
  276. eq('t', fn.mode(1))
  277. end)
  278. it('writing to an existing file with :w fails #13549', function()
  279. eq(
  280. 'Vim(write):E13: File exists (add ! to override)',
  281. pcall_err(command, 'write test/functional/fixtures/tty-test.c')
  282. )
  283. end)
  284. it('external interrupt (got_int) does not hang #20726', function()
  285. eq({ mode = 't', blocking = false }, api.nvim_get_mode())
  286. command('call timer_start(0, {-> interrupt()})')
  287. feed('<Ignore>') -- Add input to separate two RPC requests
  288. eq({ mode = 't', blocking = false }, api.nvim_get_mode())
  289. feed([[<C-\><C-N>]])
  290. eq({ mode = 'nt', blocking = false }, api.nvim_get_mode())
  291. command('bd!')
  292. end)
  293. end)
  294. describe(':terminal buffer', function()
  295. before_each(clear)
  296. it('term_close() use-after-free #4393', function()
  297. command('terminal yes')
  298. feed('<Ignore>') -- Add input to separate two RPC requests
  299. command('bdelete!')
  300. end)
  301. it('emits TermRequest events #26972', function()
  302. local term = api.nvim_open_term(0, {})
  303. local termbuf = api.nvim_get_current_buf()
  304. -- Test that <abuf> is the terminal buffer, not the current buffer
  305. command('au TermRequest * let g:termbuf = +expand("<abuf>")')
  306. command('wincmd p')
  307. -- cwd will be inserted in a file URI, which cannot contain backs
  308. local cwd = t.fix_slashes(fn.getcwd())
  309. local parent = cwd:match('^(.+/)')
  310. local expected = '\027]7;file://host' .. parent
  311. api.nvim_chan_send(term, string.format('%s\027\\', expected))
  312. eq(expected, eval('v:termrequest'))
  313. eq(termbuf, eval('g:termbuf'))
  314. end)
  315. it('TermRequest synchronization #27572', function()
  316. command('autocmd! nvim.terminal TermRequest')
  317. local term = exec_lua([[
  318. _G.input = {}
  319. local term = vim.api.nvim_open_term(0, {
  320. on_input = function(_, _, _, data)
  321. table.insert(_G.input, data)
  322. end,
  323. force_crlf = false,
  324. })
  325. vim.api.nvim_create_autocmd('TermRequest', {
  326. callback = function(args)
  327. if args.data == '\027]11;?' then
  328. table.insert(_G.input, '\027]11;rgb:0000/0000/0000\027\\')
  329. end
  330. end
  331. })
  332. return term
  333. ]])
  334. api.nvim_chan_send(term, '\027]11;?\007\027[5n\027]11;?\007\027[5n')
  335. eq({
  336. '\027]11;rgb:0000/0000/0000\027\\',
  337. '\027[0n',
  338. '\027]11;rgb:0000/0000/0000\027\\',
  339. '\027[0n',
  340. }, exec_lua('return _G.input'))
  341. end)
  342. it('no heap-buffer-overflow when using jobstart("echo",{term=true}) #3161', function()
  343. local testfilename = 'Xtestfile-functional-terminal-buffers_spec'
  344. write_file(testfilename, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa')
  345. finally(function()
  346. os.remove(testfilename)
  347. end)
  348. feed_command('edit ' .. testfilename)
  349. -- Move cursor away from the beginning of the line
  350. feed('$')
  351. -- Let jobstart(…,{term=true}) modify the buffer
  352. feed_command([[call jobstart("echo", {'term':v:true})]])
  353. assert_alive()
  354. feed_command('bdelete!')
  355. end)
  356. it('no heap-buffer-overflow when sending long line with nowrap #11548', function()
  357. feed_command('set nowrap')
  358. feed_command('autocmd TermOpen * startinsert')
  359. feed_command('call feedkeys("4000ai\\<esc>:terminal!\\<cr>")')
  360. assert_alive()
  361. end)
  362. it('truncates the size of grapheme clusters', function()
  363. local chan = api.nvim_open_term(0, {})
  364. local composing = ('a̳'):sub(2)
  365. api.nvim_chan_send(chan, 'a' .. composing:rep(20))
  366. retry(nil, nil, function()
  367. eq('a' .. composing:rep(14), api.nvim_get_current_line())
  368. end)
  369. end)
  370. it('handles extended grapheme clusters', function()
  371. local screen = Screen.new(50, 7)
  372. feed 'i'
  373. local chan = api.nvim_open_term(0, {})
  374. api.nvim_chan_send(chan, '🏴‍☠️ yarrr')
  375. screen:expect([[
  376. 🏴‍☠️ yarrr^ |
  377. |*5
  378. {5:-- TERMINAL --} |
  379. ]])
  380. eq('🏴‍☠️ yarrr', api.nvim_get_current_line())
  381. end)
  382. it('handles split UTF-8 sequences #16245', function()
  383. local screen = Screen.new(50, 7)
  384. fn.jobstart({ testprg('shell-test'), 'UTF-8' }, { term = true })
  385. screen:expect([[
  386. ^å |
  387. ref: å̲ |
  388. 1: å̲ |
  389. 2: å̲ |
  390. 3: å̲ |
  391. |*2
  392. ]])
  393. end)
  394. it('handles unprintable chars', function()
  395. local screen = Screen.new(50, 7)
  396. feed 'i'
  397. local chan = api.nvim_open_term(0, {})
  398. api.nvim_chan_send(chan, '\239\187\191') -- '\xef\xbb\xbf'
  399. screen:expect([[
  400. {18:<feff>}^ |
  401. |*5
  402. {5:-- TERMINAL --} |
  403. ]])
  404. eq('\239\187\191', api.nvim_get_current_line())
  405. end)
  406. it("handles bell respecting 'belloff' and 'visualbell'", function()
  407. local screen = Screen.new(50, 7)
  408. local chan = api.nvim_open_term(0, {})
  409. command('set belloff=')
  410. api.nvim_chan_send(chan, '\a')
  411. screen:expect(function()
  412. eq({ true, false }, { screen.bell, screen.visual_bell })
  413. end)
  414. screen.bell = false
  415. command('set visualbell')
  416. api.nvim_chan_send(chan, '\a')
  417. screen:expect(function()
  418. eq({ false, true }, { screen.bell, screen.visual_bell })
  419. end)
  420. screen.visual_bell = false
  421. command('set belloff=term')
  422. api.nvim_chan_send(chan, '\a')
  423. screen:expect({
  424. condition = function()
  425. eq({ false, false }, { screen.bell, screen.visual_bell })
  426. end,
  427. unchanged = true,
  428. })
  429. command('set belloff=all')
  430. api.nvim_chan_send(chan, '\a')
  431. screen:expect({
  432. condition = function()
  433. eq({ false, false }, { screen.bell, screen.visual_bell })
  434. end,
  435. unchanged = true,
  436. })
  437. end)
  438. end)
  439. describe('on_lines does not emit out-of-bounds line indexes when', function()
  440. before_each(function()
  441. clear()
  442. exec_lua([[
  443. function _G.register_callback(bufnr)
  444. _G.cb_error = ''
  445. vim.api.nvim_buf_attach(bufnr, false, {
  446. on_lines = function(_, bufnr, _, firstline, _, _)
  447. local status, msg = pcall(vim.api.nvim_buf_get_offset, bufnr, firstline)
  448. if not status then
  449. _G.cb_error = msg
  450. end
  451. end
  452. })
  453. end
  454. ]])
  455. end)
  456. it('creating a terminal buffer #16394', function()
  457. feed_command('autocmd TermOpen * ++once call v:lua.register_callback(str2nr(expand("<abuf>")))')
  458. feed_command('terminal')
  459. sleep(500)
  460. eq('', exec_lua([[return _G.cb_error]]))
  461. end)
  462. it('deleting a terminal buffer #16394', function()
  463. feed_command('terminal')
  464. sleep(500)
  465. feed_command('lua _G.register_callback(0)')
  466. feed_command('bdelete!')
  467. eq('', exec_lua([[return _G.cb_error]]))
  468. end)
  469. end)
  470. describe('terminal input', function()
  471. before_each(function()
  472. clear()
  473. exec_lua([[
  474. _G.input_data = ''
  475. vim.api.nvim_open_term(0, { on_input = function(_, _, _, data)
  476. _G.input_data = _G.input_data .. data
  477. end })
  478. ]])
  479. feed('i')
  480. poke_eventloop()
  481. end)
  482. it('<C-Space> is sent as NUL byte', function()
  483. feed('aaa<C-Space>bbb')
  484. eq('aaa\0bbb', exec_lua([[return _G.input_data]]))
  485. end)
  486. it('unknown special keys are not sent', function()
  487. feed('aaa<Help>bbb')
  488. eq('aaabbb', exec_lua([[return _G.input_data]]))
  489. end)
  490. end)
  491. describe('terminal input', function()
  492. it('sends various special keys with modifiers', function()
  493. clear()
  494. local screen = tt.setup_child_nvim({
  495. '-u',
  496. 'NONE',
  497. '-i',
  498. 'NONE',
  499. '--cmd',
  500. 'colorscheme vim',
  501. '--cmd',
  502. 'set notermguicolors',
  503. '-c',
  504. 'while 1 | redraw | echo keytrans(getcharstr()) | endwhile',
  505. })
  506. screen:expect([[
  507. ^ |
  508. {4:~ }|*3
  509. {5:[No Name] 0,0-1 All}|
  510. |
  511. {3:-- TERMINAL --} |
  512. ]])
  513. for _, key in ipairs({
  514. '<M-Tab>',
  515. '<M-CR>',
  516. '<M-Esc>',
  517. '<BS>',
  518. '<S-Tab>',
  519. '<Insert>',
  520. '<Del>',
  521. '<PageUp>',
  522. '<PageDown>',
  523. '<S-Up>',
  524. '<C-Up>',
  525. '<Up>',
  526. '<S-Down>',
  527. '<C-Down>',
  528. '<Down>',
  529. '<S-Left>',
  530. '<C-Left>',
  531. '<Left>',
  532. '<S-Right>',
  533. '<C-Right>',
  534. '<Right>',
  535. '<S-Home>',
  536. '<C-Home>',
  537. '<Home>',
  538. '<S-End>',
  539. '<C-End>',
  540. '<End>',
  541. '<C-LeftMouse><0,0>',
  542. '<C-LeftDrag><0,1>',
  543. '<C-LeftRelease><0,1>',
  544. '<2-LeftMouse><0,1>',
  545. '<2-LeftDrag><0,0>',
  546. '<2-LeftRelease><0,0>',
  547. '<M-MiddleMouse><0,0>',
  548. '<M-MiddleDrag><0,1>',
  549. '<M-MiddleRelease><0,1>',
  550. '<2-MiddleMouse><0,1>',
  551. '<2-MiddleDrag><0,0>',
  552. '<2-MiddleRelease><0,0>',
  553. '<S-RightMouse><0,0>',
  554. '<S-RightDrag><0,1>',
  555. '<S-RightRelease><0,1>',
  556. '<2-RightMouse><0,1>',
  557. '<2-RightDrag><0,0>',
  558. '<2-RightRelease><0,0>',
  559. '<S-X1Mouse><0,0>',
  560. '<S-X1Drag><0,1>',
  561. '<S-X1Release><0,1>',
  562. '<2-X1Mouse><0,1>',
  563. '<2-X1Drag><0,0>',
  564. '<2-X1Release><0,0>',
  565. '<S-X2Mouse><0,0>',
  566. '<S-X2Drag><0,1>',
  567. '<S-X2Release><0,1>',
  568. '<2-X2Mouse><0,1>',
  569. '<2-X2Drag><0,0>',
  570. '<2-X2Release><0,0>',
  571. '<S-ScrollWheelUp>',
  572. '<S-ScrollWheelDown>',
  573. '<ScrollWheelUp>',
  574. '<ScrollWheelDown>',
  575. '<S-ScrollWheelLeft>',
  576. '<S-ScrollWheelRight>',
  577. '<ScrollWheelLeft>',
  578. '<ScrollWheelRight>',
  579. }) do
  580. feed(key)
  581. screen:expect(([[
  582. |
  583. {4:~ }|*3
  584. {5:[No Name] 0,0-1 All}|
  585. %s^ {MATCH: *}|
  586. {3:-- TERMINAL --} |
  587. ]]):format(key:gsub('<%d+,%d+>$', '')))
  588. end
  589. end)
  590. -- TODO(bfredl): getcharstr() erases the distinction between <C-I> and <Tab>.
  591. -- If it was enhanced or replaced this could get folded into the test above.
  592. it('can send TAB/C-I and ESC/C-[ separately', function()
  593. if
  594. skip(
  595. is_os('win'),
  596. "The escape sequence to enable kitty keyboard mode doesn't work on Windows"
  597. )
  598. then
  599. return
  600. end
  601. clear()
  602. local screen = tt.setup_child_nvim({
  603. '-u',
  604. 'NONE',
  605. '-i',
  606. 'NONE',
  607. '--cmd',
  608. 'colorscheme vim',
  609. '--cmd',
  610. 'set notermguicolors',
  611. '--cmd',
  612. 'noremap <Tab> <cmd>echo "Tab!"<cr>',
  613. '--cmd',
  614. 'noremap <C-i> <cmd>echo "Ctrl-I!"<cr>',
  615. '--cmd',
  616. 'noremap <Esc> <cmd>echo "Esc!"<cr>',
  617. '--cmd',
  618. 'noremap <C-[> <cmd>echo "Ctrl-[!"<cr>',
  619. })
  620. screen:expect([[
  621. ^ |
  622. {4:~ }|*3
  623. {5:[No Name] 0,0-1 All}|
  624. |
  625. {3:-- TERMINAL --} |
  626. ]])
  627. feed('<tab>')
  628. screen:expect([[
  629. ^ |
  630. {4:~ }|*3
  631. {5:[No Name] 0,0-1 All}|
  632. Tab! |
  633. {3:-- TERMINAL --} |
  634. ]])
  635. feed('<c-i>')
  636. screen:expect([[
  637. ^ |
  638. {4:~ }|*3
  639. {5:[No Name] 0,0-1 All}|
  640. Ctrl-I! |
  641. {3:-- TERMINAL --} |
  642. ]])
  643. feed('<Esc>')
  644. screen:expect([[
  645. ^ |
  646. {4:~ }|*3
  647. {5:[No Name] 0,0-1 All}|
  648. Esc! |
  649. {3:-- TERMINAL --} |
  650. ]])
  651. feed('<c-[>')
  652. screen:expect([[
  653. ^ |
  654. {4:~ }|*3
  655. {5:[No Name] 0,0-1 All}|
  656. Ctrl-[! |
  657. {3:-- TERMINAL --} |
  658. ]])
  659. end)
  660. end)
  661. if is_os('win') then
  662. describe(':terminal in Windows', function()
  663. local screen
  664. before_each(function()
  665. clear()
  666. feed_command('set modifiable swapfile undolevels=20')
  667. poke_eventloop()
  668. local cmd = { 'cmd.exe', '/K', 'PROMPT=$g$s' }
  669. screen = tt.setup_screen(nil, cmd)
  670. end)
  671. it('"put" operator sends data normally', function()
  672. feed('<c-\\><c-n>G')
  673. feed_command('let @a = ":: tty ready"')
  674. feed_command('let @a = @a . "\\n:: appended " . @a . "\\n\\n"')
  675. feed('"ap"ap')
  676. screen:expect([[
  677. |
  678. > :: tty ready |
  679. > :: appended :: tty ready |
  680. > :: tty ready |
  681. > :: appended :: tty ready |
  682. ^> |
  683. :let @a = @a . "\n:: appended " . @a . "\n\n" |
  684. ]])
  685. -- operator count is also taken into consideration
  686. feed('3"ap')
  687. screen:expect([[
  688. > :: appended :: tty ready |
  689. > :: tty ready |
  690. > :: appended :: tty ready |
  691. > :: tty ready |
  692. > :: appended :: tty ready |
  693. ^> |
  694. :let @a = @a . "\n:: appended " . @a . "\n\n" |
  695. ]])
  696. end)
  697. it('":put" command sends data normally', function()
  698. feed('<c-\\><c-n>G')
  699. feed_command('let @a = ":: tty ready"')
  700. feed_command('let @a = @a . "\\n:: appended " . @a . "\\n\\n"')
  701. feed_command('put a')
  702. screen:expect([[
  703. |
  704. > :: tty ready |
  705. > :: appended :: tty ready |
  706. > |
  707. |
  708. ^ |
  709. :put a |
  710. ]])
  711. -- line argument is only used to move the cursor
  712. feed_command('6put a')
  713. screen:expect([[
  714. |
  715. > :: tty ready |
  716. > :: appended :: tty ready |
  717. > :: tty ready |
  718. > :: appended :: tty ready |
  719. ^> |
  720. :6put a |
  721. ]])
  722. end)
  723. end)
  724. end
  725. describe('termopen() (deprecated alias to `jobstart(…,{term=true})`)', function()
  726. before_each(clear)
  727. it('disallowed when textlocked and in cmdwin buffer', function()
  728. command("autocmd TextYankPost <buffer> ++once call termopen('foo')")
  729. matches(
  730. 'Vim%(call%):E565: Not allowed to change text or change window$',
  731. pcall_err(command, 'normal! yy')
  732. )
  733. feed('q:')
  734. eq(
  735. 'Vim:E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
  736. pcall_err(fn.termopen, 'bar')
  737. )
  738. end)
  739. describe('$COLORTERM value', function()
  740. if skip(is_os('win'), 'Not applicable for Windows') then
  741. return
  742. end
  743. before_each(function()
  744. -- Outer value should never be propagated to :terminal
  745. fn.setenv('COLORTERM', 'wrongvalue')
  746. end)
  747. local function test_term_colorterm(expected, opts)
  748. local screen = Screen.new(50, 4)
  749. fn.termopen({
  750. nvim_prog,
  751. '-u',
  752. 'NONE',
  753. '-i',
  754. 'NONE',
  755. '--headless',
  756. '-c',
  757. 'echo $COLORTERM | quit',
  758. }, opts)
  759. screen:expect(([[
  760. ^%s{MATCH:%%s+}|
  761. [Process exited 0] |
  762. |*2
  763. ]]):format(expected))
  764. end
  765. describe("with 'notermguicolors'", function()
  766. before_each(function()
  767. command('set notermguicolors')
  768. end)
  769. it('is empty by default', function()
  770. test_term_colorterm('')
  771. end)
  772. it('can be overridden', function()
  773. test_term_colorterm('expectedvalue', { env = { COLORTERM = 'expectedvalue' } })
  774. end)
  775. end)
  776. describe("with 'termguicolors'", function()
  777. before_each(function()
  778. command('set termguicolors')
  779. end)
  780. it('is "truecolor" by default', function()
  781. test_term_colorterm('truecolor')
  782. end)
  783. it('can be overridden', function()
  784. test_term_colorterm('expectedvalue', { env = { COLORTERM = 'expectedvalue' } })
  785. end)
  786. end)
  787. end)
  788. end)