patch 9.2.1006: fold functions do not accept window id
Commit:
https://github.com/vim/vim/commit/aabf101621dfd6683af44f4db14aa740775281d9
Author: Mao-Yining <
mao.y...@outlook.com>
Date: Tue Aug 25 19:59:18 2026 +0000
patch 9.2.1006: fold functions do not accept window id
Problem: fold functions do not accept window id
Solution: Add support for optional winid argument
(Mao-Yining).
closes: #21133
Signed-off-by: Mao-Yining <
mao.y...@outlook.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 6627469cd..b13bf6b4f 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -213,11 +213,13 @@ floor({expr}) Float round {expr} down
fmod({expr1}, {expr2}) Float remainder of {expr1} / {expr2}
fnameescape({fname}) String escape special characters in {fname}
fnamemodify({fname}, {mods}) String modify file name
-foldclosed({lnum}) Number first line of fold at {lnum} if closed
-foldclosedend({lnum}) Number last line of fold at {lnum} if closed
-foldlevel({lnum}) Number fold level at {lnum}
+foldclosed({lnum} [, {winid}]) Number first line of fold at {lnum} if closed
+foldclosedend({lnum} [, {winid}]))
+ Number last line of fold at {lnum} if closed
+foldlevel({lnum} [, {winid}])) Number fold level at {lnum}
foldtext() String line displayed for closed fold
-foldtextresult({lnum}) String text for closed fold at {lnum}
+foldtextresult({lnum} [, {winid}]))
+ String text for closed fold at {lnum}
foreach({expr1}, {expr2}) List/Tuple/Dict/Blob/String
for each item in {expr1} call {expr2}
foreground() none bring the Vim window to the foreground
@@ -3554,33 +3556,43 @@ fnamemodify({fname}, {mods}) *fnamemodify()*
Return type: |String|
-foldclosed({lnum}) *foldclosed()*
+foldclosed({lnum} [, {winid}]) *foldclosed()*
The result is a Number. If the line {lnum} is in a closed
fold, the result is the number of the first line in that fold.
If the line {lnum} is not in a closed fold, -1 is returned.
{lnum} is used like with |getline()|. Thus "." is the current
line, "'m" mark m, etc.
+ With the optional {winid} argument the values are obtained for
+ that window instead of the current window. If {winid} is
+ invalid, return -2.
+
Can also be used as a |method|: >
GetLnum()->foldclosed()
+ GetLnum()->foldclosed(winid)
<
Return type: |Number|
-foldclosedend({lnum}) *foldclosedend()*
+foldclosedend({lnum} [, {winid}]) *foldclosedend()*
The result is a Number. If the line {lnum} is in a closed
fold, the result is the number of the last line in that fold.
If the line {lnum} is not in a closed fold, -1 is returned.
{lnum} is used like with |getline()|. Thus "." is the current
line, "'m" mark m, etc.
+ With the optional {winid} argument the values are obtained for
+ that window instead of the current window. If {winid} is
+ invalid, return -2.
+
Can also be used as a |method|: >
GetLnum()->foldclosedend()
+ GetLnum()->foldclosedend(winid)
<
Return type: |Number|
-foldlevel({lnum}) *foldlevel()*
+foldlevel({lnum} [, {winid}]) *foldlevel()*
The result is a Number, which is the foldlevel of line {lnum}
in the current buffer. For nested folds the deepest level is
returned. If there is no fold at line {lnum}, zero is
@@ -3592,8 +3604,13 @@ foldlevel({lnum}) *foldlevel()*
{lnum} is used like with |getline()|. Thus "." is the current
line, "'m" mark m, etc.
+ With the optional {winid} argument the values are obtained for
+ that window instead of the current window. If {winid} is
+ invalid, return -2.
+
Can also be used as a |method|: >
GetLnum()->foldlevel()
+ GetLnum()->foldlevel(winid)
<
Return type: |Number|
@@ -3619,7 +3636,7 @@ foldtext() *foldtext()*
{not available when compiled without the |+folding| feature}
-foldtextresult({lnum}) *foldtextresult()*
+foldtextresult({lnum} [, {winid}]) *foldtextresult()*
Returns the text that is displayed for the closed fold at line
{lnum}. Evaluates 'foldtext' in the appropriate context.
When there is no closed fold at {lnum} an empty string is
@@ -3629,9 +3646,13 @@ foldtextresult({lnum}) *foldtextresult()*
Useful when exporting folded text, e.g., to HTML.
{not available when compiled without the |+folding| feature}
+ With the optional {winid} argument the values are obtained for
+ that window instead of the current window. If {winid} is
+ invalid, an empty string is returned.
Can also be used as a |method|: >
GetLnum()->foldtextresult()
+ GetLnum()->foldtextresult(winid)
<
Return type: |String|
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 6ecfd04c0..bbc857393 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -52709,6 +52709,7 @@ Changed ~
expected, so a request from the server can be answered.
- |complete_info()| can distinguish between manual and automatic triggered
completions via the "auto" item.
+- fold functions take an optional {winid} parameter.
*added-9.3*
Added ~
diff --git a/src/evalfunc.c b/src/evalfunc.c
index c2c2a6ab3..9083813de 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -2250,15 +2250,15 @@ static const funcentry_T global_functions[] =
ret_string, f_fnameescape},
{"fnamemodify", 2, 2, FEARG_1, arg2_string,
ret_string, f_fnamemodify},
- {"foldclosed", 1, 1, FEARG_1, arg1_lnum,
+ {"foldclosed", 1, 2, FEARG_1, arg2_lnum_number,
ret_number, f_foldclosed},
- {"foldclosedend", 1, 1, FEARG_1, arg1_lnum,
+ {"foldclosedend", 1, 2, FEARG_1, arg2_lnum_number,
ret_number, f_foldclosedend},
- {"foldlevel", 1, 1, FEARG_1, arg1_lnum,
+ {"foldlevel", 1, 2, FEARG_1, arg2_lnum_number,
ret_number, f_foldlevel},
{"foldtext", 0, 0, 0, NULL,
ret_string, f_foldtext},
- {"foldtextresult", 1, 1, FEARG_1, arg1_lnum,
+ {"foldtextresult", 1, 2, FEARG_1, arg2_lnum_number,
ret_string, f_foldtextresult},
{"foreach", 2, 2, FEARG_1, arg2_foreach,
ret_first_arg, f_foreach},
diff --git a/src/fold.c b/src/fold.c
index 04698429d..24e3defa8 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -241,31 +241,6 @@ hasFoldingWin(
return TRUE;
}
-// foldLevel() {{{2
-# ifdef FEAT_EVAL
-/*
- * Return fold level at line number "lnum" in the current window.
- */
- static int
-foldLevel(linenr_T lnum)
-{
- // While updating the folds lines between invalid_top and invalid_bot have
- // an undefined fold level. Otherwise update the folds first.
- if (invalid_top == (linenr_T)0)
- checkupdate(curwin);
- else if (lnum == prev_lnum && prev_lnum_lvl >= 0)
- return prev_lnum_lvl;
- else if (lnum >= invalid_top && lnum <= invalid_bot)
- return -1;
-
- // Return quickly when there is no folding at all in this window.
- if (!hasAnyFolding(curwin))
- return 0;
-
- return foldLevelWin(curwin, lnum);
-}
-# endif
-
// lineFolded() {{{2
/*
* Low level function to check if a line is folded. Doesn't use any caching.
@@ -3677,16 +3652,39 @@ foldclosed_both(
int end UNUSED)
{
# ifdef FEAT_FOLDING
- linenr_T lnum;
+ linenr_T lnum = 0;
linenr_T first, last;
+ win_T *wp = curwin;
- if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
+ if (in_vim9script()
+ && (check_for_lnum_arg(argvars, 0) == FAIL
+ || check_for_opt_number_arg(argvars, 1) == FAIL))
return;
- lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
+ if (argvars[1].v_type != VAR_UNKNOWN)
{
- if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL))
+ tabpage_T *tp;
+ switchwin_T switchwin;
+ // use window specified in the second argument
+ wp = win_id2wp_tp(tv_get_number(&argvars[1]), &tp);
+ if (wp != NULL && tp != NULL)
+ {
+ if (switch_win(&switchwin, wp, tp, TRUE) == OK)
+ lnum = tv_get_lnum(argvars);
+ restore_win(&switchwin, TRUE);
+ }
+ else
+ {
+ rettv->vval.v_number = -2;
+ return;
+ }
+ }
+ else
+ lnum = tv_get_lnum(argvars);
+
+ if (lnum >= 1 && lnum <= wp->w_buffer->b_ml.ml_line_count)
+ {
+ if (hasFoldingWin(wp, lnum, &first, &last, FALSE, NULL))
{
if (end)
rettv->vval.v_number = (varnumber_T)last;
@@ -3717,6 +3715,28 @@ f_foldclosedend(typval_T *argvars, typval_T *rettv)
foldclosed_both(argvars, rettv, TRUE);
}
+/*
+ * Return fold level at line number "lnum" in the current window.
+ */
+ static int
+foldLevel(linenr_T lnum)
+{
+ // While updating the folds lines between invalid_top and invalid_bot have
+ // an undefined fold level. Otherwise update the folds first.
+ if (invalid_top == (linenr_T)0)
+ checkupdate(curwin);
+ else if (lnum == prev_lnum && prev_lnum_lvl >= 0)
+ return prev_lnum_lvl;
+ else if (lnum >= invalid_top && lnum <= invalid_bot)
+ return -1;
+
+ // Return quickly when there is no folding at all in this window.
+ if (!hasAnyFolding(curwin))
+ return 0;
+
+ return foldLevelWin(curwin, lnum);
+}
+
/*
* "foldlevel()" function
*/
@@ -3725,13 +3745,37 @@ f_foldlevel(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
# ifdef FEAT_FOLDING
linenr_T lnum;
+ switchwin_T switchwin;
- if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
+ if (in_vim9script()
+ && (check_for_lnum_arg(argvars, 0) == FAIL
+ || check_for_opt_number_arg(argvars, 1) == FAIL))
return;
- lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
- rettv->vval.v_number = foldLevel(lnum);
+ if (argvars[1].v_type != VAR_UNKNOWN)
+ {
+ tabpage_T *tp;
+ win_T *wp;
+ wp = win_id2wp_tp(tv_get_number(&argvars[1]), &tp);
+ if (wp != NULL && tp != NULL)
+ {
+ if (switch_win(&switchwin, wp, tp, TRUE) == OK)
+ {
+ lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
+ rettv->vval.v_number = foldLevel(lnum);
+ }
+ restore_win(&switchwin, TRUE);
+ }
+ else
+ rettv->vval.v_number = -2;
+ }
+ else
+ {
+ lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
+ rettv->vval.v_number = foldLevel(lnum);
+ }
# endif
}
@@ -3806,34 +3850,51 @@ f_foldtext(typval_T *argvars UNUSED, typval_T *rettv)
void
f_foldtextresult(typval_T *argvars UNUSED, typval_T *rettv)
{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
# ifdef FEAT_FOLDING
- linenr_T lnum;
+ linenr_T lnum = 0;
char_u *text;
char_u buf[FOLD_TEXT_LEN];
foldinfo_T foldinfo;
int fold_count;
+ win_T *wp = curwin;
static int entered = FALSE;
-# endif
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
-
- if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
+ if (in_vim9script()
+ && (check_for_lnum_arg(argvars, 0) == FAIL
+ || check_for_opt_number_arg(argvars, 1) == FAIL))
return;
-# ifdef FEAT_FOLDING
if (entered)
return; // reject recursive use
entered = TRUE;
- lnum = tv_get_lnum(argvars);
+ if (argvars[1].v_type != VAR_UNKNOWN)
+ {
+ tabpage_T *tp;
+ switchwin_T switchwin;
+ // use window specified in the second argument
+ wp = win_id2wp_tp(tv_get_number(&argvars[1]), &tp);
+ if (wp == NULL || tp == NULL) // wrong winid
+ {
+ entered = FALSE;
+ return;
+ }
+ if (switch_win(&switchwin, wp, tp, TRUE) == OK)
+ lnum = tv_get_lnum(argvars);
+ restore_win(&switchwin, TRUE);
+ }
+ else
+ lnum = tv_get_lnum(argvars);
+
// treat illegal types and illegal string values for {lnum} the same
if (lnum < 0)
lnum = 0;
- fold_count = foldedCount(curwin, lnum, &foldinfo);
+ fold_count = foldedCount(wp, lnum, &foldinfo);
if (fold_count > 0)
{
- text = get_foldtext(curwin, lnum, lnum + fold_count - 1,
+ text = get_foldtext(wp, lnum, lnum + fold_count - 1,
&foldinfo, buf);
if (text == buf)
text = vim_strsave(text);
diff --git a/src/testdir/test_fold.vim b/src/testdir/test_fold.vim
index 1c09719b1..c823c7c71 100644
--- a/src/testdir/test_fold.vim
+++ b/src/testdir/test_fold.vim
@@ -1462,10 +1462,20 @@ func Test_foldtextresult()
call assert_equal('+-- 2 lines: two', foldtextresult(2))
setlocal foldtext=
call assert_equal('+-- 2 lines folded ', foldtextresult(2))
+ setlocal foldtext&
+
+ let main_winid = win_getid()
+ tabnew
+ call assert_equal('', foldtextresult(1))
+ call assert_equal('', foldtextresult(2, 9999)) " wrong winid
+ call assert_equal('+-- 2 lines: two', foldtextresult(2, main_winid))
+ call win_execute(main_winid, 'setlocal foldtext=')
+ call assert_equal('+-- 2 lines folded ', foldtextresult(2, main_winid))
+ call win_execute(main_winid, 'setlocal foldtext&')
+ bw! " tabnew
" Fold text for a C comment fold
%d _
- setlocal foldtext&
call setline(1, ['', '/*', ' * Comment', ' */', ''])
2,4fold
call assert_equal('+-- 3 lines: Comment', foldtextresult(2))
@@ -2143,4 +2153,85 @@ func Test_foldminlines_per_window()
bwipe!
endfunc
+" Test for foldclosed(), foldclosedend() and foldlevel() with winid argument
+func Test_foldclosed_with_winid()
+ " Create a main window with folds
+ new
+ call setline(1, [
+ \ 'one',
+ \ ' two',
+ \ ' three',
+ \ ' four',
+ \ 'five',
+ \ ' six',
+ \ ' seven',
+ \ ' eight',
+ \ 'nine'
+ \ ])
+ setlocal foldmethod=indent shiftwidth=2 foldenable
+ let main_winid = win_getid()
+
+ call cursor(6, 0) " Test foldclosed('.')
+ " Split to create a second window showing the same buffer
+ vnew
+
+ call assert_equal(2, foldclosed(2, main_winid))
+ call assert_equal(6, foldclosed(6, main_winid))
+ call assert_equal(6, foldclosed('.', main_winid))
+ call assert_equal(-1, foldclosed(1, main_winid))
+ call assert_equal(-1, foldclosed(5, main_winid))
+ call assert_equal(0, foldlevel(1, main_winid))
+ call assert_equal(1, foldlevel(2, main_winid))
+ call assert_equal(1, foldlevel('.', main_winid))
+
+ call assert_equal(4, foldclosedend(2, main_winid))
+ call assert_equal(8, foldclosedend(6, main_winid))
+ call assert_equal(8, foldclosedend('.', main_winid))
+ call assert_equal(-1, foldclosedend(1, main_winid))
+ call assert_equal(-1, foldclosedend(5, main_winid))
+
+ " Test with an invalid window ID
+ call assert_equal(-2, foldclosed(2, 99999))
+ call assert_equal(-2, foldclosedend(2, 99999))
+ call assert_equal(-2, foldlevel(2, 99999))
+
+ " Test with invalid line number
+ call assert_equal(-1, foldclosed(100, main_winid))
+ call assert_equal(-1, foldclosedend(100, main_winid))
+ call assert_equal(-1, foldclosed(0, main_winid))
+ call assert_equal(-1, foldclosedend(0, main_winid))
+
+ bwipe! " vnew
+
+ tabnew
+
+ call assert_equal(2, foldclosed(2, main_winid))
+ call assert_equal(6, foldclosed(6, main_winid))
+ call assert_equal(-1, foldclosed(1, main_winid))
+ call assert_equal(-1, foldclosed(5, main_winid))
+ call assert_equal(0, foldlevel(1, main_winid))
+ call assert_equal(1, foldlevel(2, main_winid))
+ call assert_equal(1, foldlevel('.', main_winid))
+
+ call assert_equal(4, foldclosedend(2, main_winid))
+ call assert_equal(8, foldclosedend(6, main_winid))
+ call assert_equal(-1, foldclosedend(1, main_winid))
+ call assert_equal(-1, foldclosedend(5, main_winid))
+
+ " Test with an invalid window ID
+ call assert_equal(-2, foldclosed(2, 99999))
+ call assert_equal(-2, foldclosedend(2, 99999))
+ call assert_equal(-2, foldlevel(2, 99999))
+
+ " Test with invalid line number
+ call assert_equal(-1, foldclosed(100, main_winid))
+ call assert_equal(-1, foldclosedend(100, main_winid))
+ call assert_equal(-1, foldclosed(0, main_winid))
+ call assert_equal(-1, foldclosedend(0, main_winid))
+
+ bwipe! " tabnew
+
+ bwipe! " new
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index adbb03495..a2e414b0f 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -1651,6 +1651,7 @@ enddef
def Test_foldclosed()
v9.CheckSourceDefAndScriptFailure(['foldclosed(function("min"))'], ['E1013: Argument 1: type mismatch, expected string but got func(...): unknown', 'E1220: String or Number required for argument 1'])
+ v9.CheckSourceDefAndScriptFailure(['foldclosed(".", "a")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2'])
v9.CheckSourceDefExecAndScriptFailure(['foldclosed("")'], 'E1209: Invalid value for a line number')
assert_equal(-1, foldclosed(1))
assert_equal(-1, foldclosed('$'))
@@ -1658,6 +1659,7 @@ enddef
def Test_foldclosedend()
v9.CheckSourceDefAndScriptFailure(['foldclosedend(true)'], ['E1013: Argument 1: type mismatch, expected string but got bool', 'E1220: String or Number required for argument 1'])
+ v9.CheckSourceDefAndScriptFailure(['foldclosedend(".", "a")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2'])
v9.CheckSourceDefExecAndScriptFailure(['foldclosedend("")'], 'E1209: Invalid value for a line number')
assert_equal(-1, foldclosedend(1))
assert_equal(-1, foldclosedend('w0'))
@@ -1665,6 +1667,7 @@ enddef
def Test_foldlevel()
v9.CheckSourceDefAndScriptFailure(['foldlevel(0z10)'], ['E1013: Argument 1: type mismatch, expected string but got blob', 'E1220: String or Number required for argument 1'])
+ v9.CheckSourceDefAndScriptFailure(['foldlevel(".", "a")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2'])
v9.CheckSourceDefExecAndScriptFailure(['foldlevel("")'], 'E1209: Invalid value for a line number')
assert_equal(0, foldlevel(1))
assert_equal(0, foldlevel('.'))
@@ -1672,6 +1675,7 @@ enddef
def Test_foldtextresult()
v9.CheckSourceDefAndScriptFailure(['foldtextresult(1.1)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1220: String or Number required for argument 1'])
+ v9.CheckSourceDefAndScriptFailure(['foldtextresult(".", "a")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2'])
v9.CheckSourceDefExecAndScriptFailure(['foldtextresult("")'], 'E1209: Invalid value for a line number')
assert_equal('', foldtextresult(1))
assert_equal('', foldtextresult('.'))
diff --git a/src/version.c b/src/version.c
index e4c218c29..9104f3c39 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1006,
/**/
1005,
/**/