diff options
Diffstat (limited to 'src/nvim/search.c')
-rw-r--r-- | src/nvim/search.c | 486 |
1 files changed, 305 insertions, 181 deletions
diff --git a/src/nvim/search.c b/src/nvim/search.c index 871d2f9a0a..eb5cc2e07f 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -133,7 +133,7 @@ typedef struct SearchedFile { /// @param regmatch return: pattern and ignore-case flag /// /// @return FAIL if failed, OK otherwise. -int search_regcomp(char_u *pat, char_u **used_pat, int pat_save, int pat_use, int options, +int search_regcomp(char *pat, char **used_pat, int pat_save, int pat_use, int options, regmmatch_T *regmatch) { int magic; @@ -158,11 +158,11 @@ int search_regcomp(char_u *pat, char_u **used_pat, int pat_save, int pat_use, in rc_did_emsg = true; return FAIL; } - pat = (char_u *)spats[i].pat; + pat = spats[i].pat; magic = spats[i].magic; no_smartcase = spats[i].no_scs; } else if (options & SEARCH_HIS) { // put new pattern in history - add_to_history(HIST_SEARCH, (char *)pat, true, NUL); + add_to_history(HIST_SEARCH, pat, true, NUL); } if (used_pat) { @@ -171,9 +171,9 @@ int search_regcomp(char_u *pat, char_u **used_pat, int pat_save, int pat_use, in xfree(mr_pattern); if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { - mr_pattern = reverse_text((char *)pat); + mr_pattern = reverse_text(pat); } else { - mr_pattern = xstrdup((char *)pat); + mr_pattern = xstrdup(pat); } // Save the currently used pattern in the appropriate place, @@ -181,17 +181,17 @@ int search_regcomp(char_u *pat, char_u **used_pat, int pat_save, int pat_use, in if (!(options & SEARCH_KEEP) && (cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) { // search or global command if (pat_save == RE_SEARCH || pat_save == RE_BOTH) { - save_re_pat(RE_SEARCH, (char *)pat, magic); + save_re_pat(RE_SEARCH, pat, magic); } // substitute or global command if (pat_save == RE_SUBST || pat_save == RE_BOTH) { - save_re_pat(RE_SUBST, (char *)pat, magic); + save_re_pat(RE_SUBST, pat, magic); } } - regmatch->rmm_ic = ignorecase((char *)pat); + regmatch->rmm_ic = ignorecase(pat); regmatch->rmm_maxcol = 0; - regmatch->regprog = vim_regcomp((char *)pat, magic ? RE_MAGIC : 0); + regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0); if (regmatch->regprog == NULL) { return FAIL; } @@ -206,20 +206,22 @@ char *get_search_pat(void) void save_re_pat(int idx, char *pat, int magic) { - if (spats[idx].pat != pat) { - free_spat(&spats[idx]); - spats[idx].pat = xstrdup(pat); - spats[idx].magic = magic; - spats[idx].no_scs = no_smartcase; - spats[idx].timestamp = os_time(); - spats[idx].additional_data = NULL; - last_idx = idx; - // If 'hlsearch' set and search pat changed: need redraw. - if (p_hls) { - redraw_all_later(UPD_SOME_VALID); - } - set_no_hlsearch(false); + if (spats[idx].pat == pat) { + return; + } + + free_spat(&spats[idx]); + spats[idx].pat = xstrdup(pat); + spats[idx].magic = magic; + spats[idx].no_scs = no_smartcase; + spats[idx].timestamp = os_time(); + spats[idx].additional_data = NULL; + last_idx = idx; + // If 'hlsearch' set and search pat changed: need redraw. + if (p_hls) { + redraw_all_later(UPD_SOME_VALID); } + set_no_hlsearch(false); } // Save the search patterns, so they can be restored later. @@ -228,38 +230,42 @@ static int save_level = 0; void save_search_patterns(void) { - if (save_level++ == 0) { - saved_spats[0] = spats[0]; - if (spats[0].pat != NULL) { - saved_spats[0].pat = xstrdup(spats[0].pat); - } - saved_spats[1] = spats[1]; - if (spats[1].pat != NULL) { - saved_spats[1].pat = xstrdup(spats[1].pat); - } - if (mr_pattern == NULL) { - saved_mr_pattern = NULL; - } else { - saved_mr_pattern = xstrdup(mr_pattern); - } - saved_spats_last_idx = last_idx; - saved_spats_no_hlsearch = no_hlsearch; + if (save_level++ != 0) { + return; + } + + saved_spats[0] = spats[0]; + if (spats[0].pat != NULL) { + saved_spats[0].pat = xstrdup(spats[0].pat); + } + saved_spats[1] = spats[1]; + if (spats[1].pat != NULL) { + saved_spats[1].pat = xstrdup(spats[1].pat); } + if (mr_pattern == NULL) { + saved_mr_pattern = NULL; + } else { + saved_mr_pattern = xstrdup(mr_pattern); + } + saved_spats_last_idx = last_idx; + saved_spats_no_hlsearch = no_hlsearch; } void restore_search_patterns(void) { - if (--save_level == 0) { - free_spat(&spats[0]); - spats[0] = saved_spats[0]; - set_vv_searchforward(); - free_spat(&spats[1]); - spats[1] = saved_spats[1]; - xfree(mr_pattern); - mr_pattern = saved_mr_pattern; - last_idx = saved_spats_last_idx; - set_no_hlsearch(saved_spats_no_hlsearch); + if (--save_level != 0) { + return; } + + free_spat(&spats[0]); + spats[0] = saved_spats[0]; + set_vv_searchforward(); + free_spat(&spats[1]); + spats[1] = saved_spats[1]; + xfree(mr_pattern); + mr_pattern = saved_mr_pattern; + last_idx = saved_spats_last_idx; + set_no_hlsearch(saved_spats_no_hlsearch); } static inline void free_spat(struct spat *const spat) @@ -346,9 +352,9 @@ static void restore_incsearch_state(void) search_match_lines = saved_search_match_lines; } -char_u *last_search_pattern(void) +char *last_search_pattern(void) { - return (char_u *)spats[RE_SEARCH].pat; + return spats[RE_SEARCH].pat; } /// Return true when case should be ignored for search pattern "pat". @@ -365,7 +371,7 @@ int ignorecase_opt(char *pat, int ic_in, int scs) if (ic && !no_smartcase && scs && !(ctrl_x_mode_not_default() && curbuf->b_p_inf)) { - ic = !pat_has_uppercase((char_u *)pat); + ic = !pat_has_uppercase(pat); } no_smartcase = false; @@ -373,14 +379,14 @@ int ignorecase_opt(char *pat, int ic_in, int scs) } /// Returns true if pattern `pat` has an uppercase character. -bool pat_has_uppercase(char_u *pat) +bool pat_has_uppercase(char *pat) FUNC_ATTR_NONNULL_ALL { - char *p = (char *)pat; + char *p = pat; magic_T magic_val = MAGIC_ON; // get the magicness of the pattern - (void)skip_regexp_ex((char *)pat, NUL, magic_isset(), NULL, NULL, &magic_val); + (void)skip_regexp_ex(pat, NUL, magic_isset(), NULL, NULL, &magic_val); while (*p != NUL) { const int l = utfc_ptr2len(p); @@ -431,7 +437,7 @@ int last_csearch_until(void) return last_t_cmd == true; } -void set_last_csearch(int c, char_u *s, int len) +void set_last_csearch(int c, char *s, int len) { *lastc = (char_u)c; lastc_bytelen = len; @@ -452,9 +458,9 @@ void set_csearch_until(int t_cmd) last_t_cmd = t_cmd; } -char_u *last_search_pat(void) +char *last_search_pat(void) { - return (char_u *)spats[last_idx].pat; + return spats[last_idx].pat; } // Reset search direction to forward. For "gd" and "gD" commands. @@ -466,14 +472,14 @@ void reset_search_dir(void) // Set the last search pattern. For ":let @/ =" and ShaDa file. // Also set the saved search pattern, so that this works in an autocommand. -void set_last_search_pat(const char_u *s, int idx, int magic, int setlast) +void set_last_search_pat(const char *s, int idx, int magic, int setlast) { free_spat(&spats[idx]); // An empty string means that nothing should be matched. if (*s == NUL) { spats[idx].pat = NULL; } else { - spats[idx].pat = xstrdup((char *)s); + spats[idx].pat = xstrdup(s); } spats[idx].timestamp = os_time(); spats[idx].additional_data = NULL; @@ -513,7 +519,7 @@ void last_pat_prog(regmmatch_T *regmatch) return; } emsg_off++; // So it doesn't beep if bad expr - (void)search_regcomp((char_u *)"", NULL, 0, last_idx, SEARCH_KEEP, regmatch); + (void)search_regcomp("", NULL, 0, last_idx, SEARCH_KEEP, regmatch); emsg_off--; } @@ -540,7 +546,7 @@ void last_pat_prog(regmmatch_T *regmatch) /// @returns FAIL (zero) for failure, non-zero for success. /// the index of the first matching /// subpattern plus one; one if there was none. -int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, char_u *pat, +int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, char *pat, long count, int options, int pat_use, searchit_arg_T *extra_arg) { int found; @@ -1296,7 +1302,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, long count, i } c = searchit(curwin, curbuf, &pos, NULL, dirc == '/' ? FORWARD : BACKWARD, - (char_u *)searchstr, count, + searchstr, count, (spats[0].off.end * SEARCH_END + (options & (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG @@ -2321,55 +2327,63 @@ void showmatch(int c) if ((lpos = findmatch(NULL, NUL)) == NULL) { // no match, so beep vim_beep(BO_MATCH); - } else if (lpos->lnum >= curwin->w_topline - && lpos->lnum < curwin->w_botline) { - if (!curwin->w_p_wrap) { - getvcol(curwin, lpos, NULL, &vcol, NULL); - } - if (curwin->w_p_wrap - || (vcol >= curwin->w_leftcol - && vcol < curwin->w_leftcol + curwin->w_width_inner)) { - mpos = *lpos; // save the pos, update_screen() may change it - save_cursor = curwin->w_cursor; - save_so = *so; - save_siso = *siso; - // Handle "$" in 'cpo': If the ')' is typed on top of the "$", - // stop displaying the "$". - if (dollar_vcol >= 0 && dollar_vcol == curwin->w_virtcol) { - dollar_vcol = -1; - } - curwin->w_virtcol++; // do display ')' just before "$" - update_screen(); // show the new char first - - save_dollar_vcol = dollar_vcol; - save_state = State; - State = MODE_SHOWMATCH; - ui_cursor_shape(); // may show different cursor shape - curwin->w_cursor = mpos; // move to matching char - *so = 0; // don't use 'scrolloff' here - *siso = 0; // don't use 'sidescrolloff' here - show_cursor_info(false); - setcursor(); - ui_flush(); - // Restore dollar_vcol(), because setcursor() may call curs_rows() - // which resets it if the matching position is in a previous line - // and has a higher column number. - dollar_vcol = save_dollar_vcol; - - // brief pause, unless 'm' is present in 'cpo' and a character is - // available. - if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) { - os_delay((uint64_t)p_mat * 100L + 8, true); - } else if (!char_avail()) { - os_delay((uint64_t)p_mat * 100L + 9, false); - } - curwin->w_cursor = save_cursor; // restore cursor position - *so = save_so; - *siso = save_siso; - State = save_state; - ui_cursor_shape(); // may show different cursor shape - } + return; + } + + if (lpos->lnum < curwin->w_topline || lpos->lnum >= curwin->w_botline) { + return; + } + + if (!curwin->w_p_wrap) { + getvcol(curwin, lpos, NULL, &vcol, NULL); + } + + bool col_visible = curwin->w_p_wrap + || (vcol >= curwin->w_leftcol + && vcol < curwin->w_leftcol + curwin->w_width_inner); + if (!col_visible) { + return; + } + + mpos = *lpos; // save the pos, update_screen() may change it + save_cursor = curwin->w_cursor; + save_so = *so; + save_siso = *siso; + // Handle "$" in 'cpo': If the ')' is typed on top of the "$", + // stop displaying the "$". + if (dollar_vcol >= 0 && dollar_vcol == curwin->w_virtcol) { + dollar_vcol = -1; } + curwin->w_virtcol++; // do display ')' just before "$" + update_screen(); // show the new char first + + save_dollar_vcol = dollar_vcol; + save_state = State; + State = MODE_SHOWMATCH; + ui_cursor_shape(); // may show different cursor shape + curwin->w_cursor = mpos; // move to matching char + *so = 0; // don't use 'scrolloff' here + *siso = 0; // don't use 'sidescrolloff' here + show_cursor_info(false); + setcursor(); + ui_flush(); + // Restore dollar_vcol(), because setcursor() may call curs_rows() + // which resets it if the matching position is in a previous line + // and has a higher column number. + dollar_vcol = save_dollar_vcol; + + // brief pause, unless 'm' is present in 'cpo' and a character is + // available. + if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) { + os_delay((uint64_t)p_mat * 100L + 8, true); + } else if (!char_avail()) { + os_delay((uint64_t)p_mat * 100L + 9, false); + } + curwin->w_cursor = save_cursor; // restore cursor position + *so = save_so; + *siso = save_siso; + State = save_state; + ui_cursor_shape(); // may show different cursor shape } /// Find next search match under cursor, cursor at end. @@ -2440,7 +2454,7 @@ int current_search(long count, bool forward) result = searchit(curwin, curbuf, &pos, &end_pos, (dir ? FORWARD : BACKWARD), - (char_u *)spats[last_idx].pat, i ? count : 1, + spats[last_idx].pat, i ? count : 1, SEARCH_KEEP | flags, RE_SEARCH, NULL); p_ws = old_p_ws; @@ -2527,7 +2541,7 @@ static int is_zero_width(char *pattern, int move, pos_T *cur, Direction directio pattern = spats[last_idx].pat; } - if (search_regcomp((char_u *)pattern, NULL, RE_SEARCH, RE_SEARCH, + if (search_regcomp(pattern, NULL, RE_SEARCH, RE_SEARCH, SEARCH_KEEP, ®match) == FAIL) { return -1; } @@ -2542,7 +2556,7 @@ static int is_zero_width(char *pattern, int move, pos_T *cur, Direction directio // accept a match at the cursor position flag = SEARCH_START; } - if (searchit(curwin, curbuf, &pos, NULL, direction, (char_u *)pattern, 1, + if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, 1, SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) { // Zero-width pattern should match somewhere, then we can check if // start and end are in the same position. @@ -2588,56 +2602,63 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh update_search_stat(dirc, pos, cursor_pos, &stat, recompute, maxcount, timeout); - if (stat.cur > 0) { - char t[SEARCH_STAT_BUF_LEN]; - - if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { - if (stat.incomplete == 1) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); - } else if (stat.cnt > maxcount && stat.cur > maxcount) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", - maxcount, maxcount); - } else if (stat.cnt > maxcount) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]", - maxcount, stat.cur); - } else { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", - stat.cnt, stat.cur); - } + if (stat.cur <= 0) { + return; + } + + char t[SEARCH_STAT_BUF_LEN]; + + if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { + if (stat.incomplete == 1) { + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); + } else if (stat.cnt > maxcount && stat.cur > maxcount) { + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", + maxcount, maxcount); + } else if (stat.cnt > maxcount) { + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]", + maxcount, stat.cur); } else { - if (stat.incomplete == 1) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); - } else if (stat.cnt > maxcount && stat.cur > maxcount) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", - maxcount, maxcount); - } else if (stat.cnt > maxcount) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]", - stat.cur, maxcount); - } else { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", - stat.cur, stat.cnt); - } + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", + stat.cnt, stat.cur); } - - size_t len = strlen(t); - if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) { - memmove(t + 2, t, len); - t[0] = 'W'; - t[1] = ' '; - len += 2; + } else { + if (stat.incomplete == 1) { + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); + } else if (stat.cnt > maxcount && stat.cur > maxcount) { + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", + maxcount, maxcount); + } else if (stat.cnt > maxcount) { + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]", + stat.cur, maxcount); + } else { + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", + stat.cur, stat.cnt); } + } - memmove(msgbuf + strlen(msgbuf) - len, t, len); - if (dirc == '?' && stat.cur == maxcount + 1) { - stat.cur = -1; - } + size_t len = strlen(t); + if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) { + memmove(t + 2, t, len); + t[0] = 'W'; + t[1] = ' '; + len += 2; + } + + size_t msgbuf_len = strlen(msgbuf); + if (len > msgbuf_len) { + len = msgbuf_len; + } + memmove(msgbuf + msgbuf_len - len, t, len); - // keep the message even after redraw, but don't put in history - msg_hist_off = true; - msg_ext_set_kind("search_count"); - give_warning(msgbuf, false); - msg_hist_off = false; + if (dirc == '?' && stat.cur == maxcount + 1) { + stat.cur = -1; } + + // keep the message even after redraw, but don't put in history + msg_hist_off = true; + msg_ext_set_kind("search_count"); + give_warning(msgbuf, false); + msg_hist_off = false; } // Add the search count information to "stat". @@ -2942,7 +2963,7 @@ typedef struct { #define FUZZY_MATCH_RECURSION_LIMIT 10 /// Compute a score for a fuzzy matched string. The matching character locations -/// are in 'matches'. +/// are in "matches". static int fuzzy_match_compute_score(const char *const str, const int strSz, const uint32_t *const matches, const int numMatches) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE @@ -3007,7 +3028,7 @@ static int fuzzy_match_compute_score(const char *const str, const int strSz, return score; } -/// Perform a recursive search for fuzzy matching 'fuzpat' in 'str'. +/// Perform a recursive search for fuzzy matching "fuzpat" in "str". /// @return the number of matching characters. static int fuzzy_match_recursive(const char *fuzpat, const char *str, uint32_t strIdx, int *const outScore, const char *const strBegin, const int strLen, @@ -3107,23 +3128,23 @@ static int fuzzy_match_recursive(const char *fuzpat, const char *str, uint32_t s /// Uses char_u for match indices. Therefore patterns are limited to /// MAX_FUZZY_MATCHES characters. /// -/// @return true if 'pat_arg' matches 'str'. Also returns the match score in -/// 'outScore' and the matching character positions in 'matches'. -bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matchseq, +/// @return true if "pat_arg" matches "str". Also returns the match score in +/// "outScore" and the matching character positions in "matches". +bool fuzzy_match(char *const str, const char *const pat_arg, const bool matchseq, int *const outScore, uint32_t *const matches, const int maxMatches) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ALL { - const int len = mb_charlen((char *)str); + const int len = mb_charlen(str); bool complete = false; int numMatches = 0; *outScore = 0; - char *const save_pat = xstrdup((char *)pat_arg); + char *const save_pat = xstrdup(pat_arg); char *pat = save_pat; char *p = pat; - // Try matching each word in 'pat_arg' in 'str' + // Try matching each word in "pat_arg" in "str" while (true) { if (matchseq) { complete = true; @@ -3146,7 +3167,7 @@ bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matc int score = 0; int recursionCount = 0; const int matchCount - = fuzzy_match_recursive(pat, (char *)str, 0, &score, (char *)str, len, NULL, + = fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL, matches + numMatches, maxMatches - numMatches, 0, &recursionCount); if (matchCount == 0) { @@ -3183,14 +3204,14 @@ static int fuzzy_match_item_compare(const void *const s1, const void *const s2) return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1; } -/// Fuzzy search the string 'str' in a list of 'items' and return the matching -/// strings in 'fmatchlist'. -/// If 'matchseq' is true, then for multi-word search strings, match all the +/// Fuzzy search the string "str" in a list of "items" and return the matching +/// strings in "fmatchlist". +/// If "matchseq" is true, then for multi-word search strings, match all the /// words in sequence. -/// If 'items' is a list of strings, then search for 'str' in the list. -/// If 'items' is a list of dicts, then either use 'key' to lookup the string -/// for each item or use 'item_cb' Funcref function to get the string. -/// If 'retmatchpos' is true, then return a list of positions where 'str' +/// If "items" is a list of strings, then search for "str" in the list. +/// If "items" is a list of dicts, then either use "key" to lookup the string +/// for each item or use "item_cb" Funcref function to get the string. +/// If "retmatchpos" is true, then return a list of positions where "str" /// matches for each item. static void fuzzy_match_in_list(list_T *const l, char *const str, const bool matchseq, const char *const key, Callback *const item_cb, @@ -3245,14 +3266,14 @@ static void fuzzy_match_in_list(list_T *const l, char *const str, const bool mat } int score; - if (itemstr != NULL && fuzzy_match((char_u *)itemstr, (char_u *)str, matchseq, &score, matches, + if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches, MAX_FUZZY_MATCHES)) { items[match_count].idx = (int)match_count; items[match_count].item = li; items[match_count].score = score; // Copy the list of matching positions in itemstr to a list, if - // 'retmatchpos' is set. + // "retmatchpos" is set. if (retmatchpos) { items[match_count].lmatchpos = tv_list_alloc(kListLenMayKnow); int j = 0; @@ -3326,8 +3347,8 @@ static void fuzzy_match_in_list(list_T *const l, char *const str, const bool mat xfree(items); } -/// Do fuzzy matching. Returns the list of matched strings in 'rettv'. -/// If 'retmatchpos' is true, also returns the matching character positions. +/// Do fuzzy matching. Returns the list of matched strings in "rettv". +/// If "retmatchpos" is true, also returns the matching character positions. static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, const bool retmatchpos) FUNC_ATTR_NONNULL_ALL @@ -3411,6 +3432,109 @@ void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) do_fuzzymatch(argvars, rettv, true); } +/// Same as fuzzy_match_item_compare() except for use with a string match +static int fuzzy_match_str_compare(const void *const s1, const void *const s2) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + const int v1 = ((fuzmatch_str_T *)s1)->score; + const int v2 = ((fuzmatch_str_T *)s2)->score; + const int idx1 = ((fuzmatch_str_T *)s1)->idx; + const int idx2 = ((fuzmatch_str_T *)s2)->idx; + + return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1; +} + +/// Sort fuzzy matches by score +static void fuzzy_match_str_sort(fuzmatch_str_T *const fm, const int sz) + FUNC_ATTR_NONNULL_ALL +{ + // Sort the list by the descending order of the match score + qsort(fm, (size_t)sz, sizeof(fuzmatch_str_T), fuzzy_match_str_compare); +} + +/// Same as fuzzy_match_item_compare() except for use with a function name +/// string match. <SNR> functions should be sorted to the end. +static int fuzzy_match_func_compare(const void *const s1, const void *const s2) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + const int v1 = ((fuzmatch_str_T *)s1)->score; + const int v2 = ((fuzmatch_str_T *)s2)->score; + const int idx1 = ((fuzmatch_str_T *)s1)->idx; + const int idx2 = ((fuzmatch_str_T *)s2)->idx; + const char *const str1 = ((fuzmatch_str_T *)s1)->str; + const char *const str2 = ((fuzmatch_str_T *)s2)->str; + + if (*str1 != '<' && *str2 == '<') { + return -1; + } + if (*str1 == '<' && *str2 != '<') { + return 1; + } + return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1; +} + +/// Sort fuzzy matches of function names by score. +/// <SNR> functions should be sorted to the end. +static void fuzzy_match_func_sort(fuzmatch_str_T *const fm, const int sz) + FUNC_ATTR_NONNULL_ALL +{ + // Sort the list by the descending order of the match score + qsort(fm, (size_t)sz, sizeof(fuzmatch_str_T), fuzzy_match_func_compare); +} + +/// Fuzzy match "pat" in "str". +/// @returns 0 if there is no match. Otherwise, returns the match score. +int fuzzy_match_str(char *const str, const char *const pat) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (str == NULL || pat == NULL) { + return 0; + } + + int score = 0; + uint32_t matchpos[MAX_FUZZY_MATCHES]; + fuzzy_match(str, pat, true, &score, matchpos, sizeof(matchpos) / sizeof(matchpos[0])); + + return score; +} + +/// Copy a list of fuzzy matches into a string list after sorting the matches by +/// the fuzzy score. Frees the memory allocated for "fuzmatch". +void fuzzymatches_to_strmatches(fuzmatch_str_T *const fuzmatch, char ***const matches, + const int count, const bool funcsort) + FUNC_ATTR_NONNULL_ARG(2) +{ + if (count <= 0) { + return; + } + + *matches = xmalloc((size_t)count * sizeof(char *)); + + // Sort the list by the descending order of the match score + if (funcsort) { + fuzzy_match_func_sort(fuzmatch, count); + } else { + fuzzy_match_str_sort(fuzmatch, count); + } + + for (int i = 0; i < count; i++) { + (*matches)[i] = fuzmatch[i].str; + } + xfree(fuzmatch); +} + +/// Free a list of fuzzy string matches. +void fuzmatch_str_free(fuzmatch_str_T *const fuzmatch, int count) +{ + if (count <= 0 || fuzmatch == NULL) { + return; + } + while (count--) { + xfree(fuzmatch[count].str); + } + xfree(fuzmatch); +} + /// Get line "lnum" and copy it into "buf[LSIZE]". /// The copy is made because the regexp may make the line invalid when using a /// mark. @@ -3534,9 +3658,9 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool 1L, p_fname); } else { // Use text after match with 'include'. - new_fname = (char *)file_name_in_line((char_u *)incl_regmatch.endp[0], 0, - FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, (char_u *)p_fname, - NULL); + new_fname = file_name_in_line(incl_regmatch.endp[0], 0, + FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, + NULL); } already_searched = false; if (new_fname != NULL) { |