patch 9.1.1214: matchfuzzy() can be improved for camel case matches
Problem: When searching for "Cur", CamelCase matches like "lCursor" score
higher than exact prefix matches like Cursor, which is
counter-intuitive (Maxim Kim).
Solution: Add a 'camelcase' option to matchfuzzy() that lets users disable
CamelCase bonuses when needed, making prefix matches rank higher.
(glepnir)
fixes: #16504
closes: #16797
Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/search.c b/src/search.c
index 064ed3e..c7ae4f8 100644
--- a/src/search.c
+++ b/src/search.c
@@ -42,11 +42,11 @@
static int is_zero_width(char_u *pattern, size_t patternlen, int move, pos_T *cur, int direction);
static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, int show_top_bot_msg, char_u *msgbuf, size_t msgbuflen, int recompute, int maxcount, long timeout);
static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchstat_T *stat, int recompute, int maxcount, long timeout);
-static int fuzzy_match_compute_score(char_u *fuzpat, char_u *str, int strSz, int_u *matches, int numMatches);
-static int fuzzy_match_recursive(char_u *fuzpat, char_u *str, int_u strIdx, int *outScore, char_u *strBegin, int strLen, int_u *srcMatches, int_u *matches, int maxMatches, int nextMatch, int *recursionCount);
+static int fuzzy_match_compute_score(char_u *fuzpat, char_u *str, int strSz, int_u *matches, int numMatches, int camelcase);
+static int fuzzy_match_recursive(char_u *fuzpat, char_u *str, int_u strIdx, int *outScore, char_u *strBegin, int strLen, int_u *srcMatches, int_u *matches, int maxMatches, int nextMatch, int *recursionCount, int camelcase);
#if defined(FEAT_EVAL) || defined(FEAT_PROTO)
static int fuzzy_match_item_compare(const void *s1, const void *s2);
-static void fuzzy_match_in_list(list_T *l, char_u *str, int matchseq, char_u *key, callback_T *item_cb, int retmatchpos, list_T *fmatchlist, long max_matches);
+static void fuzzy_match_in_list(list_T *l, char_u *str, int matchseq, char_u *key, callback_T *item_cb, int retmatchpos, list_T *fmatchlist, long max_matches, int camelcase);
static void do_fuzzymatch(typval_T *argvars, typval_T *rettv, int retmatchpos);
#endif
static int fuzzy_match_str_compare(const void *s1, const void *s2);
@@ -4388,7 +4388,8 @@
char_u *str,
int strSz,
int_u *matches,
- int numMatches)
+ int numMatches,
+ int camelcase)
{
int score;
int penalty;
@@ -4461,7 +4462,7 @@
}
// Enhanced camel case scoring
- if (vim_islower(neighbor) && vim_isupper(curr))
+ if (camelcase && vim_islower(neighbor) && vim_isupper(curr))
{
score += CAMEL_BONUS * 2; // Double the camel case bonus
is_camel = TRUE;
@@ -4544,7 +4545,8 @@
int_u *matches,
int maxMatches,
int nextMatch,
- int *recursionCount)
+ int *recursionCount,
+ int camelcase)
{
// Recursion params
int recursiveMatch = FALSE;
@@ -4596,7 +4598,7 @@
&recursiveScore, strBegin, strLen, matches,
recursiveMatches,
ARRAY_LENGTH(recursiveMatches),
- nextMatch, recursionCount))
+ nextMatch, recursionCount, camelcase))
{
// Pick best recursive score
if (!recursiveMatch || recursiveScore > bestRecursiveScore)
@@ -4628,7 +4630,7 @@
// Calculate score
if (matched)
*outScore = fuzzy_match_compute_score(fuzpat, strBegin, strLen, matches,
- nextMatch);
+ nextMatch, camelcase);
// Return best result
if (recursiveMatch && (!matched || bestRecursiveScore > *outScore))
@@ -4666,7 +4668,8 @@
int matchseq,
int *outScore,
int_u *matches,
- int maxMatches)
+ int maxMatches,
+ int camelcase)
{
int recursionCount = 0;
int len = MB_CHARLEN(str);
@@ -4714,7 +4717,7 @@
recursionCount = 0;
matchCount = fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL,
matches + numMatches, maxMatches - numMatches,
- 0, &recursionCount);
+ 0, &recursionCount, camelcase);
if (matchCount == 0)
{
numMatches = 0;
@@ -4775,7 +4778,8 @@
callback_T *item_cb,
int retmatchpos,
list_T *fmatchlist,
- long max_matches)
+ long max_matches,
+ int camelcase)
{
long len;
fuzzyItem_T *items;
@@ -4836,7 +4840,7 @@
if (itemstr != NULL
&& fuzzy_match(itemstr, str, matchseq, &score, matches,
- MAX_FUZZY_MATCHES))
+ MAX_FUZZY_MATCHES, camelcase))
{
items[match_count].idx = match_count;
items[match_count].item = li;
@@ -4955,6 +4959,7 @@
int ret;
int matchseq = FALSE;
long max_matches = 0;
+ int camelcase = TRUE;
if (in_vim9script()
&& (check_for_list_arg(argvars, 0) == FAIL
@@ -5020,6 +5025,16 @@
max_matches = (long)tv_get_number_chk(&di->di_tv, NULL);
}
+ if ((di = dict_find(d, (char_u *)"camelcase", -1)) != NULL)
+ {
+ if (di->di_tv.v_type != VAR_BOOL)
+ {
+ semsg(_(e_invalid_argument_str), "camelcase");
+ return;
+ }
+ camelcase = tv_get_bool_chk(&di->di_tv, NULL);
+ }
+
if (dict_has_key(d, "matchseq"))
matchseq = TRUE;
}
@@ -5063,7 +5078,8 @@
}
fuzzy_match_in_list(argvars[0].vval.v_list, tv_get_string(&argvars[1]),
- matchseq, key, &cb, retmatchpos, rettv->vval.v_list, max_matches);
+ matchseq, key, &cb, retmatchpos, rettv->vval.v_list, max_matches,
+ camelcase);
done:
free_callback(&cb);
@@ -5166,7 +5182,7 @@
return 0;
fuzzy_match(str, pat, TRUE, &score, matchpos,
- sizeof(matchpos) / sizeof(matchpos[0]));
+ sizeof(matchpos) / sizeof(matchpos[0]), TRUE);
return score;
}
@@ -5193,7 +5209,7 @@
return NULL;
ga_init2(match_positions, sizeof(int_u), 10);
- if (!fuzzy_match(str, pat, FALSE, &score, matches, MAX_FUZZY_MATCHES)
+ if (!fuzzy_match(str, pat, FALSE, &score, matches, MAX_FUZZY_MATCHES, TRUE)
|| score == 0)
{
ga_clear(match_positions);