fix(statusline): broken statusline on error #38000

Problem: after #33036, an error from evaluating 'statusline' clears it and
doesn't draw the statusline. (causing glitchy redraws)

Solution: use the default value instead. If 'stl' is somehow ever empty, still
call redraw_custom_statusline to at least draw an empty statusline.

Ideally our default 'stl' shouldn't itself error too! :-)
Also adjust some prior screen:expect()s to avoid immediate success warnings.
This commit is contained in:
Sean Dewar
2026-02-23 16:22:13 +00:00
committed by GitHub
parent 2478a7fbbd
commit 32d3dd0650
6 changed files with 62 additions and 21 deletions

View File

@@ -6286,10 +6286,10 @@ A jump table for the options with a short description can be found at |Q_op|.
current window and buffer, while %{} items are evaluated in the
context of the window that the statusline belongs to.
When there is error while evaluating the option then it will be made
empty to avoid further errors. Otherwise screen updating would loop.
When the result contains unprintable characters the result is
unpredictable.
When there is an error while evaluating the option it will be reset to
its default value to avoid further errors. Otherwise screen updating
would loop. When the result contains unprintable characters the
result is unpredictable.
Note that the only effect of 'ruler' when this option is set (and
'laststatus' is 2 or 3) is controlling the output of |CTRL-G|.

View File

@@ -6728,10 +6728,10 @@ vim.wo.stc = vim.wo.statuscolumn
--- current window and buffer, while %{} items are evaluated in the
--- context of the window that the statusline belongs to.
---
--- When there is error while evaluating the option then it will be made
--- empty to avoid further errors. Otherwise screen updating would loop.
--- When the result contains unprintable characters the result is
--- unpredictable.
--- When there is an error while evaluating the option it will be reset to
--- its default value to avoid further errors. Otherwise screen updating
--- would loop. When the result contains unprintable characters the
--- result is unpredictable.
---
--- Note that the only effect of 'ruler' when this option is set (and
--- 'laststatus' is 2 or 3) is controlling the output of `CTRL-G`.

View File

@@ -8794,10 +8794,10 @@ local options = {
current window and buffer, while %{} items are evaluated in the
context of the window that the statusline belongs to.
When there is error while evaluating the option then it will be made
empty to avoid further errors. Otherwise screen updating would loop.
When the result contains unprintable characters the result is
unpredictable.
When there is an error while evaluating the option it will be reset to
its default value to avoid further errors. Otherwise screen updating
would loop. When the result contains unprintable characters the
result is unpredictable.
Note that the only effect of 'ruler' when this option is set (and
'laststatus' is 2 or 3) is controlling the output of |CTRL-G|.

View File

@@ -85,8 +85,7 @@ void win_redr_status(win_T *wp)
// Don't redraw right now, do it later. Don't update status line when
// popup menu is visible and may be drawn over it
wp->w_redr_status = true;
} else if (*wp->w_p_stl != NUL
|| (*p_stl != NUL && (!wp->w_floating || (is_stl_global && wp == curwin)))) {
} else if (*wp->w_p_stl != NUL || !wp->w_floating || (is_stl_global && wp == curwin)) {
// redraw custom status line
redraw_custom_statusline(wp);
}
@@ -2136,13 +2135,12 @@ stcsign:
redraw_not_allowed = save_redraw_not_allowed;
// Check for an error. If there is one the display will be messed up and
// might loop redrawing. Avoid that by making the corresponding option
// empty.
// might loop redrawing. Avoid that by setting the option to its default.
// TODO(Bram): find out why using called_emsg_before makes tests fail, does it
// matter?
// if (called_emsg > called_emsg_before)
if (opt_idx != kOptInvalid && did_emsg > did_emsg_before) {
set_option_direct(opt_idx, STATIC_CSTR_AS_OPTVAL(""), opt_scope, SID_ERROR);
set_option_direct(opt_idx, get_option_default(opt_idx, opt_scope), opt_scope, SID_ERROR);
}
// A user function may reset KeyTyped, restore it.

View File

@@ -949,6 +949,12 @@ describe('default statusline', function()
it('setting statusline to empty string sets default statusline', function()
exec_lua("vim.o.statusline = 'asdf'")
eq('asdf', eval('&statusline'))
screen:expect([[
^ |
{1:~ }|*13
{3:asdf }|
|
]])
local default_statusline = table.concat({
'%<',
@@ -962,15 +968,37 @@ describe('default statusline', function()
})
exec_lua("vim.o.statusline = ''")
eq(default_statusline, eval('&statusline'))
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
|
]])
-- Reset to default (via the correct scope) if there's an error.
command('setglobal statusline=%{a%}')
eq(default_statusline, eval('&statusline'))
eq(default_statusline, eval('&g:statusline'))
eq('', eval('&l:statusline'))
command('redrawstatus') -- like Vim, statusline isn't immediately redrawn after an error
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
{9:E121: Undefined variable: a} |
]])
command('setlocal statusline=%{b%}')
eq(default_statusline, eval('&statusline'))
eq(default_statusline, eval('&g:statusline'))
eq('', eval('&l:statusline'))
command('redrawstatus') -- like Vim, statusline isn't immediately redrawn after an error
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
{9:E121: Undefined variable: b} |
]])
end)
it('shows busy status when buffer is set to be busy', function()
@@ -1064,7 +1092,7 @@ describe("'statusline' in floatwin", function()
-- no statusline is displayed because the statusline option was cleared
api.nvim_win_set_config(win, cfg)
screen:expect(without_stl)
screen:expect_unchanged()
-- displayed after the option is reset
command(set_stl)
@@ -1090,6 +1118,19 @@ describe("'statusline' in floatwin", function()
]])
command('tabfirst')
api.nvim_win_set_config(win2, { style = 'minimal' })
screen:expect([[
{5: }{101:2}{5:+ [No Name] }{24: }{102:2}{24:+ [No Name] }{2: }{24:X}|
┌──────────┐ |
{1:~}│{4:^1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}│{3:<Name] [+]}│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*10
{2:[No Name] }|
|
]])
command('tabnext')
screen:expect([[
{24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}|

View File

@@ -64,7 +64,9 @@ func Test_statusline_will_be_disabled_with_error()
catch
endtry
call assert_true(s:func_in_statusline_called)
call assert_equal('', &statusline)
" Nvim: resets to default value instead.
" call assert_equal('', &statusline)
call assert_equal(nvim_get_option_info2('statusline', {}).default, &statusline)
set statusline=
endfunc