From c51d1cc578da32939ee458e98d78b180e1235b4b Mon Sep 17 00:00:00 2001 From: Girish Palya Date: Sat, 4 Oct 2025 10:55:40 +0000 Subject: [PATCH] patch 9.1.1825: completion: flicker when LSP server is slow Problem: completion: flicker when LSP server is slow Solution: reinsert leader text before invoking user function (Girish Palya) Reference: https://github.com/girishji/vimcomplete/issues/101#issuecomment-3343063245 In insert-mode completion, the leader text is temporarily removed while searching for candidates. When the LSP server responds slowly, the client may call `:sleep` to wait, which triggers `out_flush()`. This causes the deleted text to disappear briefly before being redrawn, resulting in visible flicker. This commit reinserts the leader text before invoking the user function, and removes it again afterward to eliminate flicker. closes: #18468 Signed-off-by: Girish Palya Signed-off-by: Christian Brabandt --- src/insexpand.c | 15 ++++++++++++--- src/testdir/test_ins_complete.vim | 27 +++++++++++++-------------- src/version.c | 2 ++ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/insexpand.c b/src/insexpand.c index c231aa41b8..38b2095632 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -7690,17 +7690,26 @@ remove_old_matches(void) static void get_cpt_func_completion_matches(callback_T *cb UNUSED) { - int startcol = cpt_sources_array[cpt_sources_index].cs_startcol; + cpt_source_T *cpt_src = &cpt_sources_array[cpt_sources_index]; + int startcol = cpt_src->cs_startcol; if (startcol == -2 || startcol == -3) return; if (set_compl_globals(startcol, curwin->w_cursor.col, TRUE) == OK) { + // Insert the leader string (previously removed) before expansion. + // This prevents flicker when `func` (e.g. an LSP client) is slow and + // calls 'sleep', which triggers out_flush(). + if (!cpt_src->cs_refresh_always) + ins_compl_insert_bytes(ins_compl_leader(), -1); + expand_by_function(0, cpt_compl_pattern.string, cb); - cpt_sources_array[cpt_sources_index].cs_refresh_always = - compl_opt_refresh_always; + if (!cpt_src->cs_refresh_always) + ins_compl_delete(); + + cpt_src->cs_refresh_always = compl_opt_refresh_always; compl_opt_refresh_always = FALSE; } } diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index b10e07433a..c2465030dd 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -586,15 +586,19 @@ func Test_completefunc_info() set completefunc& endfunc -func Test_cpt_func_cursorcol() +" For ^N completion, `completefunc` receives the same leader string in both the +" 'info' and 'expansion' phases (the leader is not removed before expansion). +" This avoids flicker when `completefunc` (e.g. an LSP client) is slow and calls +" 'sleep', which triggers out_flush(). +func Test_completefunc_leader() func CptColTest(findstart, query) if a:findstart - call assert_equal(b:info_compl_line, getline(1)) - call assert_equal(b:info_cursor_col, col('.')) + call assert_equal(b:compl_line, getline(1)) + call assert_equal(b:cursor_col, col('.')) return col('.') endif - call assert_equal(b:expn_compl_line, getline(1)) - call assert_equal(b:expn_cursor_col, col('.')) + call assert_equal(b:compl_line, getline(1)) + call assert_equal(b:cursor_col, col('.')) return v:none endfunc @@ -602,17 +606,13 @@ func Test_cpt_func_cursorcol() new " Replace mode - let b:info_compl_line = "foo barxyz" - let b:expn_compl_line = "foo barbaz" - let b:info_cursor_col = 10 - let b:expn_cursor_col = 5 + let b:compl_line = "foo barxyz" + let b:cursor_col = 10 call feedkeys("ifoo barbaz\2hRxy\", "tx") " Insert mode - let b:info_compl_line = "foo bar" - let b:expn_compl_line = "foo " - let b:info_cursor_col = 8 - let b:expn_cursor_col = 5 + let b:compl_line = "foo bar" + let b:cursor_col = 8 call feedkeys("Sfoo bar\", "tx") set completeopt=longest @@ -4128,7 +4128,6 @@ func Test_autocomplete_completeopt_preinsert() endfunc set omnifunc=Omni_test complete+=o set completeopt=preinsert autocomplete - " set completeopt=preinsert,menuone autocomplete func GetLine() let g:line = getline('.') let g:col = col('.') diff --git a/src/version.c b/src/version.c index 48241d3ddc..01a0dd08a5 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1825, /**/ 1824, /**/