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
No known key found for this signature in database
GPG Key ID: 64915E6E9F735B15
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
modeline. Check the option for whether it is affected by
'modelineexpr'. Also see |modeline|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
*'modelines'* *'mls'*
'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,
evaluated and the result is used as the option value. Example: >
: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
current window and buffer, while %{} items are evaluated in the
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
completely from the statusline when none of the flags are set. >
:set statusline=...%(\ [%M%R%H]%)...
< *g:actual_curbuf*
Beware that an expression is evaluated each and every time the status
line is displayed. The current buffer and current window will be set
temporarily to that of the window (and buffer) whose statusline is
currently being drawn. The expression will evaluate in this context.
The variable "g:actual_curbuf" is set to the `bufnr()` number of the
real current buffer.
< Beware that an expression is evaluated each and every time the status
line is displayed.
*g:actual_curbuf* *g:actual_curwin*
The current buffer and current window will be set temporarily to that
of the window (and buffer) whose statusline is currently being drawn.
The expression will evaluate in this context. The variable
"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
a modeline, see |sandbox-option|.

View File

@ -3464,7 +3464,8 @@ int build_stl_str_hl(
} type;
} items[STL_MAX_ITEM];
#define TMPLEN 70
char_u tmp[TMPLEN];
char_u buf_tmp[TMPLEN];
char_u win_tmp[TMPLEN];
char_u *usefmt = fmt;
const int save_must_redraw = must_redraw;
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
// use the result as the actual format string.
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);
if (usefmt == NULL) {
usefmt = fmt;
}
do_unlet(S_LEN("g:statusline_winid"), true);
}
if (fillchar == 0) {
@ -3904,8 +3913,10 @@ int build_stl_str_hl(
// { Evaluate the expression
// Store the current buffer number as a string variable
vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum);
set_internal_string_var((char_u *)"g:actual_curbuf", tmp);
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum);
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;
win_T *const save_curwin = curwin;
@ -3926,6 +3937,7 @@ int build_stl_str_hl(
// Remove the variable we just stored
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.
// Note: We cannot store the value in `num` because
// `get_rel_pos` can return a named position. Ex: "Top"
get_rel_pos(wp, tmp, TMPLEN);
str = tmp;
get_rel_pos(wp, buf_tmp, TMPLEN);
str = buf_tmp;
break;
case STL_ARGLISTSTAT:
@ -3995,19 +4007,19 @@ int build_stl_str_hl(
// at the end of the null-terminated string.
// Setting the first byte to null means it will place the argument
// 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
// appended data to the `tmp` buffer.
if (append_arg_number(wp, tmp, (int)sizeof(tmp), false)) {
str = tmp;
// appended data to the `buf_tmp` buffer.
if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) {
str = buf_tmp;
}
break;
case STL_KEYMAP:
fillable = false;
if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN)) {
str = tmp;
if (get_keymap_str(wp, (char_u *)"<%s>", buf_tmp, TMPLEN)) {
str = buf_tmp;
}
break;
case STL_PAGENUM:
@ -4064,9 +4076,9 @@ int build_stl_str_hl(
// (including the brackets and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) {
vim_snprintf((char *)tmp, sizeof(tmp), "[%s]",
wp->w_buffer->b_p_ft);
str = tmp;
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "[%s]",
wp->w_buffer->b_p_ft);
str = buf_tmp;
}
break;
@ -4078,13 +4090,13 @@ int build_stl_str_hl(
// (including the comma and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
vim_snprintf((char *)tmp, sizeof(tmp), ",%s",
wp->w_buffer->b_p_ft);
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), ",%s",
wp->w_buffer->b_p_ft);
// 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);
}
str = tmp;
str = buf_tmp;
}
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.
///
/// @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
{
const char *varname;

View File

@ -7,6 +7,7 @@
" %X
source view_util.vim
source term_util.vim
func s:get_statusline()
return ScreenLines(&lines - 1, &columns)[0]
@ -29,7 +30,9 @@ endfunc
" Function used to display syntax group.
func SyntaxItem()
return synIDattr(synID(line("."),col("."),1),"name")
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")
endfunc
func Test_caught_error_in_statusline()
@ -218,6 +221,8 @@ func Test_statusline()
"%{: Evaluate expression between '%{' and '}' and substitute result.
syntax on
let s:expected_curbuf = string(bufnr(''))
let s:expected_curwin = string(win_getid())
set statusline=%{SyntaxItem()}
call assert_match('^vimNumber\s*$', s:get_statusline())
s/^/"/
@ -332,6 +337,23 @@ func Test_statusline()
set statusline=%!2*3+1
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
" with the 'fillchars' option.
set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:-