mirror of
https://github.com/neovim/neovim.git
synced 2026-07-09 10:59:38 +00:00
vim-patch:9.2.0750: completion: 'autocompletedelay' deferral leaks state
Problem: After 'autocompletedelay' was made non-blocking, the deferred
popup can misbehave: a pending autocomplete survives leaving
Insert mode and then keeps waking the editor in Normal mode,
the deferral is recorded into registers while recording a
macro, the popup appears an extra 'updatetime' late when
'autocompletedelay' is larger and a CursorHoldI autocommand
exists, CursorHoldI can fire twice without an intervening
keypress, and an open balloon is dismissed (after v9.2.0739)
Solution: Treat the deferral like CursorHold: only keep it pending in
Insert mode and not while recording, with pending typeahead,
or when completion is already active; drop it when Insert mode
ends; measure the delay from when the user typed so a
CursorHold in between does not push the popup back; and do not
let the deferral re-enable CursorHoldI or dismiss the balloon.
(Hirohito Higashi).
closes: vim/vim#20669
1f0f14bc2f
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -372,6 +372,8 @@ static void insert_enter(InsertState *s)
|
||||
// and return false, causing `state_enter` to be called again.
|
||||
} while (!ins_esc(&s->count, s->cmdchar, s->nomove));
|
||||
|
||||
// Drop a pending autocomplete so it does not outlive Insert mode.
|
||||
ins_compl_clear_autocomplete_delay();
|
||||
// Always update o_lnum, so that a "CTRL-O ." that adds a line
|
||||
// still puts the cursor back after the inserted text.
|
||||
if (ins_at_eol) {
|
||||
@@ -1327,8 +1329,9 @@ static void insert_do_cindent(InsertState *s)
|
||||
|
||||
static void insert_handle_key_post(InsertState *s)
|
||||
{
|
||||
// If typed something may trigger CursorHoldI again.
|
||||
if (s->c != K_EVENT
|
||||
// If typed something may trigger CursorHoldI again; K_COMPLETE_DELAY is
|
||||
// injected, not typed.
|
||||
if (s->c != K_EVENT && s->c != K_COMPLETE_DELAY
|
||||
// but not in CTRL-X mode, a script can't restore the state
|
||||
&& ctrl_x_mode_normal()) {
|
||||
did_cursorhold = false;
|
||||
|
||||
@@ -149,7 +149,13 @@ int input_get(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
|
||||
// When an autocomplete is pending, wake at the sooner of
|
||||
// 'autocompletedelay' and 'updatetime' so the delay does not postpone
|
||||
// CursorHold. Once CursorHold has fired, only the delay is left.
|
||||
bool delay_pending = ins_compl_autocomplete_pending() && p_acl > 0;
|
||||
// Gate the injection like trigger_cursorhold() so the deferred key
|
||||
// cannot fire while recording or outside Insert mode.
|
||||
bool delay_pending = ins_compl_autocomplete_pending() && p_acl > 0
|
||||
&& reg_recording == 0
|
||||
&& typebuf.tb_len == 0
|
||||
&& !ins_compl_active()
|
||||
&& (get_real_state() & MODE_INSERT) != 0;
|
||||
// Measure the delay from when it was armed (the keystroke), so a
|
||||
// CursorHold returning in between does not push the popup back.
|
||||
int64_t wait_time = p_ut - cursorhold_time;
|
||||
|
||||
@@ -6114,6 +6114,24 @@ func Test_autocompletedelay_ctrl_k()
|
||||
call Run_test_autocompletedelay_ctrl_k(150, 500)
|
||||
endfunc
|
||||
|
||||
func Test_autocompletedelay_no_record()
|
||||
" The K_COMPLETE_DELAY pseudo key must not be recorded into a register while
|
||||
" recording a macro, like K_CURSORHOLD.
|
||||
new
|
||||
call setline(1, 'foobar')
|
||||
set autocomplete autocompletedelay=100
|
||||
|
||||
let @a = ''
|
||||
" Type a char that arms the delay, idle past 'autocompletedelay' so a
|
||||
" K_COMPLETE_DELAY would be injected, then end Insert mode and stop recording.
|
||||
call timer_start(200, { -> feedkeys("\<Esc>q", 't') })
|
||||
call feedkeys("qaSf", 'tx!')
|
||||
call assert_equal("Sf\<Esc>", @a)
|
||||
|
||||
set autocomplete& autocompletedelay&
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" Preinsert longest prefix when autocomplete
|
||||
func Test_autocomplete_longest()
|
||||
func GetLine()
|
||||
|
||||
Reference in New Issue
Block a user