diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 182f03c006..a95437ba24 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -1205,9 +1205,8 @@ There are two ways to open the command-line window: When the window opens it is filled with the command-line history. The editor opens in Normal mode. The last line contains the command as typed so far. The |cmdwin-char| is shown in 'statuscolumn', indicating the type of command-line. - -The window height is decided by 'cmdwinheight' (if there is room). The window -is full width and is positioned just above the command-line. +The window is full width, positioned just above the command-line; its height +is decided by 'cmdwinheight' (if there is room). Window and tabpage commands work as usual while cmdwin is open: you can navigate, resize, close and open windows/tabpages. The mouse works as in any @@ -1236,8 +1235,9 @@ CLOSE There are several ways to leave the command-line window: - Execute the command-line under the cursor. Works both in - Insert and in Normal mode. + Execute the command-line under the cursor, then close the + window(s) where cmdwin is visible. Works both in Insert and + in Normal mode. CTRL-C Continue in Command-line mode. The command-line under the cursor is pre-filled in the command-line. Works both in Insert and in Normal mode. @@ -1249,8 +1249,8 @@ CTRL-C Continue in Command-line mode. The command-line under the When the cmdwin closes, focus returns to the window where the command-line was started from (if possible). The cmdwin scratch buffer is destroyed -(`bufhidden=wipe`). Any text edits other than the line executed by - are lost. +(`bufhidden=wipe`). Any text edits other than the line executed by +are lost. If you would like to execute the command under the cursor and then have the command-line window open again, you may find this mapping useful: > diff --git a/runtime/lua/vim/_core/cmdwin.lua b/runtime/lua/vim/_core/cmdwin.lua index 699b72e8db..05bc112b33 100644 --- a/runtime/lua/vim/_core/cmdwin.lua +++ b/runtime/lua/vim/_core/cmdwin.lua @@ -101,15 +101,22 @@ function M.open(type, init_line, init_col) caller_win = caller, } - -- Clean up if the window is closed by other means (`:q`, `:close`, etc.). + -- Clean up when the (last-visible) cmdwin is closed by other means (`:q`, `:close`, etc.). vim.api.nvim_create_autocmd({ 'WinClosed' }, { buffer = buf, - once = true, nested = true, - callback = function() - if state ~= nil then - M._cleanup() + callback = function(ev) + if state == nil then + return end + local closing = tonumber(ev.match) + for _, w in ipairs(vim.fn.win_findbuf(buf)) do + if w ~= closing then + return -- Still visible elsewhere; keep cmdwin (and this autocmd) active. + end + end + M._cleanup() + return true -- Last cmdwin window gone; delete this autocmd. end, }) @@ -125,8 +132,8 @@ function M._cleanup() state = nil pcall(vim.api.nvim__cmdwin_set, '', 0) -- Clear the C-side globals. pcall(vim.api.nvim_exec_autocmds, 'CmdwinLeave', { pattern = s.type, modeline = false }) - if vim.api.nvim_win_is_valid(s.win) then - pcall(vim.api.nvim_win_close, s.win, true) + if vim.api.nvim_buf_is_valid(s.buf) then + pcall(vim.api.nvim_buf_delete, s.buf, { force = true }) end if vim.api.nvim_win_is_valid(s.caller_win) then pcall(vim.api.nvim_set_current_win, s.caller_win) diff --git a/test/functional/editor/cmdwin_spec.lua b/test/functional/editor/cmdwin_spec.lua index c34acea192..d43cb7d4fa 100644 --- a/test/functional/editor/cmdwin_spec.lua +++ b/test/functional/editor/cmdwin_spec.lua @@ -32,6 +32,35 @@ describe('cmdwin', function() eq('', fn.getcmdwintype()) end) + it(' closes all cmdwin (split) windows #40484', function() + feed('q:') + feed('ilet g:cmdwin_result = 7') + feed('s') -- split: two windows now show the cmdwin buffer. + local cmdwin_buf = api.nvim_get_current_buf() + eq(2, #fn.win_findbuf(cmdwin_buf)) -- sanity: the split happened + + feed('') + eq(7, api.nvim_get_var('cmdwin_result')) -- the cmdline executed + eq('', fn.getcmdwintype()) + eq(0, #fn.win_findbuf(cmdwin_buf)) -- All cmdwin windows are closed. + eq(1, #api.nvim_list_wins()) -- Back to the single original window. + end) + + it(' executes when cmdwin was moved to another tabpage #40484', function() + feed('q:') + feed('ilet g:cmdwin_result = 9') + feed('T') -- Move the cmdwin to its own new tabpage. + eq(2, fn.tabpagenr('$')) -- sanity: the new tabpage exists + local cmdwin_buf = api.nvim_get_current_buf() + + feed('') + eq(9, api.nvim_get_var('cmdwin_result')) -- the cmdline executed. + eq('', fn.getcmdwintype()) + eq(0, #fn.win_findbuf(cmdwin_buf)) -- cmdwin window closed. + eq(1, fn.tabpagenr('$')) -- cmdwin's tabpage is gone. + eq(1, #api.nvim_list_wins()) + end) + it('q/ opens cmdwin with search history', function() fn.histadd('/', 'foo') fn.histadd('/', 'bar')