jump_spec.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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 dedent = t.dedent
  7. local eq = t.eq
  8. local fn = n.fn
  9. local feed = n.feed
  10. local exec_capture = n.exec_capture
  11. local write_file = t.write_file
  12. local api = n.api
  13. describe('jumplist', function()
  14. local fname1 = 'Xtest-functional-normal-jump'
  15. local fname2 = fname1 .. '2'
  16. before_each(clear)
  17. after_each(function()
  18. os.remove(fname1)
  19. os.remove(fname2)
  20. end)
  21. it('does not add a new entry on startup', function()
  22. eq('\n jump line col file/text\n>', fn.execute('jumps'))
  23. end)
  24. it('does not require two <C-O> strokes to jump back', function()
  25. write_file(fname1, 'first file contents')
  26. write_file(fname2, 'second file contents')
  27. command('args ' .. fname1 .. ' ' .. fname2)
  28. local buf1 = fn.bufnr(fname1)
  29. local buf2 = fn.bufnr(fname2)
  30. command('next')
  31. feed('<C-O>')
  32. eq(buf1, fn.bufnr('%'))
  33. command('first')
  34. command('snext')
  35. feed('<C-O>')
  36. eq(buf1, fn.bufnr('%'))
  37. feed('<C-I>')
  38. eq(buf2, fn.bufnr('%'))
  39. feed('<C-O>')
  40. eq(buf1, fn.bufnr('%'))
  41. command('drop ' .. fname2)
  42. feed('<C-O>')
  43. eq(buf1, fn.bufnr('%'))
  44. end)
  45. it('<C-O> scrolls cursor halfway when switching buffer #25763', function()
  46. write_file(fname1, ('foobar\n'):rep(100))
  47. write_file(fname2, 'baz')
  48. local screen = Screen.new(5, 25)
  49. command('set number')
  50. command('edit ' .. fname1)
  51. feed('35gg')
  52. command('edit ' .. fname2)
  53. feed('<C-O>')
  54. screen:expect {
  55. grid = [[
  56. {1: 24 }foobar |
  57. {1: 25 }foobar |
  58. {1: 26 }foobar |
  59. {1: 27 }foobar |
  60. {1: 28 }foobar |
  61. {1: 29 }foobar |
  62. {1: 30 }foobar |
  63. {1: 31 }foobar |
  64. {1: 32 }foobar |
  65. {1: 33 }foobar |
  66. {1: 34 }foobar |
  67. {1: 35 }^foobar |
  68. {1: 36 }foobar |
  69. {1: 37 }foobar |
  70. {1: 38 }foobar |
  71. {1: 39 }foobar |
  72. {1: 40 }foobar |
  73. {1: 41 }foobar |
  74. {1: 42 }foobar |
  75. {1: 43 }foobar |
  76. {1: 44 }foobar |
  77. {1: 45 }foobar |
  78. {1: 46 }foobar |
  79. {1: 47 }foobar |
  80. |
  81. ]],
  82. attr_ids = {
  83. [1] = { foreground = Screen.colors.Brown },
  84. },
  85. }
  86. end)
  87. end)
  88. describe("jumpoptions=stack behaves like 'tagstack'", function()
  89. before_each(function()
  90. clear()
  91. feed(':clearjumps<cr>')
  92. -- Add lines so that we have locations to jump to.
  93. for i = 1, 101, 1 do
  94. feed('iLine ' .. i .. '<cr><esc>')
  95. end
  96. -- Jump around to add some locations to the jump list.
  97. feed('0gg')
  98. feed('10gg')
  99. feed('20gg')
  100. feed('30gg')
  101. feed('40gg')
  102. feed('50gg')
  103. feed(':set jumpoptions=stack<cr>')
  104. end)
  105. after_each(function()
  106. feed('set jumpoptions=')
  107. end)
  108. it('discards the tail when navigating from the middle', function()
  109. feed('<C-O>')
  110. feed('<C-O>')
  111. eq(
  112. ''
  113. .. ' jump line col file/text\n'
  114. .. ' 4 102 0 \n'
  115. .. ' 3 1 0 Line 1\n'
  116. .. ' 2 10 0 Line 10\n'
  117. .. ' 1 20 0 Line 20\n'
  118. .. '> 0 30 0 Line 30\n'
  119. .. ' 1 40 0 Line 40\n'
  120. .. ' 2 50 0 Line 50',
  121. exec_capture('jumps')
  122. )
  123. feed('90gg')
  124. eq(
  125. ''
  126. .. ' jump line col file/text\n'
  127. .. ' 5 102 0 \n'
  128. .. ' 4 1 0 Line 1\n'
  129. .. ' 3 10 0 Line 10\n'
  130. .. ' 2 20 0 Line 20\n'
  131. .. ' 1 30 0 Line 30\n'
  132. .. '>',
  133. exec_capture('jumps')
  134. )
  135. end)
  136. it('does not add the same location twice adjacently', function()
  137. feed('60gg')
  138. feed('60gg')
  139. eq(
  140. ''
  141. .. ' jump line col file/text\n'
  142. .. ' 7 102 0 \n'
  143. .. ' 6 1 0 Line 1\n'
  144. .. ' 5 10 0 Line 10\n'
  145. .. ' 4 20 0 Line 20\n'
  146. .. ' 3 30 0 Line 30\n'
  147. .. ' 2 40 0 Line 40\n'
  148. .. ' 1 50 0 Line 50\n'
  149. .. '>',
  150. exec_capture('jumps')
  151. )
  152. end)
  153. it('does add the same location twice nonadjacently', function()
  154. feed('10gg')
  155. feed('20gg')
  156. eq(
  157. ''
  158. .. ' jump line col file/text\n'
  159. .. ' 8 102 0 \n'
  160. .. ' 7 1 0 Line 1\n'
  161. .. ' 6 10 0 Line 10\n'
  162. .. ' 5 20 0 Line 20\n'
  163. .. ' 4 30 0 Line 30\n'
  164. .. ' 3 40 0 Line 40\n'
  165. .. ' 2 50 0 Line 50\n'
  166. .. ' 1 10 0 Line 10\n'
  167. .. '>',
  168. exec_capture('jumps')
  169. )
  170. end)
  171. end)
  172. describe('buffer deletion with jumpoptions+=clean', function()
  173. local base_file = 'Xtest-functional-buffer-deletion'
  174. local file1 = base_file .. '1'
  175. local file2 = base_file .. '2'
  176. local file3 = base_file .. '3'
  177. local base_content = 'text'
  178. local content1 = base_content .. '1'
  179. local content2 = base_content .. '2'
  180. local content3 = base_content .. '3'
  181. local function format_jumplist(input)
  182. return dedent(input)
  183. :gsub('%{file1%}', file1)
  184. :gsub('%{file2%}', file2)
  185. :gsub('%{file3%}', file3)
  186. :gsub('%{content1%}', content1)
  187. :gsub('%{content2%}', content2)
  188. :gsub('%{content3%}', content3)
  189. end
  190. before_each(function()
  191. clear()
  192. command('clearjumps')
  193. write_file(file1, content1, false, false)
  194. write_file(file2, content2, false, false)
  195. write_file(file3, content3, false, false)
  196. command('edit ' .. file1)
  197. command('edit ' .. file2)
  198. command('edit ' .. file3)
  199. end)
  200. after_each(function()
  201. os.remove(file1)
  202. os.remove(file2)
  203. os.remove(file3)
  204. end)
  205. it('deletes jump list entries when the current buffer is deleted', function()
  206. command('edit ' .. file1)
  207. eq(
  208. format_jumplist([[
  209. jump line col file/text
  210. 3 1 0 {content1}
  211. 2 1 0 {file2}
  212. 1 1 0 {file3}
  213. >]]),
  214. exec_capture('jumps')
  215. )
  216. command('bwipeout')
  217. eq(
  218. format_jumplist([[
  219. jump line col file/text
  220. 1 1 0 {file2}
  221. > 0 1 0 {content3}]]),
  222. exec_capture('jumps')
  223. )
  224. end)
  225. it('deletes jump list entries when another buffer is deleted', function()
  226. eq(
  227. format_jumplist([[
  228. jump line col file/text
  229. 2 1 0 {file1}
  230. 1 1 0 {file2}
  231. >]]),
  232. exec_capture('jumps')
  233. )
  234. command('bwipeout ' .. file2)
  235. eq(
  236. format_jumplist([[
  237. jump line col file/text
  238. 1 1 0 {file1}
  239. >]]),
  240. exec_capture('jumps')
  241. )
  242. end)
  243. it('sets the correct jump index when the current buffer is deleted', function()
  244. feed('<C-O>')
  245. eq(
  246. format_jumplist([[
  247. jump line col file/text
  248. 1 1 0 {file1}
  249. > 0 1 0 {content2}
  250. 1 1 0 {file3}]]),
  251. exec_capture('jumps')
  252. )
  253. command('bw')
  254. eq(
  255. format_jumplist([[
  256. jump line col file/text
  257. 1 1 0 {file1}
  258. > 0 1 0 {content3}]]),
  259. exec_capture('jumps')
  260. )
  261. end)
  262. it('sets the correct jump index when the another buffer is deleted', function()
  263. feed('<C-O>')
  264. eq(
  265. format_jumplist([[
  266. jump line col file/text
  267. 1 1 0 {file1}
  268. > 0 1 0 {content2}
  269. 1 1 0 {file3}]]),
  270. exec_capture('jumps')
  271. )
  272. command('bwipeout ' .. file1)
  273. eq(
  274. format_jumplist([[
  275. jump line col file/text
  276. > 0 1 0 {content2}
  277. 1 1 0 {file3}]]),
  278. exec_capture('jumps')
  279. )
  280. end)
  281. end)
  282. describe('buffer deletion with jumpoptions-=clean', function()
  283. local base_file = 'Xtest-functional-buffer-deletion'
  284. local file1 = base_file .. '1'
  285. local file2 = base_file .. '2'
  286. local base_content = 'text'
  287. local content1 = base_content .. '1'
  288. local content2 = base_content .. '2'
  289. before_each(function()
  290. clear()
  291. command('clearjumps')
  292. command('set jumpoptions-=clean')
  293. write_file(file1, content1, false, false)
  294. write_file(file2, content2, false, false)
  295. command('edit ' .. file1)
  296. command('edit ' .. file2)
  297. end)
  298. after_each(function()
  299. os.remove(file1)
  300. os.remove(file2)
  301. end)
  302. it('Ctrl-O reopens previous buffer with :bunload or :bdelete #28968', function()
  303. eq(file2, fn.bufname(''))
  304. command('bunload')
  305. eq(file1, fn.bufname(''))
  306. feed('<C-O>')
  307. eq(file2, fn.bufname(''))
  308. command('bdelete')
  309. eq(file1, fn.bufname(''))
  310. feed('<C-O>')
  311. eq(file2, fn.bufname(''))
  312. end)
  313. end)
  314. describe('jumpoptions=view', function()
  315. local file1 = 'Xtestfile-functional-editor-jumps'
  316. local file2 = 'Xtestfile-functional-editor-jumps-2'
  317. local function content()
  318. local c = {}
  319. for i = 1, 30 do
  320. c[i] = i .. ' line'
  321. end
  322. return table.concat(c, '\n')
  323. end
  324. before_each(function()
  325. clear()
  326. write_file(file1, content(), false, false)
  327. write_file(file2, content(), false, false)
  328. command('set jumpoptions=view')
  329. end)
  330. after_each(function()
  331. os.remove(file1)
  332. os.remove(file2)
  333. end)
  334. it('restores the view', function()
  335. local screen = Screen.new(5, 8)
  336. command('edit ' .. file1)
  337. feed('12Gztj')
  338. feed('gg<C-o>')
  339. screen:expect([[
  340. 12 line |
  341. ^13 line |
  342. 14 line |
  343. 15 line |
  344. 16 line |
  345. 17 line |
  346. 18 line |
  347. |
  348. ]])
  349. end)
  350. it('restores the view across files', function()
  351. local screen = Screen.new(5, 5)
  352. command('args ' .. file1 .. ' ' .. file2)
  353. feed('12Gzt')
  354. command('next')
  355. feed('G')
  356. screen:expect([[
  357. 27 line |
  358. 28 line |
  359. 29 line |
  360. ^30 line |
  361. |
  362. ]])
  363. feed('<C-o><C-o>')
  364. screen:expect([[
  365. ^12 line |
  366. 13 line |
  367. 14 line |
  368. 15 line |
  369. |
  370. ]])
  371. end)
  372. it('restores the view across files with <C-^>', function()
  373. local screen = Screen.new(5, 5)
  374. command('args ' .. file1 .. ' ' .. file2)
  375. feed('12Gzt')
  376. command('next')
  377. feed('G')
  378. screen:expect([[
  379. 27 line |
  380. 28 line |
  381. 29 line |
  382. ^30 line |
  383. |
  384. ]])
  385. feed('<C-^>')
  386. screen:expect([[
  387. ^12 line |
  388. 13 line |
  389. 14 line |
  390. 15 line |
  391. |
  392. ]])
  393. end)
  394. it("falls back to standard behavior when view can't be recovered", function()
  395. local screen = Screen.new(5, 8)
  396. command('edit ' .. file1)
  397. feed('7GzbG')
  398. api.nvim_buf_set_lines(0, 0, 2, true, {})
  399. -- Move to line 7, and set it as the last line visible on the view with zb, meaning to recover
  400. -- the view it needs to put the cursor 7 lines from the top line. Then go to the end of the
  401. -- file, delete 2 lines before line 7, meaning the jump/mark is moved 2 lines up to line 5.
  402. -- Therefore when trying to jump back to it it's not possible to set a 7 line offset from the
  403. -- mark position to the top line, since there's only 5 lines from the mark position to line 0.
  404. -- Therefore falls back to standard behavior which is centering the view/line.
  405. feed('<C-o>')
  406. screen:expect([[
  407. 4 line |
  408. 5 line |
  409. 6 line |
  410. ^7 line |
  411. 8 line |
  412. 9 line |
  413. 10 line |
  414. |
  415. ]])
  416. end)
  417. it('falls back to standard behavior for a mark without a view', function()
  418. local screen = Screen.new(5, 8)
  419. command('edit ' .. file1)
  420. feed('10ggzzvwy')
  421. screen:expect([[
  422. 7 line |
  423. 8 line |
  424. 9 line |
  425. ^10 line |
  426. 11 line |
  427. 12 line |
  428. 13 line |
  429. |
  430. ]])
  431. feed('`]')
  432. screen:expect([[
  433. 7 line |
  434. 8 line |
  435. 9 line |
  436. 10 ^line |
  437. 11 line |
  438. 12 line |
  439. 13 line |
  440. |
  441. ]])
  442. end)
  443. end)