aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/insexpand.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/insexpand.c')
-rw-r--r--src/nvim/insexpand.c721
1 files changed, 430 insertions, 291 deletions
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index bd7ee55722..e21433f5ec 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -10,6 +10,7 @@
#include <string.h>
#include "klib/kvec.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -37,7 +38,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
@@ -156,7 +156,8 @@ typedef struct compl_S compl_T;
struct compl_S {
compl_T *cp_next;
compl_T *cp_prev;
- char *cp_str; ///< matched text
+ compl_T *cp_match_next; ///< matched next compl_T
+ String cp_str; ///< matched text
char *(cp_text[CPT_COUNT]); ///< text for the menu
typval_T cp_user_data;
char *cp_fname; ///< file containing the match, allocated when
@@ -164,6 +165,7 @@ struct compl_S {
int cp_flags; ///< CP_ values
int cp_number; ///< sequence number
int cp_score; ///< fuzzy match score
+ bool cp_in_match_array; ///< collected by compl_match_array
int cp_user_abbr_hlattr; ///< highlight attribute for abbr
int cp_user_kind_hlattr; ///< highlight attribute for kind
};
@@ -219,7 +221,7 @@ static bool compl_enter_selects = false;
/// When "compl_leader" is not NULL only matches that start with this string
/// are used.
-static char *compl_leader = NULL;
+static String compl_leader = STRING_INIT;
static bool compl_get_longest = false; ///< put longest common string in compl_leader
@@ -244,8 +246,7 @@ static bool compl_started = false;
static int ctrl_x_mode = CTRL_X_NORMAL;
static int compl_matches = 0; ///< number of completion matches
-static char *compl_pattern = NULL;
-static size_t compl_patternlen = 0;
+static String compl_pattern = STRING_INIT;
static Direction compl_direction = FORWARD;
static Direction compl_shows_dir = FORWARD;
static int compl_pending = 0; ///< > 1 for postponed CTRL-N
@@ -255,8 +256,9 @@ static pos_T compl_startpos;
static int compl_length = 0;
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
+static colnr_T compl_ins_end_col = 0;
+static String compl_orig_text = STRING_INIT; ///< 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;
@@ -281,6 +283,11 @@ static size_t spell_bad_len = 0; // length of located bad word
static int compl_selected_item = -1;
+// "compl_match_array" points the currently displayed list of entries in the
+// popup menu. It is NULL when there is no popup menu.
+static pumitem_T *compl_match_array = NULL;
+static int compl_match_arraysize;
+
/// CTRL-X pressed in Insert mode.
void ins_ctrl_x(void)
{
@@ -475,7 +482,7 @@ bool check_compl_option(bool dict_opt)
msg((dict_opt ? _("'dictionary' option is empty") : _("'thesaurus' option is empty")),
HLF_E);
if (emsg_silent == 0 && !in_assert_fails) {
- vim_beep(BO_COMPL);
+ vim_beep(kOptBoFlagComplete);
setcursor();
ui_flush();
os_delay(2004, false);
@@ -560,11 +567,19 @@ static bool is_first_match(const compl_T *const match)
return match == compl_first_match;
}
-static void do_autocmd_completedone(int c)
+static void do_autocmd_completedone(int c, int mode, char *word)
{
save_v_event_T save_v_event;
dict_T *v_event = get_v_event(&save_v_event);
+ mode = mode & ~CTRL_X_WANT_IDENT;
+ char *mode_str = NULL;
+ if (ctrl_x_mode_names[mode]) {
+ mode_str = ctrl_x_mode_names[mode];
+ }
+ tv_dict_add_str(v_event, S_LEN("complete_word"), word != NULL ? word : "");
+ tv_dict_add_str(v_event, S_LEN("complete_type"), mode_str != NULL ? mode_str : "");
+
tv_dict_add_str(v_event, S_LEN("reason"), (c == Ctrl_Y ? "accept" : "cancel"));
tv_dict_set_keys_readonly(v_event);
@@ -624,7 +639,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// Rule 1: Were any chars converted to lower?
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -644,7 +659,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// upper case.
if (!has_lower) {
bool was_letter = false;
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
@@ -660,7 +675,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// Copy the original case of the part we typed.
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -690,7 +705,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
ga_grow(&gap, IOSIZE);
*p = NUL;
STRCPY(gap.ga_data, IObuff);
- gap.ga_len = (int)strlen(IObuff);
+ gap.ga_len = (int)(p - IObuff);
} else {
p += utf_char2bytes(wca[i++], p);
}
@@ -737,7 +752,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
// Find actual length of original text.
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
compl_char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
@@ -747,7 +762,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
// "char_len" may be smaller than "compl_char_len" when using
// thesaurus, only use the minimum when comparing.
- int min_len = char_len < compl_char_len ? char_len : compl_char_len;
+ int min_len = MIN(char_len, compl_char_len);
str = ins_compl_infercase_gettext(str, char_len, compl_char_len, min_len, &tofree);
}
@@ -758,7 +773,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 +804,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 +814,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;
@@ -825,8 +841,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
match = compl_first_match;
do {
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)) {
+ && strncmp(match->cp_str.data, str, (size_t)len) == 0
+ && ((int)match->cp_str.size <= len || match->cp_str.data[len] == NUL)) {
if (cptext_allocated) {
free_cptext(cptext);
}
@@ -846,7 +862,7 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
if (flags & CP_ORIGINAL_TEXT) {
match->cp_number = 0;
}
- match->cp_str = xstrnsave(str, (size_t)len);
+ match->cp_str = cbuf_to_string(str, (size_t)len);
// match-fname is:
// - compl_curr_match->cp_fname if it is a string equal to fname.
@@ -864,8 +880,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;
@@ -928,27 +944,55 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len)
return true;
}
if (match->cp_flags & CP_ICASE) {
- return STRNICMP(match->cp_str, str, len) == 0;
+ return STRNICMP(match->cp_str.data, str, len) == 0;
+ }
+ return strncmp(match->cp_str.data, str, len) == 0;
+}
+
+/// when len is -1 mean use whole length of p otherwise part of p
+static void ins_compl_insert_bytes(char *p, int len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (len == -1) {
+ len = (int)strlen(p);
}
- return strncmp(match->cp_str, str, len) == 0;
+ assert(len >= 0);
+ ins_bytes_len(p, (size_t)len);
+ compl_ins_end_col = curwin->w_cursor.col;
+}
+
+/// Checks if the column is within the currently inserted completion text
+/// column range. If it is, it returns a special highlight attribute.
+/// -1 mean normal item.
+int ins_compl_col_range_attr(int col)
+{
+ if (get_cot_flags() & kOptCotFlagFuzzy) {
+ return -1;
+ }
+
+ if (col >= (compl_col + (int)ins_compl_leader_len()) && col < compl_ins_end_col) {
+ return syn_name2attr("ComplMatchIns");
+ }
+
+ return -1;
}
/// Reduce the longest common string for match "match".
static void ins_compl_longest_match(compl_T *match)
{
- if (compl_leader == NULL) {
+ if (compl_leader.data == NULL) {
// First match, use it as a whole.
- compl_leader = xstrdup(match->cp_str);
+ compl_leader = copy_string(match->cp_str, NULL);
bool had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(false);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
// again after redrawing.
if (!had_match) {
- ins_compl_delete();
+ ins_compl_delete(false);
}
compl_used_match = false;
@@ -956,8 +1000,8 @@ static void ins_compl_longest_match(compl_T *match)
}
// Reduce the text if this match differs from compl_leader.
- char *p = compl_leader;
- char *s = match->cp_str;
+ char *p = compl_leader.data;
+ char *s = match->cp_str.data;
while (*p != NUL) {
int c1 = utf_ptr2char(p);
int c2 = utf_ptr2char(s);
@@ -974,15 +1018,17 @@ static void ins_compl_longest_match(compl_T *match)
if (*p != NUL) {
// Leader was shortened, need to change the inserted text.
*p = NUL;
+ compl_leader.size = (size_t)(p - compl_leader.data);
+
bool had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(false);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
// again after redrawing.
if (!had_match) {
- ins_compl_delete();
+ ins_compl_delete(false);
}
}
@@ -997,9 +1043,9 @@ static void ins_compl_add_matches(int num_matches, char **matches, int icase)
Direction dir = compl_direction;
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) {
+ add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir,
+ CP_FAST | (icase ? CP_ICASE : 0), false, NULL);
+ if (add_r == OK) {
// If dir was BACKWARD then honor it just once.
dir = FORWARD;
}
@@ -1038,8 +1084,8 @@ bool ins_compl_has_shown_match(void)
/// Return whether the shown match is long enough.
bool ins_compl_long_shown_match(void)
{
- return compl_shown_match != NULL && compl_shown_match->cp_str != NULL
- && (colnr_T)strlen(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col;
+ return compl_shown_match != NULL && compl_shown_match->cp_str.data != NULL
+ && (colnr_T)compl_shown_match->cp_str.size > curwin->w_cursor.col - compl_col;
}
/// Get the local or global value of 'completeopt' flags.
@@ -1048,11 +1094,6 @@ unsigned get_cot_flags(void)
return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags;
}
-/// "compl_match_array" points the currently displayed list of entries in the
-/// popup menu. It is NULL when there is no popup menu.
-static pumitem_T *compl_match_array = NULL;
-static int compl_match_arraysize;
-
/// Remove any popup menu.
static void ins_compl_del_pum(void)
{
@@ -1069,7 +1110,7 @@ bool pum_wanted(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// "completeopt" must contain "menu" or "menuone"
- return (get_cot_flags() & COT_ANY_MENU) != 0;
+ return (get_cot_flags() & (kOptCotFlagMenu | kOptCotFlagMenuone)) != 0;
}
/// Check that there are two or more matches to be shown in the popup menu.
@@ -1088,7 +1129,7 @@ static bool pum_enough_matches(void)
comp = comp->cp_next;
} while (!is_first_match(comp));
- if (get_cot_flags() & COT_MENUONE) {
+ if (get_cot_flags() & kOptCotFlagMenuone) {
return i >= 1;
}
return i >= 2;
@@ -1099,7 +1140,7 @@ static dict_T *ins_compl_dict_alloc(compl_T *match)
{
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc_lock(VAR_FIXED);
- tv_dict_add_str(dict, S_LEN("word"), match->cp_str);
+ tv_dict_add_str(dict, S_LEN("word"), match->cp_str.data);
tv_dict_add_str(dict, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
tv_dict_add_str(dict, S_LEN("menu"), match->cp_text[CPT_MENU]);
tv_dict_add_str(dict, S_LEN("kind"), match->cp_text[CPT_KIND]);
@@ -1168,54 +1209,47 @@ static int ins_compl_build_pum(void)
XFREE_CLEAR(compl_leader);
}
- const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0;
int max_fuzzy_score = 0;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
- bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 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));
+ bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0;
+ bool fuzzy_filter = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
+ bool fuzzy_sort = fuzzy_filter && !(cur_cot_flags & kOptCotFlagNosort);
+ 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.
bool shown_match_ok = match_at_original_text(compl_shown_match);
- if (strequal(compl_leader, compl_orig_text) && !shown_match_ok) {
+ if (strequal(compl_leader.data, compl_orig_text.data) && !shown_match_ok) {
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 {
+ comp->cp_in_match_array = false;
+ // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
+ // set the cp_score for later comparisons.
+ if (fuzzy_filter && compl_leader.data != NULL && compl_leader.size > 0) {
+ comp->cp_score = fuzzy_match_str(comp->cp_str.data, compl_leader.data);
+ }
+
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))) {
- if (!shown_match_ok && !compl_fuzzy_match) {
+ && (compl_leader.data == NULL
+ || ins_compl_equal(comp, compl_leader.data, compl_leader.size)
+ || (fuzzy_filter && comp->cp_score > 0))) {
+ compl_match_arraysize++;
+ comp->cp_in_match_array = true;
+ if (match_head == NULL) {
+ match_head = comp;
+ } else {
+ match_tail->cp_match_next = comp;
+ }
+ match_tail = comp;
+ if (!shown_match_ok && !fuzzy_filter) {
if (comp == compl_shown_match || did_find_shown_match) {
// This item is the shown match or this is the
// first displayed item after the shown match.
@@ -1228,64 +1262,36 @@ static int ins_compl_build_pum(void)
shown_compl = comp;
}
cur = i;
- } else if (compl_fuzzy_match) {
+ } else if (fuzzy_filter) {
if (i == 0) {
shown_compl = comp;
}
// Update the maximum fuzzy score and the shown match
// if the current item's score is higher
- if (comp->cp_score > max_fuzzy_score) {
+ if (fuzzy_sort && comp->cp_score > max_fuzzy_score) {
did_find_shown_match = true;
max_fuzzy_score = comp->cp_score;
if (!compl_no_select) {
compl_shown_match = comp;
}
+ } else if (!fuzzy_sort && i == 0 && !compl_no_select) {
+ compl_shown_match = shown_compl;
}
-
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) {
+ if (comp == compl_shown_match && !fuzzy_filter) {
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,7 +1302,31 @@ static int ins_compl_build_pum(void)
comp = comp->cp_next;
} while (comp != NULL && !is_first_match(comp));
- if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
+ 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) {
+ compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR] != NULL
+ ? comp->cp_text[CPT_ABBR] : comp->cp_str.data;
+ 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;
+ compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU] != NULL
+ ? comp->cp_text[CPT_MENU] : comp->cp_fname;
+ compl_T *match_next = comp->cp_match_next;
+ comp->cp_match_next = NULL;
+ comp = match_next;
+ }
+
+ if (fuzzy_sort && compl_leader.data != NULL && compl_leader.size > 0) {
for (i = 0; i < compl_match_arraysize; i++) {
compl_match_array[i].pum_idx = i;
}
@@ -1334,7 +1364,7 @@ void ins_compl_show_pum(void)
} else {
// popup menu already exists, only need to find the current item.
for (int i = 0; i < compl_match_arraysize; i++) {
- if (compl_match_array[i].pum_text == compl_shown_match->cp_str
+ if (compl_match_array[i].pum_text == compl_shown_match->cp_str.data
|| compl_match_array[i].pum_text == compl_shown_match->cp_text[CPT_ABBR]) {
cur = i;
break;
@@ -1403,7 +1433,13 @@ bool compl_match_curr_select(int selected)
/// Get current completion leader
char *ins_compl_leader(void)
{
- return compl_leader != NULL ? compl_leader : compl_orig_text;
+ return compl_leader.data != NULL ? compl_leader.data : compl_orig_text.data;
+}
+
+/// Get current completion leader length
+size_t ins_compl_leader_len(void)
+{
+ return compl_leader.data != NULL ? compl_leader.size : compl_orig_text.size;
}
/// Add any identifiers that match the given pattern "pat" in the list of
@@ -1562,6 +1598,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags,
FILE *fp = os_fopen(files[i], "r"); // open dictionary file
if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN)) {
msg_hist_off = true; // reset in msg_trunc()
+ msg_ext_set_kind("completion");
vim_snprintf(IObuff, IOSIZE,
_("Scanning dictionary: %s"), files[i]);
msg_trunc(IObuff, true, HLF_R);
@@ -1577,11 +1614,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags,
char *ptr = buf;
while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) {
ptr = regmatch->startp[0];
- if (ctrl_x_mode_line_or_eval()) {
- ptr = find_line_end(ptr);
- } else {
- ptr = find_word_end(ptr);
- }
+ ptr = ctrl_x_mode_line_or_eval() ? find_line_end(ptr) : find_word_end(ptr);
int add_r = ins_compl_add_infercase(regmatch->startp[0],
(int)(ptr - regmatch->startp[0]),
p_ic, files[i], *dir, false);
@@ -1652,9 +1685,8 @@ static char *find_line_end(char *ptr)
/// Free the list of completions
static void ins_compl_free(void)
{
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_leader);
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_leader);
if (compl_first_match == NULL) {
return;
@@ -1667,7 +1699,7 @@ static void ins_compl_free(void)
do {
compl_T *match = compl_curr_match;
compl_curr_match = compl_curr_match->cp_next;
- xfree(match->cp_str);
+ API_CLEAR_STRING(match->cp_str);
// several entries may use the same fname, free it just once.
if (match->cp_flags & CP_FREE_FNAME) {
xfree(match->cp_fname);
@@ -1687,12 +1719,12 @@ void ins_compl_clear(void)
compl_cont_status = 0;
compl_started = false;
compl_matches = 0;
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_leader);
+ compl_ins_end_col = 0;
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_leader);
edit_submode_extra = NULL;
kv_destroy(compl_orig_extmarks);
- XFREE_CLEAR(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
compl_enter_selects = false;
// clear v:completed_item
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
@@ -1705,6 +1737,12 @@ bool ins_compl_active(void)
return compl_started;
}
+/// Return true when wp is the actual completion window
+bool ins_compl_win_active(win_T *wp)
+{
+ return ins_compl_active() && !(wp->w_p_pvw || wp->w_float_is_info);
+}
+
/// Selected one of the matches. When false the match was edited or using the
/// longest common string.
bool ins_compl_used_match(void)
@@ -1743,12 +1781,33 @@ int ins_compl_len(void)
return compl_length;
}
+/// Return true when preinsert is set otherwise FALSE.
+static bool ins_compl_has_preinsert(void)
+{
+ return (get_cot_flags() & (kOptCotFlagFuzzy|kOptCotFlagPreinsert)) == kOptCotFlagPreinsert;
+}
+
+/// Returns true if the pre-insert effect is valid and the cursor is within
+/// the `compl_ins_end_col` range.
+bool ins_compl_preinsert_effect(void)
+{
+ if (!ins_compl_has_preinsert()) {
+ return false;
+ }
+
+ return curwin->w_cursor.col < compl_ins_end_col;
+}
+
/// Delete one character before the cursor and show the subset of the matches
/// that match the word that is now before the cursor.
/// Returns the character to be used, NUL if the work is done and another char
/// to be got from the user.
int ins_compl_bs(void)
{
+ if (ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
char *line = get_cursor_line_ptr();
char *p = line + curwin->w_cursor.col;
MB_PTR_BACK(line, p);
@@ -1776,8 +1835,9 @@ int ins_compl_bs(void)
// TODO(bfredl): get rid of random update_screen() calls deep inside completion logic
line = get_cursor_line_ptr();
- xfree(compl_leader);
- compl_leader = xstrnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col));
+ API_CLEAR_STRING(compl_leader);
+ compl_leader = cbuf_to_string(line + compl_col,
+ (size_t)(p_off - (ptrdiff_t)compl_col));
ins_compl_new_leader();
if (compl_shown_match != NULL) {
@@ -1810,12 +1870,12 @@ static bool ins_compl_need_restart(void)
static void ins_compl_new_leader(void)
{
ins_compl_del_pum();
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(true);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
compl_used_match = false;
if (compl_started) {
- ins_compl_set_original_text(compl_leader);
+ ins_compl_set_original_text(compl_leader.data, compl_leader.size);
} else {
spell_bad_len = 0; // need to redetect bad word
// Matches were cleared, need to search for them now.
@@ -1833,8 +1893,13 @@ static void ins_compl_new_leader(void)
ins_compl_show_pum();
// Don't let Enter select the original text when there is no popup menu.
+ if (compl_match_array == NULL) {
+ compl_enter_selects = false;
+ } else if (ins_compl_has_preinsert() && compl_leader.size > 0) {
+ ins_compl_insert(false, true);
+ }
// Don't let Enter select when use user function and refresh_always is set
- if (compl_match_array == NULL || ins_compl_refresh_always()) {
+ if (ins_compl_refresh_always()) {
compl_enter_selects = false;
}
}
@@ -1857,6 +1922,10 @@ void ins_compl_addleader(int c)
{
int cc;
+ if (ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
if (stop_arrow() == FAIL) {
return;
}
@@ -1875,9 +1944,9 @@ void ins_compl_addleader(int c)
ins_compl_restart();
}
- xfree(compl_leader);
- compl_leader = xstrnsave(get_cursor_line_ptr() + compl_col,
- (size_t)(curwin->w_cursor.col - compl_col));
+ API_CLEAR_STRING(compl_leader);
+ compl_leader = cbuf_to_string(get_cursor_line_ptr() + compl_col,
+ (size_t)(curwin->w_cursor.col - compl_col));
ins_compl_new_leader();
}
@@ -1897,19 +1966,19 @@ static void ins_compl_restart(void)
}
/// Set the first match, the original text.
-static void ins_compl_set_original_text(char *str)
+static void ins_compl_set_original_text(char *str, size_t len)
FUNC_ATTR_NONNULL_ALL
{
// Replace the original text entry.
// The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
// be at the last item for backward completion
if (match_at_original_text(compl_first_match)) { // safety check
- xfree(compl_first_match->cp_str);
- compl_first_match->cp_str = xstrdup(str);
+ API_CLEAR_STRING(compl_first_match->cp_str);
+ compl_first_match->cp_str = cbuf_to_string(str, len);
} else if (compl_first_match->cp_prev != NULL
&& match_at_original_text(compl_first_match->cp_prev)) {
- xfree(compl_first_match->cp_prev->cp_str);
- compl_first_match->cp_prev->cp_str = xstrdup(str);
+ API_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
+ compl_first_match->cp_prev->cp_str = cbuf_to_string(str, len);
}
}
@@ -1919,8 +1988,8 @@ void ins_compl_addfrommatch(void)
{
int len = (int)curwin->w_cursor.col - (int)compl_col;
assert(compl_shown_match != NULL);
- char *p = compl_shown_match->cp_str;
- if ((int)strlen(p) <= len) { // the match is too short
+ char *p = compl_shown_match->cp_str.data;
+ if ((int)compl_shown_match->cp_str.size <= len) { // the match is too short
// When still at the original match use the first entry that matches
// the leader.
if (!match_at_original_text(compl_shown_match)) {
@@ -1928,15 +1997,17 @@ void ins_compl_addfrommatch(void)
}
p = NULL;
+ size_t plen = 0;
for (compl_T *cp = compl_shown_match->cp_next; cp != NULL
&& !is_first_match(cp); cp = cp->cp_next) {
- if (compl_leader == NULL
- || ins_compl_equal(cp, compl_leader, strlen(compl_leader))) {
- p = cp->cp_str;
+ if (compl_leader.data == NULL
+ || ins_compl_equal(cp, compl_leader.data, compl_leader.size)) {
+ p = cp->cp_str.data;
+ plen = cp->cp_str.size;
break;
}
}
- if (p == NULL || (int)strlen(p) <= len) {
+ if (p == NULL || (int)plen <= len) {
return;
}
}
@@ -2076,7 +2147,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// Get here when we have finished typing a sequence of ^N and
// ^P or other completion characters in CTRL-X mode. Free up
// memory that was used, and make sure we can redo the insert.
- if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
+ if (compl_curr_match != NULL || compl_leader.data != NULL || c == Ctrl_E) {
// If any of the original typed text has been changed, eg when
// ignorecase is set, we must add back-spaces to the redo
// buffer. We add as few as necessary to delete just the part
@@ -2085,7 +2156,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// CTRL-E then don't use the current match.
char *ptr;
if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
- ptr = compl_curr_match->cp_str;
+ ptr = compl_curr_match->cp_str.data;
} else {
ptr = NULL;
}
@@ -2121,36 +2192,46 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
}
}
+ char *word = NULL;
// If the popup menu is displayed pressing CTRL-Y means accepting
// the selection without inserting anything. When
// compl_enter_selects is set the Enter key does the same.
if ((c == Ctrl_Y || (compl_enter_selects
&& (c == CAR || c == K_KENTER || c == NL)))
&& pum_visible()) {
+ word = xstrdup(compl_shown_match->cp_str.data);
retval = true;
+ // May need to remove ComplMatchIns highlight.
+ redrawWinline(curwin, curwin->w_cursor.lnum);
}
// CTRL-E means completion is Ended, go back to the typed text.
// but only do this, if the Popup is still visible
if (c == Ctrl_E) {
- ins_compl_delete();
+ ins_compl_delete(false);
char *p = NULL;
- if (compl_leader != NULL) {
- p = compl_leader;
+ size_t plen = 0;
+ if (compl_leader.data != NULL) {
+ p = compl_leader.data;
+ plen = compl_leader.size;
} else if (compl_first_match != NULL) {
- p = compl_orig_text;
+ p = compl_orig_text.data;
+ plen = compl_orig_text.size;
}
if (p != NULL) {
const int compl_len = get_compl_len();
- const int len = (int)strlen(p);
- if (len > compl_len) {
- ins_bytes_len(p + compl_len, (size_t)(len - compl_len));
+ if ((int)plen > compl_len) {
+ ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len);
}
}
restore_orig_extmarks();
retval = true;
}
+ if ((c == Ctrl_W || c == Ctrl_U) && ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
auto_format(false, true);
// Trigger the CompleteDonePre event to give scripts a chance to
@@ -2184,7 +2265,8 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
}
// Trigger the CompleteDone event to give scripts a chance to act
// upon the end of completion.
- do_autocmd_completedone(c);
+ do_autocmd_completedone(c, prev_mode, word);
+ xfree(word);
return retval;
}
@@ -2236,7 +2318,7 @@ bool ins_compl_prep(int c)
// Set "compl_get_longest" when finding the first matches.
if (ctrl_x_mode_not_defined_yet()
|| (ctrl_x_mode_normal() && !compl_started)) {
- compl_get_longest = (get_cot_flags() & COT_LONGEST) != 0;
+ compl_get_longest = (get_cot_flags() & kOptCotFlagLongest) != 0;
compl_used_match = true;
}
@@ -2273,7 +2355,7 @@ bool ins_compl_prep(int c)
} else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) {
// Trigger the CompleteDone event to give scripts a chance to act
// upon the (possibly failed) completion.
- do_autocmd_completedone(c);
+ do_autocmd_completedone(c, ctrl_x_mode, NULL);
}
may_trigger_modechanged();
@@ -2293,18 +2375,18 @@ bool ins_compl_prep(int c)
/// "ptr" is the known leader text or NUL.
static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
{
- int len;
+ int len = 0;
char *ptr = ptr_arg;
if (ptr == NULL) {
- if (compl_leader != NULL) {
- ptr = compl_leader;
+ if (compl_leader.data != NULL) {
+ ptr = compl_leader.data;
} else {
return; // nothing to do
}
}
- if (compl_orig_text != NULL) {
- char *p = compl_orig_text;
+ if (compl_orig_text.data != NULL) {
+ char *p = compl_orig_text.data;
for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {}
if (len > 0) {
len -= utf_head_off(p, p + len);
@@ -2312,8 +2394,6 @@ static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
for (p += len; *p != NUL; MB_PTR_ADV(p)) {
AppendCharToRedobuff(K_BS);
}
- } else {
- len = 0;
}
AppendToRedobuffLit(ptr + len, -1);
}
@@ -2573,9 +2653,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 +2666,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", &u