mirror of
https://github.com/neovim/neovim.git
synced 2026-09-03 12:50:36 +00:00
fix(multicursor): 'autocomplete' during insert-cascade raises E565 #41625
Problem: With mcursors and 'autocomplete', a non-literal key (`<BS>`) during an active completion attempts to insert-cascade, which attempts a nested `edit()`, which refuses with E565. Solution: Defer the flush while completion is active; the pending keys will be handled later.
This commit is contained in:
@@ -65,7 +65,7 @@ typedef struct {
|
||||
#define BF_NEW 0x10 // file didn't exist when editing started
|
||||
#define BF_NEW_W 0x20 // Warned for BF_NEW and file created
|
||||
#define BF_READERR 0x40 // got errors while reading the file
|
||||
#define BF_DUMMY 0x80 // dummy buffer, only used internally
|
||||
#define BF_DUMMY 0x80 // Internal-only dummy buffer.
|
||||
#define BF_SYN_SET 0x200 // 'syntax' option was set
|
||||
|
||||
// Mask to check for flags that prevent normal writing
|
||||
|
||||
@@ -7142,6 +7142,8 @@ void restore_current_state(save_state_T *sst)
|
||||
ui_cursor_shape(); // may show different cursor shape
|
||||
}
|
||||
|
||||
/// True while evaluating an `<expr>` mapping.
|
||||
/// Buffer changes are not allowed, except for internal-only dummy buffers.
|
||||
bool expr_map_locked(void)
|
||||
{
|
||||
return expr_map_lock > 0 && !(curbuf->b_flags & BF_DUMMY);
|
||||
|
||||
@@ -1342,23 +1342,21 @@ static void insert_handle_key_post(InsertState *s)
|
||||
|
||||
/// edit(): Start inserting text.
|
||||
///
|
||||
/// "cmdchar" can be:
|
||||
/// 'i' normal insert command
|
||||
/// 'a' normal append command
|
||||
/// 'R' replace command
|
||||
/// 'r' "r<CR>" command: insert one <CR>.
|
||||
/// Note: count can be > 1, for redo, but still only one <CR> is inserted.
|
||||
/// <Esc> is not used for redo.
|
||||
/// 'g' "gI" command.
|
||||
/// 'V' "gR" command for Virtual Replace mode.
|
||||
/// 'v' "gr" command for single character Virtual Replace mode.
|
||||
/// May nest: ":normal i…" and multicursor insert-cascade enters it while an outer edit() is
|
||||
/// suspended. But i_CTRL-O does not nest: edit() returns, and the caller runs the Normal-mode
|
||||
/// command.
|
||||
///
|
||||
/// This function is not called recursively. For CTRL-O commands, it returns
|
||||
/// and lets the caller handle the Normal-mode command.
|
||||
///
|
||||
/// @param cmdchar command that started the insert
|
||||
/// @param startln if true, insert at start of line
|
||||
/// @param count repeat count for the command
|
||||
/// @param cmdchar Command that started the insert:
|
||||
/// - 'i'
|
||||
/// - 'a'
|
||||
/// - 'R'
|
||||
/// - 'r': "r<CR>" command: insert one <CR>. Note: count can be > 1, for redo, but still only one
|
||||
/// <CR> is inserted. <Esc> is not used for redo.
|
||||
/// - 'g': "gI" command.
|
||||
/// - 'V': "gR" command: Virtual Replace mode.
|
||||
/// - 'v': "gr" command: single-character Virtual Replace mode.
|
||||
/// @param startln If true, insert at start of line.
|
||||
/// @param count Repeat count for the command.
|
||||
///
|
||||
/// @return true if a CTRL-O command caused the return (insert mode pending).
|
||||
bool edit(int cmdchar, bool startln, int count)
|
||||
@@ -1381,10 +1379,10 @@ bool edit(int cmdchar, bool startln, int count)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow changes in the buffer while editing the cmdline. The
|
||||
// caller of getcmdline() may get confused.
|
||||
// Don't allow recursive insert mode when busy with completion.
|
||||
// Allow in dummy buffers since they are only used internally
|
||||
// Disallow edit() (insert-mode) when:
|
||||
// - ins-completion is active
|
||||
// - textlock (the caller of getcmdline() may get confused)
|
||||
// - <expr> mapping eval
|
||||
if (textlock != 0 || ins_compl_active() || compl_busy || pum_visible()
|
||||
|| expr_map_locked()) {
|
||||
emsg(_(e_textlock));
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "nvim/input.h"
|
||||
#include "nvim/input_cmdatom.h"
|
||||
#include "nvim/insert.h"
|
||||
#include "nvim/insexpand.h"
|
||||
#include "nvim/keycodes.h"
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
@@ -51,6 +52,7 @@
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/os/time.h"
|
||||
#include "nvim/plines.h"
|
||||
#include "nvim/popupmenu.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
#include "nvim/register.h"
|
||||
#include "nvim/register_defs.h"
|
||||
@@ -525,7 +527,7 @@ void mc_clock_edge(bool map_edit)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes cursors that overlap another cursor, so an edit does not apply N times at one position.
|
||||
/// Prunes cursors that overlap others, so an edit does not apply N times at one position.
|
||||
static void mc_dedupe(void)
|
||||
{
|
||||
const bool had_cursors = kv_size(mc_cursors) > 0;
|
||||
@@ -839,8 +841,11 @@ void mc_ins_cascade(void)
|
||||
} else if (ins.size > mc_ins_span.done_len
|
||||
&& mc_ins_keys_nonliteral(ins.data + mc_ins_span.done_len,
|
||||
ins.size - mc_ins_span.done_len)) {
|
||||
// Non-literal keys pending (BS, CTRL-U, ...): re-execute instead of previewing.
|
||||
mc_ins_span_flush(&ins, false);
|
||||
// Non-literal keys pending (BS, CTRL-U, …): re-execute instead of previewing.
|
||||
// Not during completion: edit() would raise E565. #41602
|
||||
if (!ins_compl_active() && !pum_visible()) {
|
||||
mc_ins_span_flush(&ins, false);
|
||||
}
|
||||
} else {
|
||||
MTPair p = extmark_from_id(curbuf, mc_session_ns(), mc_ins_span.region);
|
||||
if (p.start.id != 0) {
|
||||
|
||||
@@ -1401,6 +1401,17 @@ describe('multicursor', function()
|
||||
feed('cc')
|
||||
feed('f<C-n><C-n><C-e><Esc>') -- cycle, then cancel back to the leader
|
||||
eq({ 'f', 'f', 'f' }, { get_lines()[4], get_lines()[5], get_lines()[6] })
|
||||
|
||||
-- No candidates, {Visual}Q cursors: <BS> mid-completion. #41602
|
||||
clear_cursors()
|
||||
api.nvim_buf_set_lines(0, 0, -1, true, { '', '', '' })
|
||||
command('let v:errmsg = ""')
|
||||
feed('VggQ')
|
||||
feed('i a<BS>')
|
||||
eq('', api.nvim_get_vvar('errmsg'))
|
||||
feed('<Esc>')
|
||||
eq('', api.nvim_get_vvar('errmsg'))
|
||||
eq({ ' ', ' ', ' ' }, get_lines())
|
||||
end)
|
||||
|
||||
it('InsertCharPre-driven complete() plugin (cmp-style)', function()
|
||||
|
||||
Reference in New Issue
Block a user