comparison src/cmdexpand.c @ 27875:ae38d2e81fca v8.2.4463

patch 8.2.4463: completion only uses strict matching Commit: https://github.com/vim/vim/commit/38b85cb4d7216705058708bacbc25ab90cd61595 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Thu Feb 24 13:28:41 2022 +0000 patch 8.2.4463: completion only uses strict matching Problem: Completion only uses strict matching. Solution: Add the "fuzzy" item for 'wildoptions'. (Yegappan Lakshmanan, closes #9803)
author Bram Moolenaar <Bram@vim.org>
date Thu, 24 Feb 2022 14:30:05 +0100
parents 010fa62d6fe2
children 76e2115dddb8
comparison
equal deleted inserted replaced
27874:f4a227222e7a 27875:ae38d2e81fca
16 static int cmd_showtail; // Only show path tail in lists ? 16 static int cmd_showtail; // Only show path tail in lists ?
17 17
18 static void set_expand_context(expand_T *xp); 18 static void set_expand_context(expand_T *xp);
19 static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch, 19 static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch,
20 char_u ***matches, int *numMatches, 20 char_u ***matches, int *numMatches,
21 char_u *((*func)(expand_T *, int)), int escaped); 21 char_u *((*func)(expand_T *, int)), int escaped,
22 char_u *fuzzystr);
22 static int ExpandFromContext(expand_T *xp, char_u *, char_u ***, int *, int); 23 static int ExpandFromContext(expand_T *xp, char_u *, char_u ***, int *, int);
23 static int expand_showtail(expand_T *xp); 24 static int expand_showtail(expand_T *xp);
24 static int expand_shellcmd(char_u *filepat, char_u ***matches, int *numMatches, int flagsarg); 25 static int expand_shellcmd(char_u *filepat, char_u ***matches, int *numMatches, int flagsarg);
25 #if defined(FEAT_EVAL) 26 #if defined(FEAT_EVAL)
26 static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches); 27 static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches);
36 static int compl_startcol; 37 static int compl_startcol;
37 static int compl_selected; 38 static int compl_selected;
38 #endif 39 #endif
39 40
40 #define SHOW_FILE_TEXT(m) (showtail ? sm_gettail(matches[m]) : matches[m]) 41 #define SHOW_FILE_TEXT(m) (showtail ? sm_gettail(matches[m]) : matches[m])
42
43 /*
44 * Returns TRUE if fuzzy completion is supported for a given cmdline completion
45 * context.
46 */
47 static int
48 cmdline_fuzzy_completion_supported(expand_T *xp)
49 {
50 return (vim_strchr(p_wop, WOP_FUZZY) != NULL
51 && xp->xp_context != EXPAND_BOOL_SETTINGS
52 && xp->xp_context != EXPAND_COLORS
53 && xp->xp_context != EXPAND_COMPILER
54 && xp->xp_context != EXPAND_DIRECTORIES
55 && xp->xp_context != EXPAND_FILES
56 && xp->xp_context != EXPAND_FILES_IN_PATH
57 && xp->xp_context != EXPAND_FILETYPE
58 && xp->xp_context != EXPAND_HELP
59 && xp->xp_context != EXPAND_MAPPINGS
60 && xp->xp_context != EXPAND_OLD_SETTING
61 && xp->xp_context != EXPAND_OWNSYNTAX
62 && xp->xp_context != EXPAND_PACKADD
63 && xp->xp_context != EXPAND_SHELLCMD
64 && xp->xp_context != EXPAND_TAGS
65 && xp->xp_context != EXPAND_TAGS_LISTFILES
66 && xp->xp_context != EXPAND_USER_DEFINED
67 && xp->xp_context != EXPAND_USER_LIST);
68 }
69
70 /*
71 * Returns TRUE if fuzzy completion for cmdline completion is enabled and
72 * 'fuzzystr' is not empty.
73 */
74 int
75 cmdline_fuzzy_complete(char_u *fuzzystr)
76 {
77 return vim_strchr(p_wop, WOP_FUZZY) != NULL && *fuzzystr != NUL;
78 }
41 79
42 /* 80 /*
43 * sort function for the completion matches. 81 * sort function for the completion matches.
44 * <SNR> functions should be sorted to the end. 82 * <SNR> functions should be sorted to the end.
45 */ 83 */
193 // Get next/previous match for a previous expanded pattern. 231 // Get next/previous match for a previous expanded pattern.
194 p2 = ExpandOne(xp, NULL, NULL, 0, type); 232 p2 = ExpandOne(xp, NULL, NULL, 0, type);
195 } 233 }
196 else 234 else
197 { 235 {
236 if (cmdline_fuzzy_completion_supported(xp))
237 // If fuzzy matching, don't modify the search string
238 p1 = vim_strsave(xp->xp_pattern);
239 else
240 p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
241
198 // Translate string into pattern and expand it. 242 // Translate string into pattern and expand it.
199 if ((p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, 243 if (p1 == NULL)
200 xp->xp_context)) == NULL)
201 p2 = NULL; 244 p2 = NULL;
202 else 245 else
203 { 246 {
204 int use_options = options | 247 int use_options = options |
205 WILD_HOME_REPLACE|WILD_ADD_SLASH|WILD_SILENT; 248 WILD_HOME_REPLACE|WILD_ADD_SLASH|WILD_SILENT;
2186 return EXPAND_NOTHING; 2229 return EXPAND_NOTHING;
2187 } 2230 }
2188 2231
2189 // add star to file name, or convert to regexp if not exp. files. 2232 // add star to file name, or convert to regexp if not exp. files.
2190 xp->xp_pattern_len = (int)(str + col - xp->xp_pattern); 2233 xp->xp_pattern_len = (int)(str + col - xp->xp_pattern);
2191 file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context); 2234 if (cmdline_fuzzy_completion_supported(xp))
2192 if (file_str == NULL) 2235 // If fuzzy matching, don't modify the search string
2193 return EXPAND_UNSUCCESSFUL; 2236 file_str = vim_strsave(xp->xp_pattern);
2237 else
2238 {
2239 file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
2240 if (file_str == NULL)
2241 return EXPAND_UNSUCCESSFUL;
2242 }
2194 2243
2195 if (p_wic) 2244 if (p_wic)
2196 options += WILD_ICASE; 2245 options += WILD_ICASE;
2197 2246
2198 // find all files that match the description 2247 // find all files that match the description
2315 /* 2364 /*
2316 * Do the expansion based on xp->xp_context and 'rmp'. 2365 * Do the expansion based on xp->xp_context and 'rmp'.
2317 */ 2366 */
2318 static int 2367 static int
2319 ExpandOther( 2368 ExpandOther(
2369 char_u *pat,
2320 expand_T *xp, 2370 expand_T *xp,
2321 regmatch_T *rmp, 2371 regmatch_T *rmp,
2322 char_u ***matches, 2372 char_u ***matches,
2323 int *numMatches) 2373 int *numMatches)
2324 { 2374 {
2384 // right function to do the expansion. 2434 // right function to do the expansion.
2385 for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i) 2435 for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i)
2386 { 2436 {
2387 if (xp->xp_context == tab[i].context) 2437 if (xp->xp_context == tab[i].context)
2388 { 2438 {
2439 // Use fuzzy matching if 'wildoptions' has 'fuzzy'.
2440 // If no search pattern is supplied, then don't use fuzzy
2441 // matching and return all the found items.
2442 int fuzzy = cmdline_fuzzy_complete(pat);
2443
2389 if (tab[i].ic) 2444 if (tab[i].ic)
2390 rmp->rm_ic = TRUE; 2445 rmp->rm_ic = TRUE;
2391 ret = ExpandGeneric(xp, rmp, matches, numMatches, 2446 ret = ExpandGeneric(xp, rmp, matches, numMatches,
2392 tab[i].func, tab[i].escaped); 2447 tab[i].func, tab[i].escaped,
2448 fuzzy ? pat : NULL);
2393 break; 2449 break;
2394 } 2450 }
2395 } 2451 }
2396 2452
2397 return ret; 2453 return ret;
2528 // set ignore-case according to p_ic, p_scs and pat 2584 // set ignore-case according to p_ic, p_scs and pat
2529 regmatch.rm_ic = ignorecase(pat); 2585 regmatch.rm_ic = ignorecase(pat);
2530 2586
2531 if (xp->xp_context == EXPAND_SETTINGS 2587 if (xp->xp_context == EXPAND_SETTINGS
2532 || xp->xp_context == EXPAND_BOOL_SETTINGS) 2588 || xp->xp_context == EXPAND_BOOL_SETTINGS)
2533 ret = ExpandSettings(xp, &regmatch, numMatches, matches); 2589 ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches);
2534 else if (xp->xp_context == EXPAND_MAPPINGS) 2590 else if (xp->xp_context == EXPAND_MAPPINGS)
2535 ret = ExpandMappings(&regmatch, numMatches, matches); 2591 ret = ExpandMappings(&regmatch, numMatches, matches);
2536 # if defined(FEAT_EVAL) 2592 # if defined(FEAT_EVAL)
2537 else if (xp->xp_context == EXPAND_USER_DEFINED) 2593 else if (xp->xp_context == EXPAND_USER_DEFINED)
2538 ret = ExpandUserDefined(xp, &regmatch, matches, numMatches); 2594 ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
2539 # endif 2595 # endif
2540 else 2596 else
2541 ret = ExpandOther(xp, &regmatch, matches, numMatches); 2597 ret = ExpandOther(pat, xp, &regmatch, matches, numMatches);
2542 2598
2543 vim_regfree(regmatch.regprog); 2599 vim_regfree(regmatch.regprog);
2544 vim_free(tofree); 2600 vim_free(tofree);
2545 2601
2546 return ret; 2602 return ret;
2550 * Expand a list of names. 2606 * Expand a list of names.
2551 * 2607 *
2552 * Generic function for command line completion. It calls a function to 2608 * Generic function for command line completion. It calls a function to
2553 * obtain strings, one by one. The strings are matched against a regexp 2609 * obtain strings, one by one. The strings are matched against a regexp
2554 * program. Matching strings are copied into an array, which is returned. 2610 * program. Matching strings are copied into an array, which is returned.
2611 *
2612 * If 'fuzzy' is TRUE, then fuzzy matching is used. Otherwise, regex matching
2613 * is used.
2555 * 2614 *
2556 * Returns OK when no problems encountered, FAIL for error (out of memory). 2615 * Returns OK when no problems encountered, FAIL for error (out of memory).
2557 */ 2616 */
2558 static int 2617 static int
2559 ExpandGeneric( 2618 ExpandGeneric(
2561 regmatch_T *regmatch, 2620 regmatch_T *regmatch,
2562 char_u ***matches, 2621 char_u ***matches,
2563 int *numMatches, 2622 int *numMatches,
2564 char_u *((*func)(expand_T *, int)), 2623 char_u *((*func)(expand_T *, int)),
2565 // returns a string from the list 2624 // returns a string from the list
2566 int escaped) 2625 int escaped,
2626 char_u *fuzzystr)
2567 { 2627 {
2568 int i; 2628 int i;
2569 int count = 0; 2629 int count = 0;
2570 int round; 2630 int round;
2571 char_u *str; 2631 char_u *str;
2632 fuzmatch_str_T *fuzmatch = NULL;
2633 int score = 0;
2634 int fuzzy = (fuzzystr != NULL);
2635 int funcsort = FALSE;
2572 2636
2573 // do this loop twice: 2637 // do this loop twice:
2574 // round == 0: count the number of matching names 2638 // round == 0: count the number of matching names
2575 // round == 1: copy the matching names into allocated memory 2639 // round == 1: copy the matching names into allocated memory
2576 for (round = 0; round <= 1; ++round) 2640 for (round = 0; round <= 1; ++round)
2581 if (str == NULL) // end of list 2645 if (str == NULL) // end of list
2582 break; 2646 break;
2583 if (*str == NUL) // skip empty strings 2647 if (*str == NUL) // skip empty strings
2584 continue; 2648 continue;
2585 2649
2586 if (vim_regexec(regmatch, str, (colnr_T)0)) 2650 if (vim_regexec(regmatch, str, (colnr_T)0) ||
2651 (fuzzy && ((score = fuzzy_match_str(str, fuzzystr)) != 0)))
2587 { 2652 {
2588 if (round) 2653 if (round)
2589 { 2654 {
2590 if (escaped) 2655 if (escaped)
2591 str = vim_strsave_escaped(str, (char_u *)" \t\\."); 2656 str = vim_strsave_escaped(str, (char_u *)" \t\\.");
2592 else 2657 else
2593 str = vim_strsave(str); 2658 str = vim_strsave(str);
2594 if (str == NULL) 2659 if (str == NULL)
2595 { 2660 {
2596 FreeWild(count, *matches); 2661 FreeWild(count, *matches);
2662 if (fuzzy)
2663 fuzmatch_str_free(fuzmatch, count);
2597 *numMatches = 0; 2664 *numMatches = 0;
2598 *matches = NULL; 2665 *matches = NULL;
2599 return FAIL; 2666 return FAIL;
2600 } 2667 }
2601 (*matches)[count] = str; 2668 if (fuzzy)
2669 {
2670 fuzmatch[count].idx = count;
2671 fuzmatch[count].str = str;
2672 fuzmatch[count].score = score;
2673 }
2674 else
2675 (*matches)[count] = str;
2602 # ifdef FEAT_MENU 2676 # ifdef FEAT_MENU
2603 if (func == get_menu_names && str != NULL) 2677 if (func == get_menu_names && str != NULL)
2604 { 2678 {
2605 // test for separator added by get_menu_names() 2679 // test for separator added by get_menu_names()
2606 str += STRLEN(str) - 1; 2680 str += STRLEN(str) - 1;
2614 } 2688 }
2615 if (round == 0) 2689 if (round == 0)
2616 { 2690 {
2617 if (count == 0) 2691 if (count == 0)
2618 return OK; 2692 return OK;
2619 *matches = ALLOC_MULT(char_u *, count); 2693 if (fuzzy)
2620 if (*matches == NULL) 2694 fuzmatch = ALLOC_MULT(fuzmatch_str_T, count);
2695 else
2696 *matches = ALLOC_MULT(char_u *, count);
2697 if ((fuzzy && (fuzmatch == NULL)) || (*matches == NULL))
2621 { 2698 {
2622 *numMatches = 0; 2699 *numMatches = 0;
2623 *matches = NULL; 2700 *matches = NULL;
2624 return FAIL; 2701 return FAIL;
2625 } 2702 }
2633 { 2710 {
2634 if (xp->xp_context == EXPAND_EXPRESSION 2711 if (xp->xp_context == EXPAND_EXPRESSION
2635 || xp->xp_context == EXPAND_FUNCTIONS 2712 || xp->xp_context == EXPAND_FUNCTIONS
2636 || xp->xp_context == EXPAND_USER_FUNC 2713 || xp->xp_context == EXPAND_USER_FUNC
2637 || xp->xp_context == EXPAND_DISASSEMBLE) 2714 || xp->xp_context == EXPAND_DISASSEMBLE)
2715 {
2638 // <SNR> functions should be sorted to the end. 2716 // <SNR> functions should be sorted to the end.
2639 qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *), 2717 funcsort = TRUE;
2718 if (!fuzzy)
2719 qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *),
2640 sort_func_compare); 2720 sort_func_compare);
2721 }
2641 else 2722 else
2642 sort_strings(*matches, *numMatches); 2723 {
2724 if (!fuzzy)
2725 sort_strings(*matches, *numMatches);
2726 }
2643 } 2727 }
2644 2728
2645 #if defined(FEAT_SYN_HL) 2729 #if defined(FEAT_SYN_HL)
2646 // Reset the variables used for special highlight names expansion, so that 2730 // Reset the variables used for special highlight names expansion, so that
2647 // they don't show up when getting normal highlight names by ID. 2731 // they don't show up when getting normal highlight names by ID.
2648 reset_expand_highlight(); 2732 reset_expand_highlight();
2649 #endif 2733 #endif
2734
2735 if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count,
2736 funcsort) == FAIL)
2737 return FAIL;
2738
2650 return OK; 2739 return OK;
2651 } 2740 }
2652 2741
2653 /* 2742 /*
2654 * Expand shell command matches in one directory of $PATH. 2743 * Expand shell command matches in one directory of $PATH.