mirror of
https://github.com/neovim/neovim.git
synced 2025-09-05 19:08:15 +00:00
vim-patch:9.1.1672: completion: cannot add timeouts for 'cpt' sources (#35447)
Problem: completion: cannot add timeouts for 'cpt' sources
(Evgeni Chasnovski)
Solution: Add the 'autocompletetimeout' and 'completetimeout' options
(Girish Palya)
fixes: vim/vim#17908
closes: vim/vim#17967
69a337edc1
Co-authored-by: Girish Palya <girishji@gmail.com>
This commit is contained in:
@@ -1117,18 +1117,20 @@ 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' and 'autocompletedelay'.
|
||||
The menu items are collected from the sources listed in the 'complete' option.
|
||||
but triggered automatically. See 'autocomplete'. The menu items are collected
|
||||
from the sources listed in the 'complete' option, in order.
|
||||
|
||||
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
|
||||
(higher priority), but every source is guaranteed a time slice, however small.
|
||||
A decaying timeout keeps Vim responsive. Sources earlier in the 'complete'
|
||||
list get more time (higher priority), but all sources receive at least a small
|
||||
time slice.
|
||||
|
||||
This mode is fully compatible with other completion modes. You can invoke
|
||||
any of them at any time by typing |CTRL-X|, which temporarily suspends
|
||||
autocompletion. To use |i_CTRL-N| specifically, press |CTRL-E| first to
|
||||
dismiss the popup menu (see |complete_CTRL-E|).
|
||||
|
||||
See also 'autocomplete', 'autocompletetimeout' and 'autocompletedelay'.
|
||||
|
||||
To get LSP-driven auto-completion, see |lsp-completion|.
|
||||
|
||||
|
||||
|
@@ -754,6 +754,20 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
typing. If you prefer it not to open too quickly, set this value
|
||||
slightly above your typing speed. See |ins-autocompletion|.
|
||||
|
||||
*'autocompletetimeout'* *'act'*
|
||||
'autocompletetimeout' 'act' number (default 80)
|
||||
global
|
||||
Initial timeout (in milliseconds) for the decaying time-sliced
|
||||
completion algorithm. Starts at this value, halves for each slower
|
||||
source until a minimum is reached. All sources run, but slower ones
|
||||
are quickly de-prioritized. The default is tuned so the popup menu
|
||||
opens within ~200ms even with multiple slow sources on a slow system.
|
||||
Changing this value is rarely needed. Only 80 or higher is valid.
|
||||
Special case: when 'complete' contains "F" or "o" (function sources),
|
||||
a longer timeout is used, allowing up to ~1s for sources such as LSP
|
||||
servers that may sometimes take longer (e.g., while loading modules).
|
||||
See |ins-autocompletion|.
|
||||
|
||||
*'autoindent'* *'ai'* *'noautoindent'* *'noai'*
|
||||
'autoindent' 'ai' boolean (default on)
|
||||
local to buffer
|
||||
@@ -1688,6 +1702,12 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
For Insert mode completion the buffer-local value is used. For
|
||||
command line completion the global value is used.
|
||||
|
||||
*'completetimeout'* *'cto'*
|
||||
'completetimeout' 'cto' number (default 0)
|
||||
global
|
||||
Like 'autocompletetimeout', but applies to |i_CTRL-N| and |i_CTRL-P|
|
||||
completion. Value of 0 disables the timeout; positive values allowed.
|
||||
|
||||
*'concealcursor'* *'cocu'*
|
||||
'concealcursor' 'cocu' string (default "")
|
||||
local to window
|
||||
|
@@ -627,6 +627,7 @@ Short explanation of each option: *option-list*
|
||||
'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
|
||||
'autocompletetimeout' 'act' initial decay timeout for autocompletion algorithm
|
||||
'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
|
||||
@@ -670,6 +671,7 @@ Short explanation of each option: *option-list*
|
||||
'completefunc' 'cfu' function to be used for Insert mode completion
|
||||
'completeopt' 'cot' options for Insert mode completion
|
||||
'completeslash' 'csl' like 'shellslash' for completion
|
||||
'completetimeout' 'cto' initial decay timeout for CTRL-N and CTRL-P
|
||||
'concealcursor' 'cocu' whether concealable text is hidden in cursor line
|
||||
'conceallevel' 'cole' whether concealable text is shown or hidden
|
||||
'confirm' 'cf' ask what to do about unsaved/read-only files
|
||||
|
26
runtime/lua/vim/_meta/options.lua
generated
26
runtime/lua/vim/_meta/options.lua
generated
@@ -130,6 +130,23 @@ vim.o.acl = vim.o.autocompletedelay
|
||||
vim.go.autocompletedelay = vim.o.autocompletedelay
|
||||
vim.go.acl = vim.go.autocompletedelay
|
||||
|
||||
--- Initial timeout (in milliseconds) for the decaying time-sliced
|
||||
--- completion algorithm. Starts at this value, halves for each slower
|
||||
--- source until a minimum is reached. All sources run, but slower ones
|
||||
--- are quickly de-prioritized. The default is tuned so the popup menu
|
||||
--- opens within ~200ms even with multiple slow sources on a slow system.
|
||||
--- Changing this value is rarely needed. Only 80 or higher is valid.
|
||||
--- Special case: when 'complete' contains "F" or "o" (function sources),
|
||||
--- a longer timeout is used, allowing up to ~1s for sources such as LSP
|
||||
--- servers that may sometimes take longer (e.g., while loading modules).
|
||||
--- See `ins-autocompletion`.
|
||||
---
|
||||
--- @type integer
|
||||
vim.o.autocompletetimeout = 80
|
||||
vim.o.act = vim.o.autocompletetimeout
|
||||
vim.go.autocompletetimeout = vim.o.autocompletetimeout
|
||||
vim.go.act = vim.go.autocompletetimeout
|
||||
|
||||
--- 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
|
||||
@@ -1239,6 +1256,15 @@ vim.o.csl = vim.o.completeslash
|
||||
vim.bo.completeslash = vim.o.completeslash
|
||||
vim.bo.csl = vim.bo.completeslash
|
||||
|
||||
--- Like 'autocompletetimeout', but applies to `i_CTRL-N` and `i_CTRL-P`
|
||||
--- completion. Value of 0 disables the timeout; positive values allowed.
|
||||
---
|
||||
--- @type integer
|
||||
vim.o.completetimeout = 0
|
||||
vim.o.cto = vim.o.completetimeout
|
||||
vim.go.completetimeout = vim.o.completetimeout
|
||||
vim.go.cto = vim.go.completetimeout
|
||||
|
||||
--- Sets the modes in which text in the cursor line can also be concealed.
|
||||
--- When the current mode is listed then concealing happens just like in
|
||||
--- other lines.
|
||||
|
@@ -1,7 +1,7 @@
|
||||
" These commands create the option window.
|
||||
"
|
||||
" Maintainer: The Vim Project <https://github.com/vim/vim>
|
||||
" Last Change: 2025 Aug 16
|
||||
" Last Change: 2025 Aug 23
|
||||
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
|
||||
|
||||
" If there already is an option window, jump to that one.
|
||||
@@ -737,6 +737,10 @@ 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("autocompletetimeout", gettext("initial decay timeout for 'autocomplete' algorithm"))
|
||||
call append("$", " \tset act=" . &act)
|
||||
call <SID>AddOption("completetimeout", gettext("initial decay timeout for CTRL-N and CTRL-P completion"))
|
||||
call append("$", " \tset cto=" . &cto)
|
||||
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"))
|
||||
|
@@ -289,6 +289,9 @@ static buf_T *compl_curr_buf = NULL; ///< buf where completion is active
|
||||
// if the current source exceeds its timeout, it is interrupted and the next
|
||||
// begins with half the time. A small minimum timeout ensures every source
|
||||
// gets at least a brief chance.
|
||||
// Special case: when 'complete' contains "F" or "o" (function sources), a
|
||||
// longer fixed timeout is used (COMPL_FUNC_TIMEOUT_MS or
|
||||
// COMPL_FUNC_TIMEOUT_NON_KW_MS). - girish
|
||||
static bool compl_autocomplete = false; ///< whether autocompletion is active
|
||||
static uint64_t compl_timeout_ms = COMPL_INITIAL_TIMEOUT_MS;
|
||||
static bool compl_time_slice_expired = false; ///< time budget exceeded for current source
|
||||
@@ -303,6 +306,10 @@ static bool compl_from_nonkeyword = false; ///< completion started from non-
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Timeout values for F{func}, F and o values in 'complete'
|
||||
#define COMPL_FUNC_TIMEOUT_MS 300
|
||||
#define COMPL_FUNC_TIMEOUT_NON_KW_MS 1000
|
||||
|
||||
// List of flags for method of completion.
|
||||
static int compl_cont_status = 0;
|
||||
#define CONT_ADDING 1 ///< "normal" or "adding" expansion
|
||||
@@ -4640,7 +4647,7 @@ static void prepare_cpt_compl_funcs(void)
|
||||
/// Start the timer for the current completion source.
|
||||
static void compl_source_start_timer(int source_idx)
|
||||
{
|
||||
if (compl_autocomplete && cpt_sources_array != NULL) {
|
||||
if (compl_autocomplete || p_cto > 0) {
|
||||
cpt_sources_array[source_idx].compl_start_tv = os_hrtime();
|
||||
compl_time_slice_expired = false;
|
||||
}
|
||||
@@ -4657,8 +4664,6 @@ static int advance_cpt_sources_index_safe(void)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
#define COMPL_FUNC_TIMEOUT_MS 300
|
||||
#define COMPL_FUNC_TIMEOUT_NON_KW_MS 1000
|
||||
/// Get the next expansion(s), using "compl_pattern".
|
||||
/// The search starts at position "ini" in curbuf and in the direction
|
||||
/// compl_direction.
|
||||
@@ -4708,12 +4713,17 @@ static int ins_compl_get_exp(pos_T *ini)
|
||||
compl_old_match = compl_curr_match; // remember the last current match
|
||||
st.cur_match_pos = compl_dir_forward() ? &st.last_match_pos : &st.first_match_pos;
|
||||
|
||||
if (cpt_sources_array != NULL && ctrl_x_mode_normal() && !ctrl_x_mode_line_or_eval()
|
||||
&& !(compl_cont_status & CONT_LOCAL)) {
|
||||
bool normal_mode_strict = ctrl_x_mode_normal() && !ctrl_x_mode_line_or_eval()
|
||||
&& !(compl_cont_status & CONT_LOCAL)
|
||||
&& cpt_sources_array != NULL;
|
||||
if (normal_mode_strict) {
|
||||
cpt_sources_index = 0;
|
||||
if (compl_autocomplete) {
|
||||
if (compl_autocomplete || p_cto > 0) {
|
||||
compl_source_start_timer(0);
|
||||
compl_timeout_ms = COMPL_INITIAL_TIMEOUT_MS;
|
||||
compl_time_slice_expired = false;
|
||||
compl_timeout_ms = compl_autocomplete
|
||||
? (uint64_t)MAX(COMPL_INITIAL_TIMEOUT_MS, p_act)
|
||||
: (uint64_t)p_cto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4743,12 +4753,15 @@ static int ins_compl_get_exp(pos_T *ini)
|
||||
}
|
||||
}
|
||||
|
||||
if (compl_autocomplete && type == CTRL_X_FUNCTION) {
|
||||
uint64_t compl_timeout_save = 0;
|
||||
if (normal_mode_strict && type == CTRL_X_FUNCTION
|
||||
&& (compl_autocomplete || p_cto > 0)) {
|
||||
// LSP servers may sporadically take >1s to respond (e.g., while
|
||||
// loading modules), but other sources might already have matches.
|
||||
// To show results quickly use a short timeout for keyword
|
||||
// completion. Allow longer timeout for non-keyword completion
|
||||
// where only function based sources (e.g. LSP) are active.
|
||||
compl_timeout_save = compl_timeout_ms;
|
||||
compl_timeout_ms = compl_from_nonkeyword
|
||||
? COMPL_FUNC_TIMEOUT_NON_KW_MS : COMPL_FUNC_TIMEOUT_MS;
|
||||
}
|
||||
@@ -4796,9 +4809,10 @@ static int ins_compl_get_exp(pos_T *ini)
|
||||
compl_started = false;
|
||||
}
|
||||
|
||||
// Reset the timeout after collecting matches from function source
|
||||
if (compl_autocomplete && type == CTRL_X_FUNCTION) {
|
||||
compl_timeout_ms = COMPL_INITIAL_TIMEOUT_MS;
|
||||
// Restore the timeout after collecting matches from function source
|
||||
if (normal_mode_strict && type == CTRL_X_FUNCTION
|
||||
&& (compl_autocomplete || p_cto > 0)) {
|
||||
compl_timeout_ms = compl_timeout_save;
|
||||
}
|
||||
|
||||
// For `^P` completion, reset `compl_curr_match` to the head to avoid
|
||||
@@ -5295,10 +5309,6 @@ 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 || cpt_sources_index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t start_tv = cpt_sources_array[cpt_sources_index].compl_start_tv;
|
||||
uint64_t elapsed_ms = (os_hrtime() - start_tv) / 1000000;
|
||||
|
||||
@@ -5355,8 +5365,13 @@ void ins_compl_check_keys(int frequency, bool in_compl_func)
|
||||
vungetc(c);
|
||||
}
|
||||
}
|
||||
} else if (compl_autocomplete) {
|
||||
check_elapsed_time();
|
||||
} else {
|
||||
bool normal_mode_strict = ctrl_x_mode_normal() && !ctrl_x_mode_line_or_eval()
|
||||
&& !(compl_cont_status & CONT_LOCAL)
|
||||
&& cpt_sources_array != NULL && cpt_sources_index >= 0;
|
||||
if (normal_mode_strict && (compl_autocomplete || p_cto > 0)) {
|
||||
check_elapsed_time();
|
||||
}
|
||||
}
|
||||
|
||||
if (compl_pending != 0 && !got_int && !(cot_flags & kOptCotFlagNoinsert)
|
||||
|
@@ -292,6 +292,7 @@ EXTERN OptInt p_cwh; ///< 'cmdwinheight'
|
||||
EXTERN OptInt p_ch; ///< 'cmdheight'
|
||||
EXTERN char *p_cms; ///< 'commentstring'
|
||||
EXTERN char *p_cpt; ///< 'complete'
|
||||
EXTERN OptInt p_cto; ///< 'completetimeout'
|
||||
EXTERN OptInt p_columns; ///< 'columns'
|
||||
EXTERN int p_confirm; ///< 'confirm'
|
||||
EXTERN char *p_cfc; ///< 'completefuzzycollect'
|
||||
@@ -301,6 +302,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_act; ///< 'autocompletetimeout'
|
||||
EXTERN OptInt p_acl; ///< 'autocompletedelay'
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
EXTERN char *p_csl; ///< 'completeslash'
|
||||
|
@@ -255,6 +255,27 @@ local options = {
|
||||
type = 'number',
|
||||
varname = 'p_acl',
|
||||
},
|
||||
{
|
||||
abbreviation = 'act',
|
||||
defaults = 80,
|
||||
desc = [=[
|
||||
Initial timeout (in milliseconds) for the decaying time-sliced
|
||||
completion algorithm. Starts at this value, halves for each slower
|
||||
source until a minimum is reached. All sources run, but slower ones
|
||||
are quickly de-prioritized. The default is tuned so the popup menu
|
||||
opens within ~200ms even with multiple slow sources on a slow system.
|
||||
Changing this value is rarely needed. Only 80 or higher is valid.
|
||||
Special case: when 'complete' contains "F" or "o" (function sources),
|
||||
a longer timeout is used, allowing up to ~1s for sources such as LSP
|
||||
servers that may sometimes take longer (e.g., while loading modules).
|
||||
See |ins-autocompletion|.
|
||||
]=],
|
||||
full_name = 'autocompletetimeout',
|
||||
scope = { 'global' },
|
||||
short_desc = N_('initial decay timeout for autocompletion algorithm'),
|
||||
type = 'number',
|
||||
varname = 'p_act',
|
||||
},
|
||||
{
|
||||
abbreviation = 'ai',
|
||||
defaults = true,
|
||||
@@ -1722,6 +1743,19 @@ local options = {
|
||||
type = 'string',
|
||||
varname = 'p_csl',
|
||||
},
|
||||
{
|
||||
abbreviation = 'cto',
|
||||
defaults = 0,
|
||||
desc = [=[
|
||||
Like 'autocompletetimeout', but applies to |i_CTRL-N| and |i_CTRL-P|
|
||||
completion. Value of 0 disables the timeout; positive values allowed.
|
||||
]=],
|
||||
full_name = 'completetimeout',
|
||||
scope = { 'global' },
|
||||
short_desc = N_('initial decay timeout for CTRL-N and CTRL-P'),
|
||||
type = 'number',
|
||||
varname = 'p_cto',
|
||||
},
|
||||
{
|
||||
abbreviation = 'cocu',
|
||||
cb = 'did_set_concealcursor',
|
||||
|
@@ -5573,6 +5573,68 @@ func Test_omni_start_invalid_col()
|
||||
set omnifunc& complete&
|
||||
endfunc
|
||||
|
||||
func Test_completetimeout_autocompletetimeout()
|
||||
func OmniFunc(findstart, base)
|
||||
if a:findstart
|
||||
return 1
|
||||
else
|
||||
return ['fooOmni']
|
||||
endif
|
||||
endfunc
|
||||
|
||||
set omnifunc=OmniFunc
|
||||
call Ntest_override("char_avail", 1)
|
||||
inoremap <F2> <Cmd>let b:matches = complete_info(["matches"]).matches<CR>
|
||||
|
||||
call setline(1, ['foobar', 'foobarbaz'])
|
||||
new
|
||||
call setline(1, ['foo', 'foobaz', ''])
|
||||
set complete=.,o,w
|
||||
call feedkeys("G", 'xt!')
|
||||
|
||||
set autocomplete
|
||||
for tt in [1, 80, 1000, -1, 0]
|
||||
exec $'set autocompletetimeout={tt}'
|
||||
call feedkeys("\<Esc>Sf\<F2>\<Esc>0", 'xt!')
|
||||
call assert_equal(['foobaz', 'foo', 'fooOmni', 'foobar', 'foobarbaz'], b:matches->mapnew('v:val.word'))
|
||||
endfor
|
||||
set autocomplete&
|
||||
|
||||
for tt in [80, 1000, -1, 0]
|
||||
exec $'set completetimeout={tt}'
|
||||
call feedkeys("\<Esc>Sf\<C-N>\<F2>\<Esc>0", 'xt!')
|
||||
call assert_equal(['foo', 'foobaz', 'fooOmni', 'foobar', 'foobarbaz'], b:matches->mapnew('v:val.word'))
|
||||
endfor
|
||||
|
||||
" Clock does not have fine granularity, so checking 'elapsed time' is only
|
||||
" approximate. We can only test that some type of timeout is enforced.
|
||||
call feedkeys("\<Esc>", 'xt!')
|
||||
call setline(1, map(range(60000), '"foo" . v:val'))
|
||||
set completetimeout=1
|
||||
call feedkeys("Gof\<C-N>\<F2>\<Esc>0", 'xt!')
|
||||
let match_count = len(b:matches->mapnew('v:val.word'))
|
||||
call assert_true(match_count < 2000)
|
||||
|
||||
set completetimeout=1000
|
||||
call feedkeys("\<Esc>Sf\<C-N>\<F2>\<Esc>0", 'xt!')
|
||||
let match_count = len(b:matches->mapnew('v:val.word'))
|
||||
call assert_true(match_count > 2000)
|
||||
|
||||
set autocomplete
|
||||
set autocompletetimeout=81
|
||||
call feedkeys("\<Esc>Sf\<F2>\<Esc>0", 'xt!')
|
||||
let match_count = len(b:matches->mapnew('v:val.word'))
|
||||
call assert_true(match_count < 50000)
|
||||
|
||||
call feedkeys("\<Esc>", 'xt!')
|
||||
set complete& omnifunc& autocomplete& autocompletetimeout& completetimeout&
|
||||
bwipe!
|
||||
%d
|
||||
call Ntest_override("char_avail", 0)
|
||||
iunmap <F2>
|
||||
delfunc OmniFunc
|
||||
endfunc
|
||||
|
||||
func Test_autocompletedelay()
|
||||
CheckScreendump
|
||||
|
||||
|
Reference in New Issue
Block a user