fold_spec.lua 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local clear = n.clear
  4. local insert = n.insert
  5. local exec = n.exec
  6. local feed = n.feed
  7. local expect = n.expect
  8. local command = n.command
  9. local fn = n.fn
  10. local eq = t.eq
  11. local neq = t.neq
  12. describe('Folding', function()
  13. local tempfname = 'Xtest-fold.txt'
  14. setup(clear)
  15. before_each(function()
  16. command('bwipe! | new')
  17. end)
  18. after_each(function()
  19. os.remove(tempfname)
  20. end)
  21. it('manual folding adjusts with filter', function()
  22. insert([[
  23. 1
  24. 2
  25. 3
  26. 4
  27. 5
  28. 6
  29. 7
  30. 8
  31. 9
  32. 10
  33. 11
  34. 12
  35. 13
  36. 14
  37. 15
  38. 16
  39. 17
  40. 18
  41. 19
  42. 20]])
  43. command('4,$fold')
  44. command('%foldopen')
  45. command('10,$fold')
  46. command('%foldopen')
  47. command('1,8! cat')
  48. feed('5ggzdzMGdd')
  49. expect([[
  50. 1
  51. 2
  52. 3
  53. 4
  54. 5
  55. 6
  56. 7
  57. 8
  58. 9]])
  59. end)
  60. describe('adjusting folds after :move', function()
  61. local function manually_fold_indent()
  62. -- setting foldmethod twice is a trick to get vim to set the folds for me
  63. command('setlocal foldmethod=indent')
  64. command('setlocal foldmethod=manual')
  65. -- Ensure that all folds will get closed (makes it easier to test the
  66. -- length of folds).
  67. command('setlocal foldminlines=0')
  68. -- Start with all folds open (so :move ranges aren't affected by closed
  69. -- folds).
  70. command('%foldopen!')
  71. end
  72. local function get_folds()
  73. local rettab = {}
  74. for i = 1, fn.line('$') do
  75. table.insert(rettab, fn.foldlevel(i))
  76. end
  77. return rettab
  78. end
  79. local function test_move_indent(insert_string, move_command)
  80. -- This test is easy because we just need to ensure that the resulting
  81. -- fold is the same as calculated when creating folds from scratch.
  82. insert(insert_string)
  83. command(move_command)
  84. local after_move_folds = get_folds()
  85. -- Doesn't change anything, but does call foldUpdateAll()
  86. command('setlocal foldminlines=0')
  87. eq(after_move_folds, get_folds())
  88. -- Set up the buffer with insert_string for the manual fold testing.
  89. command('enew!')
  90. insert(insert_string)
  91. manually_fold_indent()
  92. command(move_command)
  93. end
  94. it('neither closes nor corrupts folds', function()
  95. test_move_indent(
  96. [[
  97. a
  98. a
  99. a
  100. a
  101. a
  102. a
  103. a
  104. a
  105. a
  106. a
  107. a
  108. a
  109. a
  110. a
  111. a
  112. a
  113. a
  114. a]],
  115. '7,12m0'
  116. )
  117. expect([[
  118. a
  119. a
  120. a
  121. a
  122. a
  123. a
  124. a
  125. a
  126. a
  127. a
  128. a
  129. a
  130. a
  131. a
  132. a
  133. a
  134. a
  135. a]])
  136. -- lines are not closed, folds are correct
  137. for i = 1, fn.line('$') do
  138. eq(-1, fn.foldclosed(i))
  139. if i == 1 or i == 7 or i == 13 then
  140. eq(0, fn.foldlevel(i))
  141. elseif i == 4 then
  142. eq(2, fn.foldlevel(i))
  143. else
  144. eq(1, fn.foldlevel(i))
  145. end
  146. end
  147. -- folds are not corrupted
  148. feed('zM')
  149. eq(6, fn.foldclosedend(2))
  150. eq(12, fn.foldclosedend(8))
  151. eq(18, fn.foldclosedend(14))
  152. end)
  153. it("doesn't split a fold when the move is within it", function()
  154. test_move_indent(
  155. [[
  156. a
  157. a
  158. a
  159. a
  160. a
  161. a
  162. a
  163. a
  164. a
  165. a]],
  166. '5m6'
  167. )
  168. eq({ 0, 1, 1, 2, 2, 2, 2, 1, 1, 0 }, get_folds())
  169. end)
  170. it('truncates folds that end in the moved range', function()
  171. test_move_indent(
  172. [[
  173. a
  174. a
  175. a
  176. a
  177. a
  178. a
  179. a]],
  180. '4,5m6'
  181. )
  182. eq({ 0, 1, 2, 0, 0, 0, 0 }, get_folds())
  183. end)
  184. it('moves folds that start between moved range and destination', function()
  185. test_move_indent(
  186. [[
  187. a
  188. a
  189. a
  190. a
  191. a
  192. a
  193. a
  194. a
  195. a
  196. a
  197. a
  198. a
  199. a]],
  200. '3,4m$'
  201. )
  202. eq({ 0, 1, 1, 0, 0, 1, 2, 1, 0, 0, 1, 0, 0 }, get_folds())
  203. end)
  204. it('does not affect folds outside changed lines', function()
  205. test_move_indent(
  206. [[
  207. a
  208. a
  209. a
  210. a
  211. a
  212. a
  213. a
  214. a
  215. a]],
  216. '4m5'
  217. )
  218. eq({ 1, 1, 1, 0, 0, 0, 1, 1, 1 }, get_folds())
  219. end)
  220. it('moves and truncates folds that start in moved range', function()
  221. test_move_indent(
  222. [[
  223. a
  224. a
  225. a
  226. a
  227. a
  228. a
  229. a
  230. a
  231. a
  232. a]],
  233. '1,3m7'
  234. )
  235. eq({ 0, 0, 0, 0, 0, 1, 2, 0, 0, 0 }, get_folds())
  236. end)
  237. it('breaks a fold when moving text into it', function()
  238. test_move_indent(
  239. [[
  240. a
  241. a
  242. a
  243. a
  244. a
  245. a
  246. a]],
  247. '$m4'
  248. )
  249. eq({ 0, 1, 2, 2, 0, 0, 0 }, get_folds())
  250. end)
  251. it('adjusts correctly when moving a range backwards', function()
  252. test_move_indent(
  253. [[
  254. a
  255. a
  256. a
  257. a
  258. a]],
  259. '2,3m0'
  260. )
  261. eq({ 1, 2, 0, 0, 0 }, get_folds())
  262. end)
  263. it('handles shifting all remaining folds', function()
  264. test_move_indent(
  265. [[
  266. a
  267. a
  268. a
  269. a
  270. a
  271. a
  272. a
  273. a
  274. a
  275. a
  276. a
  277. a
  278. a
  279. a
  280. a]],
  281. '13m7'
  282. )
  283. eq({ 1, 2, 2, 2, 1, 2, 2, 1, 1, 1, 2, 2, 2, 1, 0 }, get_folds())
  284. end)
  285. end)
  286. it('updates correctly on :read', function()
  287. -- luacheck: ignore 621
  288. t.write_file(
  289. tempfname,
  290. [[
  291. a
  292. a]]
  293. )
  294. insert([[
  295. a
  296. a
  297. a
  298. a
  299. ]])
  300. command('setlocal foldmethod=indent')
  301. command('2')
  302. command('%foldopen')
  303. command('read ' .. tempfname)
  304. -- Just to check we have the correct file text.
  305. expect([[
  306. a
  307. a
  308. a
  309. a
  310. a
  311. a
  312. ]])
  313. for i = 1, 2 do
  314. eq(1, fn.foldlevel(i))
  315. end
  316. for i = 3, 5 do
  317. eq(0, fn.foldlevel(i))
  318. end
  319. for i = 6, 8 do
  320. eq(1, fn.foldlevel(i))
  321. end
  322. end)
  323. it('combines folds when removing separating space', function()
  324. -- luacheck: ignore 621
  325. insert([[
  326. a
  327. a
  328. a
  329. a
  330. a
  331. a
  332. a
  333. a
  334. ]])
  335. command('setlocal foldmethod=indent')
  336. command('3,5d')
  337. eq(5, fn.foldclosedend(1))
  338. end)
  339. it("doesn't combine folds that have a specified end", function()
  340. insert([[
  341. {{{
  342. }}}
  343. {{{
  344. }}}
  345. ]])
  346. command('setlocal foldmethod=marker')
  347. command('3,5d')
  348. command('%foldclose')
  349. eq(2, fn.foldclosedend(1))
  350. end)
  351. it('splits folds according to >N and <N with foldexpr', function()
  352. n.source([[
  353. function TestFoldExpr(lnum)
  354. let thisline = getline(a:lnum)
  355. if thisline == 'a'
  356. return 1
  357. elseif thisline == 'b'
  358. return 0
  359. elseif thisline == 'c'
  360. return '<1'
  361. elseif thisline == 'd'
  362. return '>1'
  363. endif
  364. return 0
  365. endfunction
  366. ]])
  367. t.write_file(
  368. tempfname,
  369. [[
  370. b
  371. b
  372. a
  373. a
  374. d
  375. a
  376. a
  377. c]]
  378. )
  379. insert([[
  380. a
  381. a
  382. a
  383. a
  384. a
  385. a
  386. ]])
  387. command('setlocal foldmethod=expr foldexpr=TestFoldExpr(v:lnum)')
  388. command('2')
  389. command('foldopen')
  390. command('read ' .. tempfname)
  391. command('%foldclose')
  392. eq(2, fn.foldclosedend(1))
  393. eq(0, fn.foldlevel(3))
  394. eq(0, fn.foldlevel(4))
  395. eq(6, fn.foldclosedend(5))
  396. eq(10, fn.foldclosedend(7))
  397. eq(14, fn.foldclosedend(11))
  398. end)
  399. it('fdm=expr works correctly with :move #18668', function()
  400. exec([[
  401. set foldmethod=expr foldexpr=indent(v:lnum)
  402. let content = ['', '', 'Line1', ' Line2', ' Line3',
  403. \ 'Line4', ' Line5', ' Line6',
  404. \ 'Line7', ' Line8', ' Line9']
  405. call append(0, content)
  406. normal! zM
  407. call cursor(4, 1)
  408. move 2
  409. move 1
  410. ]])
  411. expect([[
  412. Line2
  413. Line3
  414. Line1
  415. Line4
  416. Line5
  417. Line6
  418. Line7
  419. Line8
  420. Line9
  421. ]])
  422. eq(0, fn.foldlevel(1))
  423. eq(3, fn.foldclosedend(2))
  424. eq(0, fn.foldlevel(4))
  425. eq(0, fn.foldlevel(5))
  426. eq(0, fn.foldlevel(6))
  427. eq(8, fn.foldclosedend(7))
  428. eq(0, fn.foldlevel(9))
  429. eq(11, fn.foldclosedend(10))
  430. eq(0, fn.foldlevel(12))
  431. end)
  432. it('no folds remain if :delete makes buffer empty #19671', function()
  433. command('setlocal foldmethod=manual')
  434. fn.setline(1, { 'foo', 'bar', 'baz' })
  435. command('2,3fold')
  436. command('%delete')
  437. eq(0, fn.foldlevel(1))
  438. end)
  439. it('multibyte fold markers work #20438', function()
  440. command('setlocal foldmethod=marker foldmarker=«,» commentstring=/*%s*/')
  441. insert([[
  442. bbbbb
  443. bbbbb
  444. bbbbb]])
  445. feed('zfgg')
  446. expect([[
  447. bbbbb/*«*/
  448. bbbbb
  449. bbbbb/*»*/]])
  450. eq(1, fn.foldlevel(1))
  451. end)
  452. it('fdm=indent updates correctly with visual blockwise insertion #22898', function()
  453. insert([[
  454. a
  455. b
  456. ]])
  457. command('setlocal foldmethod=indent shiftwidth=2')
  458. feed('gg0<C-v>jI <Esc>') -- indent both lines using visual blockwise mode
  459. eq(1, fn.foldlevel(1))
  460. eq(1, fn.foldlevel(2))
  461. end)
  462. it("fdm=indent doesn't open folds when inserting lower foldlevel line", function()
  463. insert([[
  464. insert an unindented line under this line
  465. keep the lines under this line folded
  466. keep this line folded 1
  467. keep this line folded 2
  468. ]])
  469. command('set foldmethod=indent shiftwidth=2 noautoindent')
  470. eq(1, fn.foldlevel(1))
  471. eq(1, fn.foldlevel(2))
  472. eq(2, fn.foldlevel(3))
  473. eq(2, fn.foldlevel(4))
  474. feed('zo') -- open the outer fold
  475. neq(-1, fn.foldclosed(3)) -- make sure the inner fold is not open
  476. feed('gg0oa<Esc>') -- insert unindented line
  477. eq(1, fn.foldlevel(1)) --| insert an unindented line under this line
  478. eq(0, fn.foldlevel(2)) --|a
  479. eq(1, fn.foldlevel(3)) --| keep the lines under this line folded
  480. eq(2, fn.foldlevel(4)) --| keep this line folded 1
  481. eq(2, fn.foldlevel(5)) --| keep this line folded 2
  482. neq(-1, fn.foldclosed(4)) -- make sure the inner fold is still not open
  483. end)
  484. end)