1
0
forked from aniani/vim

patch 8.2.0084: complete item "user_data" can only be a string

Problem:    Complete item "user_data" can only be a string.
Solution:   Accept any type of variable. (closes #5412)
This commit is contained in:
Bram Moolenaar
2020-01-04 14:32:48 +01:00
parent 5cb0b93d52
commit 0892832bb6
7 changed files with 98 additions and 43 deletions

View File

@@ -1108,7 +1108,8 @@ items:
empty when non-zero this match will be added even when it is empty when non-zero this match will be added even when it is
an empty string an empty string
user_data custom data which is associated with the item and user_data custom data which is associated with the item and
available in |v:completed_item| available in |v:completed_item|; it can be any type;
defaults to an empty string
All of these except "icase", "equal", "dup" and "empty" must be a string. If All of these except "icase", "equal", "dup" and "empty" must be a string. If
an item does not meet these requirements then an error message is given and an item does not meet these requirements then an error message is given and

View File

@@ -447,6 +447,27 @@ dict_add_list(dict_T *d, char *key, list_T *list)
return OK; return OK;
} }
/*
* Add a typval_T entry to dictionary "d".
* Returns FAIL when out of memory and when key already exists.
*/
int
dict_add_tv(dict_T *d, char *key, typval_T *tv)
{
dictitem_T *item;
item = dictitem_alloc((char_u *)key);
if (item == NULL)
return FAIL;
copy_tv(tv, &item->di_tv);
if (dict_add(d, item) == FAIL)
{
dictitem_free(item);
return FAIL;
}
return OK;
}
/* /*
* Add a callback to dictionary "d". * Add a callback to dictionary "d".
* Returns FAIL when out of memory and when key already exists. * Returns FAIL when out of memory and when key already exists.
@@ -589,6 +610,23 @@ dict_find(dict_T *d, char_u *key, int len)
return HI2DI(hi); return HI2DI(hi);
} }
/*
* Get a typval_T item from a dictionary and copy it into "rettv".
* Returns FAIL if the entry doesn't exist or out of memory.
*/
int
dict_get_tv(dict_T *d, char_u *key, typval_T *rettv)
{
dictitem_T *di;
char_u *s;
di = dict_find(d, key, -1);
if (di == NULL)
return FAIL;
copy_tv(&di->di_tv, rettv);
return OK;
}
/* /*
* Get a string item from a dictionary. * Get a string item from a dictionary.
* When "save" is TRUE allocate memory for it. * When "save" is TRUE allocate memory for it.
@@ -745,7 +783,7 @@ get_literal_key(char_u **arg, typval_T *tv)
* Return OK or FAIL. Returns NOTDONE for {expr}. * Return OK or FAIL. Returns NOTDONE for {expr}.
*/ */
int int
dict_get_tv(char_u **arg, typval_T *rettv, int evaluate, int literal) eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
{ {
dict_T *d = NULL; dict_T *d = NULL;
typval_T tvkey; typval_T tvkey;

View File

@@ -2656,7 +2656,7 @@ eval7(
case '#': if ((*arg)[1] == '{') case '#': if ((*arg)[1] == '{')
{ {
++*arg; ++*arg;
ret = dict_get_tv(arg, rettv, evaluate, TRUE); ret = eval_dict(arg, rettv, evaluate, TRUE);
} }
else else
ret = NOTDONE; ret = NOTDONE;
@@ -2668,7 +2668,7 @@ eval7(
*/ */
case '{': ret = get_lambda_tv(arg, rettv, evaluate); case '{': ret = get_lambda_tv(arg, rettv, evaluate);
if (ret == NOTDONE) if (ret == NOTDONE)
ret = dict_get_tv(arg, rettv, evaluate, FALSE); ret = eval_dict(arg, rettv, evaluate, FALSE);
break; break;
/* /*

View File

@@ -91,8 +91,7 @@ static char *ctrl_x_mode_names[] = {
#define CPT_MENU 1 // "menu" #define CPT_MENU 1 // "menu"
#define CPT_KIND 2 // "kind" #define CPT_KIND 2 // "kind"
#define CPT_INFO 3 // "info" #define CPT_INFO 3 // "info"
#define CPT_USER_DATA 4 // "user data" #define CPT_COUNT 4 // Number of entries
#define CPT_COUNT 5 // Number of entries
/* /*
* Structure used to store one match for insert completion. * Structure used to store one match for insert completion.
@@ -104,6 +103,7 @@ struct compl_S
compl_T *cp_prev; compl_T *cp_prev;
char_u *cp_str; // matched text char_u *cp_str; // matched text
char_u *(cp_text[CPT_COUNT]); // text for the menu char_u *(cp_text[CPT_COUNT]); // text for the menu
typval_T cp_user_data;
char_u *cp_fname; // file containing the match, allocated when char_u *cp_fname; // file containing the match, allocated when
// cp_flags has CP_FREE_FNAME // cp_flags has CP_FREE_FNAME
int cp_flags; // CP_ values int cp_flags; // CP_ values
@@ -187,7 +187,7 @@ static expand_T compl_xp;
static int compl_opt_refresh_always = FALSE; static int compl_opt_refresh_always = FALSE;
static int compl_opt_suppress_empty = FALSE; static int compl_opt_suppress_empty = FALSE;
static int ins_compl_add(char_u *str, int len, char_u *fname, char_u **cptext, int cdir, int flags, int adup); 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_longest_match(compl_T *match);
static void ins_compl_del_pum(void); static void ins_compl_del_pum(void);
static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir); static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir);
@@ -560,7 +560,7 @@ ins_compl_add_infercase(
if (icase) if (icase)
flags |= CP_ICASE; flags |= CP_ICASE;
return ins_compl_add(str, len, fname, NULL, dir, flags, FALSE); return ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE);
} }
/* /*
@@ -575,6 +575,7 @@ ins_compl_add(
int len, int len,
char_u *fname, char_u *fname,
char_u **cptext, // extra text for popup menu or NULL char_u **cptext, // extra text for popup menu or NULL
typval_T *user_data, // "user_data" entry or NULL
int cdir, int cdir,
int flags_arg, int flags_arg,
int adup) // accept duplicate match int adup) // accept duplicate match
@@ -646,6 +647,8 @@ ins_compl_add(
if (cptext[i] != NULL && *cptext[i] != NUL) if (cptext[i] != NULL && *cptext[i] != NUL)
match->cp_text[i] = vim_strsave(cptext[i]); match->cp_text[i] = vim_strsave(cptext[i]);
} }
if (user_data != NULL)
match->cp_user_data = *user_data;
// Link the new match structure in the list of matches. // Link the new match structure in the list of matches.
if (compl_first_match == NULL) if (compl_first_match == NULL)
@@ -783,7 +786,7 @@ ins_compl_add_matches(
int dir = compl_direction; int dir = compl_direction;
for (i = 0; i < num_matches && add_r != FAIL; i++) for (i = 0; i < num_matches && add_r != FAIL; i++)
if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, dir, if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, NULL, dir,
icase ? CP_ICASE : 0, FALSE)) == OK) icase ? CP_ICASE : 0, FALSE)) == OK)
// if dir was BACKWARD then honor it just once // if dir was BACKWARD then honor it just once
dir = FORWARD; dir = FORWARD;
@@ -952,7 +955,10 @@ ins_compl_dict_alloc(compl_T *match)
dict_add_string(dict, "menu", match->cp_text[CPT_MENU]); dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
dict_add_string(dict, "kind", match->cp_text[CPT_KIND]); dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
dict_add_string(dict, "info", match->cp_text[CPT_INFO]); dict_add_string(dict, "info", match->cp_text[CPT_INFO]);
dict_add_string(dict, "user_data", match->cp_text[CPT_USER_DATA]); if (match->cp_user_data.v_type == VAR_UNKNOWN)
dict_add_string(dict, "user_data", (char_u *)"");
else
dict_add_tv(dict, "user_data", &match->cp_user_data);
} }
return dict; return dict;
} }
@@ -1453,6 +1459,7 @@ ins_compl_free(void)
vim_free(match->cp_fname); vim_free(match->cp_fname);
for (i = 0; i < CPT_COUNT; ++i) for (i = 0; i < CPT_COUNT; ++i)
vim_free(match->cp_text[i]); vim_free(match->cp_text[i]);
clear_tv(&match->cp_user_data);
vim_free(match); vim_free(match);
} while (compl_curr_match != NULL && compl_curr_match != compl_first_match); } while (compl_curr_match != NULL && compl_curr_match != compl_first_match);
compl_first_match = compl_curr_match = NULL; compl_first_match = compl_curr_match = NULL;
@@ -2264,7 +2271,9 @@ ins_compl_add_tv(typval_T *tv, int dir)
int empty = FALSE; int empty = FALSE;
int flags = 0; int flags = 0;
char_u *(cptext[CPT_COUNT]); char_u *(cptext[CPT_COUNT]);
typval_T user_data;
user_data.v_type = VAR_UNKNOWN;
if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL)
{ {
word = dict_get_string(tv->vval.v_dict, (char_u *)"word", FALSE); word = dict_get_string(tv->vval.v_dict, (char_u *)"word", FALSE);
@@ -2276,8 +2285,7 @@ ins_compl_add_tv(typval_T *tv, int dir)
(char_u *)"kind", FALSE); (char_u *)"kind", FALSE);
cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict, cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict,
(char_u *)"info", FALSE); (char_u *)"info", FALSE);
cptext[CPT_USER_DATA] = dict_get_string(tv->vval.v_dict, dict_get_tv(tv->vval.v_dict, (char_u *)"user_data", &user_data);
(char_u *)"user_data", FALSE);
if (dict_get_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL if (dict_get_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL
&& dict_get_number(tv->vval.v_dict, (char_u *)"icase")) && dict_get_number(tv->vval.v_dict, (char_u *)"icase"))
flags |= CP_ICASE; flags |= CP_ICASE;
@@ -2296,7 +2304,7 @@ ins_compl_add_tv(typval_T *tv, int dir)
} }
if (word == NULL || (!empty && *word == NUL)) if (word == NULL || (!empty && *word == NUL))
return FAIL; return FAIL;
return ins_compl_add(word, -1, NULL, cptext, dir, flags, dup); return ins_compl_add(word, -1, NULL, cptext, &user_data, dir, flags, dup);
} }
/* /*
@@ -2373,7 +2381,7 @@ set_completion(colnr_T startcol, list_T *list)
if (p_ic) if (p_ic)
flags |= CP_ICASE; flags |= CP_ICASE;
if (compl_orig_text == NULL || ins_compl_add(compl_orig_text, if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
-1, NULL, NULL, 0, flags, FALSE) != OK) -1, NULL, NULL, NULL, 0, flags, FALSE) != OK)
return; return;
ctrl_x_mode = CTRL_X_EVAL; ctrl_x_mode = CTRL_X_EVAL;
@@ -2541,8 +2549,11 @@ get_complete_info(list_T *what_list, dict_T *retdict)
dict_add_string(di, "menu", match->cp_text[CPT_MENU]); dict_add_string(di, "menu", match->cp_text[CPT_MENU]);
dict_add_string(di, "kind", match->cp_text[CPT_KIND]); dict_add_string(di, "kind", match->cp_text[CPT_KIND]);
dict_add_string(di, "info", match->cp_text[CPT_INFO]); dict_add_string(di, "info", match->cp_text[CPT_INFO]);
dict_add_string(di, "user_data", if (match->cp_user_data.v_type == VAR_UNKNOWN)
match->cp_text[CPT_USER_DATA]); // Add an empty string for backwards compatibility
dict_add_string(di, "user_data", (char_u *)"");
else
dict_add_tv(di, "user_data", &match->cp_user_data);
} }
match = match->cp_next; match = match->cp_next;
} }
@@ -3893,7 +3904,7 @@ ins_complete(int c, int enable_pum)
if (p_ic) if (p_ic)
flags |= CP_ICASE; flags |= CP_ICASE;
if (compl_orig_text == NULL || ins_compl_add(compl_orig_text, if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
-1, NULL, NULL, 0, flags, FALSE) != OK) -1, NULL, NULL, NULL, 0, flags, FALSE) != OK)
{ {
VIM_CLEAR(compl_pattern); VIM_CLEAR(compl_pattern);
VIM_CLEAR(compl_orig_text); VIM_CLEAR(compl_orig_text);

View File

@@ -18,18 +18,20 @@ int dict_add_special(dict_T *d, char *key, varnumber_T nr);
int dict_add_string(dict_T *d, char *key, char_u *str); int dict_add_string(dict_T *d, char *key, char_u *str);
int dict_add_string_len(dict_T *d, char *key, char_u *str, int len); int dict_add_string_len(dict_T *d, char *key, char_u *str, int len);
int dict_add_list(dict_T *d, char *key, list_T *list); int dict_add_list(dict_T *d, char *key, list_T *list);
int dict_add_tv(dict_T *d, char *key, typval_T *tv);
int dict_add_callback(dict_T *d, char *key, callback_T *cb); int dict_add_callback(dict_T *d, char *key, callback_T *cb);
void dict_iterate_start(typval_T *var, dict_iterator_T *iter); void dict_iterate_start(typval_T *var, dict_iterator_T *iter);
char_u *dict_iterate_next(dict_iterator_T *iter, typval_T **tv_result); char_u *dict_iterate_next(dict_iterator_T *iter, typval_T **tv_result);
int dict_add_dict(dict_T *d, char *key, dict_T *dict); int dict_add_dict(dict_T *d, char *key, dict_T *dict);
long dict_len(dict_T *d); long dict_len(dict_T *d);
dictitem_T *dict_find(dict_T *d, char_u *key, int len); dictitem_T *dict_find(dict_T *d, char_u *key, int len);
int dict_get_tv(dict_T *d, char_u *key, typval_T *rettv);
char_u *dict_get_string(dict_T *d, char_u *key, int save); char_u *dict_get_string(dict_T *d, char_u *key, int save);
varnumber_T dict_get_number(dict_T *d, char_u *key); varnumber_T dict_get_number(dict_T *d, char_u *key);
varnumber_T dict_get_number_def(dict_T *d, char_u *key, int def); varnumber_T dict_get_number_def(dict_T *d, char_u *key, int def);
varnumber_T dict_get_number_check(dict_T *d, char_u *key); varnumber_T dict_get_number_check(dict_T *d, char_u *key);
char_u *dict2string(typval_T *tv, int copyID, int restore_copyID); char_u *dict2string(typval_T *tv, int copyID, int restore_copyID);
int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate, int literal); int eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal);
void dict_extend(dict_T *d1, dict_T *d2, char_u *action); void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
dictitem_T *dict_lookup(hashitem_T *hi); dictitem_T *dict_lookup(hashitem_T *hi);
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive); int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);

View File

@@ -228,7 +228,8 @@ func s:CompleteDone_CompleteFuncDictNoUserData(findstart, base)
\ 'abbr': 'wrd', \ 'abbr': 'wrd',
\ 'menu': 'extra text', \ 'menu': 'extra text',
\ 'info': 'words are cool', \ 'info': 'words are cool',
\ 'kind': 'W' \ 'kind': 'W',
\ 'user_data': ['one', 'two'],
\ } \ }
\ ] \ ]
\ } \ }
@@ -240,7 +241,7 @@ func s:CompleteDone_CheckCompletedItemDictNoUserData()
call assert_equal( 'extra text', v:completed_item[ 'menu' ] ) call assert_equal( 'extra text', v:completed_item[ 'menu' ] )
call assert_equal( 'words are cool', v:completed_item[ 'info' ] ) call assert_equal( 'words are cool', v:completed_item[ 'info' ] )
call assert_equal( 'W', v:completed_item[ 'kind' ] ) call assert_equal( 'W', v:completed_item[ 'kind' ] )
call assert_equal( '', v:completed_item[ 'user_data' ] ) call assert_equal( ['one', 'two'], v:completed_item[ 'user_data' ] )
let s:called_completedone = 1 let s:called_completedone = 1
endfunc endfunc
@@ -252,7 +253,7 @@ func Test_CompleteDoneDictNoUserData()
execute "normal a\<C-X>\<C-U>\<C-Y>" execute "normal a\<C-X>\<C-U>\<C-Y>"
set completefunc& set completefunc&
call assert_equal('', v:completed_item[ 'user_data' ]) call assert_equal(['one', 'two'], v:completed_item[ 'user_data' ])
call assert_true(s:called_completedone) call assert_true(s:called_completedone)
let s:called_completedone = 0 let s:called_completedone = 0

View File

@@ -742,6 +742,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
84,
/**/ /**/
83, 83,
/**/ /**/