vim-patch:8.1.1372: when evaluating 'statusline' the current window is unknown

Problem:    When evaluating 'statusline' the current window is unknown.
            (Daniel Hahler)
Solution:   Set "g:actual_curwin" for %{} items.  Set "g:statusline_winid"
            when evaluationg %!. (closes vim/vim#4406, closes vim/vim#3299)
1c6fd1e100
This commit is contained in:
Jan Edmund Lazo
2020-06-25 21:07:49 -04:00
parent 43a2e5fe4b
commit edaff441de
4 changed files with 68 additions and 28 deletions

View File

@@ -3947,6 +3947,8 @@ A jump table for the options with a short description can be found at |Q_op|.
When on allow some options that are an expression to be set in the When on allow some options that are an expression to be set in the
modeline. Check the option for whether it is affected by modeline. Check the option for whether it is affected by
'modelineexpr'. Also see |modeline|. 'modelineexpr'. Also see |modeline|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
*'modelines'* *'mls'* *'modelines'* *'mls'*
'modelines' 'mls' number (default 5) 'modelines' 'mls' number (default 5)
@@ -5807,7 +5809,9 @@ A jump table for the options with a short description can be found at |Q_op|.
When the option starts with "%!" then it is used as an expression, When the option starts with "%!" then it is used as an expression,
evaluated and the result is used as the option value. Example: > evaluated and the result is used as the option value. Example: >
:set statusline=%!MyStatusLine() :set statusline=%!MyStatusLine()
< The result can contain %{} items that will be evaluated too. < The *g:statusline_winid* variable will be set to the |window-ID| of the
window that the status line belongs to.
The result can contain %{} items that will be evaluated too.
Note that the "%!" expression is evaluated in the context of the Note that the "%!" expression is evaluated in the context of the
current window and buffer, while %{} items are evaluated in the current window and buffer, while %{} items are evaluated in the
context of the window that the statusline belongs to. context of the window that the statusline belongs to.
@@ -5936,13 +5940,15 @@ A jump table for the options with a short description can be found at |Q_op|.
become empty. This will make a group like the following disappear become empty. This will make a group like the following disappear
completely from the statusline when none of the flags are set. > completely from the statusline when none of the flags are set. >
:set statusline=...%(\ [%M%R%H]%)... :set statusline=...%(\ [%M%R%H]%)...
< *g:actual_curbuf* < Beware that an expression is evaluated each and every time the status
Beware that an expression is evaluated each and every time the status line is displayed.
line is displayed. The current buffer and current window will be set *g:actual_curbuf* *g:actual_curwin*
temporarily to that of the window (and buffer) whose statusline is The current buffer and current window will be set temporarily to that
currently being drawn. The expression will evaluate in this context. of the window (and buffer) whose statusline is currently being drawn.
The variable "g:actual_curbuf" is set to the `bufnr()` number of the The expression will evaluate in this context. The variable
real current buffer. "g:actual_curbuf" is set to the `bufnr()` number of the real current
buffer and "g:actual_curwin" to the |window-ID| of the real current
window. These values are strings.
The 'statusline' option will be evaluated in the |sandbox| if set from The 'statusline' option will be evaluated in the |sandbox| if set from
a modeline, see |sandbox-option|. a modeline, see |sandbox-option|.

View File

@@ -3464,7 +3464,8 @@ int build_stl_str_hl(
} type; } type;
} items[STL_MAX_ITEM]; } items[STL_MAX_ITEM];
#define TMPLEN 70 #define TMPLEN 70
char_u tmp[TMPLEN]; char_u buf_tmp[TMPLEN];
char_u win_tmp[TMPLEN];
char_u *usefmt = fmt; char_u *usefmt = fmt;
const int save_must_redraw = must_redraw; const int save_must_redraw = must_redraw;
const int save_redr_type = curwin->w_redr_type; const int save_redr_type = curwin->w_redr_type;
@@ -3472,10 +3473,18 @@ int build_stl_str_hl(
// When the format starts with "%!" then evaluate it as an expression and // When the format starts with "%!" then evaluate it as an expression and
// use the result as the actual format string. // use the result as the actual format string.
if (fmt[0] == '%' && fmt[1] == '!') { if (fmt[0] == '%' && fmt[1] == '!') {
typval_T tv = {
.v_type = VAR_NUMBER,
.vval.v_number = wp->handle,
};
set_var(S_LEN("g:statusline_winid"), &tv, false);
usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox); usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
if (usefmt == NULL) { if (usefmt == NULL) {
usefmt = fmt; usefmt = fmt;
} }
do_unlet(S_LEN("g:statusline_winid"), true);
} }
if (fillchar == 0) { if (fillchar == 0) {
@@ -3904,8 +3913,10 @@ int build_stl_str_hl(
// { Evaluate the expression // { Evaluate the expression
// Store the current buffer number as a string variable // Store the current buffer number as a string variable
vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum); vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum);
set_internal_string_var((char_u *)"g:actual_curbuf", tmp); set_internal_string_var((char_u *)"g:actual_curbuf", buf_tmp);
vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle);
set_internal_string_var((char_u *)"g:actual_curwin", win_tmp);
buf_T *const save_curbuf = curbuf; buf_T *const save_curbuf = curbuf;
win_T *const save_curwin = curwin; win_T *const save_curwin = curwin;
@@ -3926,6 +3937,7 @@ int build_stl_str_hl(
// Remove the variable we just stored // Remove the variable we just stored
do_unlet(S_LEN("g:actual_curbuf"), true); do_unlet(S_LEN("g:actual_curbuf"), true);
do_unlet(S_LEN("g:actual_curwin"), true);
// } // }
@@ -3984,8 +3996,8 @@ int build_stl_str_hl(
// Store the position percentage in our temporary buffer. // Store the position percentage in our temporary buffer.
// Note: We cannot store the value in `num` because // Note: We cannot store the value in `num` because
// `get_rel_pos` can return a named position. Ex: "Top" // `get_rel_pos` can return a named position. Ex: "Top"
get_rel_pos(wp, tmp, TMPLEN); get_rel_pos(wp, buf_tmp, TMPLEN);
str = tmp; str = buf_tmp;
break; break;
case STL_ARGLISTSTAT: case STL_ARGLISTSTAT:
@@ -3995,19 +4007,19 @@ int build_stl_str_hl(
// at the end of the null-terminated string. // at the end of the null-terminated string.
// Setting the first byte to null means it will place the argument // Setting the first byte to null means it will place the argument
// number string at the beginning of the buffer. // number string at the beginning of the buffer.
tmp[0] = 0; buf_tmp[0] = 0;
// Note: The call will only return true if it actually // Note: The call will only return true if it actually
// appended data to the `tmp` buffer. // appended data to the `buf_tmp` buffer.
if (append_arg_number(wp, tmp, (int)sizeof(tmp), false)) { if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) {
str = tmp; str = buf_tmp;
} }
break; break;
case STL_KEYMAP: case STL_KEYMAP:
fillable = false; fillable = false;
if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN)) { if (get_keymap_str(wp, (char_u *)"<%s>", buf_tmp, TMPLEN)) {
str = tmp; str = buf_tmp;
} }
break; break;
case STL_PAGENUM: case STL_PAGENUM:
@@ -4064,9 +4076,9 @@ int build_stl_str_hl(
// (including the brackets and null terminating character) // (including the brackets and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL if (*wp->w_buffer->b_p_ft != NUL
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) { && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) {
vim_snprintf((char *)tmp, sizeof(tmp), "[%s]", vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "[%s]",
wp->w_buffer->b_p_ft); wp->w_buffer->b_p_ft);
str = tmp; str = buf_tmp;
} }
break; break;
@@ -4078,13 +4090,13 @@ int build_stl_str_hl(
// (including the comma and null terminating character) // (including the comma and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL if (*wp->w_buffer->b_p_ft != NUL
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) { && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
vim_snprintf((char *)tmp, sizeof(tmp), ",%s", vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), ",%s",
wp->w_buffer->b_p_ft); wp->w_buffer->b_p_ft);
// Uppercase the file extension // Uppercase the file extension
for (char_u *t = tmp; *t != 0; t++) { for (char_u *t = buf_tmp; *t != 0; t++) {
*t = (char_u)TOUPPER_LOC(*t); *t = (char_u)TOUPPER_LOC(*t);
} }
str = tmp; str = buf_tmp;
} }
break; break;
} }

View File

@@ -2818,7 +2818,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)
/// @param[in] fonceit If true, do not complain if variable doesnt exist. /// @param[in] fonceit If true, do not complain if variable doesnt exist.
/// ///
/// @return OK if it existed, FAIL otherwise. /// @return OK if it existed, FAIL otherwise.
int do_unlet(const char *const name, const size_t name_len, const int forceit) int do_unlet(const char *const name, const size_t name_len, const bool forceit)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
const char *varname; const char *varname;

View File

@@ -7,6 +7,7 @@
" %X " %X
source view_util.vim source view_util.vim
source term_util.vim
func s:get_statusline() func s:get_statusline()
return ScreenLines(&lines - 1, &columns)[0] return ScreenLines(&lines - 1, &columns)[0]
@@ -29,6 +30,8 @@ endfunc
" Function used to display syntax group. " Function used to display syntax group.
func SyntaxItem() func SyntaxItem()
call assert_equal(s:expected_curbuf, g:actual_curbuf)
call assert_equal(s:expected_curwin, g:actual_curwin)
return synIDattr(synID(line("."), col("."),1), "name") return synIDattr(synID(line("."), col("."),1), "name")
endfunc endfunc
@@ -218,6 +221,8 @@ func Test_statusline()
"%{: Evaluate expression between '%{' and '}' and substitute result. "%{: Evaluate expression between '%{' and '}' and substitute result.
syntax on syntax on
let s:expected_curbuf = string(bufnr(''))
let s:expected_curwin = string(win_getid())
set statusline=%{SyntaxItem()} set statusline=%{SyntaxItem()}
call assert_match('^vimNumber\s*$', s:get_statusline()) call assert_match('^vimNumber\s*$', s:get_statusline())
s/^/"/ s/^/"/
@@ -332,6 +337,23 @@ func Test_statusline()
set statusline=%!2*3+1 set statusline=%!2*3+1
call assert_match('7\s*$', s:get_statusline()) call assert_match('7\s*$', s:get_statusline())
func GetNested()
call assert_equal(string(win_getid()), g:actual_curwin)
call assert_equal(string(bufnr('')), g:actual_curbuf)
return 'nested'
endfunc
func GetStatusLine()
call assert_equal(win_getid(), g:statusline_winid)
return 'the %{GetNested()} line'
endfunc
set statusline=%!GetStatusLine()
call assert_match('the nested line', s:get_statusline())
call assert_false(exists('g:actual_curwin'))
call assert_false(exists('g:actual_curbuf'))
call assert_false(exists('g:statusline_winid'))
delfunc GetNested
delfunc GetStatusLine
" Check statusline in current and non-current window " Check statusline in current and non-current window
" with the 'fillchars' option. " with the 'fillchars' option.
set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:- set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:-