mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge pull request #12204 from archseer/lsp-user-data
lsp/completion: Expose completion_item under completed_items.user_data + vim-patch:8.2.0084
This commit is contained in:
		| @@ -1083,7 +1083,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 | ||||||
|   | |||||||
| @@ -240,6 +240,13 @@ function M.text_document_completion_list_to_complete_items(result, prefix) | |||||||
|       icase = 1, |       icase = 1, | ||||||
|       dup = 1, |       dup = 1, | ||||||
|       empty = 1, |       empty = 1, | ||||||
|  |       user_data = { | ||||||
|  |         nvim = { | ||||||
|  |           lsp = { | ||||||
|  |             completion_item = completion_item | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|     }) |     }) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -143,6 +143,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 | ||||||
| @@ -2291,7 +2292,7 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, | |||||||
|     flags |= CP_ICASE; |     flags |= CP_ICASE; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return ins_compl_add(str, len, fname, NULL, false, dir, flags, false); |   return ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false); | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Add a match to the list of matches | /// Add a match to the list of matches | ||||||
| @@ -2315,6 +2316,7 @@ static int ins_compl_add(char_u *const str, int len, | |||||||
|                          char_u *const fname, |                          char_u *const fname, | ||||||
|                          char_u *const *const cptext, |                          char_u *const *const cptext, | ||||||
|                          const bool cptext_allocated, |                          const bool cptext_allocated, | ||||||
|  |                          typval_T *user_data, | ||||||
|                          const Direction cdir, int flags_arg, const bool adup) |                          const Direction cdir, int flags_arg, const bool adup) | ||||||
|   FUNC_ATTR_NONNULL_ARG(1) |   FUNC_ATTR_NONNULL_ARG(1) | ||||||
| { | { | ||||||
| @@ -2403,6 +2405,10 @@ static int ins_compl_add(char_u *const str, int len, | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   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. | ||||||
|    */ |    */ | ||||||
| @@ -2521,7 +2527,7 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) | |||||||
|   int dir = compl_direction; |   int dir = compl_direction; | ||||||
|  |  | ||||||
|   for (int i = 0; i < num_matches && add_r != FAIL; i++) { |   for (int i = 0; i < num_matches && add_r != FAIL; i++) { | ||||||
|     if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, dir, |     if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, 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; | ||||||
| @@ -2596,7 +2602,7 @@ void set_completion(colnr_T startcol, list_T *list) | |||||||
|   if (p_ic) { |   if (p_ic) { | ||||||
|     flags |= CP_ICASE; |     flags |= CP_ICASE; | ||||||
|   } |   } | ||||||
|   if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, 0, |   if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, | ||||||
|                     flags, false) != OK) { |                     flags, false) != OK) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -3120,6 +3126,7 @@ static void ins_compl_free(void) | |||||||
|     for (int i = 0; i < CPT_COUNT; i++) { |     for (int i = 0; i < CPT_COUNT; i++) { | ||||||
|       xfree(match->cp_text[i]); |       xfree(match->cp_text[i]); | ||||||
|     } |     } | ||||||
|  |     tv_clear(&match->cp_user_data); | ||||||
|     xfree(match); |     xfree(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; | ||||||
| @@ -3215,8 +3222,11 @@ void get_complete_info(list_T *what_list, dict_T *retdict) | |||||||
|                           (char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND])); |                           (char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND])); | ||||||
|           tv_dict_add_str(di, S_LEN("info"), |           tv_dict_add_str(di, S_LEN("info"), | ||||||
|                           (char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); |                           (char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); | ||||||
|           tv_dict_add_str(di, S_LEN("user_data"), |           if (match->cp_user_data.v_type == VAR_UNKNOWN) { | ||||||
|                           (char *)EMPTY_IF_NULL(match->cp_text[CPT_USER_DATA])); |               tv_dict_add_str(di, S_LEN("user_data"), ""); | ||||||
|  |           } else { | ||||||
|  |               tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|         match = match->cp_next; |         match = match->cp_next; | ||||||
|       } while (match != NULL && match != compl_first_match); |       } while (match != NULL && match != compl_first_match); | ||||||
| @@ -3930,15 +3940,16 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) | |||||||
|   bool empty = false; |   bool empty = false; | ||||||
|   int flags = 0; |   int flags = 0; | ||||||
|   char *(cptext[CPT_COUNT]); |   char *(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 = tv_dict_get_string(tv->vval.v_dict, "word", false); |     word = tv_dict_get_string(tv->vval.v_dict, "word", false); | ||||||
|     cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); |     cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); | ||||||
|     cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); |     cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); | ||||||
|     cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); |     cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); | ||||||
|     cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); |     cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); | ||||||
|     cptext[CPT_USER_DATA] = tv_dict_get_string(tv->vval.v_dict, |     tv_dict_get_tv(tv->vval.v_dict, "user_data", &user_data); | ||||||
|                                                "user_data", true); |  | ||||||
|  |  | ||||||
|     if (tv_dict_get_number(tv->vval.v_dict, "icase")) { |     if (tv_dict_get_number(tv->vval.v_dict, "icase")) { | ||||||
|       flags |= CP_ICASE; |       flags |= CP_ICASE; | ||||||
| @@ -3960,7 +3971,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) | |||||||
|     return FAIL; |     return FAIL; | ||||||
|   } |   } | ||||||
|   return ins_compl_add((char_u *)word, -1, NULL, |   return ins_compl_add((char_u *)word, -1, NULL, | ||||||
|                        (char_u **)cptext, true, dir, flags, dup); |                        (char_u **)cptext, true, &user_data, dir, flags, dup); | ||||||
| } | } | ||||||
|  |  | ||||||
| // Get the next expansion(s), using "compl_pattern". | // Get the next expansion(s), using "compl_pattern". | ||||||
| @@ -4450,9 +4461,11 @@ static dict_T *ins_compl_dict_alloc(compl_T *match) | |||||||
|   tv_dict_add_str( |   tv_dict_add_str( | ||||||
|       dict, S_LEN("info"), |       dict, S_LEN("info"), | ||||||
|       (const char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); |       (const char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); | ||||||
|   tv_dict_add_str( |   if (match->cp_user_data.v_type == VAR_UNKNOWN) { | ||||||
|       dict, S_LEN("user_data"), |     tv_dict_add_str(dict, S_LEN("user_data"), ""); | ||||||
|       (const char *)EMPTY_IF_NULL(match->cp_text[CPT_USER_DATA])); |   } else { | ||||||
|  |     tv_dict_add_tv(dict, S_LEN("user_data"), &match->cp_user_data); | ||||||
|  |   } | ||||||
|   return dict; |   return dict; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -5155,7 +5168,7 @@ static int ins_complete(int c, bool enable_pum) | |||||||
|     if (p_ic) { |     if (p_ic) { | ||||||
|       flags |= CP_ICASE; |       flags |= CP_ICASE; | ||||||
|     } |     } | ||||||
|     if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, 0, |     if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, | ||||||
|                       flags, false) != OK) { |                       flags, false) != OK) { | ||||||
|       XFREE_CLEAR(compl_pattern); |       XFREE_CLEAR(compl_pattern); | ||||||
|       XFREE_CLEAR(compl_orig_text); |       XFREE_CLEAR(compl_orig_text); | ||||||
|   | |||||||
| @@ -10,8 +10,7 @@ | |||||||
| #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 |  | ||||||
|  |  | ||||||
| // values for cp_flags | // values for cp_flags | ||||||
| typedef enum { | typedef enum { | ||||||
|   | |||||||
| @@ -1429,6 +1429,23 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, | |||||||
|   return TV_DICT_HI2DI(hi); |   return TV_DICT_HI2DI(hi); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Get a typval item from a dictionary and copy it into "rettv". | ||||||
|  | /// | ||||||
|  | /// @param[in]  d  Dictionary to check. | ||||||
|  | /// @param[in]  key  Dictionary key. | ||||||
|  | /// @param[in]  rettv  Return value. | ||||||
|  | /// @return OK in case of success or FAIL if nothing was found. | ||||||
|  | int tv_dict_get_tv(dict_T *d, const char *const key, typval_T *rettv) | ||||||
|  | { | ||||||
|  |   dictitem_T *const di = tv_dict_find(d, key, -1); | ||||||
|  |   if (di == NULL) { | ||||||
|  |     return FAIL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   tv_copy(&di->di_tv, rettv); | ||||||
|  |   return OK; | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Get a number item from a dictionary | /// Get a number item from a dictionary | ||||||
| /// | /// | ||||||
| /// Returns 0 if the entry does not exist. | /// Returns 0 if the entry does not exist. | ||||||
| @@ -1588,6 +1605,26 @@ int tv_dict_add_list(dict_T *const d, const char *const key, | |||||||
|   return OK; |   return OK; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Add a typval entry to dictionary. | ||||||
|  | /// | ||||||
|  | /// @param[out]  d  Dictionary to add entry to. | ||||||
|  | /// @param[in]  key  Key to add. | ||||||
|  | /// @param[in]  key_len  Key length. | ||||||
|  | /// | ||||||
|  | /// @return FAIL if out of memory or key already exists. | ||||||
|  | int tv_dict_add_tv(dict_T *d, const char *key, const size_t key_len, | ||||||
|  |                    typval_T *tv) | ||||||
|  | { | ||||||
|  |   dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); | ||||||
|  |  | ||||||
|  |   tv_copy(tv, &item->di_tv); | ||||||
|  |   if (tv_dict_add(d, item) == FAIL) { | ||||||
|  |       tv_dict_item_free(item); | ||||||
|  |       return FAIL; | ||||||
|  |   } | ||||||
|  |   return OK; | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Add a dictionary entry to dictionary | /// Add a dictionary entry to dictionary | ||||||
| /// | /// | ||||||
| /// @param[out]  d  Dictionary to add entry to. | /// @param[out]  d  Dictionary to add entry to. | ||||||
|   | |||||||
| @@ -120,7 +120,7 @@ function! s:CompleteDone_CompleteFuncDict( findstart, base ) | |||||||
|               \ 'menu': 'extra text', |               \ 'menu': 'extra text', | ||||||
|               \ 'info': 'words are cool', |               \ 'info': 'words are cool', | ||||||
|               \ 'kind': 'W', |               \ 'kind': 'W', | ||||||
|               \ 'user_data': 'test' |               \ 'user_data': ['one', 'two'] | ||||||
|             \ } |             \ } | ||||||
|           \ ] |           \ ] | ||||||
|         \ } |         \ } | ||||||
| @@ -136,7 +136,7 @@ func s:CompleteDone_CheckCompletedItemDict(pre) | |||||||
|   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( 'test',           v:completed_item[ 'user_data' ] ) |   call assert_equal( ['one', 'two'],   v:completed_item[ 'user_data' ] ) | ||||||
|  |  | ||||||
|   if a:pre |   if a:pre | ||||||
|     call assert_equal('function', complete_info().mode) |     call assert_equal('function', complete_info().mode) | ||||||
| @@ -170,7 +170,7 @@ func Test_CompleteDoneDict() | |||||||
|   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('test', 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 | ||||||
|   | |||||||
| @@ -836,12 +836,12 @@ describe('LSP', function() | |||||||
|       } |       } | ||||||
|       local completion_list_items = {items=completion_list} |       local completion_list_items = {items=completion_list} | ||||||
|       local expected = { |       local expected = { | ||||||
|         { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, |         { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar' } } } } }, | ||||||
|         { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, |         { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', textEdit={} } } }  } }, | ||||||
|         { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, |         { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar' } } } } }, | ||||||
|         { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, |         { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } }, | ||||||
|         { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, |         { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } }, | ||||||
|         { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, |         { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } }, | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix)) |       eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix)) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Matthieu Coudron
					Matthieu Coudron