patch 9.1.0598: fuzzy completion does not work with default completion

Problem:  fuzzy completion does not work with default completion
Solution: Make it work (glepnir)

closes: #15193

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 21b53d1..fa4ac7d 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -203,6 +203,8 @@
 
 static int	  compl_selected_item = -1;
 
+static int	  *compl_fuzzy_scores;
+
 static int ins_compl_add(char_u *str, int len, char_u *fname, char_u **cptext, typval_T *user_data, int cdir, int flags, int adup);
 static void ins_compl_longest_match(compl_T *match);
 static void ins_compl_del_pum(void);
@@ -3322,7 +3324,8 @@
 process_next_cpt_value(
 	ins_compl_next_state_T *st,
 	int		*compl_type_arg,
-	pos_T		*start_match_pos)
+	pos_T		*start_match_pos,
+	int		in_fuzzy)
 {
     int	    compl_type = -1;
     int	    status = INS_COMPL_CPT_OK;
@@ -3338,7 +3341,7 @@
 	st->first_match_pos = *start_match_pos;
 	// Move the cursor back one character so that ^N can match the
 	// word immediately after the cursor.
-	if (ctrl_x_mode_normal() && dec(&st->first_match_pos) < 0)
+	if (ctrl_x_mode_normal() && (!in_fuzzy && dec(&st->first_match_pos) < 0))
 	{
 	    // Move the cursor to after the last character in the
 	    // buffer, so that word at start of buffer is found
@@ -3506,6 +3509,18 @@
 }
 
 /*
+ * Compare function for qsort
+ */
+static int compare_scores(const void *a, const void *b)
+{
+    int idx_a = *(const int *)a;
+    int idx_b = *(const int *)b;
+    int score_a = compl_fuzzy_scores[idx_a];
+    int score_b = compl_fuzzy_scores[idx_b];
+    return (score_a > score_b) ? -1 : (score_a < score_b) ? 1 : 0;
+}
+
+/*
  * Get the next set of filename matching "compl_pattern".
  */
     static void
@@ -3513,6 +3528,21 @@
 {
     char_u	**matches;
     int		num_matches;
+    char_u	*ptr;
+    garray_T	fuzzy_indices;
+    int		i;
+    int		score;
+    char_u	*leader = ins_compl_leader();
+    int		leader_len = STRLEN(leader);
+    int		in_fuzzy = ((get_cot_flags() & COT_FUZZY) != 0 && leader_len > 0);
+    char_u	**sorted_matches;
+    int		*fuzzy_indices_data;
+
+    if (in_fuzzy)
+    {
+	vim_free(compl_pattern);
+	compl_pattern = vim_strsave((char_u *)"*");
+    }
 
     if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
 		EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK)
@@ -3523,12 +3553,9 @@
 #ifdef BACKSLASH_IN_FILENAME
     if (curbuf->b_p_csl[0] != NUL)
     {
-	int	    i;
-
 	for (i = 0; i < num_matches; ++i)
 	{
-	    char_u	*ptr = matches[i];
-
+	    ptr = matches[i];
 	    while (*ptr != NUL)
 	    {
 		if (curbuf->b_p_csl[0] == 's' && *ptr == '\\')
@@ -3540,6 +3567,41 @@
 	}
     }
 #endif
+
+    if (in_fuzzy)
+    {
+	ga_init2(&fuzzy_indices, sizeof(int), 10);
+	compl_fuzzy_scores = (int *)alloc(sizeof(int) * num_matches);
+
+	for (i = 0; i < num_matches; i++)
+	{
+	    ptr = matches[i];
+	    score = fuzzy_match_str(ptr, leader);
+	    if (score > 0)
+	    {
+		if (ga_grow(&fuzzy_indices, 1) == OK)
+		{
+		    ((int *)fuzzy_indices.ga_data)[fuzzy_indices.ga_len] = i;
+		    compl_fuzzy_scores[i] = score;
+		    fuzzy_indices.ga_len++;
+		}
+	    }
+	}
+
+	fuzzy_indices_data = (int *)fuzzy_indices.ga_data;
+	qsort(fuzzy_indices_data, fuzzy_indices.ga_len, sizeof(int), compare_scores);
+
+	sorted_matches = (char_u **)alloc(sizeof(char_u *) * fuzzy_indices.ga_len);
+	for (i = 0; i < fuzzy_indices.ga_len; ++i)
+	    sorted_matches[i] = vim_strsave(matches[fuzzy_indices_data[i]]);
+
+	FreeWild(num_matches, matches);
+	matches = sorted_matches;
+	num_matches = fuzzy_indices.ga_len;
+	vim_free(compl_fuzzy_scores);
+	ga_clear(&fuzzy_indices);
+    }
+
     ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
 }
 
@@ -3687,8 +3749,10 @@
     int		save_p_scs;
     int		save_p_ws;
     int		looped_around = FALSE;
-    char_u	*ptr;
-    int		len;
+    char_u	*ptr = NULL;
+    int		len = 0;
+    int		in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0 && compl_length > 0;
+    char_u	*leader = ins_compl_leader();
 
     // If 'infercase' is set, don't use 'smartcase' here
     save_p_scs = p_scs;
@@ -3702,7 +3766,7 @@
     save_p_ws = p_ws;
     if (st->ins_buf != curbuf)
 	p_ws = FALSE;
-    else if (*st->e_cpt == '.')
+    else if (*st->e_cpt == '.' && !in_fuzzy)
 	p_ws = TRUE;
     looped_around = FALSE;
     for (;;)
@@ -3713,9 +3777,13 @@
 
 	// ctrl_x_mode_line_or_eval() || word-wise search that
 	// has added a word that was at the beginning of the line
-	if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL))
+	if ((ctrl_x_mode_whole_line() && !in_fuzzy) || ctrl_x_mode_eval() || (compl_cont_status & CONT_SOL))
 	    found_new_match = search_for_exact_line(st->ins_buf,
 			    st->cur_match_pos, compl_direction, compl_pattern);
+	else if (in_fuzzy)
+	     found_new_match = search_for_fuzzy_match(st->ins_buf,
+			    st->cur_match_pos, leader, compl_direction,
+			    start_pos, &len, &ptr, ctrl_x_mode_whole_line());
 	else
 	    found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
 				NULL, compl_direction, compl_pattern, compl_patternlen,
@@ -3764,8 +3832,9 @@
 		&& start_pos->col  == st->cur_match_pos->col)
 	    continue;
 
-	ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
-							   &len, &cont_s_ipos);
+	if (!in_fuzzy)
+	    ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
+							       &len, &cont_s_ipos);
 	if (ptr == NULL)
 	    continue;
 
@@ -3864,6 +3933,7 @@
     int		i;
     int		found_new_match;
     int		type = ctrl_x_mode;
+    int		in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0;
 
     if (!compl_started)
     {
@@ -3889,8 +3959,11 @@
 	st.ins_buf = curbuf;  // In case the buffer was wiped out.
 
     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 (in_fuzzy)
+	st.cur_match_pos = (compl_dir_forward())
+				    ? &st.last_match_pos : &st.first_match_pos;
+    else
+	st.cur_match_pos = &st.last_match_pos;
 
     // For ^N/^P loop over all the flags/windows/buffers in 'complete'.
     for (;;)
@@ -3904,7 +3977,7 @@
 	if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
 					&& (!compl_started || st.found_all))
 	{
-	    int status = process_next_cpt_value(&st, &type, ini);
+	    int status = process_next_cpt_value(&st, &type, ini, in_fuzzy);
 
 	    if (status == INS_COMPL_CPT_END)
 		break;