neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 4019d3050d1955d86e461c1e1e20fe5fb60bd7e5
parent 639f9f4cdaa39918b2a182924cdeefd348ec6bdf
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Fri, 22 Aug 2025 14:54:26 +0800

vim-patch:9.1.1638: completion: not possible to delay the autcompletion

Problem:  completion: not possible to delay the autcompletion
Solution: add the 'autocompletedelay' option value (Girish Palya).

This patch introduces a new global option 'autocompletedelay'/'acl' that
specifies the delay, in milliseconds, before the autocomplete menu
appears after typing.

When set to a non-zero value, Vim waits for the specified time before
showing the completion popup, allowing users to reduce distraction from
rapid suggestion pop-ups or to fine-tune the responsiveness of
completion.

The default value is 0, which preserves the current immediate-popup
behavior.

closes: vim/vim#17960

https://github.com/vim/vim/commit/a09b1604d4fe7516d87fbcbf2ffd2dc484799b23

N/A patch: vim-patch:9.1.1641: a few compiler warnings are output

Co-authored-by: Girish Palya <girishji@gmail.com>

Diffstat:
Mruntime/doc/insert.txt | 4++--
Mruntime/doc/options.txt | 7+++++++
Mruntime/doc/quickref.txt | 1+
Mruntime/lua/vim/_meta/options.lua | 10++++++++++
Mruntime/optwin.vim | 4+++-
Msrc/nvim/insexpand.c | 39++++++++++++++++++++++++++++++++++-----
Msrc/nvim/option_vars.h | 1+
Msrc/nvim/options.lua | 14++++++++++++++
Mtest/functional/editor/completion_spec.lua | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/old/testdir/test_ins_complete.vim | 34++++++++++++++++++++++++++++++++++
10 files changed, 200 insertions(+), 8 deletions(-)

diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt @@ -1117,8 +1117,8 @@ CTRL-X CTRL-Z Stop completion without changing the text. AUTOCOMPLETION *ins-autocompletion* Vim can display a completion menu as you type, similar to using |i_CTRL-N|, -but triggered automatically. See 'autocomplete'. The menu items are collected -from the sources listed in the 'complete' option. +but triggered automatically. See 'autocomplete' and 'autocompletedelay'. +The menu items are collected from the sources listed in the 'complete' option. Unlike manual |i_CTRL-N| completion, this mode uses a decaying timeout to keep Vim responsive. Sources earlier in the 'complete' list are given more time diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt @@ -747,6 +747,13 @@ A jump table for the options with a short description can be found at |Q_op|. When on, Vim shows a completion menu as you type, similar to using |i_CTRL-N|, but triggered automatically. See |ins-autocompletion|. + *'autocompletedelay'* *'acl'* +'autocompletedelay' 'acl' number (default 0) + global + Delay in milliseconds before the autocomplete menu appears after + typing. If you prefer it not to open too quickly, set this value + slightly above your typing speed. See |ins-autocompletion|. + *'autoindent'* *'ai'* *'noautoindent'* *'noai'* 'autoindent' 'ai' boolean (default on) local to buffer diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt @@ -626,6 +626,7 @@ Short explanation of each option: *option-list* 'arabicshape' 'arshape' do shaping for Arabic characters 'autochdir' 'acd' change directory to the file in the current window 'autocomplete' 'ac' enable automatic completion in insert mode +'autocompletedelay' 'acl' delay in msec before menu appears after typing 'autoindent' 'ai' take indent for new line from previous line 'autoread' 'ar' autom. read file when changed outside of Vim 'autowrite' 'aw' automatically write file if changed diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua @@ -120,6 +120,16 @@ vim.o.ac = vim.o.autocomplete vim.go.autocomplete = vim.o.autocomplete vim.go.ac = vim.go.autocomplete +--- Delay in milliseconds before the autocomplete menu appears after +--- typing. If you prefer it not to open too quickly, set this value +--- slightly above your typing speed. See `ins-autocompletion`. +--- +--- @type integer +vim.o.autocompletedelay = 0 +vim.o.acl = vim.o.autocompletedelay +vim.go.autocompletedelay = vim.o.autocompletedelay +vim.go.acl = vim.go.autocompletedelay + --- Copy indent from current line when starting a new line (typing <CR> --- in Insert mode or when using the "o" or "O" command). If you do not --- type anything on the new line except <BS> or CTRL-D and then type diff --git a/runtime/optwin.vim b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: The Vim Project <https://github.com/vim/vim> -" Last Change: 2025 Aug 07 +" Last Change: 2025 Aug 16 " Former Maintainer: Bram Moolenaar <Bram@vim.org> " If there already is an option window, jump to that one. @@ -737,6 +737,8 @@ if has("insert_expand") call <SID>OptionL("cpt") call <SID>AddOption("autocomplete", gettext("automatic completion in insert mode")) call <SID>BinOptionG("ac", &ac) + call <SID>AddOption("autocompletedelay", gettext("delay in msec before menu appears after typing")) + call append("$", " \tset acl=" . &acl) call <SID>AddOption("completeopt", gettext("whether to use a popup menu for Insert mode completion")) call <SID>OptionL("cot") call <SID>AddOption("completeitemalign", gettext("popup menu item align order")) diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c @@ -2206,6 +2206,13 @@ static void ins_compl_new_leader(void) ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1); compl_used_match = false; + if (p_acl > 0) { + pum_undisplay(true); + redraw_later(curwin, UPD_VALID); + update_screen(); // Show char (deletion) immediately + ui_flush(); + } + if (compl_started) { ins_compl_set_original_text(compl_leader.data, compl_leader.size); if (is_cpt_func_refresh_always()) { @@ -4811,10 +4818,10 @@ static int ins_compl_get_exp(pos_T *ini) found_new_match = FAIL; } - int i = -1; // total of matches, unknown + int match_count = -1; // total of matches, unknown if (found_new_match == FAIL || (ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())) { - i = ins_compl_make_cyclic(); + match_count = ins_compl_make_cyclic(); } if (cfc_has_mode() && compl_get_longest && compl_num_bests > 0) { @@ -4838,7 +4845,7 @@ static int ins_compl_get_exp(pos_T *ini) sort_compl_match_list(cp_compare_nearest); } - return i; + return match_count; } /// Update "compl_shown_match" to the actually shown match, it may differ when @@ -5288,7 +5295,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match /// collecting, and halve the timeout. static void check_elapsed_time(void) { - if (cpt_sources_array == NULL) { + if (cpt_sources_array == NULL || cpt_sources_index < 0) { return; } @@ -6026,6 +6033,11 @@ int ins_complete(int c, bool enable_pum) return FAIL; } + // Timestamp when match collection starts + uint64_t compl_start_tv = 0; + if (compl_autocomplete && p_acl > 0) { + compl_start_tv = os_hrtime(); + } compl_curr_win = curwin; compl_curr_buf = curwin->w_buffer; compl_shown_match = compl_curr_match; @@ -6050,7 +6062,8 @@ int ins_complete(int c, bool enable_pum) } // we found no match if the list has only the "compl_orig_text"-entry - if (is_first_match(compl_first_match->cp_next)) { + bool no_matches_found = is_first_match(compl_first_match->cp_next); + if (no_matches_found) { // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, // because we couldn't expand anything at first place, but if we used // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word @@ -6074,6 +6087,22 @@ int ins_complete(int c, bool enable_pum) ins_compl_show_statusmsg(); } + // Wait for the autocompletion delay to expire + if (compl_autocomplete && p_acl > 0 && !no_matches_found + && (os_hrtime() - compl_start_tv) / 1000000 < (uint64_t)p_acl) { + setcursor(); + ui_flush(); + do { + if (char_avail()) { + ins_compl_restart(); + compl_interrupted = true; + break; + } else { + os_delay(2L, true); + } + } while ((os_hrtime() - compl_start_tv) / 1000000 < (uint64_t)p_acl); + } + // Show the popup menu, unless we got interrupted. if (enable_pum && !compl_interrupted) { show_pum(save_w_wrow, save_w_leftcol); diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h @@ -301,6 +301,7 @@ EXTERN unsigned cia_flags; ///< order flags of 'completeitemalign' EXTERN char *p_cot; ///< 'completeopt' EXTERN unsigned cot_flags; ///< flags from 'completeopt' EXTERN int p_ac; ///< 'autocomplete' +EXTERN OptInt p_acl; ///< 'autocompletedelay' #ifdef BACKSLASH_IN_FILENAME EXTERN char *p_csl; ///< 'completeslash' #endif diff --git a/src/nvim/options.lua b/src/nvim/options.lua @@ -242,6 +242,20 @@ local options = { varname = 'p_ac', }, { + abbreviation = 'acl', + defaults = 0, + desc = [=[ + Delay in milliseconds before the autocomplete menu appears after + typing. If you prefer it not to open too quickly, set this value + slightly above your typing speed. See |ins-autocompletion|. + ]=], + full_name = 'autocompletedelay', + scope = { 'global' }, + short_desc = N_('delay in msec before menu appears after typing'), + type = 'number', + varname = 'p_acl', + }, + { abbreviation = 'ai', defaults = true, desc = [=[ diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua @@ -1409,4 +1409,98 @@ describe('completion', function() {5:-- INSERT --} 4,6 All | ]]) end) + + -- oldtest: Test_autocompletedelay() + it("'autocompletedelay' option", function() + source([[ + call setline(1, ['foo', 'foobar', 'foobarbaz']) + set autocomplete + ]]) + screen:try_resize(60, 10) + + feed('Gof') + screen:expect([[ + foo | + foobar | + foobarbaz | + f^ | + {4:foobarbaz }{1: }| + {4:foobar }{1: }| + {4:foo }{1: }| + {1:~ }|*2 + {5:-- INSERT --} | + ]]) + + feed('<Esc>') + command('set autocompletedelay=500') + feed('Sf') + screen:expect([[ + foo | + foobar | + foobarbaz | + f^ | + {1:~ }|*5 + {5:-- INSERT --} | + ]]) + feed('o') + screen:expect([[ + foo | + foobar | + foobarbaz | + fo^ | + {1:~ }|*5 + {5:-- INSERT --} | + ]]) + vim.uv.sleep(500) + screen:expect([[ + foo | + foobar | + foobarbaz | + fo^ | + {4:foobarbaz }{1: }| + {4:foobar }{1: }| + {4:foo }{1: }| + {1:~ }|*2 + {5:-- INSERT --} | + ]]) + feed('<BS>') + screen:expect([[ + foo | + foobar | + foobarbaz | + f^ | + {1:~ }|*5 + {5:-- INSERT --} | + ]]) + vim.uv.sleep(500) + screen:expect([[ + foo | + foobar | + foobarbaz | + f^ | + {4:foobarbaz }{1: }| + {4:foobar }{1: }| + {4:foo }{1: }| + {1:~ }|*2 + {5:-- INSERT --} | + ]]) + + -- During delay wait, user can open menu using CTRL_N completion + feed('<Esc>') + command('set completeopt=menuone,preinsert') + feed('Sf<C-N>') + screen:expect([[ + foo | + foobar | + foobarbaz | + f^oo | + {12:foo }{1: }| + {4:foobar }{1: }| + {4:foobarbaz }{1: }| + {1:~ }|*2 + {5:-- Keyword completion (^N^P) }{6:match 1 of 3} | + ]]) + + feed('<esc>') + end) end) diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim @@ -5573,4 +5573,38 @@ func Test_omni_start_invalid_col() set omnifunc& complete& endfunc +func Test_autocompletedelay() + CheckScreendump + + let lines =<< trim [SCRIPT] + call setline(1, ['foo', 'foobar', 'foobarbaz']) + set autocomplete + [SCRIPT] + call writefile(lines, 'XTest_autocomplete_delay', 'D') + let buf = RunVimInTerminal('-S XTest_autocomplete_delay', {'rows': 10}) + + call term_sendkeys(buf, "Gof") + call VerifyScreenDump(buf, 'Test_autocompletedelay_1', {}) + + call term_sendkeys(buf, "\<Esc>:set autocompletedelay=500\<CR>") + call term_sendkeys(buf, "Sf") + call VerifyScreenDump(buf, 'Test_autocompletedelay_2', {}) + call term_sendkeys(buf, "o") + call VerifyScreenDump(buf, 'Test_autocompletedelay_3', {}) + sleep 500m + call VerifyScreenDump(buf, 'Test_autocompletedelay_4', {}) + call term_sendkeys(buf, "\<BS>") + call VerifyScreenDump(buf, 'Test_autocompletedelay_5', {}) + sleep 500m + call VerifyScreenDump(buf, 'Test_autocompletedelay_6', {}) + + " During delay wait, user can open menu using CTRL_N completion + call term_sendkeys(buf, "\<Esc>:set completeopt=menuone,preinsert\<CR>") + call term_sendkeys(buf, "Sf\<C-N>") + call VerifyScreenDump(buf, 'Test_autocompletedelay_7', {}) + + call term_sendkeys(buf, "\<esc>") + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab nofoldenable