search_spec.lua 20 KB

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