put_spec.lua 28 KB

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