mirror of
https://github.com/neovim/neovim.git
synced 2026-07-09 19:09:39 +00:00
fix(cmdwin): implement Enter/Ctrl-C as builtins
What echasnovski wants, echasnovski gets.
This commit is contained in:
@@ -101,20 +101,6 @@ function M.open(type, init_line, init_col)
|
||||
caller_win = caller,
|
||||
}
|
||||
|
||||
-- Buffer-local mappings.
|
||||
vim.keymap.set(
|
||||
{ 'n', 'i' },
|
||||
'<CR>',
|
||||
[[<C-\><C-N><Cmd>lua require'vim._core.cmdwin'.confirm()<CR>]],
|
||||
{ buffer = buf, silent = true, desc = 'cmdwin: execute' }
|
||||
)
|
||||
vim.keymap.set(
|
||||
{ 'n', 'i', 'x' },
|
||||
'<C-C>',
|
||||
[[<C-\><C-N><Cmd>lua require'vim._core.cmdwin'.cancel()<CR>]],
|
||||
{ buffer = buf, silent = true, desc = 'cmdwin: cancel' }
|
||||
)
|
||||
|
||||
-- Clean up if the window is closed by other means (`:q`, `:close`, etc.).
|
||||
vim.api.nvim_create_autocmd({ 'WinClosed' }, {
|
||||
buffer = buf,
|
||||
@@ -146,25 +132,30 @@ function M._cleanup()
|
||||
end
|
||||
end
|
||||
|
||||
local function _confirm()
|
||||
if state == nil then
|
||||
return
|
||||
end
|
||||
--- Closes the cmdwin and returns its current line and type.
|
||||
--- @return string line, string type
|
||||
local function _close()
|
||||
local line = vim.api.nvim_get_current_line()
|
||||
local type = state.type
|
||||
local type = assert(state).type
|
||||
M._cleanup()
|
||||
return line, type
|
||||
end
|
||||
|
||||
--- Confirm and execute the current line as a cmdline.
|
||||
function M.confirm()
|
||||
local line, type = _confirm()
|
||||
if state == nil then -- Not in cmdwin (closed already?).
|
||||
return
|
||||
end
|
||||
local line, type = _close()
|
||||
vim.api.nvim_feedkeys(type .. line .. vim.keycode('<CR>'), 'nt', false)
|
||||
end
|
||||
|
||||
--- Cancel: close the cmdwin and re-enter cmdline mode with the line pre-filled (no execute).
|
||||
function M.cancel()
|
||||
local line, type = _confirm()
|
||||
if state == nil then -- Not in cmdwin (closed already?).
|
||||
return
|
||||
end
|
||||
local line, type = _close()
|
||||
vim.api.nvim_feedkeys(type .. line, 'nt', false)
|
||||
end
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
#include "nvim/extmark.h"
|
||||
#include "nvim/extmark_defs.h"
|
||||
#include "nvim/fileio.h"
|
||||
@@ -740,6 +741,11 @@ static int insert_handle_key(InsertState *s)
|
||||
FALLTHROUGH;
|
||||
|
||||
case Ctrl_C: // End input mode
|
||||
if (s->c == Ctrl_C && bt_cmdwin(curbuf)) {
|
||||
got_int = false; // Don't interrupt autocmds etc.
|
||||
cmdwin_do_action("cancel"); // cmdwin: CTRL-C closes cmdwin, pre-fills cmdline.
|
||||
return 0; // exit insert mode
|
||||
}
|
||||
if (s->c == Ctrl_C && bt_prompt(curbuf)) {
|
||||
if (invoke_prompt_interrupt()) {
|
||||
if (!bt_prompt(curbuf)) {
|
||||
@@ -1100,6 +1106,10 @@ static int insert_handle_key(InsertState *s)
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (s->c == CAR && bt_cmdwin(curbuf)) {
|
||||
cmdwin_do_action("confirm"); // cmdwin: <CR> executes the command under the cursor.
|
||||
return 0; // exit insert mode
|
||||
}
|
||||
if ((mod_mask & MOD_MASK_SHIFT) == 0 && bt_prompt(curbuf)) {
|
||||
prompt_invoke_callback();
|
||||
if (!bt_prompt(curbuf)) {
|
||||
|
||||
@@ -4572,25 +4572,37 @@ const char *did_set_cedit(optset_T *args)
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int firstc;
|
||||
char *content; // owned
|
||||
int pos;
|
||||
int firstc; ///< ':'/'/'/'?'.
|
||||
char *content; ///< Initial cmdline (owned).
|
||||
int pos; ///< Cursor column.
|
||||
} CmdwinOpenArgs;
|
||||
|
||||
/// Deferred event: opens cmdwin after the current cmdline-reader exits. (Can't use vim.schedule,
|
||||
/// where the event could fire _during typeahead_; revisit after #40380).
|
||||
/// Synchronously calls `vim._core.cmdwin.<action>(...)`.
|
||||
static void cmdwin_invoke(const char *action, int firstc, char *content, int pos)
|
||||
{
|
||||
char fc[2] = { (char)firstc, 0 };
|
||||
typval_T tv_args[] = {
|
||||
{ .v_type = VAR_STRING, .vval.v_string = fc },
|
||||
{ .v_type = VAR_STRING, .vval.v_string = content ? content : "" },
|
||||
{ .v_type = VAR_NUMBER, .vval.v_number = pos + 1 },
|
||||
{ .v_type = VAR_UNKNOWN },
|
||||
};
|
||||
nlua_call_vimfn("vim._core.cmdwin", action, firstc ? tv_args : tv_args + 3, NULL);
|
||||
xfree(content);
|
||||
}
|
||||
|
||||
/// Calls `vim._core.cmdwin.<action>()`, synchronously.
|
||||
void cmdwin_do_action(const char *action)
|
||||
{
|
||||
cmdwin_invoke(action, 0, NULL, 0);
|
||||
}
|
||||
|
||||
/// Deferred event: opens cmdwin after the cmdline-reader unwinds. Can't run synchronously (cmdline
|
||||
/// is still being read), nor via vim.schedule (could fire _during typeahead_; revisit after #40380).
|
||||
static void open_cmdwin_event(void **argv)
|
||||
{
|
||||
CmdwinOpenArgs *a = argv[0];
|
||||
char fc[2] = { (char)a->firstc, 0 };
|
||||
typval_T tv_args[] = {
|
||||
{ .v_type = VAR_STRING, .vval.v_string = fc },
|
||||
{ .v_type = VAR_STRING, .vval.v_string = a->content ? a->content : "" },
|
||||
{ .v_type = VAR_NUMBER, .vval.v_number = a->pos + 1 },
|
||||
{ .v_type = VAR_UNKNOWN },
|
||||
};
|
||||
nlua_call_vimfn("vim._core.cmdwin", "open", tv_args, NULL);
|
||||
xfree(a->content);
|
||||
cmdwin_invoke("open", a->firstc, a->content, a->pos); // frees a->content
|
||||
xfree(a);
|
||||
}
|
||||
|
||||
@@ -4620,8 +4632,8 @@ static int open_cmdwin(void)
|
||||
a->pos = ccline.cmdpos;
|
||||
|
||||
// Capture the cmdline; will append to end of cmdwin.
|
||||
// Clear the live cmdline so that unwinding it (via Ctrl_C below) does not add it to history.
|
||||
a->content = ccline.cmdbuff ? xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen) : NULL;
|
||||
// Clear cmdline so that unwinding it (via Ctrl_C below) does not add it to history.
|
||||
ccline.cmdlen = 0;
|
||||
ccline.cmdpos = 0;
|
||||
|
||||
|
||||
@@ -3859,8 +3859,11 @@ static void nv_down(cmdarg_T *cap)
|
||||
// <S-Down> is page down
|
||||
cap->arg = FORWARD;
|
||||
nv_page(cap);
|
||||
} else if (bt_cmdwin(curbuf) && cap->cmdchar == CAR) {
|
||||
// cmdwin: execute the command-line under the cursor.
|
||||
cmdwin_do_action("confirm");
|
||||
} else if (bt_quickfix(curbuf) && cap->cmdchar == CAR) {
|
||||
// Quickfix window only: view the result under the cursor.
|
||||
// Quickfix window: view the result under the cursor.
|
||||
qf_view_result(false);
|
||||
} else {
|
||||
if (bt_prompt(curbuf) && cap->cmdchar == CAR
|
||||
@@ -6174,9 +6177,13 @@ static void nv_esc(cmdarg_T *cap)
|
||||
&& cap->opcount == 0
|
||||
&& cap->count0 == 0
|
||||
&& cap->oap->regname == 0);
|
||||
bool cmdwin_cancel = cap->arg && bt_cmdwin(curbuf);
|
||||
|
||||
if (cap->arg) { // true for CTRL-C
|
||||
if (restart_edit == 0 && !bt_cmdwin(curbuf) && !VIsual_active && no_reason) {
|
||||
if (cmdwin_cancel) {
|
||||
got_int = false; // CTRL-C cancels cmdwin; don't interrupt autocmds etc.
|
||||
cmdwin_do_action("cancel");
|
||||
} else if (cap->arg) { // true for CTRL-C
|
||||
if (restart_edit == 0 && !VIsual_active && no_reason) {
|
||||
if (anyBufIsChanged()) {
|
||||
msg(_("Type :qa! and press <Enter> to abandon all changes"
|
||||
" and exit Nvim"), 0);
|
||||
@@ -6197,7 +6204,7 @@ static void nv_esc(cmdarg_T *cap)
|
||||
check_cursor_col(curwin); // make sure cursor is not beyond EOL
|
||||
curwin->w_set_curswant = true;
|
||||
redraw_curbuf_later(UPD_INVERTED);
|
||||
} else if (no_reason) {
|
||||
} else if (no_reason && !cmdwin_cancel) {
|
||||
vim_beep(kOptBoFlagEsc);
|
||||
}
|
||||
clearop(cap->oap);
|
||||
|
||||
@@ -126,7 +126,7 @@ describe('cmdwin', function()
|
||||
})
|
||||
]])
|
||||
feed('q:')
|
||||
n.poke_eventloop() -- Ensure q: is processed before the <C-C> mapping fires.
|
||||
n.poke_eventloop() -- Ensure q: is processed before <C-C>.
|
||||
feed('<C-C>')
|
||||
eq({ 'enter::', 'leave::' }, exec_lua('return _G.events'))
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user