Robustly handle folds during a :move command

In order to re-order marks according to the :move command, do_move()
uses mark_adjust() in a non-standard manner. The non-standard action is
that it moves some marks *past* other marks. This doesn't matter for
marks, but mark_adjust() calls foldMarkAdjust() which simply changes
fold starts and lengths and doesn't have enough information to know that
other folds have to be checked and reordered.

The array of folds for each window are assumed to be in order of
increasing line number, and if this gets broken some folds can get
"lost".

There has been a previous patch to avoid this problem by deleting and
recalculating all folds in the window, but this comes at the cost of
closing all folds when executing :move, and doesn't cover the case of
manual folds.
This patch adds a new function foldMoveRange() specifically for the
:move command that handles reordering folds as well as simply moving
them. Additionally, we allow calling mark_adjust_nofold() that does the
same as mark_adjust() but doesn't affect any fold array.

Calling mark_adjust_nofold() should be done in the same manner as
calling mark_adjust(), but according changes to the fold arrays must be
done seperately by the calling function.

vim-patch:8.0.0457
vim-patch:8.0.0459
vim-patch:8.0.0461
vim-patch:8.0.0465
This commit is contained in:
Matthew Malcomson
2017-02-27 09:46:50 +00:00
parent 06ed7a189b
commit b2b88423aa
5 changed files with 510 additions and 22 deletions

View File

@@ -1,5 +1,9 @@
" Test for folding
func! PrepIndent(arg)
return [a:arg] + repeat(["\t".a:arg], 5)
endfu
func! Test_address_fold()
new
call setline(1, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/',
@@ -128,3 +132,140 @@ func Test_manual_fold_with_filter()
call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$'))
bwipe!
endfunc
func! Test_move_folds_around_manual()
new
let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c")
call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c"))
let folds=[-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14]
" all folds closed
set foldenable foldlevel=0 fdm=indent
" needs a forced redraw
redraw!
set fdm=manual
call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)'))
call assert_equal(input, getline(1, '$'))
7,12m0
call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$'))
call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)'))
10,12m0
call assert_equal(PrepIndent("a")[1:] + PrepIndent("b") + ["a"] + PrepIndent("c"), getline(1, '$'))
call assert_equal([1, 1, 1, 1, 1, -1, 7, 7, 7, 7, 7, -1, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)'))
" moving should not close the folds
%d
call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c"))
set fdm=indent
redraw!
set fdm=manual
call cursor(2, 1)
%foldopen
7,12m0
let folds=repeat([-1], 18)
call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$'))
call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)'))
norm! zM
" folds are not corrupted and all have been closed
call assert_equal([-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)'))
%d
call setline(1, ["a", "\tb", "\tc", "\td", "\te"])
set fdm=indent
redraw!
set fdm=manual
%foldopen
3m4
%foldclose
call assert_equal(["a", "\tb", "\td", "\tc", "\te"], getline(1, '$'))
call assert_equal([-1, 5, 5, 5, 5], map(range(1, line('$')), 'foldclosedend(v:val)'))
%d
call setline(1, ["a", "\tb", "\tc", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"])
set fdm=indent foldlevel=0
set fdm=manual
%foldopen
3m1
%foldclose
call assert_equal(["a", "\tc", "\tb", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"], getline(1, '$'))
call assert_equal(0, foldlevel(2))
call assert_equal(5, foldclosedend(3))
call assert_equal([-1, -1, 3, 3, 3, -1, 7, 7, 7, 7], map(range(1, line('$')), 'foldclosed(v:val)'))
2,6m$
%foldclose
call assert_equal(5, foldclosedend(2))
call assert_equal(0, foldlevel(6))
call assert_equal(9, foldclosedend(7))
call assert_equal([-1, 2, 2, 2, 2, -1, 7, 7, 7, -1], map(range(1, line('$')), 'foldclosed(v:val)'))
%d
" Ensure moving around the edges still works.
call setline(1, PrepIndent("a") + repeat(["a"], 3) + ["\ta"])
set fdm=indent foldlevel=0
set fdm=manual
%foldopen
6m$
" The first fold has been truncated to the 5'th line.
" Second fold has been moved up because the moved line is now below it.
call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 0], map(range(1, line('$')), 'foldlevel(v:val)'))
bw!
endfunc
func! Test_move_folds_around_indent()
new
let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c")
call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c"))
let folds=[-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14]
" all folds closed
set fdm=indent
call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)'))
call assert_equal(input, getline(1, '$'))
7,12m0
call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$'))
call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)'))
10,12m0
call assert_equal(PrepIndent("a")[1:] + PrepIndent("b") + ["a"] + PrepIndent("c"), getline(1, '$'))
call assert_equal([1, 1, 1, 1, 1, -1, 7, 7, 7, 7, 7, -1, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)'))
" moving should not close the folds
%d
call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c"))
set fdm=indent
call cursor(2, 1)
%foldopen
7,12m0
let folds=repeat([-1], 18)
call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$'))
call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)'))
norm! zM
" folds are not corrupted and all have been closed
call assert_equal([-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)'))
%d
call setline(1, ["a", "\tb", "\tc", "\td", "\te"])
set fdm=indent
%foldopen
3m4
%foldclose
call assert_equal(["a", "\tb", "\td", "\tc", "\te"], getline(1, '$'))
call assert_equal([-1, 5, 5, 5, 5], map(range(1, line('$')), 'foldclosedend(v:val)'))
%d
call setline(1, ["a", "\tb", "\tc", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"])
set fdm=indent foldlevel=0
%foldopen
3m1
%foldclose
call assert_equal(["a", "\tc", "\tb", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"], getline(1, '$'))
call assert_equal(1, foldlevel(2))
call assert_equal(5, foldclosedend(3))
call assert_equal([-1, 2, 2, 2, 2, -1, 7, 7, 7, 7], map(range(1, line('$')), 'foldclosed(v:val)'))
2,6m$
%foldclose
call assert_equal(9, foldclosedend(2))
call assert_equal(1, foldlevel(6))
call assert_equal(9, foldclosedend(7))
call assert_equal([-1, 2, 2, 2, 2, 2, 2, 2, 2, -1], map(range(1, line('$')), 'foldclosed(v:val)'))
" Ensure moving around the edges still works.
%d
call setline(1, PrepIndent("a") + repeat(["a"], 3) + ["\ta"])
set fdm=indent foldlevel=0
%foldopen
6m$
" The first fold has been truncated to the 5'th line.
" Second fold has been moved up because the moved line is now below it.
call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 1], map(range(1, line('$')), 'foldlevel(v:val)'))
bw!
endfunc