feat(cmdwin): drop CHECK_CMDWIN

Allow window navigation/creation even while cmdwin is alive! 😱
This commit is contained in:
Justin M. Keyes
2026-06-29 21:47:09 +02:00
parent d6a1b4110a
commit 5256685661
5 changed files with 16 additions and 52 deletions

View File

@@ -1223,9 +1223,12 @@ but it's not possible to open another cmdwin from there. There is no
nesting.
*E11*
|CTRL-W| navigation/split commands and some operations (e.g. |:terminal|) emit
E11. But mouse clicks may focus other windows. The cmdwin buffer is pinned by
'winfixbuf', so the window cannot switch to a different buffer.
|CTRL-W| commands work as usual: you can navigate between, split, resize, and
close windows while the command-line window is open. E11 is still emitted by
operations that would leave or repurpose the cmdwin: opening a new tab page
(CTRL-W_T, |:tabnew|) or turning its buffer into a terminal (|:terminal|).
The cmdwin buffer is pinned by 'winfixbuf', so its window cannot switch to a
different buffer.
CLOSE

View File

@@ -188,11 +188,11 @@ EDITOR
kind/menu/info/abbr for the popup menu.
• |cmdwin| (|q:|, |q/|, |q?|, |c_CTRL-F|) is implemented as a "normal"
buffer+window instead of a nested-state modal loop:
• You can create (and navigate) windows while cmdwin is active. Chaos!
• 'inccommand' works in cmdwin!
• API calls (e.g. |nvim_buf_delete()|) that previously failed with
"E11: Invalid in command-line window" while cmdwin was open, now work
normally. Async plugins no longer need special |CmdwinLeave| workarounds.
• Removed most cmdwin-specific |E11| guards, except for window navigation.
• The |cmdwin-char| is shown via 'statuscolumn'.
• |gf| and |<cfile>| support `file://…` URIs.
• |:log| opens log files.

View File

@@ -271,20 +271,11 @@ void do_window(int nchar, int Prenum, int xchar)
int Prenum1 = Prenum == 0 ? 1 : Prenum;
#define CHECK_CMDWIN \
do { \
if (cmdwin_buf != NULL) { \
emsg(_(e_cmdwin)); \
return; \
} \
} while (0)
switch (nchar) {
// split current window in two parts, horizontally
case 'S':
case Ctrl_S:
case 's':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
// When splitting the quickfix window open a new buffer in it,
// don't replicate the quickfix buffer.
@@ -297,7 +288,6 @@ void do_window(int nchar, int Prenum, int xchar)
// split current window in two parts, vertically
case Ctrl_V:
case 'v':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
// When splitting the quickfix window open a new buffer in it,
// don't replicate the quickfix buffer.
@@ -310,7 +300,6 @@ void do_window(int nchar, int Prenum, int xchar)
// split current window and edit alternate file
case Ctrl_HAT:
case '^':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
if (buflist_findnr(Prenum == 0 ? curwin->w_alt_fnum : Prenum) == NULL) {
@@ -331,7 +320,6 @@ void do_window(int nchar, int Prenum, int xchar)
// open new window
case Ctrl_N:
case 'n':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
newwindow:
if (Prenum) {
@@ -366,7 +354,6 @@ newwindow:
// close preview window
case Ctrl_Z:
case 'z':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
do_cmdline_cmd("pclose");
break;
@@ -391,7 +378,6 @@ newwindow:
// close all but current window
case Ctrl_O:
case 'o':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
cmd_with_count("only", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
@@ -402,7 +388,6 @@ newwindow:
case 'w':
// cursor to previous window with wrap around
case 'W':
CHECK_CMDWIN;
if (ONE_WINDOW && Prenum != 1) { // just one window
beep_flush();
} else {
@@ -454,7 +439,6 @@ newwindow:
case 'j':
case K_DOWN:
case Ctrl_J:
CHECK_CMDWIN;
win_goto_ver(false, Prenum1);
break;
@@ -462,7 +446,6 @@ newwindow:
case 'k':
case K_UP:
case Ctrl_K:
CHECK_CMDWIN;
win_goto_ver(true, Prenum1);
break;
@@ -471,7 +454,6 @@ newwindow:
case K_LEFT:
case Ctrl_H:
case K_BS:
CHECK_CMDWIN;
win_goto_hor(true, Prenum1);
break;
@@ -479,13 +461,11 @@ newwindow:
case 'l':
case K_RIGHT:
case Ctrl_L:
CHECK_CMDWIN;
win_goto_hor(false, Prenum1);
break;
// move window to new tab page
case 'T':
CHECK_CMDWIN;
if (one_window(curwin, NULL)) {
msg(_(m_onlyone), 0);
} else {
@@ -533,21 +513,18 @@ newwindow:
// exchange current and next window
case 'x':
case Ctrl_X:
CHECK_CMDWIN;
win_exchange(Prenum);
break;
// rotate windows downwards
case Ctrl_R:
case 'r':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
win_rotate(false, Prenum1); // downwards
break;
// rotate windows upwards
case 'R':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
win_rotate(true, Prenum1); // upwards
break;
@@ -557,7 +534,6 @@ newwindow:
case 'J':
case 'H':
case 'L':
CHECK_CMDWIN;
if (one_window(curwin, NULL)) {
beep_flush();
} else {
@@ -608,7 +584,6 @@ newwindow:
// jump to tag and split window if tag exists (in preview window)
case '}':
CHECK_CMDWIN;
if (Prenum) {
g_do_tagpreview = Prenum;
} else {
@@ -617,7 +592,6 @@ newwindow:
FALLTHROUGH;
case ']':
case Ctrl_RSB:
CHECK_CMDWIN;
// Keep visual mode, can select words to use as a tag.
if (Prenum) {
postponed_split = Prenum;
@@ -640,7 +614,6 @@ newwindow:
case 'F':
case Ctrl_F: {
wingotofile:
CHECK_CMDWIN;
if (check_text_or_curbuf_locked(NULL)) {
break;
}
@@ -698,7 +671,6 @@ wingotofile:
FALLTHROUGH;
case 'd': // Go to definition, using 'define'
case Ctrl_D: {
CHECK_CMDWIN;
size_t len;
char *ptr;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT, NULL)) == 0) {
@@ -726,7 +698,6 @@ wingotofile:
// CTRL-W g extended commands
case 'g':
case Ctrl_G:
CHECK_CMDWIN;
no_mapping++;
allow_keys++; // no mapping for xchar, but allow key codes
if (xchar == NUL) {
@@ -4936,10 +4907,6 @@ void goto_tabpage(int n)
/// @param trigger_leave_autocmds when true trigger *Leave autocommands.
void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_leave_autocmds)
{
if (trigger_enter_autocmds || trigger_leave_autocmds) {
CHECK_CMDWIN;
}
// Don't repeat a message in another tab page.
set_keep_msg(NULL, 0);

View File

@@ -60,7 +60,7 @@ describe('eval-API', function()
eq('Vim(call):E5555: API call: Invalid buffer id: 17', err)
end)
it('cannot change text or window if textlocked', function()
it('cannot change text or window if textlock', function()
command('autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])')
matches(
'Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$',
@@ -90,19 +90,16 @@ describe('eval-API', function()
api.nvim_get_vvar('errmsg')
)
-- cmdwin behavior (changed since #40312).
-- cmdwin (#40312, #40484): E11 "Invalid in command-line window" restriction was removed.
local old_win = api.nvim_get_current_win()
feed('q:')
local cmdwin_win = api.nvim_get_current_win()
eq(
'Vim:E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
pcall_err(api.nvim_set_current_win, old_win)
)
neq(old_win, cmdwin_win)
-- Switching to another window from the cmdwin now works (previously E11).
api.nvim_set_current_win(old_win)
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)
-- ...and back into the cmdwin.
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.

View File

@@ -3242,11 +3242,8 @@ func Test_normal50_commandline()
func! DoTimerWork(id)
call assert_equal(1, getbufinfo('')[0].command)
" should fail, with E11, but does fail with E23?
"call feedkeys("\<c-^>", 'tm')
" should fail with E11 - "Invalid in command-line window"
call assert_fails(":wincmd p", 'E11')
" Nvim removed the E11 "Invalid in command-line window" restriction (#40312, #40484).
"call assert_fails(":wincmd p", 'E11')
" Return from commandline window.
call feedkeys("\<CR>", 't')