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

a09b1604d4

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

Co-authored-by: Girish Palya <girishji@gmail.com>
This commit is contained in:
zeertzjq
2025-08-22 14:54:26 +08:00
parent 639f9f4cda
commit 4019d3050d
10 changed files with 200 additions and 8 deletions

View File

@@ -1117,8 +1117,8 @@ CTRL-X CTRL-Z Stop completion without changing the text.
AUTOCOMPLETION *ins-autocompletion* AUTOCOMPLETION *ins-autocompletion*
Vim can display a completion menu as you type, similar to using |i_CTRL-N|, 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 but triggered automatically. See 'autocomplete' and 'autocompletedelay'.
from the sources listed in the 'complete' option. 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 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 Vim responsive. Sources earlier in the 'complete' list are given more time

View File

@@ -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 When on, Vim shows a completion menu as you type, similar to using
|i_CTRL-N|, but triggered automatically. See |ins-autocompletion|. |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'* *'noautoindent'* *'noai'*
'autoindent' 'ai' boolean (default on) 'autoindent' 'ai' boolean (default on)
local to buffer local to buffer

View File

@@ -626,6 +626,7 @@ Short explanation of each option: *option-list*
'arabicshape' 'arshape' do shaping for Arabic characters 'arabicshape' 'arshape' do shaping for Arabic characters
'autochdir' 'acd' change directory to the file in the current window 'autochdir' 'acd' change directory to the file in the current window
'autocomplete' 'ac' enable automatic completion in insert mode '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 'autoindent' 'ai' take indent for new line from previous line
'autoread' 'ar' autom. read file when changed outside of Vim 'autoread' 'ar' autom. read file when changed outside of Vim
'autowrite' 'aw' automatically write file if changed 'autowrite' 'aw' automatically write file if changed

View File

@@ -120,6 +120,16 @@ vim.o.ac = vim.o.autocomplete
vim.go.autocomplete = vim.o.autocomplete vim.go.autocomplete = vim.o.autocomplete
vim.go.ac = vim.go.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> --- 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 --- 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 --- type anything on the new line except <BS> or CTRL-D and then type

View File

@@ -1,7 +1,7 @@
" These commands create the option window. " These commands create the option window.
" "
" Maintainer: The Vim Project <https://github.com/vim/vim> " 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> " Former Maintainer: Bram Moolenaar <Bram@vim.org>
" If there already is an option window, jump to that one. " 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>OptionL("cpt")
call <SID>AddOption("autocomplete", gettext("automatic completion in insert mode")) call <SID>AddOption("autocomplete", gettext("automatic completion in insert mode"))
call <SID>BinOptionG("ac", &ac) 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>AddOption("completeopt", gettext("whether to use a popup menu for Insert mode completion"))
call <SID>OptionL("cot") call <SID>OptionL("cot")
call <SID>AddOption("completeitemalign", gettext("popup menu item align order")) call <SID>AddOption("completeitemalign", gettext("popup menu item align order"))

View File

@@ -2206,6 +2206,13 @@ static void ins_compl_new_leader(void)
ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1); ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
compl_used_match = false; 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) { if (compl_started) {
ins_compl_set_original_text(compl_leader.data, compl_leader.size); ins_compl_set_original_text(compl_leader.data, compl_leader.size);
if (is_cpt_func_refresh_always()) { if (is_cpt_func_refresh_always()) {
@@ -4811,10 +4818,10 @@ static int ins_compl_get_exp(pos_T *ini)
found_new_match = FAIL; found_new_match = FAIL;
} }
int i = -1; // total of matches, unknown int match_count = -1; // total of matches, unknown
if (found_new_match == FAIL if (found_new_match == FAIL
|| (ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())) { || (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) { 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); 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 /// 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. /// collecting, and halve the timeout.
static void check_elapsed_time(void) static void check_elapsed_time(void)
{ {
if (cpt_sources_array == NULL) { if (cpt_sources_array == NULL || cpt_sources_index < 0) {
return; return;
} }
@@ -6026,6 +6033,11 @@ int ins_complete(int c, bool enable_pum)
return FAIL; 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_win = curwin;
compl_curr_buf = curwin->w_buffer; compl_curr_buf = curwin->w_buffer;
compl_shown_match = compl_curr_match; 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 // 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, // 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 // 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 // ^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(); 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. // Show the popup menu, unless we got interrupted.
if (enable_pum && !compl_interrupted) { if (enable_pum && !compl_interrupted) {
show_pum(save_w_wrow, save_w_leftcol); show_pum(save_w_wrow, save_w_leftcol);

View File

@@ -301,6 +301,7 @@ EXTERN unsigned cia_flags; ///< order flags of 'completeitemalign'
EXTERN char *p_cot; ///< 'completeopt' EXTERN char *p_cot; ///< 'completeopt'
EXTERN unsigned cot_flags; ///< flags from 'completeopt' EXTERN unsigned cot_flags; ///< flags from 'completeopt'
EXTERN int p_ac; ///< 'autocomplete' EXTERN int p_ac; ///< 'autocomplete'
EXTERN OptInt p_acl; ///< 'autocompletedelay'
#ifdef BACKSLASH_IN_FILENAME #ifdef BACKSLASH_IN_FILENAME
EXTERN char *p_csl; ///< 'completeslash' EXTERN char *p_csl; ///< 'completeslash'
#endif #endif

View File

@@ -241,6 +241,20 @@ local options = {
type = 'boolean', type = 'boolean',
varname = 'p_ac', 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', abbreviation = 'ai',
defaults = true, defaults = true,

View File

@@ -1409,4 +1409,98 @@ describe('completion', function()
{5:-- INSERT --} 4,6 All | {5:-- INSERT --} 4,6 All |
]]) ]])
end) 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) end)

View File

@@ -5573,4 +5573,38 @@ func Test_omni_start_invalid_col()
set omnifunc& complete& set omnifunc& complete&
endfunc 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 " vim: shiftwidth=2 sts=2 expandtab nofoldenable