From 8c0bf183745928f71fbd58f4b73afafbd6a045d4 Mon Sep 17 00:00:00 2001 From: not_compiled Date: Mon, 17 Aug 2026 08:46:11 +0530 Subject: [PATCH] fix(spell): avoid invalid window state after async spell select (#41346) Problem: When `z=` delegates to `vim.ui.select()`, the picker may change the current window before returning. `spell_suggest()` then continues to the cursor restoration branch with the new window and assigns `prev_cursor`, which belongs to the original window. This can leave Normal mode with an invalid cursor position and produce E315. Solution: Clean up the spell suggestion state and return immediately after handing control to `vim.ui.select()`. --- src/nvim/spellsuggest.c | 5 +++++ test/functional/lua/ui_select_spec.lua | 31 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index a87dc95592..955f558787 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -585,11 +585,16 @@ void spell_suggest(int count) } } else { // Hand off to (async) vim.ui.select(). + curwin->w_p_spell = wo_spell_save; select_spell_suggestion(&sug); lines_left = Rows; // avoid more prompt // don't delay for 'smd' in normal_cmd() msg_scroll = msg_scroll_save; + + spell_find_cleanup(&sug); + xfree(line); + return; } if (selected > 0 && selected <= sug.su_ga.ga_len && u_save_cursor() == OK) { diff --git a/test/functional/lua/ui_select_spec.lua b/test/functional/lua/ui_select_spec.lua index cac23aa1ed..d76f882619 100644 --- a/test/functional/lua/ui_select_spec.lua +++ b/test/functional/lua/ui_select_spec.lua @@ -229,6 +229,37 @@ describe('vim.ui.select()', function() eq(got.items[1].word, api.nvim_buf_get_lines(0, 0, -1, false)[1]) end) + it('does not produce E315 when picker changes the current window', function() + prepare_test() + + api.nvim_buf_set_lines(0, 0, -1, false, { + 'Praesent enim diam,', + 'Praesent enim diam,', + 'Praesent enim diam,', + 'Praesent enim diam,', + 'Praesent enim diam,', + }) + -- Keep the cursor beyond the popup's single line to reproduce E315. + api.nvim_win_set_cursor(0, { 5, 0 }) + + exec_lua(function() + vim.ui.select = function() + local buf = vim.api.nvim_create_buf(false, true) + + vim.api.nvim_open_win(buf, true, { + row = 10, + col = 10, + height = 10, + width = 10, + relative = 'editor', + style = 'minimal', + }) + end + + vim.cmd('normal! wz=') + end) + end) + it('does nothing when the user cancels', function() prepare_test()