Merge pull request #24704 from seandewar/cmdwin-madness

vim-patch:9.1.{0047,0048,0049}: fun cmdwin fixes
This commit is contained in:
Sean Dewar 2024-01-28 13:03:56 +00:00 committed by GitHub
commit a757195a60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 355 additions and 33 deletions

View File

@ -2079,6 +2079,8 @@ getbufinfo([{dict}]) *getbufinfo()*
bufnr Buffer number. bufnr Buffer number.
changed TRUE if the buffer is modified. changed TRUE if the buffer is modified.
changedtick Number of changes made to the buffer. changedtick Number of changes made to the buffer.
command TRUE if the buffer belongs to the
command-line window |cmdwin|.
hidden TRUE if the buffer is hidden. hidden TRUE if the buffer is hidden.
lastused Timestamp in seconds, like lastused Timestamp in seconds, like
|localtime()|, when the buffer was |localtime()|, when the buffer was

View File

@ -2562,6 +2562,8 @@ function vim.fn.getbufinfo(buf) end
--- bufnr Buffer number. --- bufnr Buffer number.
--- changed TRUE if the buffer is modified. --- changed TRUE if the buffer is modified.
--- changedtick Number of changes made to the buffer. --- changedtick Number of changes made to the buffer.
--- command TRUE if the buffer belongs to the
--- command-line window |cmdwin|.
--- hidden TRUE if the buffer is hidden. --- hidden TRUE if the buffer is hidden.
--- lastused Timestamp in seconds, like --- lastused Timestamp in seconds, like
--- |localtime()|, when the buffer was --- |localtime()|, when the buffer was

View File

@ -1022,7 +1022,7 @@ Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err)
return 0; return 0;
} }
if (cmdwin_type != 0 && buf == curbuf) { if (buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin); api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return 0; return 0;
} }

View File

@ -181,7 +181,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
if (!buf) { if (!buf) {
return 0; return 0;
} }
if (cmdwin_type != 0 && (enter || buf == curbuf)) { if ((cmdwin_type != 0 && enter) || buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin); api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return 0; return 0;
} }

View File

@ -61,7 +61,7 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
if (!win || !buf) { if (!win || !buf) {
return; return;
} }
if (cmdwin_type != 0 && (win == curwin || win == cmdwin_old_curwin || buf == curbuf)) { if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin); api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return; return;
} }

View File

@ -4012,6 +4012,9 @@ char *buf_spname(buf_T *buf)
if (buf->b_fname != NULL) { if (buf->b_fname != NULL) {
return buf->b_fname; return buf->b_fname;
} }
if (buf == cmdwin_buf) {
return _("[Command Line]");
}
if (bt_prompt(buf)) { if (bt_prompt(buf)) {
return _("[Prompt]"); return _("[Prompt]");
} }
@ -4129,6 +4132,7 @@ void wipe_buffer(buf_T *buf, bool aucmd)
/// - Always considered 'nomodified' /// - Always considered 'nomodified'
/// ///
/// @param bufnr Buffer to switch to, or 0 to create a new buffer. /// @param bufnr Buffer to switch to, or 0 to create a new buffer.
/// @param bufname Buffer name, or NULL.
/// ///
/// @see curbufIsChanged() /// @see curbufIsChanged()
/// ///
@ -4138,9 +4142,11 @@ int buf_open_scratch(handle_T bufnr, char *bufname)
if (do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL) == FAIL) { if (do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL) == FAIL) {
return FAIL; return FAIL;
} }
apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); if (bufname != NULL) {
setfname(curbuf, bufname, NULL, true); apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); setfname(curbuf, bufname, NULL, true);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
}
set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL);
set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL);
set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL);

View File

@ -1219,7 +1219,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
statuscol.draw = true; statuscol.draw = true;
statuscol.sattrs = wlv.sattrs; statuscol.sattrs = wlv.sattrs;
statuscol.foldinfo = foldinfo; statuscol.foldinfo = foldinfo;
statuscol.width = win_col_off(wp) - (cmdwin_type != 0 && wp == curwin); statuscol.width = win_col_off(wp) - (wp == cmdwin_win);
statuscol.use_cul = use_cursor_line_highlight(wp, lnum); statuscol.use_cul = use_cursor_line_highlight(wp, lnum);
statuscol.sign_cul_id = statuscol.use_cul ? sign_cul_attr : 0; statuscol.sign_cul_id = statuscol.use_cul ? sign_cul_attr : 0;
statuscol.num_attr = sign_num_attr > 0 ? syn_id2attr(sign_num_attr) : 0; statuscol.num_attr = sign_num_attr > 0 ? syn_id2attr(sign_num_attr) : 0;
@ -1511,7 +1511,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
assert(wlv.off == 0); assert(wlv.off == 0);
if (cmdwin_type != 0 && wp == curwin) { if (wp == cmdwin_win) {
// Draw the cmdline character. // Draw the cmdline character.
draw_col_fill(&wlv, schar_from_ascii(cmdwin_type), 1, win_hl_attr(wp, HLF_AT)); draw_col_fill(&wlv, schar_from_ascii(cmdwin_type), 1, win_hl_attr(wp, HLF_AT));
} }

View File

@ -3219,6 +3219,8 @@ M.funcs = {
bufnr Buffer number. bufnr Buffer number.
changed TRUE if the buffer is modified. changed TRUE if the buffer is modified.
changedtick Number of changes made to the buffer. changedtick Number of changes made to the buffer.
command TRUE if the buffer belongs to the
command-line window |cmdwin|.
hidden TRUE if the buffer is hidden. hidden TRUE if the buffer is hidden.
lastused Timestamp in seconds, like lastused Timestamp in seconds, like
|localtime()|, when the buffer was |localtime()|, when the buffer was

View File

@ -494,6 +494,7 @@ static dict_T *get_buffer_info(buf_T *buf)
tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf));
tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf)); tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf));
tv_dict_add_nr(dict, S_LEN("hidden"), buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); tv_dict_add_nr(dict, S_LEN("hidden"), buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0);
tv_dict_add_nr(dict, S_LEN("command"), buf == cmdwin_buf);
// Get a reference to buffer variables // Get a reference to buffer variables
tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);

View File

@ -755,7 +755,7 @@ void f_win_gettype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_string = xstrdup("preview"); rettv->vval.v_string = xstrdup("preview");
} else if (wp->w_floating) { } else if (wp->w_floating) {
rettv->vval.v_string = xstrdup("popup"); rettv->vval.v_string = xstrdup("popup");
} else if (wp == curwin && cmdwin_type != 0) { } else if (wp == cmdwin_win) {
rettv->vval.v_string = xstrdup("command"); rettv->vval.v_string = xstrdup("command");
} else if (bt_quickfix(wp->w_buffer)) { } else if (bt_quickfix(wp->w_buffer)) {
rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix")); rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix"));

View File

@ -2305,10 +2305,19 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// If the current buffer was empty and has no file name, curbuf // If the current buffer was empty and has no file name, curbuf
// is returned by buflist_new(), nothing to do here. // is returned by buflist_new(), nothing to do here.
if (buf != curbuf) { if (buf != curbuf) {
// Should only be possible to get here if the cmdwin is closed, or
// if it's opening and its buffer hasn't been set yet (the new
// buffer is for it).
assert(cmdwin_buf == NULL);
const int save_cmdwin_type = cmdwin_type; const int save_cmdwin_type = cmdwin_type;
win_T *const save_cmdwin_win = cmdwin_win;
win_T *const save_cmdwin_old_curwin = cmdwin_old_curwin;
// BufLeave applies to the old buffer. // BufLeave applies to the old buffer.
cmdwin_type = 0; cmdwin_type = 0;
cmdwin_win = NULL;
cmdwin_old_curwin = NULL;
// Be careful: The autocommands may delete any buffer and change // Be careful: The autocommands may delete any buffer and change
// the current buffer. // the current buffer.
@ -2324,7 +2333,11 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
const bufref_T save_au_new_curbuf = au_new_curbuf; const bufref_T save_au_new_curbuf = au_new_curbuf;
set_bufref(&au_new_curbuf, buf); set_bufref(&au_new_curbuf, buf);
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf); apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf);
cmdwin_type = save_cmdwin_type; cmdwin_type = save_cmdwin_type;
cmdwin_win = save_cmdwin_win;
cmdwin_old_curwin = save_cmdwin_old_curwin;
if (!bufref_valid(&au_new_curbuf)) { if (!bufref_valid(&au_new_curbuf)) {
// New buffer has been deleted. // New buffer has been deleted.
delbuf_msg(new_name); // Frees new_name. delbuf_msg(new_name); // Frees new_name.

View File

@ -219,6 +219,9 @@ static int cedit_key = -1; ///< key value of 'cedit' option
static handle_T cmdpreview_bufnr = 0; static handle_T cmdpreview_bufnr = 0;
static int cmdpreview_ns = 0; static int cmdpreview_ns = 0;
static const char e_active_window_or_buffer_changed_or_deleted[]
= N_("E199: Active window or buffer changed or deleted");
static void save_viewstate(win_T *wp, viewstate_T *vs) static void save_viewstate(win_T *wp, viewstate_T *vs)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
@ -4323,23 +4326,50 @@ static int open_cmdwin(void)
ga_clear(&winsizes); ga_clear(&winsizes);
return K_IGNORE; return K_IGNORE;
} }
// win_split() autocommands may have messed with the old window or buffer.
// Treat it as abandoning this command-line.
if (!win_valid(old_curwin) || curwin == old_curwin || !bufref_valid(&old_curbuf)
|| old_curwin->w_buffer != old_curbuf.br_buf) {
beep_flush();
ga_clear(&winsizes);
return Ctrl_C;
}
// Don't let quitting the More prompt make this fail. // Don't let quitting the More prompt make this fail.
got_int = false; got_int = false;
// Set "cmdwin_type" before any autocommands may mess things up. // Set "cmdwin_..." variables before any autocommands may mess things up.
cmdwin_type = get_cmdline_type(); cmdwin_type = get_cmdline_type();
cmdwin_level = ccline.level; cmdwin_level = ccline.level;
cmdwin_win = curwin;
cmdwin_old_curwin = old_curwin; cmdwin_old_curwin = old_curwin;
// Create empty command-line buffer. // Create empty command-line buffer. Be especially cautious of BufLeave
if (buf_open_scratch(0, _("[Command Line]")) == FAIL) { // autocommands from do_ecmd(), as cmdwin restrictions do not apply to them!
// Some autocommand messed it up? const int newbuf_status = buf_open_scratch(0, NULL);
win_close(curwin, true, false); const bool cmdwin_valid = win_valid(cmdwin_win);
ga_clear(&winsizes); if (newbuf_status == FAIL || !cmdwin_valid || curwin != cmdwin_win || !win_valid(old_curwin)
|| !bufref_valid(&old_curbuf) || old_curwin->w_buffer != old_curbuf.br_buf) {
if (newbuf_status == OK) {
set_bufref(&bufref, curbuf);
}
if (cmdwin_valid && !last_window(cmdwin_win)) {
win_close(cmdwin_win, true, false);
}
// win_close() autocommands may have already deleted the buffer.
if (newbuf_status == OK && bufref_valid(&bufref) && bufref.br_buf != curbuf) {
close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false);
}
cmdwin_type = 0; cmdwin_type = 0;
cmdwin_level = 0;
cmdwin_win = NULL;
cmdwin_old_curwin = NULL; cmdwin_old_curwin = NULL;
beep_flush();
ga_clear(&winsizes);
return Ctrl_C; return Ctrl_C;
} }
cmdwin_buf = curbuf;
// Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer.
set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL);
curbuf->b_p_ma = true; curbuf->b_p_ma = true;
@ -4434,15 +4464,18 @@ static int open_cmdwin(void)
cmdwin_type = 0; cmdwin_type = 0;
cmdwin_level = 0; cmdwin_level = 0;
cmdwin_buf = NULL;
cmdwin_win = NULL;
cmdwin_old_curwin = NULL; cmdwin_old_curwin = NULL;
exmode_active = save_exmode; exmode_active = save_exmode;
// Safety check: The old window or buffer was deleted: It's a bug when // Safety check: The old window or buffer was changed or deleted: It's a bug
// this happens! // when this happens!
if (!win_valid(old_curwin) || !bufref_valid(&old_curbuf)) { if (!win_valid(old_curwin) || !bufref_valid(&old_curbuf)
|| old_curwin->w_buffer != old_curbuf.br_buf) {
cmdwin_result = Ctrl_C; cmdwin_result = Ctrl_C;
emsg(_("E199: Active window or buffer deleted")); emsg(_(e_active_window_or_buffer_changed_or_deleted));
} else { } else {
win_T *wp; win_T *wp;
// autocmds may abort script processing // autocmds may abort script processing

View File

@ -752,6 +752,8 @@ EXTERN bool km_startsel INIT( = false);
EXTERN int cmdwin_type INIT( = 0); ///< type of cmdline window or 0 EXTERN int cmdwin_type INIT( = 0); ///< type of cmdline window or 0
EXTERN int cmdwin_result INIT( = 0); ///< result of cmdline window or 0 EXTERN int cmdwin_result INIT( = 0); ///< result of cmdline window or 0
EXTERN int cmdwin_level INIT( = 0); ///< cmdline recursion level EXTERN int cmdwin_level INIT( = 0); ///< cmdline recursion level
EXTERN buf_T *cmdwin_buf INIT( = NULL); ///< buffer of cmdline window or NULL
EXTERN win_T *cmdwin_win INIT( = NULL); ///< window of cmdline window or NULL
EXTERN win_T *cmdwin_old_curwin INIT( = NULL); ///< curwin before opening cmdline window or NULL EXTERN win_T *cmdwin_old_curwin INIT( = NULL); ///< curwin before opening cmdline window or NULL
EXTERN char no_lines_msg[] INIT( = N_("--No lines in buffer--")); EXTERN char no_lines_msg[] INIT( = N_("--No lines in buffer--"));

View File

@ -1326,18 +1326,18 @@ retnomove:
&& !sep_line_offset && !sep_line_offset
&& (wp->w_p_rl && (wp->w_p_rl
? col < wp->w_width_inner - fdc ? col < wp->w_width_inner - fdc
: col >= fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1)) : col >= fdc + (wp != cmdwin_win ? 0 : 1))
&& (flags & MOUSE_MAY_STOP_VIS)))) { && (flags & MOUSE_MAY_STOP_VIS)))) {
end_visual_mode(); end_visual_mode();
redraw_curbuf_later(UPD_INVERTED); // delete the inversion redraw_curbuf_later(UPD_INVERTED); // delete the inversion
} }
if (cmdwin_type != 0 && wp != curwin) { if (cmdwin_type != 0 && wp != cmdwin_win) {
// A click outside the command-line window: Use modeless // A click outside the command-line window: Use modeless
// selection if possible. Allow dragging the status lines. // selection if possible. Allow dragging the status lines.
sep_line_offset = 0; sep_line_offset = 0;
row = 0; row = 0;
col += wp->w_wincol; col += wp->w_wincol;
wp = curwin; wp = cmdwin_win;
} }
// Only change window focus when not clicking on or dragging the // Only change window focus when not clicking on or dragging the
// status line. Do change focus when releasing the mouse button // status line. Do change focus when releasing the mouse button

View File

@ -760,7 +760,7 @@ int win_col_off(win_T *wp)
{ {
return ((wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc != NUL) return ((wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc != NUL)
? (number_width(wp) + (*wp->w_p_stc == NUL)) : 0) ? (number_width(wp) + (*wp->w_p_stc == NUL)) : 0)
+ ((cmdwin_type == 0 || wp != curwin) ? 0 : 1) + ((wp != cmdwin_win) ? 0 : 1)
+ win_fdccol_count(wp) + (wp->w_scwidth * SIGN_WIDTH); + win_fdccol_count(wp) + (wp->w_scwidth * SIGN_WIDTH);
} }

View File

@ -763,7 +763,7 @@ int comp_textwidth(bool ff)
// The width is the window width minus 'wrapmargin' minus all the // The width is the window width minus 'wrapmargin' minus all the
// things that add to the margin. // things that add to the margin.
textwidth = curwin->w_width_inner - (int)curbuf->b_p_wm; textwidth = curwin->w_width_inner - (int)curbuf->b_p_wm;
if (cmdwin_type != 0) { if (curbuf == cmdwin_buf) {
textwidth -= 1; textwidth -= 1;
} }
textwidth -= win_fdccol_count(curwin); textwidth -= win_fdccol_count(curwin);

View File

@ -2485,7 +2485,7 @@ bool can_close_in_cmdwin(win_T *win, Error *err)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (cmdwin_type != 0) { if (cmdwin_type != 0) {
if (win == curwin) { if (win == cmdwin_win) {
cmdwin_result = Ctrl_C; cmdwin_result = Ctrl_C;
return false; return false;
} else if (win == cmdwin_old_curwin) { } else if (win == cmdwin_old_curwin) {
@ -3030,6 +3030,9 @@ void win_free_all(void)
{ {
// avoid an error for switching tabpage with the cmdline window open // avoid an error for switching tabpage with the cmdline window open
cmdwin_type = 0; cmdwin_type = 0;
cmdwin_buf = NULL;
cmdwin_win = NULL;
cmdwin_old_curwin = NULL;
while (first_tabpage->tp_next != NULL) { while (first_tabpage->tp_next != NULL) {
tabpage_close(true); tabpage_close(true);

View File

@ -1,18 +1,20 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local clear, curbuf, curbuf_contents, curwin, eq, neq, ok, feed, insert, eval = local clear, curbuf, curbuf_contents, curwin, eq, neq, matches, ok, feed, insert, eval =
helpers.clear, helpers.clear,
helpers.api.nvim_get_current_buf, helpers.api.nvim_get_current_buf,
helpers.curbuf_contents, helpers.curbuf_contents,
helpers.api.nvim_get_current_win, helpers.api.nvim_get_current_win,
helpers.eq, helpers.eq,
helpers.neq, helpers.neq,
helpers.matches,
helpers.ok, helpers.ok,
helpers.feed, helpers.feed,
helpers.insert, helpers.insert,
helpers.eval helpers.eval
local poke_eventloop = helpers.poke_eventloop local poke_eventloop = helpers.poke_eventloop
local exec = helpers.exec local exec = helpers.exec
local exec_lua = helpers.exec_lua
local fn = helpers.fn local fn = helpers.fn
local request = helpers.request local request = helpers.request
local NIL = vim.NIL local NIL = vim.NIL
@ -51,7 +53,7 @@ describe('API/win', function()
eq('Invalid window id: 23', pcall_err(api.nvim_win_set_buf, 23, api.nvim_get_current_buf())) eq('Invalid window id: 23', pcall_err(api.nvim_win_set_buf, 23, api.nvim_get_current_buf()))
end) end)
it('disallowed in cmdwin if win={old_}curwin or buf=curbuf', function() it('disallowed in cmdwin if win=cmdwin_{old_cur}win or buf=cmdwin_buf', function()
local new_buf = api.nvim_create_buf(true, true) local new_buf = api.nvim_create_buf(true, true)
local old_win = api.nvim_get_current_win() local old_win = api.nvim_get_current_win()
local new_win = api.nvim_open_win(new_buf, false, { local new_win = api.nvim_open_win(new_buf, false, {
@ -74,6 +76,36 @@ describe('API/win', function()
'E11: Invalid in command-line window; <CR> executes, CTRL-C quits', 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
pcall_err(api.nvim_win_set_buf, new_win, 0) pcall_err(api.nvim_win_set_buf, new_win, 0)
) )
matches(
'E11: Invalid in command%-line window; <CR> executes, CTRL%-C quits$',
pcall_err(
exec_lua,
[[
local cmdwin_buf = vim.api.nvim_get_current_buf()
local new_win, new_buf = ...
vim.api.nvim_buf_call(new_buf, function()
vim.api.nvim_win_set_buf(new_win, cmdwin_buf)
end)
]],
new_win,
new_buf
)
)
matches(
'E11: Invalid in command%-line window; <CR> executes, CTRL%-C quits$',
pcall_err(
exec_lua,
[[
local cmdwin_win = vim.api.nvim_get_current_win()
local new_win, new_buf = ...
vim.api.nvim_win_call(new_win, function()
vim.api.nvim_win_set_buf(cmdwin_win, new_buf)
end)
]],
new_win,
new_buf
)
)
local next_buf = api.nvim_create_buf(true, true) local next_buf = api.nvim_create_buf(true, true)
api.nvim_win_set_buf(new_win, next_buf) api.nvim_win_set_buf(new_win, next_buf)
@ -546,6 +578,7 @@ describe('API/win', function()
it('in cmdline-window #9767', function() it('in cmdline-window #9767', function()
command('split') command('split')
eq(2, #api.nvim_list_wins()) eq(2, #api.nvim_list_wins())
local oldbuf = api.nvim_get_current_buf()
local oldwin = api.nvim_get_current_win() local oldwin = api.nvim_get_current_win()
local otherwin = api.nvim_open_win(0, false, { local otherwin = api.nvim_open_win(0, false, {
relative = 'editor', relative = 'editor',
@ -570,6 +603,46 @@ describe('API/win', function()
api.nvim_win_close(0, true) api.nvim_win_close(0, true)
eq(2, #api.nvim_list_wins()) eq(2, #api.nvim_list_wins())
eq('', fn.getcmdwintype()) eq('', fn.getcmdwintype())
-- Closing curwin in context of a different window shouldn't close cmdwin.
otherwin = api.nvim_open_win(0, false, {
relative = 'editor',
row = 10,
col = 10,
width = 10,
height = 10,
})
feed('q:')
exec_lua(
[[
vim.api.nvim_win_call(..., function()
vim.api.nvim_win_close(0, true)
end)
]],
otherwin
)
eq(false, api.nvim_win_is_valid(otherwin))
eq(':', fn.getcmdwintype())
-- Closing cmdwin in context of a non-previous window is still OK.
otherwin = api.nvim_open_win(oldbuf, false, {
relative = 'editor',
row = 10,
col = 10,
width = 10,
height = 10,
})
exec_lua(
[[
local otherwin, cmdwin = ...
vim.api.nvim_win_call(otherwin, function()
vim.api.nvim_win_close(cmdwin, true)
end)
]],
otherwin,
api.nvim_get_current_win()
)
eq('', fn.getcmdwintype())
eq(true, api.nvim_win_is_valid(otherwin))
end) end)
it('closing current (float) window of another tabpage #15313', function() it('closing current (float) window of another tabpage #15313', function()
@ -646,6 +719,7 @@ describe('API/win', function()
api.nvim_win_hide(0) api.nvim_win_hide(0)
eq('', fn.getcmdwintype()) eq('', fn.getcmdwintype())
local old_buf = api.nvim_get_current_buf()
local old_win = api.nvim_get_current_win() local old_win = api.nvim_get_current_win()
local other_win = api.nvim_open_win(0, false, { local other_win = api.nvim_open_win(0, false, {
relative = 'win', relative = 'win',
@ -663,6 +737,45 @@ describe('API/win', function()
-- Can close other windows. -- Can close other windows.
api.nvim_win_hide(other_win) api.nvim_win_hide(other_win)
eq(false, api.nvim_win_is_valid(other_win)) eq(false, api.nvim_win_is_valid(other_win))
-- Closing curwin in context of a different window shouldn't close cmdwin.
other_win = api.nvim_open_win(old_buf, false, {
relative = 'editor',
row = 10,
col = 10,
width = 10,
height = 10,
})
exec_lua(
[[
vim.api.nvim_win_call(..., function()
vim.api.nvim_win_hide(0)
end)
]],
other_win
)
eq(false, api.nvim_win_is_valid(other_win))
eq(':', fn.getcmdwintype())
-- Closing cmdwin in context of a non-previous window is still OK.
other_win = api.nvim_open_win(old_buf, false, {
relative = 'editor',
row = 10,
col = 10,
width = 10,
height = 10,
})
exec_lua(
[[
local otherwin, cmdwin = ...
vim.api.nvim_win_call(otherwin, function()
vim.api.nvim_win_hide(cmdwin)
end)
]],
other_win,
api.nvim_get_current_win()
)
eq('', fn.getcmdwintype())
eq(true, api.nvim_win_is_valid(other_win))
end) end)
end) end)
@ -1055,7 +1168,7 @@ describe('API/win', function()
eq(1, fn.exists('g:fired')) eq(1, fn.exists('g:fired'))
end) end)
it('disallowed in cmdwin if enter=true or buf=curbuf', function() it('disallowed in cmdwin if enter=true or buf=cmdwin_buf', function()
local new_buf = api.nvim_create_buf(true, true) local new_buf = api.nvim_create_buf(true, true)
feed('q:') feed('q:')
eq( eq(
@ -1078,6 +1191,20 @@ describe('API/win', function()
height = 5, height = 5,
}) })
) )
matches(
'E11: Invalid in command%-line window; <CR> executes, CTRL%-C quits$',
pcall_err(
exec_lua,
[[
local cmdwin_buf = vim.api.nvim_get_current_buf()
vim.api.nvim_buf_call(vim.api.nvim_create_buf(false, true), function()
vim.api.nvim_open_win(cmdwin_buf, false, {
relative='editor', row=5, col=5, width=5, height=5,
})
end)
]]
)
)
eq( eq(
new_buf, new_buf,

View File

@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen')
local neq, eq, command = helpers.neq, helpers.eq, helpers.command local neq, eq, command = helpers.neq, helpers.eq, helpers.command
local clear = helpers.clear local clear = helpers.clear
local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval
local exec_lua = helpers.exec_lua
local insert, pcall_err = helpers.insert, helpers.pcall_err local insert, pcall_err = helpers.insert, helpers.pcall_err
local matches = helpers.matches local matches = helpers.matches
local api = helpers.api local api = helpers.api
@ -106,6 +107,19 @@ describe('eval-API', function()
pcall_err(api.nvim_open_term, 0, {}) pcall_err(api.nvim_open_term, 0, {})
) )
matches(
'E11: Invalid in command%-line window; <CR> executes, CTRL%-C quits$',
pcall_err(
exec_lua,
[[
local cmdwin_buf = vim.api.nvim_get_current_buf()
vim.api.nvim_buf_call(vim.api.nvim_create_buf(false, true), function()
vim.api.nvim_open_term(cmdwin_buf, {})
end)
]]
)
)
-- But turning a different buffer into a terminal from the cmdwin is OK. -- But turning a different buffer into a terminal from the cmdwin is OK.
local term_buf = api.nvim_create_buf(false, true) local term_buf = api.nvim_create_buf(false, true)
api.nvim_open_term(term_buf, {}) api.nvim_open_term(term_buf, {})

View File

@ -1885,7 +1885,7 @@ func Test_cmdwin_tabpage()
tabclose! tabclose!
endfunc endfunc
func Test_cmdwin_interrupted() func Test_cmdwin_interrupted_more_prompt()
CheckScreendump CheckScreendump
" aborting the :smile output caused the cmdline window to use the current " aborting the :smile output caused the cmdline window to use the current

View File

@ -93,4 +93,116 @@ func Test_cmdwin_restore_heights()
set cmdheight& showtabline& laststatus& set cmdheight& showtabline& laststatus&
endfunc endfunc
func Test_cmdwin_temp_curwin()
func CheckWraps(expect_wrap)
setlocal textwidth=0 wrapmargin=1
call deletebufline('', 1, '$')
let as = repeat('a', winwidth(0) - 2 - &wrapmargin)
call setline(1, as .. ' b')
normal! gww
setlocal textwidth& wrapmargin&
call assert_equal(a:expect_wrap ? [as, 'b'] : [as .. ' b'], getline(1, '$'))
endfunc
func CheckCmdWin()
call assert_equal('command', win_gettype())
" textoff and &wrapmargin formatting considers the cmdwin_type char.
call assert_equal(1, getwininfo(win_getid())[0].textoff)
call CheckWraps(1)
endfunc
func CheckOtherWin()
call assert_equal('', win_gettype())
call assert_equal(0, getwininfo(win_getid())[0].textoff)
call CheckWraps(0)
endfunc
call feedkeys("q::call CheckCmdWin()\<CR>:call win_execute(win_getid(winnr('#')), 'call CheckOtherWin()')\<CR>:q<CR>", 'ntx')
%bwipe!
delfunc CheckWraps
delfunc CheckCmdWin
delfunc CheckOtherWin
endfunc
func Test_cmdwin_interrupted()
func CheckInterrupted()
call feedkeys("q::call assert_equal('', getcmdwintype())\<CR>:call assert_equal('', getcmdtype())\<CR>:q<CR>", 'ntx')
endfunc
augroup CmdWin
" While opening the cmdwin's split:
" Close the cmdwin's window.
au WinEnter * ++once quit
call CheckInterrupted()
" Close the old window.
au WinEnter * ++once execute winnr('#') 'quit'
call CheckInterrupted()
" Switch back to the old window.
au WinEnter * ++once wincmd p
call CheckInterrupted()
" Change the old window's buffer.
au WinEnter * ++once call win_execute(win_getid(winnr('#')), 'enew')
call CheckInterrupted()
" Using BufLeave autocmds as cmdwin restrictions do not apply to them when
" fired from opening the cmdwin...
" After opening the cmdwin's split, while creating the cmdwin's buffer:
" Delete the cmdwin's buffer.
au BufLeave * ++once bwipe
call CheckInterrupted()
" Close the cmdwin's window.
au BufLeave * ++once quit
call CheckInterrupted()
" Close the old window.
au BufLeave * ++once execute winnr('#') 'quit'
call CheckInterrupted()
" Switch to a different window.
au BufLeave * ++once split
call CheckInterrupted()
" Change the old window's buffer.
au BufLeave * ++once call win_execute(win_getid(winnr('#')), 'enew')
call CheckInterrupted()
" However, changing the current buffer is OK and does not interrupt.
au BufLeave * ++once edit other
call feedkeys("q::let t=getcmdwintype()\<CR>:let b=bufnr()\<CR>:clo<CR>", 'ntx')
call assert_equal(':', t)
call assert_equal(1, bufloaded('other'))
call assert_notequal(b, bufnr('other'))
augroup END
" No autocmds should remain, but clear the augroup to be sure.
augroup CmdWin
au!
augroup END
%bwipe!
delfunc CheckInterrupted
endfunc
func Test_cmdwin_existing_bufname()
func CheckName()
call assert_equal(1, getbufinfo('')[0].command)
call assert_equal(0, getbufinfo('[Command Line]')[0].command)
call assert_match('#a\s*"\[Command Line\]"', execute('ls'))
call assert_match('%a\s*"\[Command Line\]"', execute('ls'))
endfunc
file [Command Line]
call feedkeys("q::call CheckName()\<CR>:q\<CR>", 'ntx')
0file
delfunc CheckName
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -3167,15 +3167,18 @@ endfunc
func Test_normal50_commandline() func Test_normal50_commandline()
CheckFeature timers CheckFeature timers
CheckFeature cmdline_hist CheckFeature cmdline_hist
func! DoTimerWork(id) func! DoTimerWork(id)
call assert_equal('[Command Line]', bufname('')) call assert_equal(1, getbufinfo('')[0].command)
" should fail, with E11, but does fail with E23? " should fail, with E11, but does fail with E23?
"call feedkeys("\<c-^>", 'tm') "call feedkeys("\<c-^>", 'tm')
" should also fail with E11 " should fail with E11 - "Invalid in command-line window"
call assert_fails(":wincmd p", 'E11') call assert_fails(":wincmd p", 'E11')
" return from commandline window
call feedkeys("\<cr>") " Return from commandline window.
call feedkeys("\<CR>", 't')
endfunc endfunc
let oldlang=v:lang let oldlang=v:lang
@ -3188,7 +3191,9 @@ func Test_normal50_commandline()
catch /E23/ catch /E23/
" no-op " no-op
endtry endtry
" clean up " clean up
delfunc DoTimerWork
set updatetime=4000 set updatetime=4000
exe "lang" oldlang exe "lang" oldlang
bw! bw!