patch 9.1.0503: cannot use fuzzy keyword completion

Problem:  cannot use fuzzy keyword completion
          (Maxim Kim)
Solution: add the "fuzzycollect" value for the 'completeopt'
          setting, to gather matches using fuzzy logic (glepnir)

fixes: #14912
closes: #14976

Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/insexpand.c b/src/insexpand.c
index c673df9..78fea51 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -4100,8 +4100,7 @@
     int		is_backward = compl_shows_dir_backward();
     compl_T	*comp = NULL;
 
-    if (compl_match_array == NULL ||
-	    (is_forward && compl_selected_item == compl_match_arraysize - 1)
+    if ((is_forward && compl_selected_item == compl_match_arraysize - 1)
 	    || (is_backward && compl_selected_item == 0))
 	return compl_first_match;
 
@@ -4508,6 +4507,11 @@
     static int
 get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
 {
+    int		i;
+    int		char_len;
+    size_t	fuzzy_len;
+    char_u	*fuzzy_pattern;
+
     if ((compl_cont_status & CONT_SOL) || ctrl_x_mode_path_defines())
     {
 	if (!compl_status_adding())
@@ -4622,6 +4626,38 @@
 
     compl_patternlen = STRLEN(compl_pattern);
 
+    if ((get_cot_flags() & COT_FUZZYCOLLECT) != 0)
+    {
+	// Adjust size to avoid buffer overflow
+	fuzzy_len = (size_t)compl_length * 5 + 10;
+	// Allocate enough space
+	fuzzy_pattern = alloc(fuzzy_len);
+	if (fuzzy_pattern == NULL)
+	{
+	    compl_patternlen = 0;
+	    return FAIL;
+	}
+	// Use 'very magic' mode for simpler syntax
+	STRCPY(fuzzy_pattern, "\\v");
+	i = 2; // Start from 2 to skip "\\v"
+	while (i < compl_length + 2)
+	{
+	    // Append "\\k*" before each character
+	    STRNCAT(fuzzy_pattern, "\\k*", fuzzy_len - STRLEN(fuzzy_pattern) - 1);
+	    // Get length of current multi-byte character
+	    char_len = mb_ptr2len(compl_pattern + i);
+	    // Concatenate the character safely
+	    STRNCAT(fuzzy_pattern, compl_pattern + i, char_len);
+	    // Move to the next character
+	    i += char_len;
+	}
+	// Append "\\k*" at the end to match any characters after the pattern
+	STRNCAT(fuzzy_pattern, "\\k*", fuzzy_len - STRLEN(fuzzy_pattern) - 1);
+	vim_free(compl_pattern);
+	compl_pattern = fuzzy_pattern;
+	compl_patternlen = STRLEN(compl_pattern);
+    }
+
     return OK;
 }