From b2bf7bcfb15125fe65f3e503af00174f31eb92d8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 19 Jun 2026 12:56:38 +0200 Subject: feat(cmdwin): implement cmdwin as a normal buf+win MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: cmdwin (the `:q` cmdline buffer) has various limitations which require special-casing all over the codebase. Besides complicating the code, it also breaks async plugins if they try to create buffers/windows after some work is done, if the user happens to open cmdwin at the wrong the moment: Lua callback: …/guh.nvim/lua/guh/util.lua:531: E11: Invalid in command-line window; executes, CTRL-C quits stack traceback: [C]: in function 'nvim_buf_delete' …/guh.nvim/lua/guh/util.lua:531: in function <…/guh.nvim/lua/guh/util.lua:526> Solution: Just say no to "inception". Reimplement cmdwin as a normal buffer+window. All of the cmdwin contortions (in both core, and innocent plugins) exist literally only to support "inception": recursive cmdwin-in-cmdline-things, like `=`, `/`, search-during-substitute, `:input()`, etc. So we just won't support that (though I have a potential plan for that later, which I call "modal parking lot"). The benefit is that plugins, and core, no longer have to care about cmdwin. BONUS: - mouse-drag on vertical separators works (it only worked for horizontal/statusline before) - inccommand-in-cmdwin now works correctly, for free (thus don't need #40077). POTENTIAL FOLLOWUPS - Drop `CHECK_CMDWIN` ("E11: Invalid in command-line window"), allow chaos. - Unify `BUFLOCK_OK` / `LOCK_OK` ? DESIGN: - Eliminate lots of C globals, `EX_CMDWIN`, etc. - `text_locked()` no longer reports true for cmdwin. - cmdwin = a normal window with 'winfixbuf', 'bufhidden=wipe', 'buftype=nofile'. Invariants come from those options rather than special cases throughout the codebase. - `nv_record` for q:/q//q? calls Lua `nlua_call_vimfn("vim._core.cmdwin", …)`. No `K_CMDWIN` / cmdline-reader detour. - `cedit_key` (`c_CTRL-F`) schedules a deferred event that calls `vim._core.cmdwin.open(type, content, pos)` and returns `Ctrl_C` so the in-flight cmdline cancels. Reader state is not serialized; instead the captured `(type, line, col)` is replayed via `nvim_feedkeys(type..line.."", "nt", …)` after user confirms. - On confirm/cancel: `` / `` calls into Lua which closes the window and re-feeds the cmdline. BREAKING CHANGES: - Expression-register cmdline (`=` from insert-mode) no longer supports cmdwin. Same applies to `input()` / `inputlist()` (already covered by `text_locked`). - Usage of cmdwin in macros/mappings will probably break (assuming they ever worked). --- test/functional/vimscript/api_functions_spec.lua | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'test/functional/vimscript/api_functions_spec.lua') diff --git a/test/functional/vimscript/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua index b36acbd312..b341c191d6 100644 --- a/test/functional/vimscript/api_functions_spec.lua +++ b/test/functional/vimscript/api_functions_spec.lua @@ -10,7 +10,6 @@ local insert, pcall_err = n.insert, t.pcall_err local matches = t.matches local api = n.api local feed = n.feed -local command = n.command describe('eval-API', function() before_each(clear) @@ -91,16 +90,22 @@ describe('eval-API', function() api.nvim_get_vvar('errmsg') ) - -- Some functions checking textlock (usually those that may change the current window or buffer) - -- also ought to not be usable in the cmdwin. + -- cmdwin behavior (changed since #40312). local old_win = api.nvim_get_current_win() feed('q:') + local cmdwin_win = api.nvim_get_current_win() eq( - 'E11: Invalid in command-line window; executes, CTRL-C quits', + 'Vim:E11: Invalid in command-line window; executes, CTRL-C quits', pcall_err(api.nvim_set_current_win, old_win) ) - - -- But others, like nvim_buf_set_lines(), which just changes text, is OK. + eq(old_win, api.nvim_get_current_win()) + -- TODO(justinmk): awkward: E11 is raised but the focus switch happens anyway; inherited bug in + -- goto_tabpage_win (calls win_enter unconditionally even when goto_tabpage_tp's CHECK_CMDWIN + -- emsg'd). We should probably just remove CHECK_CMDWIN from goto_tabpage_tp. #40407 + pcall(api.nvim_set_current_win, cmdwin_win) + eq(cmdwin_win, api.nvim_get_current_win()) + + -- nvim_buf_set_lines() in the cmdwin buffer is OK. api.nvim_buf_set_lines(0, 0, -1, 1, { 'wow!' }) eq({ 'wow!' }, api.nvim_buf_get_lines(0, 0, -1, 1)) -- cgit