search_spec.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. local helpers = require('test.functional.helpers')(after_each)
  2. local Screen = require('test.functional.ui.screen')
  3. local clear = helpers.clear
  4. local command = helpers.command
  5. local eq = helpers.eq
  6. local eval = helpers.eval
  7. local feed = helpers.feed
  8. local funcs = helpers.funcs
  9. local poke_eventloop = helpers.poke_eventloop
  10. describe('search cmdline', function()
  11. local screen
  12. before_each(function()
  13. clear()
  14. command('set nohlsearch')
  15. screen = Screen.new(20, 3)
  16. screen:attach()
  17. screen:set_default_attr_ids({
  18. inc = {reverse = true},
  19. err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
  20. more = { bold = true, foreground = Screen.colors.SeaGreen4 },
  21. tilde = { bold = true, foreground = Screen.colors.Blue1 },
  22. hl = { background = Screen.colors.Yellow },
  23. })
  24. end)
  25. local function tenlines()
  26. funcs.setline(1, {
  27. ' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there',
  28. ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar'
  29. })
  30. command('1')
  31. end
  32. it('history can be navigated with <C-N>/<C-P>', function()
  33. tenlines()
  34. command('set noincsearch')
  35. feed('/foobar<CR>')
  36. feed('/the<CR>')
  37. eq('the', eval('@/'))
  38. feed('/thes<C-P><C-P><CR>')
  39. eq('foobar', eval('@/'))
  40. end)
  41. describe('can traverse matches', function()
  42. before_each(tenlines)
  43. local function forwarditer(wrapscan)
  44. command('set incsearch '..wrapscan)
  45. feed('/the')
  46. screen:expect([[
  47. 1 |
  48. 2 {inc:the}se |
  49. /the^ |
  50. ]])
  51. feed('<C-G>')
  52. screen:expect([[
  53. 2 these |
  54. 3 {inc:the} |
  55. /the^ |
  56. ]])
  57. eq({0, 0, 0, 0}, funcs.getpos('"'))
  58. feed('<C-G>')
  59. screen:expect([[
  60. 3 the |
  61. 4 {inc:the}ir |
  62. /the^ |
  63. ]])
  64. feed('<C-G>')
  65. screen:expect([[
  66. 4 their |
  67. 5 {inc:the}re |
  68. /the^ |
  69. ]])
  70. feed('<C-G>')
  71. screen:expect([[
  72. 5 there |
  73. 6 {inc:the}ir |
  74. /the^ |
  75. ]])
  76. feed('<C-G>')
  77. screen:expect([[
  78. 6 their |
  79. 7 {inc:the} |
  80. /the^ |
  81. ]])
  82. feed('<C-G>')
  83. screen:expect([[
  84. 7 the |
  85. 8 {inc:the}m |
  86. /the^ |
  87. ]])
  88. feed('<C-G>')
  89. screen:expect([[
  90. 8 them |
  91. 9 {inc:the}se |
  92. /the^ |
  93. ]])
  94. screen.bell = false
  95. feed('<C-G>')
  96. if wrapscan == 'wrapscan' then
  97. screen:expect([[
  98. 2 {inc:the}se |
  99. 3 the |
  100. /the^ |
  101. ]])
  102. else
  103. screen:expect{grid=[[
  104. 8 them |
  105. 9 {inc:the}se |
  106. /the^ |
  107. ]], condition=function()
  108. eq(true, screen.bell)
  109. end}
  110. feed('<CR>')
  111. eq({0, 0, 0, 0}, funcs.getpos('"'))
  112. end
  113. end
  114. local function backiter(wrapscan)
  115. command('set incsearch '..wrapscan)
  116. command('$')
  117. feed('?the')
  118. screen:expect([[
  119. 9 {inc:the}se |
  120. 10 foobar |
  121. ?the^ |
  122. ]])
  123. screen.bell = false
  124. if wrapscan == 'wrapscan' then
  125. feed('<C-G>')
  126. screen:expect([[
  127. 2 {inc:the}se |
  128. 3 the |
  129. ?the^ |
  130. ]])
  131. feed('<CR>')
  132. screen:expect([[
  133. 2 ^these |
  134. 3 the |
  135. ?the |
  136. ]])
  137. else
  138. feed('<C-G>')
  139. screen:expect{grid=[[
  140. 9 {inc:the}se |
  141. 10 foobar |
  142. ?the^ |
  143. ]], condition=function()
  144. eq(true, screen.bell)
  145. end}
  146. feed('<CR>')
  147. screen:expect([[
  148. 9 ^these |
  149. 10 foobar |
  150. ?the |
  151. ]])
  152. end
  153. command('$')
  154. feed('?the')
  155. screen:expect([[
  156. 9 {inc:the}se |
  157. 10 foobar |
  158. ?the^ |
  159. ]])
  160. feed('<C-T>')
  161. screen:expect([[
  162. 8 {inc:the}m |
  163. 9 these |
  164. ?the^ |
  165. ]])
  166. for i = 1, 6 do
  167. feed('<C-T>')
  168. -- Avoid sleep just before expect, otherwise expect will take the full
  169. -- timeout
  170. if i ~= 6 then
  171. screen:sleep(1)
  172. end
  173. end
  174. screen:expect([[
  175. 2 {inc:the}se |
  176. 3 the |
  177. ?the^ |
  178. ]])
  179. screen.bell = false
  180. feed('<C-T>')
  181. if wrapscan == 'wrapscan' then
  182. screen:expect([[
  183. 9 {inc:the}se |
  184. 10 foobar |
  185. ?the^ |
  186. ]])
  187. else
  188. screen:expect{grid=[[
  189. 2 {inc:the}se |
  190. 3 the |
  191. ?the^ |
  192. ]], condition=function()
  193. eq(true, screen.bell)
  194. end}
  195. end
  196. end
  197. it("using <C-G> and 'nowrapscan'", function()
  198. forwarditer('nowrapscan')
  199. end)
  200. it("using <C-G> and 'wrapscan'", function()
  201. forwarditer('wrapscan')
  202. end)
  203. it("using <C-T> and 'nowrapscan'", function()
  204. backiter('nowrapscan')
  205. end)
  206. it("using <C-T> and 'wrapscan'", function()
  207. backiter('wrapscan')
  208. end)
  209. end)
  210. it('expands pattern with <C-L>', function()
  211. tenlines()
  212. command('set incsearch wrapscan')
  213. feed('/the')
  214. screen:expect([[
  215. 1 |
  216. 2 {inc:the}se |
  217. /the^ |
  218. ]])
  219. feed('<C-L>')
  220. screen:expect([[
  221. 1 |
  222. 2 {inc:thes}e |
  223. /thes^ |
  224. ]])
  225. feed('<C-G>')
  226. screen:expect([[
  227. 9 {inc:thes}e |
  228. 10 foobar |
  229. /thes^ |
  230. ]])
  231. feed('<C-G>')
  232. screen:expect([[
  233. 2 {inc:thes}e |
  234. 3 the |
  235. /thes^ |
  236. ]])
  237. feed('<CR>')
  238. screen:expect([[
  239. 2 ^these |
  240. 3 the |
  241. /thes |
  242. ]])
  243. command('1')
  244. command('set nowrapscan')
  245. feed('/the')
  246. screen:expect([[
  247. 1 |
  248. 2 {inc:the}se |
  249. /the^ |
  250. ]])
  251. feed('<C-L>')
  252. screen:expect([[
  253. 1 |
  254. 2 {inc:thes}e |
  255. /thes^ |
  256. ]])
  257. feed('<C-G>')
  258. screen:expect([[
  259. 9 {inc:thes}e |
  260. 10 foobar |
  261. /thes^ |
  262. ]])
  263. feed('<C-G><CR>')
  264. screen:expect([[
  265. 9 ^these |
  266. 10 foobar |
  267. /thes |
  268. ]])
  269. end)
  270. it('reduces pattern with <BS> and keeps cursor position', function()
  271. tenlines()
  272. command('set incsearch wrapscan')
  273. -- First match
  274. feed('/thei')
  275. screen:expect([[
  276. 4 {inc:thei}r |
  277. 5 there |
  278. /thei^ |
  279. ]])
  280. -- Match from initial cursor position when modifying search
  281. feed('<BS>')
  282. screen:expect([[
  283. 1 |
  284. 2 {inc:the}se |
  285. /the^ |
  286. ]])
  287. -- New text advances to next match
  288. feed('s')
  289. screen:expect([[
  290. 1 |
  291. 2 {inc:thes}e |
  292. /thes^ |
  293. ]])
  294. -- Stay on this match when deleting a character
  295. feed('<BS>')
  296. screen:expect([[
  297. 1 |
  298. 2 {inc:the}se |
  299. /the^ |
  300. ]])
  301. -- Advance to previous match
  302. feed('<C-T>')
  303. screen:expect([[
  304. 9 {inc:the}se |
  305. 10 foobar |
  306. /the^ |
  307. ]])
  308. -- Extend search to include next character
  309. feed('<C-L>')
  310. screen:expect([[
  311. 9 {inc:thes}e |
  312. 10 foobar |
  313. /thes^ |
  314. ]])
  315. -- Deleting all characters resets the cursor position
  316. feed('<BS><BS><BS><BS>')
  317. screen:expect([[
  318. 1 |
  319. 2 these |
  320. /^ |
  321. ]])
  322. feed('the')
  323. screen:expect([[
  324. 1 |
  325. 2 {inc:the}se |
  326. /the^ |
  327. ]])
  328. feed('\\>')
  329. screen:expect([[
  330. 2 these |
  331. 3 {inc:the} |
  332. /the\>^ |
  333. ]])
  334. end)
  335. it('can traverse matches in the same line with <C-G>/<C-T>', function()
  336. funcs.setline(1, { ' 1', ' 2 these', ' 3 the theother' })
  337. command('1')
  338. command('set incsearch')
  339. -- First match
  340. feed('/the')
  341. screen:expect([[
  342. 1 |
  343. 2 {inc:the}se |
  344. /the^ |
  345. ]])
  346. -- Next match, different line
  347. feed('<C-G>')
  348. screen:expect([[
  349. 2 these |
  350. 3 {inc:the} theother |
  351. /the^ |
  352. ]])
  353. -- Next match, same line
  354. feed('<C-G>')
  355. screen:expect([[
  356. 2 these |
  357. 3 the {inc:the}other |
  358. /the^ |
  359. ]])
  360. feed('<C-G>')
  361. screen:expect([[
  362. 2 these |
  363. 3 the theo{inc:the}r |
  364. /the^ |
  365. ]])
  366. -- Previous match, same line
  367. feed('<C-T>')
  368. screen:expect([[
  369. 2 these |
  370. 3 the {inc:the}other |
  371. /the^ |
  372. ]])
  373. feed('<C-T>')
  374. screen:expect([[
  375. 2 these |
  376. 3 {inc:the} theother |
  377. /the^ |
  378. ]])
  379. -- Previous match, different line
  380. feed('<C-T>')
  381. screen:expect([[
  382. 2 {inc:the}se |
  383. 3 the theother |
  384. /the^ |
  385. ]])
  386. end)
  387. it('keeps the view after deleting a char from the search', function()
  388. screen:try_resize(20, 6)
  389. tenlines()
  390. feed('/foo')
  391. screen:expect([[
  392. 6 their |
  393. 7 the |
  394. 8 them |
  395. 9 these |
  396. 10 {inc:foo}bar |
  397. /foo^ |
  398. ]])
  399. feed('<BS>')
  400. screen:expect([[
  401. 6 their |
  402. 7 the |
  403. 8 them |
  404. 9 these |
  405. 10 {inc:fo}obar |
  406. /fo^ |
  407. ]])
  408. feed('<CR>')
  409. screen:expect([[
  410. 6 their |
  411. 7 the |
  412. 8 them |
  413. 9 these |
  414. 10 ^foobar |
  415. /fo |
  416. ]])
  417. eq({lnum = 10, leftcol = 0, col = 4, topfill = 0, topline = 6,
  418. coladd = 0, skipcol = 0, curswant = 4},
  419. funcs.winsaveview())
  420. end)
  421. it('restores original view after failed search', function()
  422. screen:try_resize(40, 3)
  423. tenlines()
  424. feed('0')
  425. feed('/foo')
  426. screen:expect([[
  427. 9 these |
  428. 10 {inc:foo}bar |
  429. /foo^ |
  430. ]])
  431. feed('<C-W>')
  432. screen:expect([[
  433. 1 |
  434. 2 these |
  435. /^ |
  436. ]])
  437. feed('<CR>')
  438. screen:expect([[
  439. / |
  440. {err:E35: No previous regular expression} |
  441. {more:Press ENTER or type command to continue}^ |
  442. ]])
  443. feed('<CR>')
  444. eq({lnum = 1, leftcol = 0, col = 0, topfill = 0, topline = 1,
  445. coladd = 0, skipcol = 0, curswant = 0},
  446. funcs.winsaveview())
  447. end)
  448. it("CTRL-G with 'incsearch' and ? goes in the right direction", function()
  449. -- oldtest: Test_search_cmdline4().
  450. screen:try_resize(40, 4)
  451. command('enew!')
  452. funcs.setline(1, {' 1 the first', ' 2 the second', ' 3 the third'})
  453. command('set laststatus=0 shortmess+=s')
  454. command('set incsearch')
  455. command('$')
  456. -- Send the input in chunks, so the cmdline logic regards it as
  457. -- "interactive". This mimics Vim's test_override("char_avail").
  458. -- (See legacy test: test_search.vim)
  459. feed('?the')
  460. poke_eventloop()
  461. feed('<c-g>')
  462. poke_eventloop()
  463. feed('<cr>')
  464. screen:expect([[
  465. 1 the first |
  466. 2 the second |
  467. 3 ^the third |
  468. ?the |
  469. ]])
  470. command('$')
  471. feed('?the')
  472. poke_eventloop()
  473. feed('<c-g>')
  474. poke_eventloop()
  475. feed('<c-g>')
  476. poke_eventloop()
  477. feed('<cr>')
  478. screen:expect([[
  479. 1 ^the first |
  480. 2 the second |
  481. 3 the third |
  482. ?the |
  483. ]])
  484. command('$')
  485. feed('?the')
  486. poke_eventloop()
  487. feed('<c-g>')
  488. poke_eventloop()
  489. feed('<c-g>')
  490. poke_eventloop()
  491. feed('<c-g>')
  492. poke_eventloop()
  493. feed('<cr>')
  494. screen:expect([[
  495. 1 the first |
  496. 2 ^the second |
  497. 3 the third |
  498. ?the |
  499. ]])
  500. command('$')
  501. feed('?the')
  502. poke_eventloop()
  503. feed('<c-t>')
  504. poke_eventloop()
  505. feed('<cr>')
  506. screen:expect([[
  507. 1 ^the first |
  508. 2 the second |
  509. 3 the third |
  510. ?the |
  511. ]])
  512. command('$')
  513. feed('?the')
  514. poke_eventloop()
  515. feed('<c-t>')
  516. poke_eventloop()
  517. feed('<c-t>')
  518. poke_eventloop()
  519. feed('<cr>')
  520. screen:expect([[
  521. 1 the first |
  522. 2 the second |
  523. 3 ^the third |
  524. ?the |
  525. ]])
  526. command('$')
  527. feed('?the')
  528. poke_eventloop()
  529. feed('<c-t>')
  530. poke_eventloop()
  531. feed('<c-t>')
  532. poke_eventloop()
  533. feed('<c-t>')
  534. poke_eventloop()
  535. feed('<cr>')
  536. screen:expect([[
  537. 1 the first |
  538. 2 ^the second |
  539. 3 the third |
  540. ?the |
  541. ]])
  542. end)
  543. it('incsearch works with :sort', function()
  544. -- oldtest: Test_incsearch_sort_dump().
  545. screen:try_resize(20, 4)
  546. command('set incsearch hlsearch scrolloff=0')
  547. funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'})
  548. feed(':sort ni u /on')
  549. screen:expect([[
  550. another {inc:on}e 2 |
  551. that {hl:on}e 3 |
  552. the {hl:on}e 1 |
  553. :sort ni u /on^ |
  554. ]])
  555. feed('<esc>')
  556. end)
  557. it('incsearch works with :vimgrep family', function()
  558. -- oldtest: Test_incsearch_vimgrep_dump().
  559. screen:try_resize(30, 4)
  560. command('set incsearch hlsearch scrolloff=0')
  561. funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'})
  562. feed(':vimgrep on')
  563. screen:expect([[
  564. another {inc:on}e 2 |
  565. that {hl:on}e 3 |
  566. the {hl:on}e 1 |
  567. :vimgrep on^ |
  568. ]])
  569. feed('<esc>')
  570. feed(':vimg /on/ *.txt')
  571. screen:expect([[
  572. another {inc:on}e 2 |
  573. that {hl:on}e 3 |
  574. the {hl:on}e 1 |
  575. :vimg /on/ *.txt^ |
  576. ]])
  577. feed('<esc>')
  578. feed(':vimgrepadd "\\<LT>on')
  579. screen:expect([[
  580. another {inc:on}e 2 |
  581. that {hl:on}e 3 |
  582. the {hl:on}e 1 |
  583. :vimgrepadd "\<on^ |
  584. ]])
  585. feed('<esc>')
  586. feed(':lv "tha')
  587. screen:expect([[
  588. another one 2 |
  589. {inc:tha}t one 3 |
  590. the one 1 |
  591. :lv "tha^ |
  592. ]])
  593. feed('<esc>')
  594. feed(':lvimgrepa "the" **/*.txt')
  595. screen:expect([[
  596. ano{inc:the}r one 2 |
  597. that one 3 |
  598. {hl:the} one 1 |
  599. :lvimgrepa "the" **/*.txt^ |
  600. ]])
  601. feed('<esc>')
  602. end)
  603. end)