diff options
Diffstat (limited to 'src/insexpand.c')
-rw-r--r-- | src/insexpand.c | 251 |
1 files changed, 207 insertions, 44 deletions
diff --git a/src/insexpand.c b/src/insexpand.c index 93a56a8..c673df9 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -113,6 +113,7 @@ struct compl_S // cp_flags has CP_FREE_FNAME int cp_flags; // CP_ values int cp_number; // sequence number + int cp_score; // fuzzy match score }; // values for cp_flags @@ -147,13 +148,6 @@ static char_u *compl_leader = NULL; static int compl_get_longest = FALSE; // put longest common string // in compl_leader -static int compl_no_insert = FALSE; // FALSE: select & insert - // TRUE: noinsert -static int compl_no_select = FALSE; // FALSE: select & insert - // TRUE: noselect -static int compl_longest = FALSE; // FALSE: insert full match - // TRUE: insert longest prefix - // Selected one of the matches. When FALSE the match was edited or using the // longest common string. static int compl_used_match; @@ -176,6 +170,7 @@ static int ctrl_x_mode = CTRL_X_NORMAL; static int compl_matches = 0; // number of completion matches static char_u *compl_pattern = NULL; +static size_t compl_patternlen = 0; static int compl_direction = FORWARD; static int compl_shows_dir = FORWARD; static int compl_pending = 0; // > 1 for postponed CTRL-N @@ -206,6 +201,8 @@ static int compl_cont_status = 0; static int compl_opt_refresh_always = FALSE; static int compl_opt_suppress_empty = FALSE; +static int compl_selected_item = -1; + 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); @@ -1049,21 +1046,12 @@ ins_compl_long_shown_match(void) } /* - * Set variables that store noselect and noinsert behavior from the - * 'completeopt' value. + * Get the local or global value of 'completeopt' flags. */ - void -completeopt_was_set(void) + unsigned int +get_cot_flags(void) { - compl_no_insert = FALSE; - compl_no_select = FALSE; - compl_longest = FALSE; - if (strstr((char *)p_cot, "noselect") != NULL) - compl_no_select = TRUE; - if (strstr((char *)p_cot, "noinsert") != NULL) - compl_no_insert = TRUE; - if (strstr((char *)p_cot, "longest") != NULL) - compl_longest = TRUE; + return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags; } @@ -1110,7 +1098,7 @@ ins_compl_del_pum(void) pum_wanted(void) { // 'completeopt' must contain "menu" or "menuone" - if (vim_strchr(p_cot, 'm') == NULL) + if ((get_cot_flags() & COT_ANY_MENU) == 0) return FALSE; // The display looks bad on a B&W display. @@ -1144,7 +1132,7 @@ pum_enough_matches(void) compl = compl->cp_next; } while (!is_first_match(compl)); - if (strstr((char *)p_cot, "menuone") != NULL) + if (get_cot_flags() & COT_MENUONE) return (i >= 1); return (i >= 2); } @@ -1212,6 +1200,19 @@ trigger_complete_changed_event(int cur) #endif /* + * pumitem qsort compare func + */ + static int +ins_compl_fuzzy_cmp(const void *a, const void *b) +{ + const int sa = (*(pumitem_T *)a).pum_score; + const int sb = (*(pumitem_T *)b).pum_score; + const int ia = (*(pumitem_T *)a).pum_idx; + const int ib = (*(pumitem_T *)b).pum_idx; + return sa == sb ? (ia == ib ? 0 : (ia < ib ? -1 : 1)) : (sa < sb ? 1 : -1); +} + +/* * Build a popup menu to show the completion matches. * Returns the popup menu entry that should be selected. Returns -1 if nothing * should be selected. @@ -1226,6 +1227,10 @@ ins_compl_build_pum(void) int i; int cur = -1; int lead_len = 0; + int max_fuzzy_score = 0; + 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; // Need to build the popup menu list. compl_match_arraysize = 0; @@ -1235,9 +1240,15 @@ ins_compl_build_pum(void) do { + // When 'completeopt' contains "fuzzy" and leader is not NULL or empty, + // set the cp_score for later comparisons. + if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) + compl->cp_score = fuzzy_match_str(compl->cp_str, compl_leader); + if (!match_at_original_text(compl) && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) + || ins_compl_equal(compl, compl_leader, lead_len) + || (compl_fuzzy_match && compl->cp_score > 0))) ++compl_match_arraysize; compl = compl->cp_next; } while (compl != NULL && !is_first_match(compl)); @@ -1254,15 +1265,22 @@ ins_compl_build_pum(void) if (match_at_original_text(compl_shown_match)) shown_match_ok = TRUE; + if (compl_leader != NULL + && STRCMP(compl_leader, compl_orig_text) == 0 + && shown_match_ok == FALSE) + compl_shown_match = compl_no_select ? compl_first_match + : compl_first_match->cp_next; + i = 0; compl = compl_first_match; do { if (!match_at_original_text(compl) && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) + || ins_compl_equal(compl, compl_leader, lead_len) + || (compl_fuzzy_match && compl->cp_score > 0))) { - if (!shown_match_ok) + if (!shown_match_ok && !compl_fuzzy_match) { if (compl == compl_shown_match || did_find_shown_match) { @@ -1278,6 +1296,34 @@ ins_compl_build_pum(void) shown_compl = compl; cur = i; } + else if (compl_fuzzy_match) + { + if (i == 0) + shown_compl = compl; + // Update the maximum fuzzy score and the shown match + // if the current item's score is higher + if (compl->cp_score > max_fuzzy_score) + { + did_find_shown_match = TRUE; + max_fuzzy_score = compl->cp_score; + compl_shown_match = compl; + shown_match_ok = TRUE; + } + + // If there is no "no select" condition and the max fuzzy + // score is positive, or there is no completion leader or the + // leader length is zero, mark the shown match as valid and + // reset the current index. + if (!compl_no_select + && (max_fuzzy_score > 0 + || (compl_leader == NULL || lead_len == 0))) + { + shown_match_ok = TRUE; + cur = 0; + if (match_at_original_text(compl_shown_match)) + compl_shown_match = shown_compl; + } + } if (compl->cp_text[CPT_ABBR] != NULL) compl_match_array[i].pum_text = @@ -1286,6 +1332,7 @@ ins_compl_build_pum(void) compl_match_array[i].pum_text = compl->cp_str; compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; + compl_match_array[i].pum_score = compl->cp_score; if (compl->cp_text[CPT_MENU] != NULL) compl_match_array[i++].pum_extra = compl->cp_text[CPT_MENU]; @@ -1293,7 +1340,7 @@ ins_compl_build_pum(void) compl_match_array[i++].pum_extra = compl->cp_fname; } - if (compl == compl_shown_match) + if (compl == compl_shown_match && !compl_fuzzy_match) { did_find_shown_match = TRUE; @@ -1313,6 +1360,15 @@ ins_compl_build_pum(void) compl = compl->cp_next; } while (compl != NULL && !is_first_match(compl)); + if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) + { + for (i = 0; i < compl_match_arraysize; i++) + compl_match_array[i].pum_idx = i; + // sort by the largest score of fuzzy match + qsort(compl_match_array, (size_t)compl_match_arraysize, + sizeof(pumitem_T), ins_compl_fuzzy_cmp); + } + if (!shown_match_ok) // no displayed match at all cur = -1; @@ -1369,6 +1425,7 @@ ins_compl_show_pum(void) // Use the cursor to get all wrapping and other settings right. col = curwin->w_cursor.col; curwin->w_cursor.col = compl_col; + compl_selected_item = cur; pum_display(compl_match_array, compl_match_arraysize, cur); curwin->w_cursor.col = col; @@ -1386,6 +1443,15 @@ ins_compl_show_pum(void) #define DICT_EXACT (2) // "dict" is the exact name of a file /* + * Get current completion leader + */ + char_u * +ins_compl_leader(void) +{ + return compl_leader != NULL ? compl_leader : compl_orig_text; +} + +/* * Add any identifiers that match the given pattern "pat" in the list of * dictionary files "dict_start" to the list of completions. */ @@ -1708,6 +1774,7 @@ ins_compl_free(void) int i; VIM_CLEAR(compl_pattern); + compl_patternlen = 0; VIM_CLEAR(compl_leader); if (compl_first_match == NULL) @@ -1747,6 +1814,7 @@ ins_compl_clear(void) compl_started = FALSE; compl_matches = 0; VIM_CLEAR(compl_pattern); + compl_patternlen = 0; VIM_CLEAR(compl_leader); edit_submode_extra = NULL; VIM_CLEAR(compl_orig_text); @@ -2404,9 +2472,8 @@ ins_compl_prep(int c) if (ctrl_x_mode_not_defined_yet() || (ctrl_x_mode_normal() && !compl_started)) { - compl_get_longest = compl_longest; + compl_get_longest = (get_cot_flags() & COT_LONGEST) != 0; compl_used_match = TRUE; - } if (ctrl_x_mode_not_defined_yet()) @@ -2878,6 +2945,10 @@ set_completion(colnr_T startcol, list_T *list) int save_w_wrow = curwin->w_wrow; int save_w_leftcol = curwin->w_leftcol; int flags = CP_ORIGINAL_TEXT; + unsigned int cur_cot_flags = get_cot_flags(); + int compl_longest = (cur_cot_flags & COT_LONGEST) != 0; + int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0; + int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0; // If already doing completions stop it. if (ctrl_x_mode_not_default()) @@ -3374,7 +3445,7 @@ done: get_next_include_file_completion(int compl_type) { find_pattern_in_path(compl_pattern, compl_direction, - (int)STRLEN(compl_pattern), FALSE, FALSE, + (int)compl_patternlen, FALSE, FALSE, (compl_type == CTRL_X_PATH_DEFINES && !(compl_cont_status & CONT_SOL)) ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND, @@ -3478,8 +3549,7 @@ get_next_cmdline_completion(void) int num_matches; if (expand_cmdline(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), - &num_matches, &matches) == EXPAND_OK) + (int)compl_patternlen, &num_matches, &matches) == EXPAND_OK) ins_compl_add_matches(num_matches, matches, FALSE); } @@ -3644,8 +3714,8 @@ get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos) st->cur_match_pos, compl_direction, compl_pattern); else found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos, - NULL, compl_direction, compl_pattern, 1L, - SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL); + NULL, compl_direction, compl_pattern, compl_patternlen, + 1L, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL); --msg_silent; if (!compl_started || st->set_match_pos) { @@ -4018,6 +4088,43 @@ ins_compl_show_filename(void) } /* + * Find a completion item when 'completeopt' contains "fuzzy". + */ + static compl_T * +find_comp_when_fuzzy(void) +{ + int score; + char_u* str; + int target_idx = -1; + int is_forward = compl_shows_dir_forward(); + 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) + || (is_backward && compl_selected_item == 0)) + return compl_first_match; + + if (is_forward) + target_idx = compl_selected_item + 1; + else if (is_backward) + target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1 + : compl_selected_item - 1; + + score = compl_match_array[target_idx].pum_score; + str = compl_match_array[target_idx].pum_text; + + comp = compl_first_match; + do { + if (comp->cp_score == score && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR])) + return comp; + comp = comp->cp_next; + } while (comp != NULL && !is_first_match(comp)); + + return NULL; +} + +/* * Find the next set of matches for completion. Repeat the completion "todo" * times. The number of matches found is returned in 'num_matches'. * @@ -4039,12 +4146,16 @@ find_next_completion_match( { int found_end = FALSE; compl_T *found_compl = NULL; + 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; while (--todo >= 0) { if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) { - compl_shown_match = compl_shown_match->cp_next; + compl_shown_match = compl_fuzzy_match && compl_match_array != NULL + ? find_comp_when_fuzzy() : compl_shown_match->cp_next; found_end = (compl_first_match != NULL && (is_first_match(compl_shown_match->cp_next) || is_first_match(compl_shown_match))); @@ -4053,7 +4164,8 @@ find_next_completion_match( && compl_shown_match->cp_prev != NULL) { found_end = is_first_match(compl_shown_match); - compl_shown_match = compl_shown_match->cp_prev; + compl_shown_match = compl_fuzzy_match && compl_match_array != NULL + ? find_comp_when_fuzzy() : compl_shown_match->cp_prev; found_end |= is_first_match(compl_shown_match); } else @@ -4103,7 +4215,8 @@ find_next_completion_match( if (!match_at_original_text(compl_shown_match) && compl_leader != NULL && !ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader))) + compl_leader, (int)STRLEN(compl_leader)) + && !(compl_fuzzy_match && compl_shown_match->cp_score > 0)) ++todo; else // Remember a matching item. @@ -4153,13 +4266,18 @@ ins_compl_next( int advance; int started = compl_started; buf_T *orig_curbuf = curbuf; + unsigned int cur_cot_flags = get_cot_flags(); + int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0; + int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0; // When user complete function return -1 for findstart which is next // time of 'always', compl_shown_match become NULL. if (compl_shown_match == NULL) return -1; - if (compl_leader != NULL && !match_at_original_text(compl_shown_match)) + if (compl_leader != NULL + && !match_at_original_text(compl_shown_match) + && !compl_fuzzy_match) // Update "compl_shown_match" to the actually shown match ins_compl_update_shown_match(); @@ -4305,7 +4423,7 @@ ins_compl_check_keys(int frequency, int in_compl_func) } } } - if (compl_pending != 0 && !got_int && !compl_no_insert) + if (compl_pending != 0 && !got_int && !(cot_flags & COT_NOINSERT)) { int todo = compl_pending > 0 ? compl_pending : -compl_pending; @@ -4383,7 +4501,8 @@ ins_compl_use_match(int c) /* * Get the pattern, column and length for normal completion (CTRL-N CTRL-P * completion) - * Sets the global variables: compl_col, compl_length and compl_pattern. + * Sets the global variables: compl_col, compl_length, compl_pattern and + * compl_patternlen. * Uses the global variables: compl_cont_status and ctrl_x_mode */ static int @@ -4404,32 +4523,45 @@ get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col) else compl_pattern = vim_strnsave(line + compl_col, compl_length); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } } else if (compl_status_adding()) { char_u *prefix = (char_u *)"\\<"; + size_t prefixlen = STRLEN_LITERAL("\\<"); // we need up to 2 extra chars for the prefix compl_pattern = alloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); + compl_length) + prefixlen); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } if (!vim_iswordp(line + compl_col) || (compl_col > 0 && (vim_iswordp(mb_prevptr(line, line + compl_col))))) + { prefix = (char_u *)""; + prefixlen = 0; + } STRCPY((char *)compl_pattern, prefix); - (void)quote_meta(compl_pattern + STRLEN(prefix), + (void)quote_meta(compl_pattern + prefixlen, line + compl_col, compl_length); } else if (--startcol < 0 || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { // Match any word of at least two chars - compl_pattern = vim_strsave((char_u *)"\\<\\k\\k"); + compl_pattern = vim_strnsave((char_u *)"\\<\\k\\k", STRLEN_LITERAL("\\<\\k\\k")); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } compl_col += curs_col; compl_length = 0; } @@ -4465,7 +4597,10 @@ get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col) // alloc(7) is enough -- Acevedo compl_pattern = alloc(7); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } STRCPY((char *)compl_pattern, "\\<"); (void)quote_meta(compl_pattern + 2, line + compl_col, 1); STRCAT((char *)compl_pattern, "\\k"); @@ -4475,13 +4610,18 @@ get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col) compl_pattern = alloc(quote_meta(NULL, line + compl_col, compl_length) + 2); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } STRCPY((char *)compl_pattern, "\\<"); (void)quote_meta(compl_pattern + 2, line + compl_col, compl_length); } } + compl_patternlen = STRLEN(compl_pattern); + return OK; } @@ -4503,7 +4643,12 @@ get_wholeline_compl_info(char_u *line, colnr_T curs_col) else compl_pattern = vim_strnsave(line + compl_col, compl_length); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } + + compl_patternlen = STRLEN(compl_pattern); return OK; } @@ -4533,7 +4678,12 @@ get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col) compl_length = (int)curs_col - startcol; compl_pattern = addstar(line + compl_col, compl_length, EXPAND_FILES); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } + + compl_patternlen = STRLEN(compl_pattern); return OK; } @@ -4547,9 +4697,13 @@ get_cmdline_compl_info(char_u *line, colnr_T curs_col) { compl_pattern = vim_strnsave(line, curs_col); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } + compl_patternlen = curs_col; set_cmd_context(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), curs_col, FALSE); + (int)compl_patternlen, curs_col, FALSE); if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL || compl_xp.xp_context == EXPAND_NOTHING) // No completion possible, use an empty pattern to get a @@ -4647,8 +4801,12 @@ get_userdefined_compl_info(colnr_T curs_col UNUSED) compl_length = curs_col - compl_col; compl_pattern = vim_strnsave(line + compl_col, compl_length); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } + compl_patternlen = compl_length; ret = OK; #endif @@ -4685,8 +4843,12 @@ get_spell_compl_info(int startcol UNUSED, colnr_T curs_col UNUSED) line = ml_get(curwin->w_cursor.lnum); compl_pattern = vim_strnsave(line + compl_col, compl_length); if (compl_pattern == NULL) + { + compl_patternlen = 0; return FAIL; + } + compl_patternlen = compl_length; ret = OK; #endif @@ -4907,6 +5069,7 @@ ins_compl_start(void) -1, NULL, NULL, NULL, 0, flags, FALSE) != OK) { VIM_CLEAR(compl_pattern); + compl_patternlen = 0; VIM_CLEAR(compl_orig_text); return FAIL; } @@ -5195,7 +5358,7 @@ spell_back_to_badword(void) { pos_T tpos = curwin->w_cursor; - spell_bad_len = spell_move_to(curwin, BACKWARD, TRUE, TRUE, NULL); + spell_bad_len = spell_move_to(curwin, BACKWARD, SMT_ALL, TRUE, NULL); if (curwin->w_cursor.col != tpos.col) start_arrow(&tpos); } |