put_spec.lua 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local Screen = require('test.functional.ui.screen')
  4. local clear = n.clear
  5. local insert = n.insert
  6. local feed = n.feed
  7. local expect = n.expect
  8. local eq = t.eq
  9. local map = vim.tbl_map
  10. local filter = vim.tbl_filter
  11. local feed_command = n.feed_command
  12. local command = n.command
  13. local curbuf_contents = n.curbuf_contents
  14. local fn = n.fn
  15. local dedent = t.dedent
  16. local function reset()
  17. command('bwipe! | new')
  18. insert([[
  19. Line of words 1
  20. Line of words 2]])
  21. command('goto 1')
  22. feed('itest_string.<esc>u')
  23. fn.setreg('a', 'test_stringa', 'V')
  24. fn.setreg('b', 'test_stringb\ntest_stringb\ntest_stringb', 'b')
  25. fn.setreg('"', 'test_string"', 'v')
  26. end
  27. -- We check the last inserted register ". in each of these tests because it is
  28. -- implemented completely differently in do_put().
  29. -- It is implemented differently so that control characters and imap'ped
  30. -- characters work in the same manner when pasted as when inserted.
  31. describe('put command', function()
  32. clear()
  33. before_each(reset)
  34. local function visual_marks_zero()
  35. for _, v in pairs(fn.getpos("'<")) do
  36. if v ~= 0 then
  37. return false
  38. end
  39. end
  40. for _, v in pairs(fn.getpos("'>")) do
  41. if v ~= 0 then
  42. return false
  43. end
  44. end
  45. return true
  46. end
  47. -- {{{ Where test definitions are run
  48. local function run_test_variations(test_variations, extra_setup)
  49. reset()
  50. if extra_setup then
  51. extra_setup()
  52. end
  53. local init_contents = curbuf_contents()
  54. local init_cursorpos = fn.getcurpos()
  55. local assert_no_change = function(exception_table, after_undo)
  56. expect(init_contents)
  57. -- When putting the ". register forwards, undo doesn't move
  58. -- the cursor back to where it was before.
  59. -- This is because it uses the command character 'a' to
  60. -- start the insert, and undo after that leaves the cursor
  61. -- one place to the right (unless we were at the end of the
  62. -- line when we pasted).
  63. if not (exception_table.undo_position and after_undo) then
  64. eq(init_cursorpos, fn.getcurpos())
  65. end
  66. end
  67. for _, test in pairs(test_variations) do
  68. it(test.description, function()
  69. if extra_setup then
  70. extra_setup()
  71. end
  72. local orig_dotstr = fn.getreg('.')
  73. t.ok(visual_marks_zero())
  74. -- Make sure every test starts from the same conditions
  75. assert_no_change(test.exception_table, false)
  76. local was_cli = test.test_action()
  77. test.test_assertions(test.exception_table, false)
  78. -- Check that undo twice puts us back to the original conditions
  79. -- (i.e. puts the cursor and text back to before)
  80. feed('u')
  81. assert_no_change(test.exception_table, true)
  82. -- Should not have changed the ". register
  83. -- If we paste the ". register with a count we can't avoid
  84. -- changing this register, hence avoid this check.
  85. if not test.exception_table.dot_reg_changed then
  86. eq(orig_dotstr, fn.getreg('.'))
  87. end
  88. -- Doing something, undoing it, and then redoing it should
  89. -- leave us in the same state as just doing it once.
  90. -- For :ex actions we want '@:', for normal actions we want '.'
  91. -- The '.' redo doesn't work for visual put so just exit if
  92. -- it was tested.
  93. -- We check that visual put was used by checking if the '< and
  94. -- '> marks were changed.
  95. if not visual_marks_zero() then
  96. return
  97. end
  98. if test.exception_table.undo_position then
  99. fn.setpos('.', init_cursorpos)
  100. end
  101. if was_cli then
  102. feed('@:')
  103. else
  104. feed('.')
  105. end
  106. test.test_assertions(test.exception_table, true)
  107. end)
  108. end
  109. end -- run_test_variations()
  110. -- }}}
  111. local function create_test_defs(
  112. test_defs,
  113. command_base,
  114. command_creator, -- {{{
  115. expect_base,
  116. expect_creator
  117. )
  118. local rettab = {}
  119. local exceptions
  120. for _, v in pairs(test_defs) do
  121. if v[4] then
  122. exceptions = v[4]
  123. else
  124. exceptions = {}
  125. end
  126. table.insert(rettab, {
  127. test_action = command_creator(command_base, v[1]),
  128. test_assertions = expect_creator(expect_base, v[2]),
  129. description = v[3],
  130. exception_table = exceptions,
  131. })
  132. end
  133. return rettab
  134. end -- create_test_defs() }}}
  135. local function find_cursor_position(expect_string) -- {{{
  136. -- There must only be one occurrence of the character 'x' in
  137. -- expect_string.
  138. -- This function removes that occurrence, and returns the position that
  139. -- it was in.
  140. -- This returns the cursor position that would leave the 'x' in that
  141. -- place if we feed 'ix<esc>' and the string existed before it.
  142. for linenum, line in pairs(fn.split(expect_string, '\n', 1)) do
  143. local column = line:find('x')
  144. if column then
  145. return { linenum, column }, expect_string:gsub('x', '')
  146. end
  147. end
  148. end -- find_cursor_position() }}}
  149. -- Action function creators {{{
  150. local function create_p_action(test_map, substitution)
  151. local temp_val = test_map:gsub('p', substitution)
  152. return function()
  153. feed(temp_val)
  154. return false
  155. end
  156. end
  157. local function create_put_action(command_base, substitution)
  158. local temp_val = command_base:gsub('put', substitution)
  159. return function()
  160. feed_command(temp_val)
  161. return true
  162. end
  163. end
  164. -- }}}
  165. -- Expect function creator {{{
  166. local function expect_creator(conversion_function, expect_base, conversion_table)
  167. local temp_expect_string = conversion_function(expect_base, conversion_table)
  168. local cursor_position, expect_string = find_cursor_position(temp_expect_string)
  169. return function(exception_table, after_redo)
  170. expect(expect_string)
  171. -- Have to use getcurpos() instead of api.nvim_win_get_cursor(0) in
  172. -- order to account for virtualedit.
  173. -- We always want the curswant element in getcurpos(), which is
  174. -- sometimes different to the column element in
  175. -- api.nvim_win_get_cursor(0).
  176. -- NOTE: The ".gp command leaves the cursor after the pasted text
  177. -- when running, but does not when the command is redone with the
  178. -- '.' command.
  179. if not (exception_table.redo_position and after_redo) then
  180. local actual_position = fn.getcurpos()
  181. eq(cursor_position, { actual_position[2], actual_position[5] })
  182. end
  183. end
  184. end -- expect_creator() }}}
  185. -- Test definitions {{{
  186. local function copy_def(def)
  187. local rettab = { '', {}, '', nil }
  188. rettab[1] = def[1]
  189. for k, v in pairs(def[2]) do
  190. rettab[2][k] = v
  191. end
  192. rettab[3] = def[3]
  193. if def[4] then
  194. rettab[4] = {}
  195. for k, v in pairs(def[4]) do
  196. rettab[4][k] = v
  197. end
  198. end
  199. return rettab
  200. end
  201. local normal_command_defs = {
  202. {
  203. 'p',
  204. { cursor_after = false, put_backwards = false, dot_register = false },
  205. 'pastes after cursor with p',
  206. },
  207. {
  208. 'gp',
  209. { cursor_after = true, put_backwards = false, dot_register = false },
  210. 'leaves cursor after text with gp',
  211. },
  212. {
  213. '".p',
  214. { cursor_after = false, put_backwards = false, dot_register = true },
  215. 'works with the ". register',
  216. },
  217. {
  218. '".gp',
  219. { cursor_after = true, put_backwards = false, dot_register = true },
  220. 'gp works with the ". register',
  221. { redo_position = true },
  222. },
  223. {
  224. 'P',
  225. { cursor_after = false, put_backwards = true, dot_register = false },
  226. 'pastes before cursor with P',
  227. },
  228. {
  229. 'gP',
  230. { cursor_after = true, put_backwards = true, dot_register = false },
  231. 'gP pastes before cursor and leaves cursor after text',
  232. },
  233. {
  234. '".P',
  235. { cursor_after = false, put_backwards = true, dot_register = true },
  236. 'P works with ". register',
  237. },
  238. {
  239. '".gP',
  240. { cursor_after = true, put_backwards = true, dot_register = true },
  241. 'gP works with ". register',
  242. { redo_position = true },
  243. },
  244. }
  245. -- Add a definition applying a count for each definition above.
  246. -- Could do this for each transformation (p -> P, p -> gp etc), but I think
  247. -- it's neater this way (balance between being explicit and too verbose).
  248. for i = 1, #normal_command_defs do
  249. local cur = normal_command_defs[i]
  250. -- Make modified copy of current definition that includes a count.
  251. local newdef = copy_def(cur)
  252. newdef[2].count = 2
  253. cur[2].count = 1
  254. newdef[1] = '2' .. newdef[1]
  255. newdef[3] = 'double ' .. newdef[3]
  256. if cur[2].dot_register then
  257. if not cur[4] then
  258. newdef[4] = {}
  259. end
  260. newdef[4].dot_reg_changed = true
  261. end
  262. normal_command_defs[#normal_command_defs + 1] = newdef
  263. end
  264. local ex_command_defs = {
  265. {
  266. 'put',
  267. { put_backwards = false, dot_register = false },
  268. 'pastes linewise forwards with :put',
  269. },
  270. {
  271. 'put!',
  272. { put_backwards = true, dot_register = false },
  273. 'pastes linewise backwards with :put!',
  274. },
  275. {
  276. 'put .',
  277. { put_backwards = false, dot_register = true },
  278. 'pastes linewise with the dot register',
  279. },
  280. {
  281. 'put! .',
  282. { put_backwards = true, dot_register = true },
  283. 'pastes linewise backwards with the dot register',
  284. },
  285. }
  286. local function non_dotdefs(def_table)
  287. return filter(function(d)
  288. return not d[2].dot_register
  289. end, def_table)
  290. end
  291. -- }}}
  292. -- Conversion functions {{{
  293. local function convert_charwise(expect_base, conversion_table, virtualedit_end, visual_put)
  294. expect_base = dedent(expect_base)
  295. -- There is no difference between 'P' and 'p' when VIsual_active
  296. if not visual_put then
  297. if conversion_table.put_backwards then
  298. -- Special case for virtualedit at the end of a line.
  299. local replace_string
  300. if not virtualedit_end then
  301. replace_string = 'test_stringx"%1'
  302. else
  303. replace_string = 'test_stringx"'
  304. end
  305. expect_base = expect_base:gsub('(.)test_stringx"', replace_string)
  306. end
  307. end
  308. if conversion_table.count > 1 then
  309. local rep_string = 'test_string"'
  310. local extra_puts = rep_string:rep(conversion_table.count - 1)
  311. expect_base = expect_base:gsub('test_stringx"', extra_puts .. 'test_stringx"')
  312. end
  313. if conversion_table.cursor_after then
  314. expect_base = expect_base:gsub('test_stringx"', 'test_string"x')
  315. end
  316. if conversion_table.dot_register then
  317. expect_base = expect_base:gsub('(test_stringx?)"', '%1.')
  318. end
  319. return expect_base
  320. end -- convert_charwise()
  321. local function make_back(string)
  322. local prev_line
  323. local rettab = {}
  324. local string_found = false
  325. for _, line in pairs(fn.split(string, '\n', 1)) do
  326. if line:find('test_string') then
  327. string_found = true
  328. table.insert(rettab, line)
  329. else
  330. if string_found then
  331. if prev_line then
  332. table.insert(rettab, prev_line)
  333. prev_line = nil
  334. end
  335. table.insert(rettab, line)
  336. else
  337. table.insert(rettab, prev_line)
  338. prev_line = line
  339. end
  340. end
  341. end
  342. -- In case there are no lines after the text that was put.
  343. if prev_line and string_found then
  344. table.insert(rettab, prev_line)
  345. end
  346. return table.concat(rettab, '\n')
  347. end -- make_back()
  348. local function convert_linewise(expect_base, conversion_table, _, use_a, indent)
  349. expect_base = dedent(expect_base)
  350. if conversion_table.put_backwards then
  351. expect_base = make_back(expect_base)
  352. end
  353. local p_str = 'test_string"'
  354. if use_a then
  355. p_str = 'test_stringa'
  356. end
  357. if conversion_table.dot_register then
  358. expect_base = expect_base:gsub('x' .. p_str, 'xtest_string.')
  359. p_str = 'test_string.'
  360. end
  361. if conversion_table.cursor_after then
  362. expect_base = expect_base:gsub('x' .. p_str .. '\n', p_str .. '\nx')
  363. end
  364. -- The 'indent' argument is only used here because a single put with an
  365. -- indent doesn't require special handling. It doesn't require special
  366. -- handling because the cursor is never put before the indent, hence
  367. -- the modification of 'test_stringx"' gives the same overall answer as
  368. -- modifying ' test_stringx"'.
  369. -- Only happens when using normal mode command actions.
  370. if conversion_table.count and conversion_table.count > 1 then
  371. if not indent then
  372. indent = ''
  373. end
  374. local rep_string = indent .. p_str .. '\n'
  375. local extra_puts = rep_string:rep(conversion_table.count - 1)
  376. local orig_string, new_string
  377. if conversion_table.cursor_after then
  378. orig_string = indent .. p_str .. '\nx'
  379. new_string = extra_puts .. orig_string
  380. else
  381. orig_string = indent .. 'x' .. p_str .. '\n'
  382. new_string = orig_string .. extra_puts
  383. end
  384. expect_base = expect_base:gsub(orig_string, new_string)
  385. end
  386. return expect_base
  387. end
  388. local function put_x_last(orig_line, p_str)
  389. local prev_end, cur_end, cur_start = 0, 0, 0
  390. while cur_start do
  391. prev_end = cur_end
  392. cur_start, cur_end = orig_line:find(p_str, prev_end)
  393. end
  394. -- Assume (because that is the only way I call it) that p_str matches
  395. -- the pattern 'test_string.'
  396. return orig_line:sub(1, prev_end - 1) .. 'x' .. orig_line:sub(prev_end)
  397. end
  398. local function convert_blockwise(
  399. expect_base,
  400. conversion_table,
  401. visual,
  402. use_b,
  403. trailing_whitespace
  404. )
  405. expect_base = dedent(expect_base)
  406. local p_str = 'test_string"'
  407. if use_b then
  408. p_str = 'test_stringb'
  409. end
  410. if conversion_table.dot_register then
  411. expect_base = expect_base:gsub('(x?)' .. p_str, '%1test_string.')
  412. -- Looks strange, but the dot is a special character in the pattern
  413. -- and a literal character in the replacement.
  414. expect_base = expect_base:gsub('test_stringx.', 'test_stringx.')
  415. p_str = 'test_string.'
  416. end
  417. -- No difference between 'p' and 'P' in visual mode.
  418. if not visual then
  419. if conversion_table.put_backwards then
  420. -- One for the line where the cursor is left, one for all other
  421. -- lines.
  422. expect_base = expect_base:gsub('([^x])' .. p_str, p_str .. '%1')
  423. expect_base = expect_base:gsub('([^x])x' .. p_str, 'x' .. p_str .. '%1')
  424. if not trailing_whitespace then
  425. expect_base = expect_base:gsub(' \n', '\n')
  426. expect_base = expect_base:gsub(' $', '')
  427. end
  428. end
  429. end
  430. if conversion_table.count and conversion_table.count > 1 then
  431. local p_pattern = p_str:gsub('%.', '%%.')
  432. expect_base = expect_base:gsub(p_pattern, p_str:rep(conversion_table.count))
  433. expect_base =
  434. expect_base:gsub('test_stringx([b".])', p_str:rep(conversion_table.count - 1) .. '%0')
  435. end
  436. if conversion_table.cursor_after then
  437. if not visual then
  438. local prev_line
  439. local rettab = {}
  440. local prev_in_block = false
  441. for _, line in pairs(fn.split(expect_base, '\n', 1)) do
  442. if line:find('test_string') then
  443. if prev_line then
  444. prev_line = prev_line:gsub('x', '')
  445. table.insert(rettab, prev_line)
  446. end
  447. prev_line = line
  448. prev_in_block = true
  449. else
  450. if prev_in_block then
  451. prev_line = put_x_last(prev_line, p_str)
  452. table.insert(rettab, prev_line)
  453. prev_in_block = false
  454. end
  455. table.insert(rettab, line)
  456. end
  457. end
  458. if prev_line and prev_in_block then
  459. table.insert(rettab, put_x_last(prev_line, p_str))
  460. end
  461. expect_base = table.concat(rettab, '\n')
  462. else
  463. expect_base = expect_base:gsub('x(.)', '%1x')
  464. end
  465. end
  466. return expect_base
  467. end
  468. -- }}}
  469. -- Convenience functions {{{
  470. local function run_normal_mode_tests(
  471. test_string,
  472. base_map,
  473. extra_setup,
  474. virtualedit_end,
  475. selection_string
  476. )
  477. local function convert_closure(e, c)
  478. return convert_charwise(e, c, virtualedit_end, selection_string)
  479. end
  480. local function expect_normal_creator(expect_base, conversion_table)
  481. local test_expect = expect_creator(convert_closure, expect_base, conversion_table)
  482. return function(exception_table, after_redo)
  483. test_expect(exception_table, after_redo)
  484. if selection_string then
  485. if not conversion_table.put_backwards then
  486. eq(selection_string, fn.getreg('"'))
  487. end
  488. else
  489. eq('test_string"', fn.getreg('"'))
  490. end
  491. end
  492. end
  493. run_test_variations(
  494. create_test_defs(
  495. normal_command_defs,
  496. base_map,
  497. create_p_action,
  498. test_string,
  499. expect_normal_creator
  500. ),
  501. extra_setup
  502. )
  503. end -- run_normal_mode_tests()
  504. local function convert_linewiseer(expect_base, conversion_table)
  505. return expect_creator(convert_linewise, expect_base, conversion_table)
  506. end
  507. local function run_linewise_tests(expect_base, base_command, extra_setup)
  508. local linewise_test_defs = create_test_defs(
  509. ex_command_defs,
  510. base_command,
  511. create_put_action,
  512. expect_base,
  513. convert_linewiseer
  514. )
  515. run_test_variations(linewise_test_defs, extra_setup)
  516. end -- run_linewise_tests()
  517. -- }}}
  518. -- Actual tests
  519. describe('default pasting', function()
  520. local expect_string = [[
  521. Ltest_stringx"ine of words 1
  522. Line of words 2]]
  523. run_normal_mode_tests(expect_string, 'p')
  524. run_linewise_tests(
  525. [[
  526. Line of words 1
  527. xtest_string"
  528. Line of words 2]],
  529. 'put'
  530. )
  531. end)
  532. describe('linewise register', function()
  533. -- put with 'p'
  534. local local_ex_command_defs = non_dotdefs(normal_command_defs)
  535. local base_expect_string = [[
  536. Line of words 1
  537. xtest_stringa
  538. Line of words 2]]
  539. local function local_convert_linewise(expect_base, conversion_table)
  540. return convert_linewise(expect_base, conversion_table, nil, true)
  541. end
  542. local function expect_lineput(expect_base, conversion_table)
  543. return expect_creator(local_convert_linewise, expect_base, conversion_table)
  544. end
  545. run_test_variations(
  546. create_test_defs(
  547. local_ex_command_defs,
  548. '"ap',
  549. create_p_action,
  550. base_expect_string,
  551. expect_lineput
  552. )
  553. )
  554. -- put with :put
  555. local linewise_put_defs = non_dotdefs(ex_command_defs)
  556. base_expect_string = [[
  557. Line of words 1
  558. xtest_stringa
  559. Line of words 2]]
  560. run_test_variations(
  561. create_test_defs(
  562. linewise_put_defs,
  563. 'put a',
  564. create_put_action,
  565. base_expect_string,
  566. convert_linewiseer
  567. )
  568. )
  569. end)
  570. describe('blockwise register', function()
  571. local blockwise_put_defs = non_dotdefs(normal_command_defs)
  572. local test_base = [[
  573. Lxtest_stringbine of words 1
  574. Ltest_stringbine of words 2
  575. test_stringb]]
  576. local function expect_block_creator(expect_base, conversion_table)
  577. return expect_creator(function(e, c)
  578. return convert_blockwise(e, c, nil, true)
  579. end, expect_base, conversion_table)
  580. end
  581. run_test_variations(
  582. create_test_defs(blockwise_put_defs, '"bp', create_p_action, test_base, expect_block_creator)
  583. )
  584. end)
  585. it('adds correct indentation when put with [p and ]p', function()
  586. feed('G>>"a]pix<esc>')
  587. -- luacheck: ignore
  588. expect([[
  589. Line of words 1
  590. Line of words 2
  591. xtest_stringa]])
  592. feed('uu"a[pix<esc>')
  593. -- luacheck: ignore
  594. expect([[
  595. Line of words 1
  596. xtest_stringa
  597. Line of words 2]])
  598. end)
  599. describe('linewise paste with autoindent', function()
  600. -- luacheck: ignore
  601. run_linewise_tests(
  602. [[
  603. Line of words 1
  604. Line of words 2
  605. xtest_string"]],
  606. 'put',
  607. function()
  608. fn.setline('$', ' Line of words 2')
  609. -- Set curswant to '8' to be at the end of the tab character
  610. -- This is where the cursor is put back after the 'u' command.
  611. fn.setpos('.', { 0, 2, 1, 0, 8 })
  612. command('set autoindent')
  613. end
  614. )
  615. end)
  616. describe('put inside tabs with virtualedit', function()
  617. local test_string = [[
  618. Line of words 1
  619. test_stringx" Line of words 2]]
  620. run_normal_mode_tests(test_string, 'p', function()
  621. fn.setline('$', ' Line of words 2')
  622. command('setlocal virtualedit=all')
  623. fn.setpos('.', { 0, 2, 1, 2, 3 })
  624. end)
  625. end)
  626. describe('put after the line with virtualedit', function()
  627. -- luacheck: ignore 621
  628. local test_string = [[
  629. Line of words 1 test_stringx"
  630. Line of words 2]]
  631. run_normal_mode_tests(test_string, 'p', function()
  632. fn.setline('$', ' Line of words 2')
  633. command('setlocal virtualedit=all')
  634. fn.setpos('.', { 0, 1, 16, 1, 17 })
  635. end, true)
  636. end)
  637. describe('Visual put', function()
  638. describe('basic put', function()
  639. local test_string = [[
  640. test_stringx" words 1
  641. Line of words 2]]
  642. run_normal_mode_tests(test_string, 'v2ep', nil, nil, 'Line of')
  643. end)
  644. describe('over trailing newline', function()
  645. local test_string = 'Line of test_stringx"Line of words 2'
  646. run_normal_mode_tests(test_string, 'v$p', function()
  647. fn.setpos('.', { 0, 1, 9, 0, 9 })
  648. end, nil, 'words 1\n')
  649. end)
  650. describe('linewise mode', function()
  651. local test_string = [[
  652. xtest_string"
  653. Line of words 2]]
  654. local function expect_vis_linewise(expect_base, conversion_table)
  655. return expect_creator(function(e, c)
  656. return convert_linewise(e, c, nil, nil)
  657. end, expect_base, conversion_table)
  658. end
  659. run_test_variations(
  660. create_test_defs(
  661. normal_command_defs,
  662. 'Vp',
  663. create_p_action,
  664. test_string,
  665. expect_vis_linewise
  666. ),
  667. function()
  668. fn.setpos('.', { 0, 1, 1, 0, 1 })
  669. end
  670. )
  671. describe('with whitespace at bol', function()
  672. local function expect_vis_lineindented(expect_base, conversion_table)
  673. local test_expect = expect_creator(function(e, c)
  674. return convert_linewise(e, c, nil, nil, ' ')
  675. end, expect_base, conversion_table)
  676. return function(exception_table, after_redo)
  677. test_expect(exception_table, after_redo)
  678. if not conversion_table.put_backwards then
  679. eq('Line of words 1\n', fn.getreg('"'))
  680. end
  681. end
  682. end
  683. local base_expect_string = [[
  684. xtest_string"
  685. Line of words 2]]
  686. run_test_variations(
  687. create_test_defs(
  688. normal_command_defs,
  689. 'Vp',
  690. create_p_action,
  691. base_expect_string,
  692. expect_vis_lineindented
  693. ),
  694. function()
  695. feed('i test_string.<esc>u')
  696. fn.setreg('"', ' test_string"', 'v')
  697. end
  698. )
  699. end)
  700. end)
  701. describe('blockwise visual mode', function()
  702. local test_base = [[
  703. test_stringx"e of words 1
  704. test_string"e of words 2]]
  705. local function expect_block_creator(expect_base, conversion_table)
  706. local test_expect = expect_creator(function(e, c)
  707. return convert_blockwise(e, c, true)
  708. end, expect_base, conversion_table)
  709. return function(e, c)
  710. test_expect(e, c)
  711. if not conversion_table.put_backwards then
  712. eq('Lin\nLin', fn.getreg('"'))
  713. end
  714. end
  715. end
  716. local select_down_test_defs = create_test_defs(
  717. normal_command_defs,
  718. '<C-v>jllp',
  719. create_p_action,
  720. test_base,
  721. expect_block_creator
  722. )
  723. run_test_variations(select_down_test_defs)
  724. -- Undo and redo of a visual block put leave the cursor in the top
  725. -- left of the visual block area no matter where the cursor was
  726. -- when it started.
  727. local undo_redo_no = map(function(table)
  728. local rettab = copy_def(table)
  729. if not rettab[4] then
  730. rettab[4] = {}
  731. end
  732. rettab[4].undo_position = true
  733. rettab[4].redo_position = true
  734. return rettab
  735. end, normal_command_defs)
  736. -- Selection direction doesn't matter
  737. run_test_variations(
  738. create_test_defs(
  739. undo_redo_no,
  740. '<C-v>kllp',
  741. create_p_action,
  742. test_base,
  743. expect_block_creator
  744. ),
  745. function()
  746. fn.setpos('.', { 0, 2, 1, 0, 1 })
  747. end
  748. )
  749. describe('blockwise cursor after undo', function()
  750. -- A bit of a hack of the reset above.
  751. -- In the tests that selection direction doesn't matter, we
  752. -- don't check the undo/redo position because it doesn't fit
  753. -- the same pattern as everything else.
  754. -- Here we fix this by directly checking the undo/redo position
  755. -- in the test_assertions of our test definitions.
  756. local function assertion_creator(_, _)
  757. return function(_, _)
  758. feed('u')
  759. -- Have to use feed('u') here to set curswant, because
  760. -- ex_undo() doesn't do that.
  761. eq({ 0, 1, 1, 0, 1 }, fn.getcurpos())
  762. feed('<C-r>')
  763. eq({ 0, 1, 1, 0, 1 }, fn.getcurpos())
  764. end
  765. end
  766. run_test_variations(
  767. create_test_defs(undo_redo_no, '<C-v>kllp', create_p_action, test_base, assertion_creator),
  768. function()
  769. fn.setpos('.', { 0, 2, 1, 0, 1 })
  770. end
  771. )
  772. end)
  773. end)
  774. describe("with 'virtualedit'", function()
  775. describe('splitting a tab character', function()
  776. local base_expect_string = [[
  777. Line of words 1
  778. test_stringx" Line of words 2]]
  779. run_normal_mode_tests(base_expect_string, 'vp', function()
  780. fn.setline('$', ' Line of words 2')
  781. command('setlocal virtualedit=all')
  782. fn.setpos('.', { 0, 2, 1, 2, 3 })
  783. end, nil, ' ')
  784. end)
  785. describe('after end of line', function()
  786. local base_expect_string = [[
  787. Line of words 1 test_stringx"
  788. Line of words 2]]
  789. run_normal_mode_tests(base_expect_string, 'vp', function()
  790. command('setlocal virtualedit=all')
  791. fn.setpos('.', { 0, 1, 16, 2, 18 })
  792. end, true, ' ')
  793. end)
  794. end)
  795. end)
  796. describe('. register special tests', function()
  797. -- luacheck: ignore 621
  798. before_each(reset)
  799. it('applies control character actions', function()
  800. feed('i<C-t><esc>u')
  801. expect([[
  802. Line of words 1
  803. Line of words 2]])
  804. feed('".p')
  805. expect([[
  806. Line of words 1
  807. Line of words 2]])
  808. feed('u1go<C-v>j".p')
  809. eq(
  810. [[
  811. ine of words 1
  812. ine of words 2]],
  813. curbuf_contents()
  814. )
  815. end)
  816. local screen
  817. setup(function()
  818. screen = Screen.new()
  819. end)
  820. local function bell_test(actions, should_ring)
  821. if should_ring then
  822. -- check bell is not set by nvim before the action
  823. screen:sleep(50)
  824. end
  825. t.ok(not screen.bell and not screen.visualbell)
  826. actions()
  827. screen:expect {
  828. condition = function()
  829. if should_ring then
  830. if not screen.bell and not screen.visualbell then
  831. error('Bell was not rung after action')
  832. end
  833. else
  834. if screen.bell or screen.visualbell then
  835. error('Bell was rung after action')
  836. end
  837. end
  838. end,
  839. unchanged = not should_ring,
  840. }
  841. screen.bell = false
  842. screen.visualbell = false
  843. end
  844. it('should not ring the bell with gp at end of line', function()
  845. bell_test(function()
  846. feed('$".gp')
  847. end)
  848. -- Even if the last character is a multibyte character.
  849. reset()
  850. fn.setline(1, 'helloม')
  851. bell_test(function()
  852. feed('$".gp')
  853. end)
  854. end)
  855. it('should not ring the bell with gp and end of file', function()
  856. fn.setpos('.', { 0, 2, 1, 0 })
  857. bell_test(function()
  858. feed('$vl".gp')
  859. end)
  860. end)
  861. it('should ring the bell when deleting if not appropriate', function()
  862. command('goto 2')
  863. feed('i<bs><esc>')
  864. expect([[
  865. ine of words 1
  866. Line of words 2]])
  867. bell_test(function()
  868. feed('".P')
  869. end, true)
  870. end)
  871. it('should restore cursor position after undo of ".p', function()
  872. local origpos = fn.getcurpos()
  873. feed('".pu')
  874. eq(origpos, fn.getcurpos())
  875. end)
  876. it("should be unaffected by 'autoindent' with V\".2p", function()
  877. command('set autoindent')
  878. feed('i test_string.<esc>u')
  879. feed('V".2p')
  880. expect([[
  881. test_string.
  882. test_string.
  883. Line of words 2]])
  884. end)
  885. end)
  886. end)