Merge pull request #35427 from zeertzjq/vim-9.1.1638

vim-patch: 'autocompletedelay'
This commit is contained in:
zeertzjq
2025-08-23 21:24:17 +08:00
committed by GitHub
12 changed files with 288 additions and 23 deletions

View File

@@ -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

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
|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

View File

@@ -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

View File

@@ -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

View File

@@ -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"))

View File

@@ -849,10 +849,11 @@ static int insert_handle_key(InsertState *s)
auto_format(false, true);
if (s->did_backspace && p_ac && !char_avail() && curwin->w_cursor.col > 0) {
s->c = char_before_cursor();
if (ins_compl_setup_autocompl(s->c)) {
if (vim_isprintc(s->c)) {
redraw_later(curwin, UPD_VALID);
update_screen(); // Show char deletion immediately
ui_flush();
ins_compl_enable_autocomplete();
insert_do_complete(s); // Trigger autocompletion
return 1;
}
@@ -1233,10 +1234,11 @@ normalchar:
// closed fold.
foldOpenCursor();
// Trigger autocompletion
if (p_ac && !char_avail() && ins_compl_setup_autocompl(s->c)) {
if (p_ac && !char_avail() && vim_isprintc(s->c)) {
redraw_later(curwin, UPD_VALID);
update_screen(); // Show character immediately
ui_flush();
ins_compl_enable_autocomplete();
insert_do_complete(s);
}

View File

@@ -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;
}
@@ -6015,6 +6022,10 @@ static void ins_compl_show_statusmsg(void)
/// Returns OK if completion was done, FAIL if something failed.
int ins_complete(int c, bool enable_pum)
{
const bool disable_ac_delay = compl_started && ctrl_x_mode_normal()
&& (c == Ctrl_N || c == Ctrl_P || c == Ctrl_R
|| ins_compl_pum_key(c));
compl_direction = ins_compl_key2dir(c);
int insert_match = ins_compl_use_match(c);
@@ -6026,6 +6037,10 @@ int ins_complete(int c, bool enable_pum)
return FAIL;
}
uint64_t compl_start_tv = 0; ///< Time when match collection starts
if (compl_autocomplete && p_acl > 0 && !disable_ac_delay) {
compl_start_tv = os_hrtime();
}
compl_curr_win = curwin;
compl_curr_buf = curwin->w_buffer;
compl_shown_match = compl_curr_match;
@@ -6050,7 +6065,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 +6090,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 && !disable_ac_delay && !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);
@@ -6084,15 +6116,10 @@ int ins_complete(int c, bool enable_pum)
return OK;
}
/// Returns true if the given character 'c' can be used to trigger
/// autocompletion.
bool ins_compl_setup_autocompl(int c)
/// Enable autocompletion
void ins_compl_enable_autocomplete(void)
{
if (vim_isprintc(c)) {
compl_autocomplete = true;
return true;
}
return false;
}
/// Remove (if needed) and show the popup menu

View File

@@ -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

View File

@@ -241,6 +241,20 @@ local options = {
type = 'boolean',
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,

View File

@@ -1409,4 +1409,157 @@ 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)
screen:expect([[
^foo |
foobar |
foobarbaz |
{1:~ }|*6
|
]])
screen.timeout = 200
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} |
]])
-- After the menu is open, ^N/^P and Up/Down should not delay
feed('<Esc>')
command('set completeopt=menu')
feed('Sf')
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
vim.uv.sleep(500)
feed('<C-N>')
screen:expect([[
foo |
foobar |
foobarbaz |
foobarbaz^ |
{12:foobarbaz }{1: }|
{4:foobar }{1: }|
{4:foo }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<Down>')
screen:expect([[
foo |
foobar |
foobarbaz |
foobarbaz^ |
{4:foobarbaz }{1: }|
{12:foobar }{1: }|
{4:foo }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
-- When menu is not open Up/Down moves cursor to different line
feed('<Esc>Sf')
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
feed('<Down>')
screen:expect_unchanged()
feed('<esc>')
end)
end)

View File

@@ -5573,4 +5573,52 @@ 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', {})
" After the menu is open, ^N/^P and Up/Down should not delay
call term_sendkeys(buf, "\<Esc>:set completeopt=menu noruler\<CR>")
call term_sendkeys(buf, "\<Esc>Sf")
sleep 500ms
call term_sendkeys(buf, "\<C-N>")
call VerifyScreenDump(buf, 'Test_autocompletedelay_8', {})
call term_sendkeys(buf, "\<Down>")
call VerifyScreenDump(buf, 'Test_autocompletedelay_9', {})
" When menu is not open Up/Down moves cursor to different line
call term_sendkeys(buf, "\<Esc>Sf")
call term_sendkeys(buf, "\<Down>")
call VerifyScreenDump(buf, 'Test_autocompletedelay_10', {})
call term_sendkeys(buf, "\<esc>")
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab nofoldenable