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
|
* before where we started looking, extend it. If it
|
||||||
* starts at another line, update nested folds to keep
|
* starts at another line, update nested folds to keep
|
||||||
* their position, compensating for the new fd_top. */
|
* their position, compensating for the new fd_top. */
|
||||||
if (fp->fd_top >= startlnum && fp->fd_top != firstlnum) {
|
if (fp->fd_top == firstlnum) {
|
||||||
if (fp->fd_top > firstlnum)
|
// We have found a fold beginning exactly where we want one.
|
||||||
/* like lines are inserted */
|
} 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,
|
foldMarkAdjustRecurse(&fp->fd_nested,
|
||||||
(linenr_T)0, (linenr_T)MAXLNUM,
|
(linenr_T)0, (linenr_T)MAXLNUM,
|
||||||
(long)(fp->fd_top - firstlnum), 0L);
|
(long)(fp->fd_top - firstlnum), 0L);
|
||||||
else
|
} else {
|
||||||
/* like lines are deleted */
|
// Will move fold down, move nested folds relatively up.
|
||||||
foldMarkAdjustRecurse(&fp->fd_nested,
|
foldMarkAdjustRecurse(&fp->fd_nested,
|
||||||
(linenr_T)0,
|
(linenr_T)0,
|
||||||
(long)(firstlnum - fp->fd_top - 1),
|
(long)(firstlnum - fp->fd_top - 1),
|
||||||
(linenr_T)MAXLNUM,
|
(linenr_T)MAXLNUM,
|
||||||
(long)(fp->fd_top - firstlnum));
|
(long)(fp->fd_top - firstlnum));
|
||||||
|
}
|
||||||
fp->fd_len += fp->fd_top - firstlnum;
|
fp->fd_len += fp->fd_top - firstlnum;
|
||||||
fp->fd_top = firstlnum;
|
fp->fd_top = firstlnum;
|
||||||
fold_changed = TRUE;
|
fold_changed = true;
|
||||||
} else if (flp->start != 0 && lvl == level
|
} else if ((flp->start != 0 && lvl == level)
|
||||||
&& fp->fd_top != firstlnum) {
|
|| (firstlnum != startlnum)) {
|
||||||
/* Existing fold that includes startlnum must stop
|
// Before there was a fold spanning from above startlnum to below
|
||||||
* if we find the start of a new fold at the same
|
// firstlnum. This fold is valid above startlnum (because we are
|
||||||
* level. Split it. Delete contained folds at
|
// not updating that range), but there is now a break in it.
|
||||||
* this point to split them too. */
|
// If the break is because we are now forced to start a new fold
|
||||||
foldRemove(&fp->fd_nested, flp->lnum - fp->fd_top,
|
// at the level "level" at line fline->lnum, then we need to
|
||||||
flp->lnum - fp->fd_top);
|
// 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);
|
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;
|
fp = (fold_T *)gap->ga_data + i + 1;
|
||||||
/* If using the "marker" or "syntax" method, we
|
/* If using the "marker" or "syntax" method, we
|
||||||
* need to continue until the end of the fold is
|
* 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)
|
|| getlevel == foldlevelSyntax)
|
||||||
finish = TRUE;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if (fp->fd_top >= startlnum) {
|
if (fp->fd_top >= startlnum) {
|
||||||
|
@ -100,22 +100,6 @@ func! Test_indent_fold2()
|
|||||||
bw!
|
bw!
|
||||||
endfunc
|
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()
|
func Test_manual_fold_with_filter()
|
||||||
if !executable('cat')
|
if !executable('cat')
|
||||||
return
|
return
|
||||||
@ -138,6 +122,108 @@ func Test_manual_fold_with_filter()
|
|||||||
endfor
|
endfor
|
||||||
endfunc
|
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()
|
func! Test_move_folds_around_manual()
|
||||||
new
|
new
|
||||||
let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c")
|
let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c")
|
||||||
|
@ -6,12 +6,15 @@ local feed = helpers.feed
|
|||||||
local expect = helpers.expect
|
local expect = helpers.expect
|
||||||
local execute = helpers.execute
|
local execute = helpers.execute
|
||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local foldlevel, foldclosedend = funcs.foldlevel, funcs.foldclosedend
|
local foldlevel = funcs.foldlevel
|
||||||
|
local foldclosedend = funcs.foldclosedend
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
|
|
||||||
describe('Folds', function()
|
describe('Folds', function()
|
||||||
|
local tempfname = 'Xtest-fold.txt'
|
||||||
clear()
|
clear()
|
||||||
before_each(function() execute('enew!') end)
|
before_each(function() execute('enew!') end)
|
||||||
|
after_each(function() os.remove(tempfname) end)
|
||||||
it('manual folding adjusts with filter', function()
|
it('manual folding adjusts with filter', function()
|
||||||
insert([[
|
insert([[
|
||||||
1
|
1
|
||||||
@ -230,4 +233,111 @@ a]], '2,3m0')
|
|||||||
eq({1, 2, 0, 0, 0}, get_folds())
|
eq({1, 2, 0, 0, 0}, get_folds())
|
||||||
end)
|
end)
|
||||||
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)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user