aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/ex_cmds.c35
-rw-r--r--src/nvim/fold.c148
-rw-r--r--src/nvim/mark.c22
-rw-r--r--src/nvim/testdir/test_fold.vim141
-rw-r--r--test/functional/normal/fold_spec.lua186
5 files changed, 510 insertions, 22 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 1b83677807..c560ff9210 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -756,14 +756,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
linenr_T num_lines; // Num lines moved
linenr_T last_line; // Last line in file after adding new text
- // Moving lines seems to corrupt the folds, delete folding info now
- // and recreate it when finished. Don't do this for manual folding, it
- // would delete all folds.
- bool isFolded = hasAnyFolding(curwin) && !foldmethodIsManual(curwin);
- if (isFolded) {
- deleteFoldRecurse(&curwin->w_folds);
- }
-
if (dest >= line1 && dest < line2) {
EMSG(_("E134: Move lines into themselves"));
return FAIL;
@@ -801,21 +793,29 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
* their final destination at the new text position -- webb
*/
last_line = curbuf->b_ml.ml_line_count;
- mark_adjust(line1, line2, last_line - line2, 0L);
- changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines);
+ mark_adjust_nofold(line1, line2, last_line - line2, 0L);
if (dest >= line2) {
- mark_adjust(line2 + 1, dest, -num_lines, 0L);
+ mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L);
+ FOR_ALL_TAB_WINDOWS(tab, win) {
+ if (win->w_buffer == curbuf) {
+ foldMoveRange(&win->w_folds, line1, line2, dest);
+ }
+ }
curbuf->b_op_start.lnum = dest - num_lines + 1;
curbuf->b_op_end.lnum = dest;
} else {
- mark_adjust(dest + 1, line1 - 1, num_lines, 0L);
+ mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L);
+ FOR_ALL_TAB_WINDOWS(tab, win) {
+ if (win->w_buffer == curbuf) {
+ foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2);
+ }
+ }
curbuf->b_op_start.lnum = dest + 1;
curbuf->b_op_end.lnum = dest + num_lines;
}
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
- mark_adjust(last_line - num_lines + 1, last_line,
- -(last_line - dest - extra), 0L);
- changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra);
+ mark_adjust_nofold(last_line - num_lines + 1, last_line,
+ -(last_line - dest - extra), 0L);
/*
* Now we delete the original text -- webb
@@ -851,11 +851,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
changed_lines(dest + 1, 0, line1 + num_lines, 0L);
}
- // recreate folds
- if (isFolded) {
- foldUpdateAll(curwin);
- }
-
return OK;
}
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 7c0283971e..e2d1b017b6 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -2597,6 +2597,154 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot)
}
}
+// foldMoveRange() {{{2
+static void reverse_fold_order(garray_T *gap, size_t start, size_t end)
+{
+ for (; start < end; start++, end--) {
+ fold_T *left = (fold_T *)gap->ga_data + start;
+ fold_T *right = (fold_T *)gap->ga_data + end;
+ fold_T tmp = *left;
+ *left = *right;
+ *right = tmp;
+ }
+}
+
+// Move folds within the inclusive range "line1" to "line2" to after "dest"
+// require "line1" <= "line2" <= "dest"
+//
+// There are the following situations for the first fold at or below line1 - 1.
+// 1 2 3 4
+// 1 2 3 4
+// line1 2 3 4
+// 2 3 4 5 6 7
+// line2 3 4 5 6 7
+// 3 4 6 7 8 9
+// dest 4 7 8 9
+// 4 7 8 10
+// 4 7 8 10
+//
+// In the following descriptions, "moved" means moving in the buffer, *and* in
+// the fold array.
+// Meanwhile, "shifted" just means moving in the buffer.
+// 1. not changed
+// 2. truncated above line1
+// 3. length reduced by line2 - line1, folds starting between the end of 3 and
+// dest are truncated and shifted up
+// 4. internal folds moved (from [line1, line2] to dest)
+// 5. moved to dest.
+// 6. truncated below line2 and moved.
+// 7. length reduced by line2 - dest, folds starting between line2 and dest are
+// removed, top is moved down by move_len.
+// 8. truncated below dest and shifted up.
+// 9. shifted up
+// 10. not changed
+static void truncate_fold(fold_T *fp, linenr_T end)
+{
+ // I want to stop *at here*, foldRemove() stops *above* top
+ end += 1;
+ foldRemove(&fp->fd_nested, end - fp->fd_top, MAXLNUM);
+ fp->fd_len = end - fp->fd_top;
+}
+
+#define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1)
+#define valid_fold(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len))
+#define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data)))
+void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2,
+ const linenr_T dest)
+{
+ fold_T *fp;
+ const linenr_T range_len = line2 - line1 + 1;
+ const linenr_T move_len = dest - line2;
+ const bool at_start = foldFind(gap, line1 - 1, &fp);
+
+ if (at_start) {
+ if (fold_end(fp) > dest) {
+ // Case 4 -- don't have to change this fold, but have to move nested
+ // folds.
+ foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 -
+ fp->fd_top, dest - fp->fd_top);
+ return;
+ } else if (fold_end(fp) > line2) {
+ // Case 3 -- Remove nested folds between line1 and line2 & reduce the
+ // length of fold by "range_len".
+ // Folds after this one must be dealt with.
+ foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top,
+ line2 - fp->fd_top, MAXLNUM, -range_len);
+ fp->fd_len -= range_len;
+ } else {
+ // Case 2 -- truncate fold *above* line1.
+ // Folds after this one must be dealt with.
+ truncate_fold(fp, line1 - 1);
+ }
+ // Look at the next fold, and treat that one as if it were the first after
+ // "line1" (because now it is).
+ fp = fp + 1;
+ }
+
+ if (!valid_fold(fp, gap) || fp->fd_top > dest) {
+ // No folds after "line1" and before "dest"
+ // Case 10.
+ return;
+ } else if (fp->fd_top > line2) {
+ for (; valid_fold(fp, gap) && fold_end(fp) <= dest; fp++) {
+ // Case 9. (for all case 9's) -- shift up.
+ fp->fd_top -= range_len;
+ }
+ if (valid_fold(fp, gap) && fp->fd_top <= dest) {
+ // Case 8. -- ensure truncated at dest, shift up
+ truncate_fold(fp, dest);
+ fp->fd_top -= range_len;
+ }
+ return;
+ } else if (fold_end(fp) > dest) {
+ // Case 7 -- remove nested folds and shrink
+ foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top,
+ dest - fp->fd_top, MAXLNUM, -move_len);
+ fp->fd_len -= move_len;
+ fp->fd_top += move_len;
+ return;
+ }
+
+ // Case 5 or 6: changes rely on whether there are folds between the end of
+ // this fold and "dest".
+ size_t move_start = fold_index(fp, gap);
+ size_t move_end = 0, dest_index = 0;
+ for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++) {
+ if (fp->fd_top <= line2) {
+ // 5, or 6
+ if (fold_end(fp) > line2) {
+ // 6, truncate before moving
+ truncate_fold(fp, line2);
+ }
+ fp->fd_top += move_len;
+ continue;
+ }
+
+ // Record index of the first fold after the moved range.
+ if (move_end == 0) {
+ move_end = fold_index(fp, gap);
+ }
+
+ if (fold_end(fp) > dest) {
+ truncate_fold(fp, dest);
+ }
+
+ fp->fd_top -= range_len;
+ }
+ dest_index = fold_index(fp, gap);
+
+ // All folds are now correct, but they are not necessarily in the correct
+ // order.
+ // We have to swap folds in the range [move_end, dest_index) with those in
+ // the range [move_start, move_end).
+ reverse_fold_order(gap, move_start, dest_index - 1);
+ reverse_fold_order(gap, move_start, move_start + dest_index - move_end - 1);
+ reverse_fold_order(gap, move_start + dest_index - move_end, dest_index - 1);
+}
+#undef fold_end
+#undef valid_fold
+#undef fold_index
+
/* foldMerge() {{{2 */
/*
* Merge two adjacent folds (and the nested ones in them).
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 4e05845eb5..de2fdd7f13 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -887,6 +887,23 @@ void ex_changes(exarg_T *eap)
*/
void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
{
+ mark_adjust_internal(line1, line2, amount, amount_after, true);
+}
+
+// mark_adjust_nofold() does the same as mark_adjust() but without adjusting
+// folds in any way. Folds must be adjusted manually by the caller.
+// This is only useful when folds need to be moved in a way different to
+// calling foldMarkAdjust() with arguments line1, line2, amount, amount_after,
+// for an example of why this may be necessary, see do_move().
+void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount,
+ long amount_after)
+{
+ mark_adjust_internal(line1, line2, amount, amount_after, false);
+}
+
+static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount,
+ long amount_after, bool adjust_folds)
+{
int i;
int fnum = curbuf->b_fnum;
linenr_T *lp;
@@ -1011,8 +1028,9 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
}
}
- /* adjust folds */
- foldMarkAdjust(win, line1, line2, amount, amount_after);
+ if (adjust_folds) {
+ foldMarkAdjust(win, line1, line2, amount, amount_after);
+ }
}
}
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 7cb9faa75f..de07a05a2c 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -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
diff --git a/test/functional/normal/fold_spec.lua b/test/functional/normal/fold_spec.lua
index a2a2a35a8b..5584db20ba 100644
--- a/test/functional/normal/fold_spec.lua
+++ b/test/functional/normal/fold_spec.lua
@@ -5,9 +5,13 @@ local insert = helpers.insert
local feed = helpers.feed
local expect = helpers.expect
local execute = helpers.execute
+local funcs = helpers.funcs
+local foldlevel, foldclosedend = funcs.foldlevel, funcs.foldclosedend
+local eq = helpers.eq
describe('Folds', function()
clear()
+ before_each(function() execute('enew!') end)
it('manual folding adjusts with filter', function()
insert([[
1
@@ -44,4 +48,186 @@ describe('Folds', function()
8
9]])
end)
+ describe('adjusting folds after :move', function()
+ local function manually_fold_indent()
+ -- setting foldmethod twice is a trick to get vim to set the folds for me
+ execute('set foldmethod=indent', 'set foldmethod=manual')
+ -- Ensure that all folds will get closed (makes it easier to test the
+ -- length of folds).
+ execute('set foldminlines=0')
+ -- Start with all folds open (so :move ranges aren't affected by closed
+ -- folds).
+ execute('%foldopen!')
+ end
+
+ local function get_folds()
+ local rettab = {}
+ for i = 1, funcs.line('$') do
+ table.insert(rettab, foldlevel(i))
+ end
+ return rettab
+ end
+
+ local function test_move_indent(insert_string, move_command)
+ -- This test is easy because we just need to ensure that the resulting
+ -- fold is the same as calculated when creating folds from scratch.
+ insert(insert_string)
+ execute(move_command)
+ local after_move_folds = get_folds()
+ -- Doesn't change anything, but does call foldUpdateAll()
+ execute('set foldminlines=0')
+ eq(after_move_folds, get_folds())
+ -- Set up the buffer with insert_string for the manual fold testing.
+ execute('enew!')
+ insert(insert_string)
+ manually_fold_indent()
+ execute(move_command)
+ end
+
+ it('neither closes nor corrupts folds', function()
+ test_move_indent([[
+a
+ a
+ a
+ a
+ a
+ a
+a
+ a
+ a
+ a
+ a
+ a
+a
+ a
+ a
+ a
+ a
+ a]], '7,12m0')
+ expect([[
+a
+ a
+ a
+ a
+ a
+ a
+a
+ a
+ a
+ a
+ a
+ a
+a
+ a
+ a
+ a
+ a
+ a]])
+ -- lines are not closed, folds are correct
+ for i = 1,funcs.line('$') do
+ eq(-1, funcs.foldclosed(i))
+ if i == 1 or i == 7 or i == 13 then
+ eq(0, foldlevel(i))
+ elseif i == 4 then
+ eq(2, foldlevel(i))
+ else
+ eq(1, foldlevel(i))
+ end
+ end
+ -- folds are not corrupted
+ feed('zM')
+ eq(6, foldclosedend(2))
+ eq(12, foldclosedend(8))
+ eq(18, foldclosedend(14))
+ end)
+ it("doesn't split a fold when the move is within it", function()
+ test_move_indent([[
+a
+ a
+ a
+ a
+ a
+ a
+ a
+ a
+ a
+a]], '5m6')
+ eq({0, 1, 1, 2, 2, 2, 2, 1, 1, 0}, get_folds())
+ end)
+ it('truncates folds that end in the moved range', function()
+ test_move_indent([[
+a
+ a
+ a
+ a
+ a
+a
+a]], '4,5m6')
+ eq({0, 1, 2, 0, 0, 0, 0}, get_folds())
+ end)
+ it('moves folds that start between moved range and destination', function()
+ test_move_indent([[
+a
+ a
+ a
+ a
+ a
+a
+a
+ a
+ a
+ a
+a
+a
+ a]], '3,4m$')
+ eq({0, 1, 1, 0, 0, 1, 2, 1, 0, 0, 1, 0, 0}, get_folds())
+ end)
+ it('does not affect folds outside changed lines', function()
+ test_move_indent([[
+ a
+ a
+ a
+a
+a
+a
+ a
+ a
+ a]], '4m5')
+ eq({1, 1, 1, 0, 0, 0, 1, 1, 1}, get_folds())
+ end)
+ it('moves and truncates folds that start in moved range', function()
+ test_move_indent([[
+a
+ a
+ a
+ a
+ a
+a
+a
+a
+a
+a]], '1,3m7')
+ eq({0, 0, 0, 0, 0, 1, 2, 0, 0, 0}, get_folds())
+ end)
+ it('breaks a fold when moving text into it', function()
+ test_move_indent([[
+a
+ a
+ a
+ a
+ a
+a
+a]], '$m4')
+ eq({0, 1, 2, 2, 0, 0, 0}, get_folds())
+ end)
+ it('adjusts correctly when moving a range backwards', function()
+ test_move_indent([[
+a
+ a
+ a
+ a
+a]], '2,3m0')
+ eq({1, 2, 0, 0, 0}, get_folds())
+ end)
+ end)
end)