0
0
mirror of https://github.com/vim/vim.git synced 2025-10-28 09:27:14 -04:00

patch 9.1.1672: completion: cannot add timeouts for 'cpt' sources

Problem:  completion: cannot add timeouts for 'cpt' sources
          (Evgeni Chasnovski)
Solution: Add the 'autocompletetimeout' and 'completetimeout' options
          (Girish Palya)

fixes: #17908
closes: #17967

Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Girish Palya
2025-08-23 16:20:03 +02:00
committed by Christian Brabandt
parent f66674cf42
commit 69a337edc1
14 changed files with 176 additions and 43 deletions

View File

@@ -203,6 +203,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 int compl_autocomplete = FALSE; // whether autocompletion is active
static int compl_timeout_ms = COMPL_INITIAL_TIMEOUT_MS;
static int compl_time_slice_expired = FALSE; // time budget exceeded for current source
@@ -216,6 +219,10 @@ static int compl_from_nonkeyword = FALSE; // completion started from non-ke
compl_timeout_ms /= 2; \
} 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
@@ -5393,7 +5400,7 @@ prepare_cpt_compl_funcs(void)
compl_source_start_timer(int source_idx UNUSED)
{
#ifdef ELAPSED_FUNC
if (compl_autocomplete && cpt_sources_array != NULL)
if (compl_autocomplete || p_cto > 0)
{
ELAPSED_INIT(cpt_sources_array[source_idx].compl_start_tv);
compl_time_slice_expired = FALSE;
@@ -5418,8 +5425,6 @@ 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
@@ -5439,6 +5444,8 @@ ins_compl_get_exp(pos_T *ini)
int type = ctrl_x_mode;
int may_advance_cpt_idx = FALSE;
pos_T start_pos = *ini;
int normal_mode_strict = FALSE;
long compl_timeout_save = 0;
if (!compl_started)
{
@@ -5476,15 +5483,18 @@ ins_compl_get_exp(pos_T *ini)
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))
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
? MAX(COMPL_INITIAL_TIMEOUT_MS, p_act)
: p_cto;
}
}
@@ -5517,14 +5527,18 @@ ins_compl_get_exp(pos_T *ini)
}
}
if (compl_autocomplete && type == CTRL_X_FUNCTION)
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;
}
// get the next set of completion matches
found_new_match = get_next_completion_match(type, &st, &start_pos);
@@ -5567,9 +5581,10 @@ 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
// mixing matches from different sources.
@@ -6136,9 +6151,6 @@ ins_compl_next(
check_elapsed_time(void)
{
#ifdef ELAPSED_FUNC
if (cpt_sources_array == NULL || cpt_sources_index < 0)
return;
elapsed_T *start_tv
= &cpt_sources_array[cpt_sources_index].compl_start_tv;
long elapsed_ms = ELAPSED_FUNC(*start_tv);
@@ -6211,8 +6223,14 @@ ins_compl_check_keys(int frequency, int in_compl_func)
}
}
}
else if (compl_autocomplete)
check_elapsed_time();
else
{
int 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 & COT_NOINSERT)
&& !compl_autocomplete)

View File

@@ -513,6 +513,7 @@ EXTERN long p_ch; // 'cmdheight'
EXTERN char_u *p_cms; // 'commentstring'
#endif
EXTERN char_u *p_cpt; // 'complete'
EXTERN long p_cto; // 'completetimeout'
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
EXTERN int p_confirm; // 'confirm'
#endif
@@ -524,6 +525,7 @@ EXTERN unsigned cia_flags; // order flags of 'completeitemalign'
EXTERN char_u *p_cot; // 'completeopt'
EXTERN unsigned cot_flags; // flags from 'completeopt'
EXTERN int p_ac; // 'autocomplete'
EXTERN long p_act; // 'autocompletetimeout'
EXTERN long p_acl; // 'autocompletedelay'
// Keep in sync with p_cot_values in optionstr.c
#define COT_MENU 0x001

View File

@@ -395,6 +395,9 @@ static struct vimoption options[] =
{"autocompletedelay", "acl", P_NUM|P_VI_DEF,
(char_u *)&p_acl, PV_NONE, NULL, NULL,
{(char_u *)0L, (char_u *)0L} SCTX_INIT},
{"autocompletetimeout", "act", P_NUM|P_VI_DEF,
(char_u *)&p_act, PV_NONE, NULL, NULL,
{(char_u *)80L, (char_u *)0L} SCTX_INIT},
#endif
{"autoindent", "ai", P_BOOL|P_VI_DEF,
(char_u *)&p_ai, PV_AI, NULL, NULL,
@@ -723,6 +726,9 @@ static struct vimoption options[] =
{(char_u *)0L, (char_u *)0L}
#endif
SCTX_INIT},
{"completetimeout", "cto", P_NUM|P_VI_DEF,
(char_u *)&p_cto, PV_NONE, NULL, NULL,
{(char_u *)0L, (char_u *)0L} SCTX_INIT},
{"concealcursor","cocu", P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF|P_FLAGLIST,
#ifdef FEAT_CONCEAL
(char_u *)VAR_WIN, PV_COCU, did_set_concealcursor, expand_set_concealcursor,

8
src/po/vim.pot generated
View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Vim\n"
"Report-Msgid-Bugs-To: vim-dev@vim.org\n"
"POT-Creation-Date: 2025-08-18 21:30+0200\n"
"POT-Creation-Date: 2025-08-23 16:16+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -9952,6 +9952,12 @@ msgstr ""
msgid "automatic completion in insert mode"
msgstr ""
msgid "initial decay timeout for 'autocomplete' algorithm"
msgstr ""
msgid "initial decay timeout for CTRL-N and CTRL-P completion"
msgstr ""
msgid "delay in msec before menu appears after typing"
msgstr ""

View File

@@ -22,8 +22,10 @@ int diffopt_horizontal(void);
int diffopt_hiddenoff(void);
int diffopt_closeoff(void);
void diff_update_line(linenr_T lnum);
#ifdef FEAT_DIFF
int diff_change_parse(diffline_T *diffline, diffline_change_T *change, int *change_start, int *change_end);
int diff_find_change(win_T *wp, linenr_T lnum, diffline_T *diffline);
#endif
int diff_infold(win_T *wp, linenr_T lnum);
void nv_diffgetput(int put, long count);
void ex_diffgetput(exarg_T *eap);

View File

@@ -5460,6 +5460,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 test_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 test_override("char_avail", 0)
iunmap <F2>
delfunc OmniFunc
endfunc
func Test_autocompletedelay()
CheckScreendump

View File

@@ -724,6 +724,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1672,
/**/
1671,
/**/