changeset 25513:b9e8c9ed792b v8.2.3293

patch 8.2.3293: finding completions may cause an endless loop Commit: https://github.com/vim/vim/commit/6a230c6b32695393785ae64b440ce5f023a22382 Author: Andy Gozas <andy@gozas.me> Date: Thu Aug 5 16:23:27 2021 +0200 patch 8.2.3293: finding completions may cause an endless loop Problem: Finding completions may cause an endless loop. Solution: Use a better way to check coming back where the search started. (Andy Gozas, closes #8672, closes #8671)
author Bram Moolenaar <Bram@vim.org>
date Thu, 05 Aug 2021 16:30:04 +0200
parents 0aa14b201e89
children bb806540f72e
files src/insexpand.c src/testdir/Make_all.mak src/testdir/test_ins_complete_no_halt.vim src/version.c
diffstat 4 files changed, 82 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -2712,6 +2712,8 @@ ins_compl_get_exp(pos_T *ini)
     char_u	*dict = NULL;
     int		dict_f = 0;
     int		set_match_pos;
+    pos_T	prev_pos = {0, 0, 0};
+    int		looped_around = FALSE;
 
     if (!compl_started)
     {
@@ -2964,6 +2966,7 @@ ins_compl_get_exp(pos_T *ini)
 		p_ws = FALSE;
 	    else if (*e_cpt == '.')
 		p_ws = TRUE;
+	    looped_around = FALSE;
 	    for (;;)
 	    {
 		int	cont_s_ipos = FALSE;
@@ -2991,8 +2994,31 @@ ins_compl_get_exp(pos_T *ini)
 		    set_match_pos = FALSE;
 		}
 		else if (first_match_pos.lnum == last_match_pos.lnum
-				 && first_match_pos.col == last_match_pos.col)
+                                && first_match_pos.col == last_match_pos.col)
+		{
 		    found_new_match = FAIL;
+		}
+		else if ((compl_direction == FORWARD)
+			&& (prev_pos.lnum > pos->lnum
+			    || (prev_pos.lnum == pos->lnum
+				&& prev_pos.col >= pos->col)))
+		{
+		    if (looped_around)
+			found_new_match = FAIL;
+		    else
+			looped_around = TRUE;
+		}
+		else if ((compl_direction != FORWARD)
+			&& (prev_pos.lnum < pos->lnum
+			    || (prev_pos.lnum == pos->lnum
+				&& prev_pos.col <= pos->col)))
+		{
+		    if (looped_around)
+			found_new_match = FAIL;
+		    else
+			looped_around = TRUE;
+		}
+		prev_pos = *pos;
 		if (found_new_match == FAIL)
 		{
 		    if (ins_buf == curbuf)
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -162,6 +162,7 @@ NEW_TESTS = \
 	test_increment_dbcs \
 	test_indent \
 	test_ins_complete \
+	test_ins_complete_no_halt \
 	test_interrupt \
 	test_job_fails \
 	test_join \
@@ -405,6 +406,7 @@ NEW_TESTS_RES = \
 	test_increment_dbcs.res \
 	test_indent.res \
 	test_ins_complete.res \
+	test_ins_complete_no_halt.res \
 	test_interrupt.res \
 	test_job_fails.res \
 	test_join.res \
new file mode 100644
--- /dev/null
+++ b/src/testdir/test_ins_complete_no_halt.vim
@@ -0,0 +1,51 @@
+" Test insert mode completion does not get stuck when looping around.
+" In a separate file to avoid the settings to leak to other test cases.
+
+set complete+=kspell
+set completeopt+=menu
+set completeopt+=menuone
+set completeopt+=noselect
+set completeopt+=noinsert
+let g:autocompletion = v:true
+
+func Test_ins_complete_no_halt()
+  function! OpenCompletion()
+    if pumvisible() && (g:autocompletion == v:true)
+      call feedkeys("\<C-e>\<C-n>", "i")
+      return
+    endif
+    if ((v:char >= 'a' && v:char <= 'z') || (v:char >= 'A' && v:char <= 'Z')) && (g:autocompletion == v:true)
+      call feedkeys("\<C-n>", "i")
+      redraw
+    endif
+  endfunction
+
+  autocmd InsertCharPre * noautocmd call OpenCompletion()
+
+  setlocal spell! spelllang=en_us
+
+  call feedkeys("iauto-complete-halt-test test test test test test test test test test test test test test test test test test test\<C-c>", "tx!")
+  call assert_equal(["auto-complete-halt-test test test test test test test test test test test test test test test test test test test"], getline(1, "$"))
+endfunc
+
+func Test_auto_complete_backwards_no_halt()
+  function! OpenCompletion()
+    if pumvisible() && (g:autocompletion == v:true)
+      call feedkeys("\<C-e>\<C-p>", "i")
+      return
+    endif
+    if ((v:char >= 'a' && v:char <= 'z') || (v:char >= 'A' && v:char <= 'Z')) && (g:autocompletion == v:true)
+      call feedkeys("\<C-p>", "i")
+      redraw
+    endif
+  endfunction
+
+  autocmd InsertCharPre * noautocmd call OpenCompletion()
+
+  setlocal spell! spelllang=en_us
+
+  call feedkeys("iauto-complete-halt-test test test test test test test test test test test test test test test test test test test\<C-c>", "tx!")
+  call assert_equal(["auto-complete-halt-test test test test test test test test test test test test test test test test test test test"], getline(1, "$"))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -756,6 +756,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3293,
+/**/
     3292,
 /**/
     3291,