diff options
Diffstat (limited to 'src/nvim/edit.c')
| -rw-r--r-- | src/nvim/edit.c | 274 |
1 files changed, 220 insertions, 54 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 667bc54e2e..49bc2ab2f0 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -58,9 +58,10 @@ #include "nvim/os/input.h" #include "nvim/os/time.h" -/* - * definitions used for CTRL-X submode - */ +// Definitions used for CTRL-X submode. +// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] +// and ctrl_x_mode_names[]. + #define CTRL_X_WANT_IDENT 0x100 #define CTRL_X_NOT_DEFINED_YET 1 @@ -83,17 +84,18 @@ #define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] #define CTRL_X_MODE_LINE_OR_EVAL(m) (m == CTRL_X_WHOLE_LINE || m == CTRL_X_EVAL) +// Message for CTRL-X mode, index is ctrl_x_mode. static char *ctrl_x_msgs[] = { - N_(" Keyword completion (^N^P)"), /* ctrl_x_mode == 0, ^P/^N compl. */ + N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl. N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), - NULL, + NULL, // CTRL_X_SCROLL: depends on state N_(" Whole line completion (^L^N^P)"), N_(" File name completion (^F^N^P)"), N_(" Tag completion (^]^N^P)"), N_(" Path pattern completion (^N^P)"), N_(" Definition completion (^D^N^P)"), - NULL, + NULL, // CTRL_X_FINISHED N_(" Dictionary completion (^K^N^P)"), N_(" Thesaurus completion (^T^N^P)"), N_(" Command-line completion (^V^N^P)"), @@ -104,6 +106,26 @@ static char *ctrl_x_msgs[] = NULL, // CTRL_X_EVAL doesn't use msg. }; +static char *ctrl_x_mode_names[] = { + "keyword", + "ctrl_x", + "unknown", // CTRL_X_SCROLL + "whole_line", + "files", + "tags", + "path_patterns", + "path_defines", + "unknown", // CTRL_X_FINISHED + "dictionary", + "thesaurus", + "cmdline", + "function", + "omni", + "spell", + NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" + "eval" +}; + static char e_hitend[] = N_("Hit end of paragraph"); static char e_complwin[] = N_("E839: Completion function changed window"); static char e_compldel[] = N_("E840: Completion function deleted text"); @@ -115,16 +137,18 @@ typedef struct compl_S compl_T; struct compl_S { compl_T *cp_next; compl_T *cp_prev; - char_u *cp_str; /* matched text */ - char cp_icase; /* TRUE or FALSE: ignore case */ - char_u *(cp_text[CPT_COUNT]); /* text for the menu */ - char_u *cp_fname; /* file containing the match, allocated when - * cp_flags has FREE_FNAME */ - int cp_flags; /* ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME */ - int cp_number; /* sequence number */ + char_u *cp_str; // matched text + char cp_icase; // TRUE or FALSE: ignore case + char cp_equal; // TRUE or FALSE: ins_compl_equal always ok + char_u *(cp_text[CPT_COUNT]); // text for the menu + char_u *cp_fname; // file containing the match, allocated when + // cp_flags has FREE_FNAME + int cp_flags; // ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME + int cp_number; // sequence number }; -#define ORIGINAL_TEXT (1) /* the original text when the expansion begun */ +// flags for ins_compl_add() +#define ORIGINAL_TEXT (1) // the original text when the expansion begun #define FREE_FNAME (2) /* @@ -645,8 +669,9 @@ static int insert_execute(VimState *state, int key) // there is nothing to add, CTRL-L works like CTRL-P then. if (s->c == Ctrl_L && (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode) - || (int)STRLEN(compl_shown_match->cp_str) - > curwin->w_cursor.col - compl_col)) { + || (compl_shown_match->cp_str != NULL + && (int)STRLEN(compl_shown_match->cp_str) + > curwin->w_cursor.col - compl_col))) { ins_compl_addfrommatch(); return 1; // continue } @@ -1376,7 +1401,7 @@ ins_redraw ( // Trigger CursorMoved if the cursor moved. Not when the popup menu is // visible, the command might delete it. if (ready && (has_event(EVENT_CURSORMOVEDI) || curwin->w_p_cole > 0) - && !equalpos(last_cursormoved, curwin->w_cursor) + && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor) && !pum_visible()) { // Need to update the screen first, to make sure syntax // highlighting is correct after making a change (e.g., inserting @@ -1392,7 +1417,7 @@ ins_redraw ( ins_apply_autocmds(EVENT_CURSORMOVEDI); } conceal_cursor_moved = true; - last_cursormoved = curwin->w_cursor; + curwin->w_last_cursormoved = curwin->w_cursor; } // Trigger TextChangedI if changedtick differs. @@ -2012,14 +2037,14 @@ static bool ins_compl_accept_char(int c) return vim_iswordc(c); } -/* - * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the - * case of the originally typed text is used, and the case of the completed - * text is inferred, ie this tries to work out what case you probably wanted - * the rest of the word to be in -- webb - */ -int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags) +// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the +// case of the originally typed text is used, and the case of the completed +// text is inferred, ie this tries to work out what case you probably wanted +// the rest of the word to be in -- webb +int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname, + int dir, int flags) { + char_u *str = str_arg; int i, c; int actual_len; /* Take multi-byte characters */ int actual_compl_length; /* into account. */ @@ -2148,10 +2173,10 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int xfree(wca); - return ins_compl_add(IObuff, len, icase, fname, NULL, false, dir, flags, - false); + str = IObuff; } - return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false); + return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, + false, false); } /// Add a match to the list of matches @@ -2168,6 +2193,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /// cptext itself will not be freed. /// @param[in] cdir Completion direction. /// @param[in] adup True if duplicate matches are to be accepted. +/// @param[in] equal Match is always accepted by ins_compl_equal. /// /// @return NOTDONE if the given string is already in the list of completions, /// otherwise it is added to the list and OK is returned. FAIL will be @@ -2176,7 +2202,8 @@ static int ins_compl_add(char_u *const str, int len, const bool icase, char_u *const fname, char_u *const *const cptext, const bool cptext_allocated, - const Direction cdir, int flags, const bool adup) + const Direction cdir, int flags, const bool adup, + int equal) FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; @@ -2228,6 +2255,7 @@ static int ins_compl_add(char_u *const str, int len, match->cp_number = 0; match->cp_str = vim_strnsave(str, len); match->cp_icase = icase; + match->cp_equal = equal; /* match-fname is: * - compl_curr_match->cp_fname if it is a string equal to fname. @@ -2301,6 +2329,9 @@ static int ins_compl_add(char_u *const str, int len, static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { + if (match->cp_equal) { + return true; + } if (match->cp_icase) { return STRNICMP(match->cp_str, str, len) == 0; } @@ -2374,7 +2405,8 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) for (i = 0; i < num_matches && add_r != FAIL; i++) if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, false, dir, 0, false)) == OK) { + NULL, NULL, false, dir, 0, false, + false)) == OK) { // If dir was BACKWARD then honor it just once. dir = FORWARD; } @@ -2443,7 +2475,7 @@ void set_completion(colnr_T startcol, list_T *list) compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, compl_length); if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, - ORIGINAL_TEXT, false) != OK) { + ORIGINAL_TEXT, false, false) != OK) { return; } @@ -2656,8 +2688,25 @@ void ins_compl_show_pum(void) col = curwin->w_cursor.col; curwin->w_cursor.col = compl_col; pum_selected_item = cur; - pum_display(compl_match_array, compl_match_arraysize, cur, array_changed); + pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); curwin->w_cursor.col = col; + + if (!has_event(EVENT_COMPLETECHANGED)) { + return; + } + dict_T *dict = get_vim_var_dict(VV_EVENT); + if (cur < 0) { + tv_dict_add_dict(dict, S_LEN("completed_item"), tv_dict_alloc()); + } else { + dict_T *item = ins_compl_dict_alloc(compl_curr_match); + tv_dict_add_dict(dict, S_LEN("completed_item"), item); + } + pum_set_boundings(dict); + tv_dict_set_keys_readonly(dict); + textlock++; + apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, false, curbuf); + textlock--; + tv_dict_clear(dict); } #define DICT_FIRST (1) /* use just first element in "dict" */ @@ -2963,6 +3012,99 @@ bool ins_compl_active(void) return compl_started; } +// Get complete information +void get_complete_info(list_T *what_list, dict_T *retdict) +{ +#define CI_WHAT_MODE 0x01 +#define CI_WHAT_PUM_VISIBLE 0x02 +#define CI_WHAT_ITEMS 0x04 +#define CI_WHAT_SELECTED 0x08 +#define CI_WHAT_INSERTED 0x10 +#define CI_WHAT_ALL 0xff + int what_flag; + + if (what_list == NULL) { + what_flag = CI_WHAT_ALL; + } else { + what_flag = 0; + for (listitem_T *item = tv_list_first(what_list) + ; item != NULL + ; item = TV_LIST_ITEM_NEXT(what_list, item)) { + const char *what = tv_get_string(TV_LIST_ITEM_TV(item)); + + if (STRCMP(what, "mode") == 0) { + what_flag |= CI_WHAT_MODE; + } else if (STRCMP(what, "pum_visible") == 0) { + what_flag |= CI_WHAT_PUM_VISIBLE; + } else if (STRCMP(what, "items") == 0) { + what_flag |= CI_WHAT_ITEMS; + } else if (STRCMP(what, "selected") == 0) { + what_flag |= CI_WHAT_SELECTED; + } else if (STRCMP(what, "inserted") == 0) { + what_flag |= CI_WHAT_INSERTED; + } + } + } + + int ret = OK; + if (what_flag & CI_WHAT_MODE) { + ret = tv_dict_add_str(retdict, S_LEN("mode"), + (char *)ins_compl_mode()); + } + + if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) { + ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible()); + } + + if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { + list_T *li = tv_list_alloc(ins_compl_len()); + + ret = tv_dict_add_list(retdict, S_LEN("items"), li); + if (ret == OK && compl_first_match != NULL) { + compl_T *match = compl_first_match; + do { + if (!(match->cp_flags & ORIGINAL_TEXT)) { + dict_T *di = tv_dict_alloc(); + + tv_list_append_dict(li, di); + tv_dict_add_str(di, S_LEN("word"), + (char *)EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(di, S_LEN("abbr"), + (char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(di, S_LEN("menu"), + (char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(di, S_LEN("kind"), + (char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(di, S_LEN("info"), + (char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + tv_dict_add_str(di, S_LEN("user_data"), + (char *)EMPTY_IF_NULL(match->cp_text[CPT_USER_DATA])); + } + match = match->cp_next; + } while (match != NULL && match != compl_first_match); + } + } + + if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { + ret = tv_dict_add_nr(retdict, S_LEN("selected"), + (compl_curr_match != NULL) + ? compl_curr_match->cp_number - 1 : -1); + } + + // TODO(vim): + // if (ret == OK && (what_flag & CI_WHAT_INSERTED)) +} + +// Return Insert completion mode name string +static char_u * ins_compl_mode(void) +{ + if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || compl_started) { + return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; + } + return (char_u *)""; +} + + /* * Delete one character before the cursor and show the subset of the matches * that match the word that is now before the cursor. @@ -3626,6 +3768,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) bool icase = false; bool adup = false; bool aempty = false; + bool aequal = false; char *(cptext[CPT_COUNT]); if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { @@ -3640,6 +3783,9 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase"); adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); + if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL) { + aequal = tv_dict_get_number(tv->vval.v_dict, "equal"); + } } else { word = (const char *)tv_get_string_chk(tv); memset(cptext, 0, sizeof(cptext)); @@ -3651,7 +3797,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) return FAIL; } return ins_compl_add((char_u *)word, -1, icase, NULL, - (char_u **)cptext, true, dir, 0, adup); + (char_u **)cptext, true, dir, 0, adup, aequal); } /* @@ -4097,31 +4243,37 @@ static void ins_compl_insert(int in_compl_func) else compl_used_match = TRUE; - // Set completed item. + dict_T *dict = ins_compl_dict_alloc(compl_shown_match); + set_vim_var_dict(VV_COMPLETED_ITEM, dict); + if (!in_compl_func) { + compl_curr_match = compl_shown_match; + } +} + +// Convert to complete item dict +static dict_T *ins_compl_dict_alloc(compl_T *match) +{ // { word, abbr, menu, kind, info } dict_T *dict = tv_dict_alloc(); tv_dict_add_str( dict, S_LEN("word"), - (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str)); + (const char *)EMPTY_IF_NULL(match->cp_str)); tv_dict_add_str( dict, S_LEN("abbr"), - (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); tv_dict_add_str( dict, S_LEN("menu"), - (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU])); tv_dict_add_str( dict, S_LEN("kind"), - (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND])); tv_dict_add_str( dict, S_LEN("info"), - (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); tv_dict_add_str( dict, S_LEN("user_data"), - (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_USER_DATA])); - set_vim_var_dict(VV_COMPLETED_ITEM, dict); - if (!in_compl_func) { - compl_curr_match = compl_shown_match; - } + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_USER_DATA])); + return dict; } /* @@ -4807,7 +4959,7 @@ static int ins_complete(int c, bool enable_pum) xfree(compl_orig_text); compl_orig_text = vim_strnsave(line + compl_col, compl_length); if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, - ORIGINAL_TEXT, false) != OK) { + ORIGINAL_TEXT, false, false) != OK) { xfree(compl_pattern); compl_pattern = NULL; xfree(compl_orig_text); @@ -5486,16 +5638,33 @@ internal_format ( /* remember position of blank just before text */ end_col = curwin->w_cursor.col; - /* find start of sequence of blanks */ + // find start of sequence of blanks + int wcc = 0; // counter for whitespace chars while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) { dec_cursor(); cc = gchar_cursor(); + + // Increment count of how many whitespace chars in this + // group; we only need to know if it's more than one. + if (wcc < 2) { + wcc++; + } } - if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) - break; /* only spaces in front of text */ - /* Don't break until after the comment leader */ - if (curwin->w_cursor.col < leader_len) + if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) { + break; // only spaces in front of text + } + + // Don't break after a period when 'formatoptions' has 'p' and + // there are less than two spaces. + if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) { + continue; + } + + // Don't break until after the comment leader + if (curwin->w_cursor.col < leader_len) { break; + } + if (has_format_option(FO_ONE_LETTER)) { /* do not break after one-letter words */ if (curwin->w_cursor.col == 0) @@ -5859,10 +6028,7 @@ comp_textwidth ( textwidth -= 1; } textwidth -= curwin->w_p_fdc; - - if (signcolumn_on(curwin)) { - textwidth -= 1; - } + textwidth -= win_signcol_count(curwin); if (curwin->w_p_nu || curwin->w_p_rnu) textwidth -= 8; |
