1
0
forked from aniani/vim

patch 9.1.1441: completion: code can be improved

Problem:  completion: code can be improved
Solution: remove reposition_match() and use mergesort_list(),
          for fuzzy completion, sort by fuzzy score immediately after
          setting a new leader (Girish Palya)

closes: #17460

Co-authored-by: glepnir <glephunter@gmail.com>
Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Girish Palya 2025-06-08 16:20:06 +02:00 committed by Christian Brabandt
parent 80a7921a02
commit b8ee1cf56e
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
3 changed files with 99 additions and 125 deletions

View File

@ -818,77 +818,6 @@ is_nearest_active(void)
return (get_cot_flags() & (COT_NEAREST | COT_FUZZY)) == COT_NEAREST; return (get_cot_flags() & (COT_NEAREST | COT_FUZZY)) == COT_NEAREST;
} }
/*
* Repositions a match in the completion list based on its proximity score.
* If the match is at the head and has a higher score than the next node,
* or if it's in the middle/tail and has a lower score than the previous node,
* it is moved to the correct position while maintaining ascending order.
*/
static void
reposition_match(compl_T *match)
{
compl_T *insert_before = NULL;
compl_T *insert_after = NULL;
// Node is at head and score is too big
if (!match->cp_prev)
{
if (match->cp_next && match->cp_next->cp_score > 0 &&
match->cp_next->cp_score < match->cp_score)
{
// <c-p>: compl_first_match is at head and newly inserted node
compl_first_match = compl_curr_match = match->cp_next;
// Find the correct position in ascending order
insert_before = match->cp_next;
do
{
insert_after = insert_before;
insert_before = insert_before->cp_next;
} while (insert_before && insert_before->cp_score > 0 &&
insert_before->cp_score < match->cp_score);
}
else
return;
}
// Node is at tail or in the middle but score is too small
else
{
if (match->cp_prev->cp_score > 0 && match->cp_prev->cp_score > match->cp_score)
{
// <c-n>: compl_curr_match (and newly inserted match) is at tail
if (!match->cp_next)
compl_curr_match = compl_curr_match->cp_prev;
// Find the correct position in ascending order
insert_after = match->cp_prev;
do
{
insert_before = insert_after;
insert_after = insert_after->cp_prev;
} while (insert_after && insert_after->cp_score > 0 &&
insert_after->cp_score > match->cp_score);
}
else
return;
}
if (insert_after)
{
// Remove the match from its current position
if (match->cp_prev)
match->cp_prev->cp_next = match->cp_next;
else
compl_first_match = match->cp_next;
if (match->cp_next)
match->cp_next->cp_prev = match->cp_prev;
// Insert the match at the correct position
match->cp_next = insert_before;
match->cp_prev = insert_after;
insert_after->cp_next = match;
insert_before->cp_prev = match;
}
}
/* /*
* Add a match to the list of matches. The arguments are: * Add a match to the list of matches. The arguments are:
* str - text of the match to add * str - text of the match to add
@ -948,10 +877,7 @@ ins_compl_add(
|| match->cp_str.string[len] == NUL)) || match->cp_str.string[len] == NUL))
{ {
if (is_nearest_active() && score > 0 && score < match->cp_score) if (is_nearest_active() && score > 0 && score < match->cp_score)
{
match->cp_score = score; match->cp_score = score;
reposition_match(match);
}
return NOTDONE; return NOTDONE;
} }
match = match->cp_next; match = match->cp_next;
@ -1063,9 +989,6 @@ ins_compl_add(
compl_first_match = match; compl_first_match = match;
compl_curr_match = match; compl_curr_match = match;
if (is_nearest_active() && score > 0)
reposition_match(match);
// Find the longest common string if still doing that. // Find the longest common string if still doing that.
if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0 && !cfc_has_mode()) if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0 && !cfc_has_mode())
ins_compl_longest_match(match); ins_compl_longest_match(match);
@ -1476,6 +1399,69 @@ cp_compare_fuzzy(const void* a, const void* b)
return (score_b > score_a) ? 1 : (score_b < score_a) ? -1 : 0; return (score_b > score_a) ? 1 : (score_b < score_a) ? -1 : 0;
} }
static int
cp_compare_nearest(const void* a, const void* b)
{
int score_a = ((compl_T*)a)->cp_score;
int score_b = ((compl_T*)b)->cp_score;
if (score_a < 0 || score_b < 0)
return 0;
return (score_a > score_b) ? 1 : (score_a < score_b) ? -1 : 0;
}
/*
* Set fuzzy score.
*/
static void
set_fuzzy_score(void)
{
compl_T *compl = compl_first_match->cp_prev;
if (compl_leader.string != NULL && compl_leader.length > 0)
{
compl = compl_first_match;
do
{
compl->cp_score = fuzzy_match_str(compl->cp_str.string,
compl_leader.string);
compl = compl->cp_next;
} while (compl != NULL && !is_first_match(compl));
}
}
/*
* Sort completion matches, excluding the node that contains the leader.
*/
static void
sort_compl_match_list(int (*compare)(const void *, const void *))
{
compl_T *compl = compl_first_match->cp_prev;
if (!compl_first_match || is_first_match(compl_first_match->cp_next))
return;
ins_compl_make_linear();
if (compl_shows_dir_forward())
{
compl_first_match->cp_next->cp_prev = NULL;
compl_first_match->cp_next = mergesort_list(compl_first_match->cp_next,
cp_get_next, cp_set_next, cp_get_prev, cp_set_prev, compare);
compl_first_match->cp_next->cp_prev = compl_first_match;
}
else
{
compl->cp_prev->cp_next = NULL;
compl_first_match = mergesort_list(compl_first_match, cp_get_next,
cp_set_next, cp_get_prev, cp_set_prev, compare);
compl_T *tail = compl_first_match;
while (tail->cp_next != NULL)
tail = tail->cp_next;
tail->cp_next = compl;
compl->cp_prev = tail;
}
(void)ins_compl_make_cyclic();
}
/* /*
* Build a popup menu to show the completion matches. * Build a popup menu to show the completion matches.
* Returns the popup menu entry that should be selected. Returns -1 if nothing * Returns the popup menu entry that should be selected. Returns -1 if nothing
@ -1493,7 +1479,6 @@ ins_compl_build_pum(void)
unsigned int cur_cot_flags = get_cot_flags(); unsigned int cur_cot_flags = get_cot_flags();
int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0; int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
int fuzzy_filter = (cur_cot_flags & COT_FUZZY) != 0; int fuzzy_filter = (cur_cot_flags & COT_FUZZY) != 0;
int fuzzy_sort = fuzzy_filter && !(cur_cot_flags & COT_NOSORT);
compl_T *match_head = NULL; compl_T *match_head = NULL;
compl_T *match_tail = NULL; compl_T *match_tail = NULL;
compl_T *match_next = NULL; compl_T *match_next = NULL;
@ -1515,45 +1500,6 @@ ins_compl_build_pum(void)
compl_shown_match = compl_no_select ? compl_first_match compl_shown_match = compl_no_select ? compl_first_match
: compl_first_match->cp_next; : compl_first_match->cp_next;
// When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
// set the cp_score for later comparisons.
if (fuzzy_filter && compl_leader.string != NULL && compl_leader.length > 0)
{
compl = compl_first_match;
do
{
compl->cp_score = fuzzy_match_str(compl->cp_str.string, compl_leader.string);
compl = compl->cp_next;
} while (compl != NULL && !is_first_match(compl));
}
// Sort the linked list based on fuzzy score
if (fuzzy_sort && compl_leader.string != NULL && compl_leader.length > 0
&& !is_first_match(compl_first_match->cp_next))
{
compl = compl_first_match->cp_prev;
ins_compl_make_linear();
if (is_forward)
{
compl_first_match->cp_next->cp_prev = NULL;
compl_first_match->cp_next = mergesort_list(compl_first_match->cp_next, cp_get_next,
cp_set_next, cp_get_prev, cp_set_prev, cp_compare_fuzzy);
compl_first_match->cp_next->cp_prev = compl_first_match;
}
else
{
compl->cp_prev->cp_next = NULL;
compl_first_match = mergesort_list(compl_first_match, cp_get_next,
cp_set_next, cp_get_prev, cp_set_prev, cp_compare_fuzzy);
compl_T *tail = compl_first_match;
while (tail->cp_next != NULL)
tail = tail->cp_next;
tail->cp_next = compl;
compl->cp_prev = tail;
}
(void)ins_compl_make_cyclic();
}
if (is_cpt_completion) if (is_cpt_completion)
{ {
match_count = ALLOC_CLEAR_MULT(int, cpt_sources_count); match_count = ALLOC_CLEAR_MULT(int, cpt_sources_count);
@ -2402,6 +2348,23 @@ ins_compl_new_leader(void)
compl_restarting = FALSE; compl_restarting = FALSE;
} }
// When 'cot' contains "fuzzy" set the cp_score
if (get_cot_flags() & COT_FUZZY)
set_fuzzy_score();
// Sort the matches linked list based on fuzzy score
int cur_cot_flags = get_cot_flags();
if ((cur_cot_flags & COT_FUZZY) && !(cur_cot_flags & COT_NOSORT))
{
sort_compl_match_list(cp_compare_fuzzy);
if ((cur_cot_flags & COT_NOINSERT) && !(cur_cot_flags & COT_NOSELECT)
&& compl_first_match)
{
compl_shown_match = compl_first_match;
if (compl_shows_dir_forward())
compl_shown_match = compl_first_match->cp_next;
}
}
compl_enter_selects = !compl_used_match && compl_selected_item != -1; compl_enter_selects = !compl_used_match && compl_selected_item != -1;
// Show the popup menu with a different set of matches. // Show the popup menu with a different set of matches.
@ -5296,6 +5259,9 @@ ins_compl_get_exp(pos_T *ini)
} }
may_trigger_modechanged(); may_trigger_modechanged();
if (is_nearest_active())
sort_compl_match_list(cp_compare_nearest);
return i; return i;
} }
@ -5518,7 +5484,7 @@ ins_compl_show_filename(void)
* in 'compl_match_array'. * in 'compl_match_array'.
*/ */
static compl_T * static compl_T *
find_comp_when_cpt_sources(void) find_next_match_in_menu(void)
{ {
int is_forward = compl_shows_dir_forward(); int is_forward = compl_shows_dir_forward();
compl_T *match = compl_shown_match; compl_T *match = compl_shown_match;
@ -5555,14 +5521,13 @@ find_next_completion_match(
unsigned int cur_cot_flags = get_cot_flags(); unsigned int cur_cot_flags = get_cot_flags();
int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0; int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0; int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
int cpt_sources_active = compl_match_array && cpt_sources_array;
while (--todo >= 0) while (--todo >= 0)
{ {
if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL)
{ {
if (cpt_sources_active) if (compl_match_array != NULL)
compl_shown_match = find_comp_when_cpt_sources(); compl_shown_match = find_next_match_in_menu();
else else
compl_shown_match = compl_shown_match->cp_next; compl_shown_match = compl_shown_match->cp_next;
found_end = (compl_first_match != NULL found_end = (compl_first_match != NULL
@ -5573,8 +5538,8 @@ find_next_completion_match(
&& compl_shown_match->cp_prev != NULL) && compl_shown_match->cp_prev != NULL)
{ {
found_end = is_first_match(compl_shown_match); found_end = is_first_match(compl_shown_match);
if (cpt_sources_active) if (compl_match_array != NULL)
compl_shown_match = find_comp_when_cpt_sources(); compl_shown_match = find_next_match_in_menu();
else else
compl_shown_match = compl_shown_match->cp_prev; compl_shown_match = compl_shown_match->cp_prev;
found_end |= is_first_match(compl_shown_match); found_end |= is_first_match(compl_shown_match);

View File

@ -3394,8 +3394,11 @@ func Test_complete_opt_fuzzy()
endif endif
if g:change == 0 if g:change == 0
return [#{word: "foo"}, #{word: "foobar"}, #{word: "fooBaz"}, #{word: "foobala"}, #{word: "你好吗"}, #{word: "我好"}] return [#{word: "foo"}, #{word: "foobar"}, #{word: "fooBaz"}, #{word: "foobala"}, #{word: "你好吗"}, #{word: "我好"}]
endif elseif g:change == 1
return [#{word: "cp_match_array"}, #{word: "cp_str"}, #{word: "cp_score"}]
else
return [#{word: "for i = .."}, #{word: "bar"}, #{word: "foo"}, #{word: "for .. ipairs"}, #{word: "for .. pairs"}] return [#{word: "for i = .."}, #{word: "bar"}, #{word: "foo"}, #{word: "for .. ipairs"}, #{word: "for .. pairs"}]
endif
endfunc endfunc
new new
@ -3493,7 +3496,7 @@ func Test_complete_opt_fuzzy()
call assert_equal('alpha bravio charlie', getline('.')) call assert_equal('alpha bravio charlie', getline('.'))
set cot=fuzzy,menu,noinsert set cot=fuzzy,menu,noinsert
call feedkeys(":let g:change=1\<CR>") call feedkeys(":let g:change=2\<CR>")
call feedkeys("S\<C-X>\<C-O>for\<C-N>\<C-N>\<C-N>", 'tx') call feedkeys("S\<C-X>\<C-O>for\<C-N>\<C-N>\<C-N>", 'tx')
call assert_equal('for', getline('.')) call assert_equal('for', getline('.'))
call feedkeys("S\<C-X>\<C-O>for\<C-P>", 'tx') call feedkeys("S\<C-X>\<C-O>for\<C-P>", 'tx')
@ -3501,6 +3504,10 @@ func Test_complete_opt_fuzzy()
call feedkeys("S\<C-X>\<C-O>for\<C-P>\<C-P>", 'tx') call feedkeys("S\<C-X>\<C-O>for\<C-P>\<C-P>", 'tx')
call assert_equal('for .. ipairs', getline('.')) call assert_equal('for .. ipairs', getline('.'))
call feedkeys(":let g:change=1\<CR>")
call feedkeys("S\<C-X>\<C-O>c\<C-Y>", 'tx')
call assert_equal('cp_str', getline('.'))
" clean up " clean up
set omnifunc= set omnifunc=
bw! bw!

View File

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