Patch 8.2.0084
Problem:    Complete item "user_data" can only be a string.
Solution:   Accept any type of variable. (closes #5412)
Files:      src/testdir/test_ins_complete.vim, src/insexpand.c, src/dict.c,
            src/proto/dict.pro, src/eval.c, runtime/doc/insert.txt


*** ../vim-8.2.0083/src/testdir/test_ins_complete.vim   2019-12-15 
14:54:17.609139105 +0100
--- src/testdir/test_ins_complete.vim   2020-01-04 14:31:05.710982360 +0100
***************
*** 158,174 ****
    endif
  
    return {
!           \ 'words': [
!             \ {
!               \ 'word': 'aword',
!               \ 'abbr': 'wrd',
!               \ 'menu': 'extra text',
!               \ 'info': 'words are cool',
!               \ 'kind': 'W',
!               \ 'user_data': 'test'
!             \ }
!           \ ]
!         \ }
  endfunc
  
  func s:CompleteDone_CheckCompletedItemNone()
--- 158,174 ----
    endif
  
    return {
!         \ 'words': [
!           \ {
!             \ 'word': 'aword',
!             \ 'abbr': 'wrd',
!             \ 'menu': 'extra text',
!             \ 'info': 'words are cool',
!             \ 'kind': 'W',
!             \ 'user_data': 'test'
!           \ }
!         \ ]
!       \ }
  endfunc
  
  func s:CompleteDone_CheckCompletedItemNone()
***************
*** 222,237 ****
    endif
  
    return {
!           \ 'words': [
!             \ {
!               \ 'word': 'aword',
!               \ 'abbr': 'wrd',
!               \ 'menu': 'extra text',
!               \ 'info': 'words are cool',
!               \ 'kind': 'W'
!             \ }
!           \ ]
!         \ }
  endfunc
  
  func s:CompleteDone_CheckCompletedItemDictNoUserData()
--- 222,238 ----
    endif
  
    return {
!         \ 'words': [
!           \ {
!             \ 'word': 'aword',
!             \ 'abbr': 'wrd',
!             \ 'menu': 'extra text',
!             \ 'info': 'words are cool',
!             \ 'kind': 'W',
!             \ 'user_data': ['one', 'two'],
!           \ }
!         \ ]
!       \ }
  endfunc
  
  func s:CompleteDone_CheckCompletedItemDictNoUserData()
***************
*** 240,246 ****
    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' ] )
  
    let s:called_completedone = 1
  endfunc
--- 241,247 ----
    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( ['one', 'two'],   v:completed_item[ 'user_data' ] )
  
    let s:called_completedone = 1
  endfunc
***************
*** 252,258 ****
    execute "normal a\<C-X>\<C-U>\<C-Y>"
    set completefunc&
  
!   call assert_equal('', v:completed_item[ 'user_data' ])
    call assert_true(s:called_completedone)
  
    let s:called_completedone = 0
--- 253,259 ----
    execute "normal a\<C-X>\<C-U>\<C-Y>"
    set completefunc&
  
!   call assert_equal(['one', 'two'], v:completed_item[ 'user_data' ])
    call assert_true(s:called_completedone)
  
    let s:called_completedone = 0
*** ../vim-8.2.0083/src/insexpand.c     2019-12-21 18:47:05.128754192 +0100
--- src/insexpand.c     2020-01-04 14:25:01.127855687 +0100
***************
*** 91,98 ****
  #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
  
  /*
   * Structure used to store one match for insert completion.
--- 91,97 ----
  #define CPT_MENU      1       // "menu"
  #define CPT_KIND      2       // "kind"
  #define CPT_INFO      3       // "info"
! #define CPT_COUNT     4       // Number of entries
  
  /*
   * Structure used to store one match for insert completion.
***************
*** 104,109 ****
--- 103,109 ----
      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,193 ****
  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 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);
--- 187,193 ----
  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, 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,566 ****
      if (icase)
        flags |= CP_ICASE;
  
!     return ins_compl_add(str, len, fname, NULL, dir, flags, FALSE);
  }
  
  /*
--- 560,566 ----
      if (icase)
        flags |= CP_ICASE;
  
!     return ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE);
  }
  
  /*
***************
*** 574,583 ****
      char_u    *str,
      int               len,
      char_u    *fname,
!     char_u    **cptext,   // extra text for popup menu or NULL
      int               cdir,
      int               flags_arg,
!     int               adup)       // accept duplicate match
  {
      compl_T   *match;
      int               dir = (cdir == 0 ? compl_direction : cdir);
--- 574,584 ----
      char_u    *str,
      int               len,
      char_u    *fname,
!     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
  {
      compl_T   *match;
      int               dir = (cdir == 0 ? compl_direction : cdir);
***************
*** 646,651 ****
--- 647,654 ----
            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,789 ****
      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,
                                           icase ? CP_ICASE : 0, FALSE)) == OK)
            // if dir was BACKWARD then honor it just once
            dir = FORWARD;
--- 786,792 ----
      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, NULL, dir,
                                           icase ? CP_ICASE : 0, FALSE)) == OK)
            // if dir was BACKWARD then honor it just once
            dir = FORWARD;
***************
*** 952,958 ****
        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]);
      }
      return dict;
  }
--- 955,964 ----
        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]);
!       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,1458 ****
--- 1459,1465 ----
            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,2270 ****
--- 2271,2279 ----
      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,2283 ****
                                                     (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);
        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;
--- 2285,2291 ----
                                                     (char_u *)"kind", FALSE);
        cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict,
                                                     (char_u *)"info", 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,2302 ****
      }
      if (word == NULL || (!empty && *word == NUL))
        return FAIL;
!     return ins_compl_add(word, -1, NULL, cptext, dir, flags, dup);
  }
  
  /*
--- 2304,2310 ----
      }
      if (word == NULL || (!empty && *word == NUL))
        return FAIL;
!     return ins_compl_add(word, -1, NULL, cptext, &user_data, dir, flags, dup);
  }
  
  /*
***************
*** 2373,2379 ****
      if (p_ic)
        flags |= CP_ICASE;
      if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
!                                       -1, NULL, NULL, 0, flags, FALSE) != OK)
        return;
  
      ctrl_x_mode = CTRL_X_EVAL;
--- 2381,2387 ----
      if (p_ic)
        flags |= CP_ICASE;
      if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
!                                 -1, NULL, NULL, NULL, 0, flags, FALSE) != OK)
        return;
  
      ctrl_x_mode = CTRL_X_EVAL;
***************
*** 2541,2548 ****
                    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]);
                }
                match = match->cp_next;
            }
--- 2549,2559 ----
                    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]);
!                   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,3899 ****
        if (p_ic)
            flags |= CP_ICASE;
        if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
!                                       -1, NULL, NULL, 0, flags, FALSE) != OK)
        {
            VIM_CLEAR(compl_pattern);
            VIM_CLEAR(compl_orig_text);
--- 3904,3910 ----
        if (p_ic)
            flags |= CP_ICASE;
        if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
!                                 -1, NULL, NULL, NULL, 0, flags, FALSE) != OK)
        {
            VIM_CLEAR(compl_pattern);
            VIM_CLEAR(compl_orig_text);
*** ../vim-8.2.0083/src/dict.c  2019-12-01 20:48:29.000000000 +0100
--- src/dict.c  2020-01-04 14:13:18.474544574 +0100
***************
*** 448,453 ****
--- 448,474 ----
  }
  
  /*
+  * 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.
   */
***************
*** 590,595 ****
--- 611,633 ----
  }
  
  /*
+  * 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.
   * When FALSE a shared buffer is used, can only be used once!
***************
*** 745,751 ****
   * Return OK or FAIL.  Returns NOTDONE for {expr}.
   */
      int
! dict_get_tv(char_u **arg, typval_T *rettv, int evaluate, int literal)
  {
      dict_T    *d = NULL;
      typval_T  tvkey;
--- 783,789 ----
   * Return OK or FAIL.  Returns NOTDONE for {expr}.
   */
      int
! eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
  {
      dict_T    *d = NULL;
      typval_T  tvkey;
*** ../vim-8.2.0083/src/proto/dict.pro  2019-12-12 12:55:17.000000000 +0100
--- src/proto/dict.pro  2020-01-04 14:18:50.185166239 +0100
***************
*** 18,35 ****
  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_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);
  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);
  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);
--- 18,37 ----
  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 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);
*** ../vim-8.2.0083/src/eval.c  2019-12-31 22:36:14.561918852 +0100
--- src/eval.c  2020-01-04 14:09:52.211397473 +0100
***************
*** 2656,2662 ****
      case '#': if ((*arg)[1] == '{')
                {
                    ++*arg;
!                   ret = dict_get_tv(arg, rettv, evaluate, TRUE);
                }
                else
                    ret = NOTDONE;
--- 2656,2662 ----
      case '#': if ((*arg)[1] == '{')
                {
                    ++*arg;
!                   ret = eval_dict(arg, rettv, evaluate, TRUE);
                }
                else
                    ret = NOTDONE;
***************
*** 2668,2674 ****
       */
      case '{': ret = get_lambda_tv(arg, rettv, evaluate);
                if (ret == NOTDONE)
!                   ret = dict_get_tv(arg, rettv, evaluate, FALSE);
                break;
  
      /*
--- 2668,2674 ----
       */
      case '{': ret = get_lambda_tv(arg, rettv, evaluate);
                if (ret == NOTDONE)
!                   ret = eval_dict(arg, rettv, evaluate, FALSE);
                break;
  
      /*
*** ../vim-8.2.0083/runtime/doc/insert.txt      2019-12-17 21:27:14.686319918 
+0100
--- runtime/doc/insert.txt      2020-01-04 14:25:55.927750843 +0100
***************
*** 1108,1114 ****
        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|
  
  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
--- 1108,1115 ----
        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|; 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
***************
*** 2008,2017 ****
  changed, the detected format is only used while reading the file.
  A similar thing happens with 'fileencodings'.
  
! On non-MS-DOS and Win32 systems the message "[dos format]" is shown if a file
! is read in DOS format, to remind you that something unusual is done.  On
! Macintosh and Win32 the message "[unix format]" is shown if a file is read in
! Unix format.
  On non-Macintosh systems, the message "[Mac format]" is shown if a file is
  read in Mac format.
  
--- 2009,2018 ----
  changed, the detected format is only used while reading the file.
  A similar thing happens with 'fileencodings'.
  
! The message "[dos format]" is shown if a file is read in DOS format, to remind
! you that something unusual is done.
! On Macintosh and Win32 the message "[unix format]" is shown if a file is read
! in Unix format.
  On non-Macintosh systems, the message "[Mac format]" is shown if a file is
  read in Mac format.
  
*** ../vim-8.2.0083/src/version.c       2020-01-03 21:25:55.325995770 +0100
--- src/version.c       2020-01-04 14:01:58.017331255 +0100
***************
*** 744,745 ****
--- 744,747 ----
  {   /* Add new patch number below this line */
+ /**/
+     84,
  /**/

-- 
MONK: ... and the Lord spake, saying, "First shalt thou take out the Holy Pin,
      then shalt thou count to three, no more, no less.  Three shalt be the
      number thou shalt count, and the number of the counting shalt be three.
      Four shalt thou not count, neither count thou two, excepting that thou
      then proceed to three.  Five is right out.  Once the number three, being
      the third number, be reached, then lobbest thou thy Holy Hand Grenade of
      Antioch towards thou foe, who being naughty in my sight, shall snuff it.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202001041333.004DXNt4001095%40masaka.moolenaar.net.

Raspunde prin e-mail lui