summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorBarrett Ruth <62671086+barrettruth@users.noreply.github.com>2026-04-23 18:37:59 -0400
committerGitHub <noreply@github.com>2026-04-23 18:37:59 -0400
commitc39be1713153ddb83843cc431bc56ef24558a3f9 (patch)
treec6d04759f9ea6e1f0574d1b566d1007d1e19835b
parent645a588aa60f4e816a704c97685e2036958af176 (diff)
fix(options): repair stale UI state after `:set all&` #39026
Problem: `set all&` resets option values directly and leaves UI-derived state stale for `guicursor`, `laststatus`, and `showtabline`. Solution: Repair some of the stale UI state in the bulk reset path by reparsing `guicursor`, refreshing statusline state, and recomputing tabline/window rows.
-rw-r--r--src/nvim/option.c21
-rw-r--r--test/functional/ui/cursor_spec.lua16
-rw-r--r--test/functional/ui/options_spec.lua27
3 files changed, 64 insertions, 0 deletions
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 0e33292f77..4644fba49a 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1579,6 +1579,7 @@ int do_set(char *arg, int opt_flags)
arg++;
// Only for :set command set global value of local options.
set_options_default(opt_flags);
+ didset_options_all();
didset_options();
didset_options2();
ui_refresh_options();
@@ -1827,6 +1828,26 @@ static void didset_options2(void)
tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array);
}
+/// Repair UI state after `:set all&`.
+///
+/// `set_options_default` resets option values via `set_option_default` and
+/// `set_option_direct` without invoking per-option `did_set` callbacks, so
+/// UI-derived state (cursor shape, statusline, tabline) can get out of sync.
+/// This function patches the known cases.
+///
+/// Note: We intentionally do not replay all `did_set` callbacks
+/// (`opt_did_set_cb`) because they have order-dependent side effects and
+/// old/new transition logic that does not hold when values are already reset.
+static void didset_options_all(void)
+{
+ const char *errmsg = parse_shape_opt(SHAPE_CURSOR);
+ assert(errmsg == NULL);
+ (void)errmsg;
+ last_status(false);
+ win_float_update_statusline();
+ win_new_screen_rows();
+}
+
/// Check for string options that are NULL (normally only termcap options).
void check_options(void)
{
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index 1a075534ed..fb636df3af 100644
--- a/test/functional/ui/cursor_spec.lua
+++ b/test/functional/ui/cursor_spec.lua
@@ -378,6 +378,22 @@ describe('ui/cursor', function()
end)
end)
+ it("'set all&' reapplies 'guicursor'", function()
+ command('set guicursor=n:ver25')
+ screen:expect(function()
+ eq('vertical', screen._mode_info[1].cursor_shape)
+ eq(25, screen._mode_info[1].cell_percentage)
+ end)
+
+ command('set all&')
+ screen:expect(function()
+ eq('block', screen._mode_info[1].cursor_shape)
+ eq(0, screen._mode_info[1].blinkon)
+ eq(0, screen._mode_info[1].blinkoff)
+ eq(true, screen._cursor_style_enabled)
+ end)
+ end)
+
it(':sleep does not hide cursor when sleeping', function()
n.feed(':sleep 300m | echo 42')
screen:expect([[
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
index 7dda24207f..0ce427fbb1 100644
--- a/test/functional/ui/options_spec.lua
+++ b/test/functional/ui/options_spec.lua
@@ -172,6 +172,33 @@ describe('UI receives option updates', function()
end)
end)
+ it("restores statusline and tabline after 'set all&'", function()
+ reset()
+ command('tabnew | tabnew')
+ command('set laststatus=0 showtabline=0')
+ screen:expect({
+ unchanged = true,
+ condition = function()
+ local function row_text(row)
+ local chunks = {}
+ for _, cell in ipairs(screen._grid.rows[row]) do
+ table.insert(chunks, cell.text)
+ end
+ return table.concat(chunks)
+ end
+
+ eq(nil, row_text(1):find('%[No Name%]'))
+ eq(nil, row_text(screen._grid.height - 1):find('All', 1, true))
+ end,
+ })
+
+ command('set all&')
+ screen:expect({
+ unchanged = true,
+ any = { '[No Name]', 'All' },
+ })
+ end)
+
it('with UI extensions', function()
local expected = reset({ ext_cmdline = true, ext_wildmenu = true })