patch 9.1.1441: completion: code can be improved Commit: https://github.com/vim/vim/commit/b8ee1cf56e687a02756b15d8c530206827e3ef1e Author: Girish Palya <giris...@gmail.com> Date: Sun Jun 8 16:20:06 2025 +0200
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 <glephun...@gmail.com> Signed-off-by: Girish Palya <giris...@gmail.com> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/src/insexpand.c b/src/insexpand.c index 33d00e0c6..ff8e87e4e 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -818,77 +818,6 @@ is_nearest_active(void) 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: * str - text of the match to add @@ -948,10 +877,7 @@ ins_compl_add( || match->cp_str.string[len] == NUL)) { if (is_nearest_active() && score > 0 && score < match->cp_score) - { match->cp_score = score; - reposition_match(match); - } return NOTDONE; } match = match->cp_next; @@ -1063,9 +989,6 @@ ins_compl_add( compl_first_match = match; compl_curr_match = match; - if (is_nearest_active() && score > 0) - reposition_match(match); - // Find the longest common string if still doing that. if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0 && !cfc_has_mode()) 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; } + 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. * 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(); int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 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_tail = 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_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) { match_count = ALLOC_CLEAR_MULT(int, cpt_sources_count); @@ -2402,6 +2348,23 @@ ins_compl_new_leader(void) 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; // Show the popup menu with a different set of matches. @@ -5296,6 +5259,9 @@ ins_compl_get_exp(pos_T *ini) } may_trigger_modechanged(); + if (is_nearest_active()) + sort_compl_match_list(cp_compare_nearest); + return i; } @@ -5518,7 +5484,7 @@ ins_compl_show_filename(void) * in 'compl_match_array'. */ static compl_T * -find_comp_when_cpt_sources(void) +find_next_match_in_menu(void) { int is_forward = compl_shows_dir_forward(); compl_T *match = compl_shown_match; @@ -5555,14 +5521,13 @@ find_next_completion_match( unsigned int cur_cot_flags = get_cot_flags(); int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0; int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0; - int cpt_sources_active = compl_match_array && cpt_sources_array; while (--todo >= 0) { if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) { - if (cpt_sources_active) - compl_shown_match = find_comp_when_cpt_sources(); + if (compl_match_array != NULL) + compl_shown_match = find_next_match_in_menu(); else compl_shown_match = compl_shown_match->cp_next; found_end = (compl_first_match != NULL @@ -5573,8 +5538,8 @@ find_next_completion_match( && compl_shown_match->cp_prev != NULL) { found_end = is_first_match(compl_shown_match); - if (cpt_sources_active) - compl_shown_match = find_comp_when_cpt_sources(); + if (compl_match_array != NULL) + compl_shown_match = find_next_match_in_menu(); else compl_shown_match = compl_shown_match->cp_prev; found_end |= is_first_match(compl_shown_match); diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index fcd2e6c6b..33c87cb2c 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -3394,8 +3394,11 @@ func Test_complete_opt_fuzzy() endif if g:change == 0 return [#{word: "foo"}, #{word: "foobar"}, #{word: "fooBaz"}, #{word: "foobala"}, #{word: "你好吗"}, #{word: "我好"}] + 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"}] endif - return [#{word: "for i = .."}, #{word: "bar"}, #{word: "foo"}, #{word: "for .. ipairs"}, #{word: "for .. pairs"}] endfunc new @@ -3493,7 +3496,7 @@ func Test_complete_opt_fuzzy() call assert_equal('alpha bravio charlie', getline('.')) 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 assert_equal('for', getline('.')) 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 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 set omnifunc= bw! diff --git a/src/version.c b/src/version.c index 6dc4bf818..7964c14ad 100644 --- a/src/version.c +++ b/src/version.c @@ -709,6 +709,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1441, /**/ 1440, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1uOH2C-007Wea-E4%40256bit.org.