mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
fold.c: more edge-cases when updating (#6207)
When foldUpdateIEMSRecurse() re-uses an existing fold, it misses the case where the existing fold spans from before startlnum to after firstlnum, the new fold does not span this range, and there is no "forced start" of a fold. We add a case for this in. Ensure that if there was no forced break in folds, we merge folds that now touch each other. Include testing for a tricky foldmethod=expr case that has never been a bug. This case works at the moment because of some effects that are not obvious when reading the code. A test for this could be useful to ensure a regression doesn't happen. vim-patch:8.0.0408
This commit is contained in:
parent
831eb2a9bf
commit
3a9dd13f9e
@ -2232,32 +2232,51 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level,
|
||||
* before where we started looking, extend it. If it
|
||||
* starts at another line, update nested folds to keep
|
||||
* their position, compensating for the new fd_top. */
|
||||
if (fp->fd_top >= startlnum && fp->fd_top != firstlnum) {
|
||||
if (fp->fd_top > firstlnum)
|
||||
/* like lines are inserted */
|
||||
if (fp->fd_top == firstlnum) {
|
||||
// We have found a fold beginning exactly where we want one.
|
||||
} else if (fp->fd_top >= startlnum) {
|
||||
if (fp->fd_top > firstlnum) {
|
||||
// We will move the start of this fold up, hence we move all
|
||||
// nested folds (with relative line numbers) down.
|
||||
foldMarkAdjustRecurse(&fp->fd_nested,
|
||||
(linenr_T)0, (linenr_T)MAXLNUM,
|
||||
(long)(fp->fd_top - firstlnum), 0L);
|
||||
else
|
||||
/* like lines are deleted */
|
||||
} else {
|
||||
// Will move fold down, move nested folds relatively up.
|
||||
foldMarkAdjustRecurse(&fp->fd_nested,
|
||||
(linenr_T)0,
|
||||
(long)(firstlnum - fp->fd_top - 1),
|
||||
(linenr_T)MAXLNUM,
|
||||
(long)(fp->fd_top - firstlnum));
|
||||
}
|
||||
fp->fd_len += fp->fd_top - firstlnum;
|
||||
fp->fd_top = firstlnum;
|
||||
fold_changed = TRUE;
|
||||
} else if (flp->start != 0 && lvl == level
|
||||
&& fp->fd_top != firstlnum) {
|
||||
/* Existing fold that includes startlnum must stop
|
||||
* if we find the start of a new fold at the same
|
||||
* level. Split it. Delete contained folds at
|
||||
* this point to split them too. */
|
||||
foldRemove(&fp->fd_nested, flp->lnum - fp->fd_top,
|
||||
flp->lnum - fp->fd_top);
|
||||
fold_changed = true;
|
||||
} else if ((flp->start != 0 && lvl == level)
|
||||
|| (firstlnum != startlnum)) {
|
||||
// Before there was a fold spanning from above startlnum to below
|
||||
// firstlnum. This fold is valid above startlnum (because we are
|
||||
// not updating that range), but there is now a break in it.
|
||||
// If the break is because we are now forced to start a new fold
|
||||
// at the level "level" at line fline->lnum, then we need to
|
||||
// split the fold at fline->lnum.
|
||||
// If the break is because the range [startlnum, firstlnum) is
|
||||
// now at a lower indent than "level", we need to split the fold
|
||||
// in this range.
|
||||
// Any splits have to be done recursively.
|
||||
linenr_T breakstart;
|
||||
linenr_T breakend;
|
||||
if (firstlnum != startlnum) {
|
||||
breakstart = startlnum;
|
||||
breakend = firstlnum;
|
||||
} else {
|
||||
breakstart = flp->lnum;
|
||||
breakend = flp->lnum;
|
||||
}
|
||||
foldRemove(&fp->fd_nested, breakstart - fp->fd_top,
|
||||
breakend - fp->fd_top);
|
||||
i = (int)(fp - (fold_T *)gap->ga_data);
|
||||
foldSplit(gap, i, flp->lnum, flp->lnum - 1);
|
||||
foldSplit(gap, i, breakstart, breakend - 1);
|
||||
fp = (fold_T *)gap->ga_data + i + 1;
|
||||
/* If using the "marker" or "syntax" method, we
|
||||
* need to continue until the end of the fold is
|
||||
@ -2267,6 +2286,16 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level,
|
||||
|| getlevel == foldlevelSyntax)
|
||||
finish = TRUE;
|
||||
}
|
||||
if (fp->fd_top == startlnum && concat) {
|
||||
i = (int)(fp - (fold_T *)gap->ga_data);
|
||||
if (i != 0) {
|
||||
fp2 = fp - 1;
|
||||
if (fp2->fd_top + fp2->fd_len == fp->fd_top) {
|
||||
foldMerge(fp2, gap, fp);
|
||||
fp = fp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (fp->fd_top >= startlnum) {
|
||||
|
@ -100,22 +100,6 @@ func! Test_indent_fold2()
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
func Test_folds_marker_in_comment()
|
||||
new
|
||||
call setline(1, ['" foo', 'bar', 'baz'])
|
||||
setl fen fdm=marker
|
||||
setl com=sO:\"\ -,mO:\"\ \ ,eO:\"\",:\" cms=\"%s
|
||||
norm! zf2j
|
||||
setl nofen
|
||||
:1y
|
||||
call assert_equal(['" foo{{{'], getreg(0,1,1))
|
||||
:+2y
|
||||
call assert_equal(['baz"}}}'], getreg(0,1,1))
|
||||
|
||||
set foldmethod&
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_manual_fold_with_filter()
|
||||
if !executable('cat')
|
||||
return
|
||||
@ -138,6 +122,108 @@ func Test_manual_fold_with_filter()
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
func! Test_indent_fold_with_read()
|
||||
new
|
||||
set foldmethod=indent
|
||||
call setline(1, repeat(["\<Tab>a"], 4))
|
||||
for n in range(1, 4)
|
||||
call assert_equal(1, foldlevel(n))
|
||||
endfor
|
||||
|
||||
call writefile(["a", "", "\<Tab>a"], 'Xfile')
|
||||
foldopen
|
||||
2read Xfile
|
||||
%foldclose
|
||||
call assert_equal(1, foldlevel(1))
|
||||
call assert_equal(2, foldclosedend(1))
|
||||
call assert_equal(0, foldlevel(3))
|
||||
call assert_equal(0, foldlevel(4))
|
||||
call assert_equal(1, foldlevel(5))
|
||||
call assert_equal(7, foldclosedend(5))
|
||||
|
||||
bwipe!
|
||||
set foldmethod&
|
||||
call delete('Xfile')
|
||||
endfunc
|
||||
|
||||
func Test_combining_folds_indent()
|
||||
new
|
||||
let one = "\<Tab>a"
|
||||
let zero = 'a'
|
||||
call setline(1, [one, one, zero, zero, zero, one, one, one])
|
||||
set foldmethod=indent
|
||||
3,5d
|
||||
%foldclose
|
||||
call assert_equal(5, foldclosedend(1))
|
||||
|
||||
set foldmethod&
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_combining_folds_marker()
|
||||
new
|
||||
call setline(1, ['{{{', '}}}', '', '', '', '{{{', '', '}}}'])
|
||||
set foldmethod=marker
|
||||
3,5d
|
||||
%foldclose
|
||||
call assert_equal(2, foldclosedend(1))
|
||||
|
||||
set foldmethod&
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_folds_marker_in_comment()
|
||||
new
|
||||
call setline(1, ['" foo', 'bar', 'baz'])
|
||||
setl fen fdm=marker
|
||||
setl com=sO:\"\ -,mO:\"\ \ ,eO:\"\",:\" cms=\"%s
|
||||
norm! zf2j
|
||||
setl nofen
|
||||
:1y
|
||||
call assert_equal(['" foo{{{'], getreg(0,1,1))
|
||||
:+2y
|
||||
call assert_equal(['baz"}}}'], getreg(0,1,1))
|
||||
|
||||
set foldmethod&
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func s:TestFoldExpr(lnum)
|
||||
let thisline = getline(a:lnum)
|
||||
if thisline == 'a'
|
||||
return 1
|
||||
elseif thisline == 'b'
|
||||
return 0
|
||||
elseif thisline == 'c'
|
||||
return '<1'
|
||||
elseif thisline == 'd'
|
||||
return '>1'
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
func Test_update_folds_expr_read()
|
||||
new
|
||||
call setline(1, ['a', 'a', 'a', 'a', 'a', 'a'])
|
||||
set foldmethod=expr
|
||||
set foldexpr=s:TestFoldExpr(v:lnum)
|
||||
2
|
||||
foldopen
|
||||
call writefile(['b', 'b', 'a', 'a', 'd', 'a', 'a', 'c'], 'Xfile')
|
||||
read Xfile
|
||||
%foldclose
|
||||
call assert_equal(2, foldclosedend(1))
|
||||
call assert_equal(0, foldlevel(3))
|
||||
call assert_equal(0, foldlevel(4))
|
||||
call assert_equal(6, foldclosedend(5))
|
||||
call assert_equal(10, foldclosedend(7))
|
||||
call assert_equal(14, foldclosedend(11))
|
||||
|
||||
call delete('Xfile')
|
||||
bwipe!
|
||||
set foldmethod& foldexpr&
|
||||
endfunc
|
||||
|
||||
func! Test_move_folds_around_manual()
|
||||
new
|
||||
let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c")
|
||||
|
@ -6,12 +6,15 @@ local feed = helpers.feed
|
||||
local expect = helpers.expect
|
||||
local execute = helpers.execute
|
||||
local funcs = helpers.funcs
|
||||
local foldlevel, foldclosedend = funcs.foldlevel, funcs.foldclosedend
|
||||
local foldlevel = funcs.foldlevel
|
||||
local foldclosedend = funcs.foldclosedend
|
||||
local eq = helpers.eq
|
||||
|
||||
describe('Folds', function()
|
||||
local tempfname = 'Xtest-fold.txt'
|
||||
clear()
|
||||
before_each(function() execute('enew!') end)
|
||||
after_each(function() os.remove(tempfname) end)
|
||||
it('manual folding adjusts with filter', function()
|
||||
insert([[
|
||||
1
|
||||
@ -230,4 +233,111 @@ a]], '2,3m0')
|
||||
eq({1, 2, 0, 0, 0}, get_folds())
|
||||
end)
|
||||
end)
|
||||
it('updates correctly on :read', function()
|
||||
-- luacheck: ignore 621
|
||||
helpers.write_file(tempfname, [[
|
||||
a
|
||||
|
||||
|
||||
a]])
|
||||
insert([[
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
]])
|
||||
execute('set foldmethod=indent', '2', '%foldopen')
|
||||
execute('read ' .. tempfname)
|
||||
-- Just to check we have the correct file text.
|
||||
expect([[
|
||||
a
|
||||
a
|
||||
a
|
||||
|
||||
|
||||
a
|
||||
a
|
||||
a
|
||||
]])
|
||||
for i = 1,2 do
|
||||
eq(1, funcs.foldlevel(i))
|
||||
end
|
||||
for i = 3,5 do
|
||||
eq(0, funcs.foldlevel(i))
|
||||
end
|
||||
for i = 6,8 do
|
||||
eq(1, funcs.foldlevel(i))
|
||||
end
|
||||
end)
|
||||
it('combines folds when removing separating space', function()
|
||||
-- luacheck: ignore 621
|
||||
insert([[
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
]])
|
||||
execute('set foldmethod=indent', '3,5d')
|
||||
eq(5, funcs.foldclosedend(1))
|
||||
end)
|
||||
it("doesn't combine folds that have a specified end", function()
|
||||
insert([[
|
||||
{{{
|
||||
}}}
|
||||
|
||||
|
||||
|
||||
{{{
|
||||
|
||||
}}}
|
||||
]])
|
||||
execute('set foldmethod=marker', '3,5d', '%foldclose')
|
||||
eq(2, funcs.foldclosedend(1))
|
||||
end)
|
||||
it('splits folds according to >N and <N with foldexpr', function()
|
||||
helpers.source([[
|
||||
function TestFoldExpr(lnum)
|
||||
let thisline = getline(a:lnum)
|
||||
if thisline == 'a'
|
||||
return 1
|
||||
elseif thisline == 'b'
|
||||
return 0
|
||||
elseif thisline == 'c'
|
||||
return '<1'
|
||||
elseif thisline == 'd'
|
||||
return '>1'
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
]])
|
||||
helpers.write_file(tempfname, [[
|
||||
b
|
||||
b
|
||||
a
|
||||
a
|
||||
d
|
||||
a
|
||||
a
|
||||
c]])
|
||||
insert([[
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
]])
|
||||
execute('set foldmethod=expr', 'set foldexpr=TestFoldExpr(v:lnum)', '2', 'foldopen')
|
||||
execute('read ' .. tempfname, '%foldclose')
|
||||
eq(2, funcs.foldclosedend(1))
|
||||
eq(0, funcs.foldlevel(3))
|
||||
eq(0, funcs.foldlevel(4))
|
||||
eq(6, funcs.foldclosedend(5))
|
||||
eq(10, funcs.foldclosedend(7))
|
||||
eq(14, funcs.foldclosedend(11))
|
||||
end)
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user