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