mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	vim-patch:8.2.4760: using matchfuzzy() on a long list can take a while
Problem:    Using matchfuzzy() on a long list can take a while.
Solution:   Add a limit to the number of matches. (Yasuhiro Matsumoto,
            closes vim/vim#10189)
9029a6e993
			
			
This commit is contained in:
		| @@ -4975,7 +4975,7 @@ matchfuzzy({list}, {str} [, {dict}])			*matchfuzzy()* | |||||||
|  |  | ||||||
| 		If {list} is a list of dictionaries, then the optional {dict} | 		If {list} is a list of dictionaries, then the optional {dict} | ||||||
| 		argument supports the following additional items: | 		argument supports the following additional items: | ||||||
| 		    key		key of the item which is fuzzy matched against | 		    key		Key of the item which is fuzzy matched against | ||||||
| 				{str}. The value of this item should be a | 				{str}. The value of this item should be a | ||||||
| 				string. | 				string. | ||||||
| 		    text_cb	|Funcref| that will be called for every item | 		    text_cb	|Funcref| that will be called for every item | ||||||
| @@ -4983,6 +4983,8 @@ matchfuzzy({list}, {str} [, {dict}])			*matchfuzzy()* | |||||||
| 				This should accept a dictionary item as the | 				This should accept a dictionary item as the | ||||||
| 				argument and return the text for that item to | 				argument and return the text for that item to | ||||||
| 				use for fuzzy matching. | 				use for fuzzy matching. | ||||||
|  | 		    limit	Maximum number of matches in {list} to be | ||||||
|  | 				returned.  Zero means no limit. | ||||||
|  |  | ||||||
| 		{str} is treated as a literal string and regular expression | 		{str} is treated as a literal string and regular expression | ||||||
| 		matching is NOT supported.  The maximum supported {str} length | 		matching is NOT supported.  The maximum supported {str} length | ||||||
| @@ -4995,6 +4997,9 @@ matchfuzzy({list}, {str} [, {dict}])			*matchfuzzy()* | |||||||
| 		empty list is returned. If length of {str} is greater than | 		empty list is returned. If length of {str} is greater than | ||||||
| 		256, then returns an empty list. | 		256, then returns an empty list. | ||||||
|  |  | ||||||
|  | 		When {limit} is given, matchfuzzy() will find up to this | ||||||
|  | 		number of matches in {list} and return them in sorted order. | ||||||
|  |  | ||||||
| 		Refer to |fuzzy-matching| for more information about fuzzy | 		Refer to |fuzzy-matching| for more information about fuzzy | ||||||
| 		matching strings. | 		matching strings. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5095,7 +5095,8 @@ static int fuzzy_match_item_compare(const void *const s1, const void *const s2) | |||||||
| /// matches for each item. | /// matches for each item. | ||||||
| static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bool matchseq, | static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bool matchseq, | ||||||
|                                 const char_u *const key, Callback *const item_cb, |                                 const char_u *const key, Callback *const item_cb, | ||||||
|                                 const bool retmatchpos, list_T *const fmatchlist) |                                 const bool retmatchpos, list_T *const fmatchlist, | ||||||
|  |                                 const long max_matches) | ||||||
|   FUNC_ATTR_NONNULL_ARG(2, 5, 7) |   FUNC_ATTR_NONNULL_ARG(2, 5, 7) | ||||||
| { | { | ||||||
|   const long len = tv_list_len(items); |   const long len = tv_list_len(items); | ||||||
| @@ -5103,9 +5104,10 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // TODO(vim): when using a limit use that instead of "len" | ||||||
|   fuzzyItem_T *const ptrs = xcalloc(len, sizeof(fuzzyItem_T)); |   fuzzyItem_T *const ptrs = xcalloc(len, sizeof(fuzzyItem_T)); | ||||||
|   long i = 0; |   long i = 0; | ||||||
|   bool found_match = false; |   long found_match = 0; | ||||||
|   uint32_t matches[MAX_FUZZY_MATCHES]; |   uint32_t matches[MAX_FUZZY_MATCHES]; | ||||||
|  |  | ||||||
|   // For all the string items in items, get the fuzzy matching score |   // For all the string items in items, get the fuzzy matching score | ||||||
| @@ -5113,6 +5115,14 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo | |||||||
|     ptrs[i].idx = i; |     ptrs[i].idx = i; | ||||||
|     ptrs[i].item = li; |     ptrs[i].item = li; | ||||||
|     ptrs[i].score = SCORE_NONE; |     ptrs[i].score = SCORE_NONE; | ||||||
|  |  | ||||||
|  |     // TODO(vim): instead of putting all items in ptrs[] should only add | ||||||
|  |     // matching items there. | ||||||
|  |     if (max_matches > 0 && found_match >= max_matches) { | ||||||
|  |       i++; | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char_u *itemstr = NULL; |     char_u *itemstr = NULL; | ||||||
|     typval_T rettv; |     typval_T rettv; | ||||||
|     rettv.v_type = VAR_UNKNOWN; |     rettv.v_type = VAR_UNKNOWN; | ||||||
| @@ -5159,13 +5169,13 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       ptrs[i].score = score; |       ptrs[i].score = score; | ||||||
|       found_match = true; |       found_match++; | ||||||
|     } |     } | ||||||
|     i++; |     i++; | ||||||
|     tv_clear(&rettv); |     tv_clear(&rettv); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   if (found_match) { |   if (found_match > 0) { | ||||||
|     // Sort the list by the descending order of the match score |     // Sort the list by the descending order of the match score | ||||||
|     qsort(ptrs, len, sizeof(fuzzyItem_T), fuzzy_match_item_compare); |     qsort(ptrs, len, sizeof(fuzzyItem_T), fuzzy_match_item_compare); | ||||||
|  |  | ||||||
| @@ -5239,6 +5249,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, | |||||||
|   Callback cb = CALLBACK_NONE; |   Callback cb = CALLBACK_NONE; | ||||||
|   const char_u *key = NULL; |   const char_u *key = NULL; | ||||||
|   bool matchseq = false; |   bool matchseq = false; | ||||||
|  |   long max_matches = 0; | ||||||
|   if (argvars[2].v_type != VAR_UNKNOWN) { |   if (argvars[2].v_type != VAR_UNKNOWN) { | ||||||
|     if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) { |     if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) { | ||||||
|       emsg(_(e_dictreq)); |       emsg(_(e_dictreq)); | ||||||
| @@ -5248,8 +5259,8 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, | |||||||
|     // To search a dict, either a callback function or a key can be |     // To search a dict, either a callback function or a key can be | ||||||
|     // specified. |     // specified. | ||||||
|     dict_T *const d = argvars[2].vval.v_dict; |     dict_T *const d = argvars[2].vval.v_dict; | ||||||
|     const dictitem_T *const di = tv_dict_find(d, "key", -1); |     const dictitem_T *di; | ||||||
|     if (di != NULL) { |     if ((di = tv_dict_find(d, "key", -1)) != NULL) { | ||||||
|       if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL |       if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL | ||||||
|           || *di->di_tv.vval.v_string == NUL) { |           || *di->di_tv.vval.v_string == NUL) { | ||||||
|         semsg(_(e_invarg2), tv_get_string(&di->di_tv)); |         semsg(_(e_invarg2), tv_get_string(&di->di_tv)); | ||||||
| @@ -5259,7 +5270,14 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, | |||||||
|     } else if (!tv_dict_get_callback(d, "text_cb", -1, &cb)) { |     } else if (!tv_dict_get_callback(d, "text_cb", -1, &cb)) { | ||||||
|       semsg(_(e_invargval), "text_cb"); |       semsg(_(e_invargval), "text_cb"); | ||||||
|       return; |       return; | ||||||
|  |     } else if ((di = tv_dict_find(d, "limit", -1)) != NULL) { | ||||||
|  |       if (di->di_tv.v_type != VAR_NUMBER) { | ||||||
|  |         semsg(_(e_invarg2), tv_get_string(&di->di_tv)); | ||||||
|  |         return; | ||||||
|       } |       } | ||||||
|  |       max_matches = (long)tv_get_number_chk(&di->di_tv, NULL); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (tv_dict_find(d, "matchseq", -1) != NULL) { |     if (tv_dict_find(d, "matchseq", -1) != NULL) { | ||||||
|       matchseq = true; |       matchseq = true; | ||||||
|     } |     } | ||||||
| @@ -5278,7 +5296,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   fuzzy_match_in_list(argvars[0].vval.v_list, (char_u *)tv_get_string(&argvars[1]), matchseq, key, |   fuzzy_match_in_list(argvars[0].vval.v_list, (char_u *)tv_get_string(&argvars[1]), matchseq, key, | ||||||
|                       &cb, retmatchpos, rettv->vval.v_list); |                       &cb, retmatchpos, rettv->vval.v_list, max_matches); | ||||||
|   callback_free(&cb); |   callback_free(&cb); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -245,4 +245,16 @@ func Test_matchfuzzypos_mbyte() | |||||||
|   call assert_equal([['xффйд'], [[2, 3, 4]], [168]], matchfuzzypos(['xффйд'], 'фйд')) |   call assert_equal([['xффйд'], [[2, 3, 4]], [168]], matchfuzzypos(['xффйд'], 'фйд')) | ||||||
| endfunc | endfunc | ||||||
|  |  | ||||||
|  | " Test for matchfuzzy() with limit | ||||||
|  | func Test_matchfuzzy_limit() | ||||||
|  |   let x = ['1', '2', '3', '2'] | ||||||
|  |   call assert_equal(['2', '2'], x->matchfuzzy('2')) | ||||||
|  |   call assert_equal(['2', '2'], x->matchfuzzy('2', #{})) | ||||||
|  |   call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 0})) | ||||||
|  |   call assert_equal(['2'], x->matchfuzzy('2', #{limit: 1})) | ||||||
|  |   call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 2})) | ||||||
|  |   call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 3})) | ||||||
|  |   call assert_fails("call matchfuzzy(x, '2', #{limit: '2'})", 'E475:') | ||||||
|  | endfunc | ||||||
|  |  | ||||||
| " vim: shiftwidth=2 sts=2 expandtab | " vim: shiftwidth=2 sts=2 expandtab | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 zeertzjq
					zeertzjq