diff options
Diffstat (limited to 'src/nvim/insexpand.c')
-rw-r--r-- | src/nvim/insexpand.c | 174 |
1 files changed, 128 insertions, 46 deletions
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 12543a2d42..41b964323e 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -9,34 +9,43 @@ #include <stdlib.h> #include <string.h> +#include "klib/kvec.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" +#include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -51,9 +60,11 @@ #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/textformat.h" @@ -62,6 +73,7 @@ #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/winfloat.h" // Definitions used for CTRL-X submode. // Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] @@ -252,6 +264,8 @@ static colnr_T compl_col = 0; ///< column where the text starts ///< that is being completed static char *compl_orig_text = NULL; ///< text as it was before ///< completion started +/// Undo information to restore extmarks for original text. +static extmark_undo_vec_t compl_orig_extmarks; static int compl_cont_mode = 0; static expand_T compl_xp; @@ -593,7 +607,6 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp int min_len, char **tofree) { bool has_lower = false; - bool was_letter = false; // Allocate wide character array for the completion and fill it. int *const wca = xmalloc((size_t)char_len * sizeof(*wca)); @@ -625,6 +638,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp // Rule 2: No lower case, 2nd consecutive letter converted to // upper case. if (!has_lower) { + bool was_letter = false; const char *p = compl_orig_text; for (int i = 0; i < min_len; i++) { const int c = mb_ptr2char_adv(&p); @@ -744,6 +758,16 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir return res; } +/// free cptext +static inline void free_cptext(char *const *const cptext) +{ + if (cptext != NULL) { + for (size_t i = 0; i < CPT_COUNT; i++) { + xfree(cptext[i]); + } + } +} + /// Add a match to the list of matches /// /// @param[in] str text of the match to add @@ -781,16 +805,10 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons } else { os_breakcheck(); } -#define FREE_CPTEXT(cptext, cptext_allocated) \ - do { \ - if ((cptext) != NULL && (cptext_allocated)) { \ - for (size_t i = 0; i < CPT_COUNT; i++) { \ - xfree((cptext)[i]); \ - } \ - } \ - } while (0) if (got_int) { - FREE_CPTEXT(cptext, cptext_allocated); + if (cptext_allocated) { + free_cptext(cptext); + } return FAIL; } if (len < 0) { @@ -804,7 +822,9 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons if (!match_at_original_text(match) && strncmp(match->cp_str, str, (size_t)len) == 0 && ((int)strlen(match->cp_str) <= len || match->cp_str[len] == NUL)) { - FREE_CPTEXT(cptext, cptext_allocated); + if (cptext_allocated) { + free_cptext(cptext); + } return NOTDONE; } match = match->cp_next; @@ -909,13 +929,11 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len) /// Reduce the longest common string for match "match". static void ins_compl_longest_match(compl_T *match) { - int had_match; - if (compl_leader == NULL) { // First match, use it as a whole. compl_leader = xstrdup(match->cp_str); - had_match = (curwin->w_cursor.col > compl_col); + bool had_match = (curwin->w_cursor.col > compl_col); ins_compl_delete(); ins_bytes(compl_leader + get_compl_len()); ins_redraw(false); @@ -949,7 +967,7 @@ static void ins_compl_longest_match(compl_T *match) if (*p != NUL) { // Leader was shortened, need to change the inserted text. *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); + bool had_match = (curwin->w_cursor.col > compl_col); ins_compl_delete(); ins_bytes(compl_leader + get_compl_len()); ins_redraw(false); @@ -1261,6 +1279,9 @@ void ins_compl_show_pum(void) } if (compl_match_array == NULL) { + if (compl_started && has_event(EVENT_COMPLETECHANGED)) { + trigger_complete_changed_event(cur); + } return; } @@ -1276,11 +1297,38 @@ void ins_compl_show_pum(void) pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); curwin->w_cursor.col = col; + // After adding leader, set the current match to shown match. + if (compl_started && compl_curr_match != compl_shown_match) { + compl_curr_match = compl_shown_match; + } + if (has_event(EVENT_COMPLETECHANGED)) { trigger_complete_changed_event(cur); } } +/// used for set or update info +void compl_set_info(int pum_idx) +{ + compl_T *comp = compl_first_match; + char *pum_text = compl_match_array[pum_idx].pum_text; + + while (comp != NULL) { + if (pum_text == comp->cp_str + || pum_text == comp->cp_text[CPT_ABBR]) { + comp->cp_text[CPT_INFO] = compl_match_array[pum_idx].pum_info; + + // if comp is current match update completed_item value + if (comp == compl_curr_match) { + dict_T *dict = ins_compl_dict_alloc(compl_curr_match); + set_vim_var_dict(VV_COMPLETED_ITEM, dict); + } + break; + } + comp = comp->cp_next; + } +} + #define DICT_FIRST (1) ///< use just first element in "dict" #define DICT_EXACT (2) ///< "dict" is the exact name of a file @@ -1289,7 +1337,7 @@ void ins_compl_show_pum(void) /// /// @param flags DICT_FIRST and/or DICT_EXACT /// @param thesaurus Thesaurus completion -static void ins_compl_dictionaries(char *dict_start, char *pat, int flags, int thesaurus) +static void ins_compl_dictionaries(char *dict_start, char *pat, int flags, bool thesaurus) { char *dict = dict_start; char *ptr; @@ -1432,8 +1480,8 @@ static int thesaurus_add_words_in_line(char *fname, char **buf_arg, int dir, con /// Process "count" dictionary/thesaurus "files" and add the text matching /// "regmatch". -static void ins_compl_files(int count, char **files, int thesaurus, int flags, regmatch_T *regmatch, - char *buf, Direction *dir) +static void ins_compl_files(int count, char **files, bool thesaurus, int flags, + regmatch_T *regmatch, char *buf, Direction *dir) FUNC_ATTR_NONNULL_ARG(2, 7) { for (int i = 0; i < count && !got_int && !compl_interrupted; i++) { @@ -1442,7 +1490,7 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r msg_hist_off = true; // reset in msg_trunc() vim_snprintf(IObuff, IOSIZE, _("Scanning dictionary: %s"), files[i]); - (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); + msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } if (fp == NULL) { @@ -1549,9 +1597,7 @@ static void ins_compl_free(void) if (match->cp_flags & CP_FREE_FNAME) { xfree(match->cp_fname); } - for (int i = 0; i < CPT_COUNT; i++) { - xfree(match->cp_text[i]); - } + free_cptext(match->cp_text); tv_clear(&match->cp_user_data); xfree(match); } while (compl_curr_match != NULL && !is_first_match(compl_curr_match)); @@ -1569,6 +1615,7 @@ void ins_compl_clear(void) XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_leader); edit_submode_extra = NULL; + kv_destroy(compl_orig_extmarks); XFREE_CLEAR(compl_orig_text); compl_enter_selects = false; // clear v:completed_item @@ -2019,6 +2066,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) ins_bytes_len(p + compl_len, (size_t)(len - compl_len)); } } + restore_orig_extmarks(); retval = true; } @@ -2454,9 +2502,7 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) CLEAR_FIELD(cptext); } if (word == NULL || (!empty && *word == NUL)) { - for (size_t i = 0; i < CPT_COUNT; i++) { - xfree(cptext[i]); - } + free_cptext(cptext); tv_clear(&user_data); return FAIL; } @@ -2505,6 +2551,22 @@ static void ins_compl_add_dict(dict_T *dict) } } +/// Save extmarks in "compl_orig_text" so that they may be restored when the +/// completion is cancelled, or the original text is completed. +static void save_orig_extmarks(void) +{ + extmark_splice_delete(curbuf, curwin->w_cursor.lnum - 1, compl_col, curwin->w_cursor.lnum - 1, + compl_col + compl_length, &compl_orig_extmarks, true, kExtmarkUndo); +} + +static void restore_orig_extmarks(void) +{ + for (long i = (int)kv_size(compl_orig_extmarks) - 1; i > -1; i--) { + ExtmarkUndoObject undo_info = kv_A(compl_orig_extmarks, i); + extmark_apply_undo(undo_info, true); + } +} + /// Start completion for the complete() function. /// /// @param startcol where the matched text starts (1 is first column). @@ -2526,10 +2588,10 @@ static void set_completion(colnr_T startcol, list_T *list) startcol = curwin->w_cursor.col; } compl_col = startcol; - compl_length = (int)curwin->w_cursor.col - (int)startcol; + compl_length = curwin->w_cursor.col - startcol; // compl_pattern doesn't need to be set - compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col, - (size_t)compl_length); + compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col, (size_t)compl_length); + save_orig_extmarks(); if (p_ic) { flags |= CP_ICASE; } @@ -2706,9 +2768,11 @@ static int info_add_completion_info(list_T *li) // Skip the element with the CP_ORIGINAL_TEXT flag at the beginning, in case of // forward completion, or at the end, in case of backward completion. - match = forward ? match->cp_next - : (compl_no_select && match_at_original_text(match) - ? match->cp_prev : match->cp_prev->cp_prev); + match = (forward || match->cp_prev == NULL + ? match->cp_next + : (compl_no_select && match_at_original_text(match) + ? match->cp_prev + : match->cp_prev->cp_prev)); while (match != NULL && !match_at_original_text(match)) { dict_T *di = tv_dict_alloc(); @@ -2790,6 +2854,11 @@ static void get_complete_info(list_T *what_list, dict_T *retdict) ret = tv_dict_add_nr(retdict, S_LEN("selected"), (compl_curr_match != NULL) ? compl_curr_match->cp_number - 1 : -1); + win_T *wp = win_float_find_preview(); + if (wp != NULL) { + tv_dict_add_nr(retdict, S_LEN("preview_winid"), wp->handle); + tv_dict_add_nr(retdict, S_LEN("preview_bufnr"), wp->w_buffer->handle); + } } (void)ret; @@ -2903,7 +2972,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar : st->ins_buf->b_sfname == NULL ? st->ins_buf->b_fname : st->ins_buf->b_sfname); - (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); + msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } } else if (*st->e_cpt == NUL) { status = INS_COMPL_CPT_END; @@ -2931,12 +3000,12 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar if (!shortmess(SHM_COMPLETIONSCAN)) { msg_hist_off = true; // reset in msg_trunc() vim_snprintf(IObuff, IOSIZE, "%s", _("Scanning tags.")); - (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); + msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } } // in any case e_cpt is advanced to the next entry - (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ","); + copy_option_part(&st->e_cpt, IObuff, IOSIZE, ","); st->found_all = true; if (compl_type == -1) { @@ -3488,7 +3557,12 @@ void ins_compl_delete(void) /// "in_compl_func" is true when called from complete_check(). void ins_compl_insert(bool in_compl_func) { - ins_bytes(compl_shown_match->cp_str + get_compl_len()); + int compl_len = get_compl_len(); + // Make sure we don't go over the end of the string, this can happen with + // illegal bytes. + if (compl_len < (int)strlen(compl_shown_match->cp_str)) { + ins_bytes(compl_shown_match->cp_str + compl_len); + } compl_used_match = !match_at_original_text(compl_shown_match); dict_T *dict = ins_compl_dict_alloc(compl_shown_match); @@ -3689,12 +3763,16 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match if (compl_no_insert && !started) { ins_bytes(compl_orig_text + get_compl_len()); compl_used_match = false; + restore_orig_extmarks(); } else if (insert_match) { if (!compl_get_longest || compl_used_match) { ins_compl_insert(in_compl_func); } else { ins_bytes(compl_leader + get_compl_len()); } + if (!strcmp(compl_curr_match->cp_str, compl_orig_text)) { + restore_orig_extmarks(); + } } else { compl_used_match = false; } @@ -3759,8 +3837,8 @@ void ins_compl_check_keys(int frequency, bool in_compl_func) if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) { c = safe_vgetc(); // Eat the character compl_shows_dir = ins_compl_key2dir(c); - (void)ins_compl_next(false, ins_compl_key2count(c), - c != K_UP && c != K_DOWN, in_compl_func); + ins_compl_next(false, ins_compl_key2count(c), + c != K_UP && c != K_DOWN, in_compl_func); } else { // Need to get the character to have KeyTyped set. We'll put it // back with vungetc() below. But skip K_IGNORE. @@ -3780,7 +3858,7 @@ void ins_compl_check_keys(int frequency, bool in_compl_func) int todo = compl_pending > 0 ? compl_pending : -compl_pending; compl_pending = 0; - (void)ins_compl_next(false, todo, true, in_compl_func); + ins_compl_next(false, todo, true, in_compl_func); } } @@ -3882,8 +3960,8 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) prefix = ""; } STRCPY(compl_pattern, prefix); - (void)quote_meta(compl_pattern + strlen(prefix), - line + compl_col, compl_length); + quote_meta(compl_pattern + strlen(prefix), + 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 @@ -3910,12 +3988,12 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) // xmalloc(7) is enough -- Acevedo compl_pattern = xmalloc(7); STRCPY(compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, 1); + quote_meta(compl_pattern + 2, line + compl_col, 1); STRCAT(compl_pattern, "\\k"); } else { compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2); STRCPY(compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, compl_length); + quote_meta(compl_pattern + 2, line + compl_col, compl_length); } } @@ -4267,7 +4345,9 @@ static int ins_compl_start(void) // Always add completion for the original text. xfree(compl_orig_text); + kv_destroy(compl_orig_extmarks); compl_orig_text = xstrnsave(line + compl_col, (size_t)compl_length); + save_orig_extmarks(); int flags = CP_ORIGINAL_TEXT; if (p_ic) { flags |= CP_ICASE; @@ -4276,6 +4356,7 @@ static int ins_compl_start(void) flags, false) != OK) { XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_orig_text); + kv_destroy(compl_orig_extmarks); return FAIL; } @@ -4391,7 +4472,7 @@ int ins_complete(int c, bool enable_pum) // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert // mode. if (got_int && !global_busy) { - (void)vgetc(); + vgetc(); got_int = false; } @@ -4508,6 +4589,7 @@ static unsigned quote_meta(char *dest, char *src, int len) void free_insexpand_stuff(void) { XFREE_CLEAR(compl_orig_text); + kv_destroy(compl_orig_extmarks); callback_free(&cfu_cb); callback_free(&ofu_cb); callback_free(&tsrfu_cb); |