diff options
-rw-r--r-- | src/nvim/insexpand.c | 133 | ||||
-rw-r--r-- | test/old/testdir/test_popup.vim | 25 |
2 files changed, 87 insertions, 71 deletions
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index d3517667fb..820647df97 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -156,6 +156,7 @@ typedef struct compl_S compl_T; struct compl_S { compl_T *cp_next; compl_T *cp_prev; + compl_T *cp_match_next; ///< matched next compl_T char *cp_str; ///< matched text char *(cp_text[CPT_COUNT]); ///< text for the menu typval_T cp_user_data; @@ -758,7 +759,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir flags |= CP_ICASE; } - int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false, -1, -1); + int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false, NULL); xfree(tofree); return res; } @@ -789,6 +790,7 @@ static inline void free_cptext(char *const *const cptext) /// @param[in] cdir match direction. If 0, use "compl_direction". /// @param[in] flags_arg match flags (cp_flags) /// @param[in] adup accept this match even if it is already present. +/// @param[in] user_hl list of extra highlight attributes for abbr kind. /// /// If "cdir" is FORWARD, then the match is added after the current match. /// Otherwise, it is added before the current match. @@ -798,7 +800,7 @@ static inline void free_cptext(char *const *const cptext) /// returned in case of error. static int ins_compl_add(char *const str, int len, char *const fname, char *const *const cptext, const bool cptext_allocated, typval_T *user_data, const Direction cdir, - int flags_arg, const bool adup, int user_abbr_hlattr, int user_kind_hlattr) + int flags_arg, const bool adup, const int *user_hl) FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; @@ -864,8 +866,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons match->cp_fname = NULL; } match->cp_flags = flags; - match->cp_user_abbr_hlattr = user_abbr_hlattr; - match->cp_user_kind_hlattr = user_kind_hlattr; + match->cp_user_abbr_hlattr = user_hl ? user_hl[0] : -1; + match->cp_user_kind_hlattr = user_hl ? user_hl[1] : -1; if (cptext != NULL) { int i; @@ -999,7 +1001,7 @@ static void ins_compl_add_matches(int num_matches, char **matches, int icase) for (int i = 0; i < num_matches && add_r != FAIL; i++) { if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir, CP_FAST | (icase ? CP_ICASE : 0), - false, -1, -1)) == OK) { + false, NULL)) == OK) { // If dir was BACKWARD then honor it just once. dir = FORWARD; } @@ -1173,29 +1175,7 @@ static int ins_compl_build_pum(void) unsigned cur_cot_flags = get_cot_flags(); bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0; bool compl_fuzzy_match = (cur_cot_flags & kOptCotFlagFuzzy) != 0; - - 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) { - comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader); - } - - if (!match_at_original_text(comp) - && (compl_leader == NULL - || ins_compl_equal(comp, compl_leader, (size_t)lead_len) - || (compl_fuzzy_match && comp->cp_score > 0))) { - compl_match_arraysize++; - } - comp = comp->cp_next; - } while (comp != NULL && !is_first_match(comp)); - - if (compl_match_arraysize == 0) { - return -1; - } - - assert(compl_match_arraysize >= 0); - compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); + compl_T *match_head = NULL, *match_tail = NULL; // If the current match is the original text don't find the first // match after it, don't highlight anything. @@ -1205,16 +1185,29 @@ static int ins_compl_build_pum(void) compl_shown_match = compl_no_select ? compl_first_match : compl_first_match->cp_next; } - compl_T *shown_compl = NULL; bool did_find_shown_match = false; - int cur = -1; + compl_T *shown_compl = NULL; int i = 0; - comp = compl_first_match; + int cur = -1; + 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) { + comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader); + } + if (!match_at_original_text(comp) && (compl_leader == NULL || ins_compl_equal(comp, compl_leader, (size_t)lead_len) || (compl_fuzzy_match && comp->cp_score > 0))) { + compl_match_arraysize++; + if (match_head == NULL) { + match_head = comp; + } else { + match_tail->cp_match_next = comp; + } + match_tail = comp; if (!shown_match_ok && !compl_fuzzy_match) { if (comp == compl_shown_match || did_find_shown_match) { // This item is the shown match or this is the @@ -1241,51 +1234,21 @@ static int ins_compl_build_pum(void) compl_shown_match = comp; } } - if (!shown_match_ok && comp == compl_shown_match && !compl_no_select) { cur = i; 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))) { - if (match_at_original_text(compl_shown_match)) { - compl_shown_match = shown_compl; - } - } - } - - if (comp->cp_text[CPT_ABBR] != NULL) { - compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR]; - } else { - compl_match_array[i].pum_text = comp->cp_str; - } - compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND]; - compl_match_array[i].pum_info = comp->cp_text[CPT_INFO]; - compl_match_array[i].pum_score = comp->cp_score; - compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr; - compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr; - if (comp->cp_text[CPT_MENU] != NULL) { - compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU]; - } else { - compl_match_array[i++].pum_extra = comp->cp_fname; } + i++; } if (comp == compl_shown_match && !compl_fuzzy_match) { did_find_shown_match = true; - // When the original text is the shown match don't set // compl_shown_match. if (match_at_original_text(comp)) { shown_match_ok = true; } - if (!shown_match_ok && shown_compl != NULL) { // The shown match isn't displayed, set it to the // previously displayed match. @@ -1296,6 +1259,36 @@ static int ins_compl_build_pum(void) comp = comp->cp_next; } while (comp != NULL && !is_first_match(comp)); + if (compl_match_arraysize == 0) { + return -1; + } + + assert(compl_match_arraysize >= 0); + compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); + + i = 0; + comp = match_head; + while (comp != NULL) { + if (comp->cp_text[CPT_ABBR] != NULL) { + compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR]; + } else { + compl_match_array[i].pum_text = comp->cp_str; + } + compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND]; + compl_match_array[i].pum_info = comp->cp_text[CPT_INFO]; + compl_match_array[i].pum_score = comp->cp_score; + compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr; + compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr; + if (comp->cp_text[CPT_MENU] != NULL) { + compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU]; + } else { + compl_match_array[i++].pum_extra = comp->cp_fname; + } + compl_T *match_next = comp->cp_match_next; + comp->cp_match_next = NULL; + comp = match_next; + } + 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; @@ -2573,9 +2566,8 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) int flags = fast ? CP_FAST : 0; char *(cptext[CPT_COUNT]); char *user_abbr_hlname = NULL; - int user_abbr_hlattr = -1; char *user_kind_hlname = NULL; - int user_kind_hlattr = -1; + int user_hl[2] = { -1, -1 }; typval_T user_data; user_data.v_type = VAR_UNKNOWN; @@ -2587,10 +2579,10 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); user_abbr_hlname = tv_dict_get_string(tv->vval.v_dict, "abbr_hlgroup", false); - user_abbr_hlattr = get_user_highlight_attr(user_abbr_hlname); + user_hl[0] = get_user_highlight_attr(user_abbr_hlname); user_kind_hlname = tv_dict_get_string(tv->vval.v_dict, "kind_hlgroup", false); - user_kind_hlattr = get_user_highlight_attr(user_kind_hlname); + user_hl[1] = get_user_highlight_attr(user_kind_hlname); tv_dict_get_tv(tv->vval.v_dict, "user_data", &user_data); @@ -2613,8 +2605,7 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) return FAIL; } int status = ins_compl_add((char *)word, -1, NULL, cptext, true, - &user_data, dir, flags, dup, - user_abbr_hlattr, user_kind_hlattr); + &user_data, dir, flags, dup, user_hl); if (status != OK) { tv_clear(&user_data); } @@ -2707,7 +2698,7 @@ static void set_completion(colnr_T startcol, list_T *list) flags |= CP_ICASE; } if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, - flags | CP_FAST, false, -1, -1) != OK) { + flags | CP_FAST, false, NULL) != OK) { return; } @@ -3452,7 +3443,7 @@ static void get_next_bufname_token(void) char *tail = path_tail(b->b_sfname); if (strncmp(tail, compl_orig_text, strlen(compl_orig_text)) == 0) { ins_compl_add(tail, (int)strlen(tail), NULL, NULL, false, NULL, 0, - p_ic ? CP_ICASE : 0, false, -1, -1); + p_ic ? CP_ICASE : 0, false, NULL); } } } @@ -4490,7 +4481,7 @@ static int ins_compl_start(void) flags |= CP_ICASE; } if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, - flags, false, -1, -1) != OK) { + flags, false, NULL) != OK) { XFREE_CLEAR(compl_pattern); compl_patternlen = 0; XFREE_CLEAR(compl_orig_text); diff --git a/test/old/testdir/test_popup.vim b/test/old/testdir/test_popup.vim index 601ba6c688..33e86678c8 100644 --- a/test/old/testdir/test_popup.vim +++ b/test/old/testdir/test_popup.vim @@ -1675,4 +1675,29 @@ func Test_pum_completeitemalign() call StopVimInTerminal(buf) endfunc +func Test_pum_keep_select() + CheckScreendump + let lines =<< trim END + set completeopt=menu,menuone,noinsert + END + call writefile(lines, 'Xscript', 'D') + let buf = RunVimInTerminal('-S Xscript', {}) + call TermWait(buf) + + call term_sendkeys(buf, "ggSFab\<CR>Five\<CR>find\<CR>film\<CR>\<C-X>\<C-P>") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_pum_keep_select_01', {}) + call term_sendkeys(buf, "\<C-E>\<Esc>") + call TermWait(buf, 50) + + call term_sendkeys(buf, "S\<C-X>\<C-P>") + call TermWait(buf, 50) + call term_sendkeys(buf, "F") + call VerifyScreenDump(buf, 'Test_pum_keep_select_02', {}) + call term_sendkeys(buf, "\<C-E>\<Esc>") + + call TermWait(buf, 50) + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab |