]> granicus.if.org Git - vim/commitdiff
patch 8.2.0084: complete item "user_data" can only be a string v8.2.0084
authorBram Moolenaar <Bram@vim.org>
Sat, 4 Jan 2020 13:32:48 +0000 (14:32 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 4 Jan 2020 13:32:48 +0000 (14:32 +0100)
Problem:    Complete item "user_data" can only be a string.
Solution:   Accept any type of variable. (closes #5412)

runtime/doc/insert.txt
src/dict.c
src/eval.c
src/insexpand.c
src/proto/dict.pro
src/testdir/test_ins_complete.vim
src/version.c

index 419213dba556ad494e2f7176fb7a63869312b6da..fa7c0f7c45bccc62348e1362621477cf9559461d 100644 (file)
@@ -1108,7 +1108,8 @@ items:
        empty           when non-zero this match will be added even when it is
                        an empty string
        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
 an item does not meet these requirements then an error message is given and
index 5022a5fd0135f05e501b6f97638791ac48a4138c..f170937b61631e600e3ead84e7845300a7c75577 100644 (file)
@@ -447,6 +447,27 @@ dict_add_list(dict_T *d, char *key, list_T *list)
     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".
  * 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);
 }
 
+/*
+ * 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.
  * 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}.
  */
     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;
     typval_T   tvkey;
index 04abbabae5b5b96487d70715f01b2545f1439096..2a2d9d463e87d2de0ce20fbc71dca24b6f730023 100644 (file)
@@ -2656,7 +2656,7 @@ eval7(
     case '#':  if ((*arg)[1] == '{')
                {
                    ++*arg;
-                   ret = dict_get_tv(arg, rettv, evaluate, TRUE);
+                   ret = eval_dict(arg, rettv, evaluate, TRUE);
                }
                else
                    ret = NOTDONE;
@@ -2668,7 +2668,7 @@ eval7(
      */
     case '{':  ret = get_lambda_tv(arg, rettv, evaluate);
                if (ret == NOTDONE)
-                   ret = dict_get_tv(arg, rettv, evaluate, FALSE);
+                   ret = eval_dict(arg, rettv, evaluate, FALSE);
                break;
 
     /*
index 1cada4fd10240cf7b343c2d9a6465ca31ca51ac1..7407647490f663840a2b6ab5c2ef0161da754886 100644 (file)
@@ -91,8 +91,7 @@ static char *ctrl_x_mode_names[] = {
 #define CPT_MENU       1       // "menu"
 #define CPT_KIND       2       // "kind"
 #define CPT_INFO       3       // "info"
-#define CPT_USER_DATA  4       // "user data"
-#define CPT_COUNT      5       // Number of entries
+#define CPT_COUNT      4       // Number of entries
 
 /*
  * Structure used to store one match for insert completion.
@@ -104,6 +103,7 @@ struct compl_S
     compl_T    *cp_prev;
     char_u     *cp_str;        // matched text
     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
                                // cp_flags has CP_FREE_FNAME
     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_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_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);
@@ -560,7 +560,7 @@ ins_compl_add_infercase(
     if (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);
 }
 
 /*
@@ -574,10 +574,11 @@ ins_compl_add(
     char_u     *str,
     int                len,
     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                flags_arg,
-    int                adup)       // accept duplicate match
+    int                adup)           // accept duplicate match
 {
     compl_T    *match;
     int                dir = (cdir == 0 ? compl_direction : cdir);
@@ -646,6 +647,8 @@ ins_compl_add(
            if (cptext[i] != NULL && *cptext[i] != NUL)
                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.
     if (compl_first_match == NULL)
@@ -783,7 +786,7 @@ ins_compl_add_matches(
     int                dir = compl_direction;
 
     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)
            // if dir was BACKWARD then honor it just once
            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, "kind", match->cp_text[CPT_KIND]);
        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;
 }
@@ -1453,6 +1459,7 @@ ins_compl_free(void)
            vim_free(match->cp_fname);
        for (i = 0; i < CPT_COUNT; ++i)
            vim_free(match->cp_text[i]);
+       clear_tv(&match->cp_user_data);
        vim_free(match);
     } while (compl_curr_match != NULL && compl_curr_match != compl_first_match);
     compl_first_match = compl_curr_match = NULL;
@@ -2264,7 +2271,9 @@ ins_compl_add_tv(typval_T *tv, int dir)
     int                empty = FALSE;
     int                flags = 0;
     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)
     {
        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);
        cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict,
                                                     (char_u *)"info", FALSE);
-       cptext[CPT_USER_DATA] = dict_get_string(tv->vval.v_dict,
-                                                (char_u *)"user_data", FALSE);
+       dict_get_tv(tv->vval.v_dict, (char_u *)"user_data", &user_data);
        if (dict_get_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL
                        && dict_get_number(tv->vval.v_dict, (char_u *)"icase"))
            flags |= CP_ICASE;
@@ -2296,7 +2304,7 @@ ins_compl_add_tv(typval_T *tv, int dir)
     }
     if (word == NULL || (!empty && *word == NUL))
        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)
        flags |= CP_ICASE;
     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;
 
     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, "kind", match->cp_text[CPT_KIND]);
                    dict_add_string(di, "info", match->cp_text[CPT_INFO]);
-                   dict_add_string(di, "user_data",
-                                           match->cp_text[CPT_USER_DATA]);
+                   if (match->cp_user_data.v_type == VAR_UNKNOWN)
+                       // 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;
            }
@@ -3893,7 +3904,7 @@ ins_complete(int c, int enable_pum)
        if (p_ic)
            flags |= CP_ICASE;
        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_orig_text);
index 3185edc8c35bebbc37484668971f0843d3210fbb..75519a404771c60574ed3fa1024b1bba15c522b5 100644 (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_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_tv(dict_T *d, char *key, typval_T *tv);
 int dict_add_callback(dict_T *d, char *key, callback_T *cb);
 void dict_iterate_start(typval_T *var, dict_iterator_T *iter);
 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);
 long dict_len(dict_T *d);
 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);
 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_check(dict_T *d, char_u *key);
 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);
 dictitem_T *dict_lookup(hashitem_T *hi);
 int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
index 95954f536b27e4f1a4d6d0cb7ddc136a3fa7910e..bec6ffc6f91c1b35d2bb4cfd35bc4036ef2a7245 100644 (file)
@@ -158,17 +158,17 @@ func s:CompleteDone_CompleteFuncDict( findstart, base )
   endif
 
   return {
-          \ 'words': [
-            \ {
-              \ 'word': 'aword',
-              \ 'abbr': 'wrd',
-              \ 'menu': 'extra text',
-              \ 'info': 'words are cool',
-              \ 'kind': 'W',
-              \ 'user_data': 'test'
-            \ }
-          \ ]
-        \ }
+         \ 'words': [
+           \ {
+             \ 'word': 'aword',
+             \ 'abbr': 'wrd',
+             \ 'menu': 'extra text',
+             \ 'info': 'words are cool',
+             \ 'kind': 'W',
+             \ 'user_data': 'test'
+           \ }
+         \ ]
+       \ }
 endfunc
 
 func s:CompleteDone_CheckCompletedItemNone()
@@ -222,16 +222,17 @@ func s:CompleteDone_CompleteFuncDictNoUserData(findstart, base)
   endif
 
   return {
-          \ 'words': [
-            \ {
-              \ 'word': 'aword',
-              \ 'abbr': 'wrd',
-              \ 'menu': 'extra text',
-              \ 'info': 'words are cool',
-              \ 'kind': 'W'
-            \ }
-          \ ]
-        \ }
+         \ 'words': [
+           \ {
+             \ 'word': 'aword',
+             \ 'abbr': 'wrd',
+             \ 'menu': 'extra text',
+             \ 'info': 'words are cool',
+             \ 'kind': 'W',
+             \ 'user_data': ['one', 'two'],
+           \ }
+         \ ]
+       \ }
 endfunc
 
 func s:CompleteDone_CheckCompletedItemDictNoUserData()
@@ -240,7 +241,7 @@ func s:CompleteDone_CheckCompletedItemDictNoUserData()
   call assert_equal( 'extra text',     v:completed_item[ 'menu' ] )
   call assert_equal( 'words are cool', v:completed_item[ 'info' ] )
   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
 endfunc
@@ -252,7 +253,7 @@ func Test_CompleteDoneDictNoUserData()
   execute "normal a\<C-X>\<C-U>\<C-Y>"
   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)
 
   let s:called_completedone = 0
index 3c8feee7c057b7edb5f31d623e859a65fe8040fc..6b2c9c82c6245ff953a56e68a8dbf3d9cdfb2f90 100644 (file)
@@ -742,6 +742,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    84,
 /**/
     83,
 /**/