Experiment to rework wildcard matching

Preparation for zsh-style intermediate component
tab completion
This commit is contained in:
ridiculousfish 2015-08-04 00:32:34 -07:00
parent b7e16cb0dd
commit fd96bafbc8
2 changed files with 47 additions and 19 deletions

View file

@ -139,19 +139,26 @@ bool wildcard_has(const wcstring &str, bool internal)
\param wc The wildcard. \param wc The wildcard.
\param is_first Whether files beginning with dots should not be matched against wildcards. \param is_first Whether files beginning with dots should not be matched against wildcards.
*/ */
static bool wildcard_match_internal(const wchar_t *str, const wchar_t *wc, bool leading_dots_fail_to_match, bool is_first) static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const wchar_t *wc, bool leading_dots_fail_to_match, bool is_first, enum fuzzy_match_type_t max_type)
{ {
if (*str == 0 && *wc==0) if (*str == 0 && *wc==0)
{ {
/* We're done */ /* We're done */
return true; return fuzzy_match_exact;
} }
/* Hackish fix for https://github.com/fish-shell/fish-shell/issues/270 . Prevent wildcards from matching . or .., but we must still allow literal matches. */ /* Hackish fix for #270 . Prevent wildcards from matching . or .., but we must still allow literal matches. */
if (leading_dots_fail_to_match && is_first && contains(str, L".", L"..")) if (leading_dots_fail_to_match && is_first && contains(str, L".", L".."))
{ {
/* The string is '.' or '..'. Return true if the wildcard exactly matches. */ /* The string is '.' or '..'. Return true if the wildcard exactly matches. */
return ! wcscmp(str, wc); return wcscmp(str, wc) ? fuzzy_match_none : fuzzy_match_exact;
}
/* Hackish fuzzy match support */
if (0 && ! wildcard_has(wc, true))
{
const string_fuzzy_match_t match = string_fuzzy_match_string(wc, str);
return match.type <= max_type ? match.type : fuzzy_match_none;
} }
if (*wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) if (*wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE)
@ -159,23 +166,23 @@ static bool wildcard_match_internal(const wchar_t *str, const wchar_t *wc, bool
/* Ignore hidden file */ /* Ignore hidden file */
if (leading_dots_fail_to_match && is_first && *str == L'.') if (leading_dots_fail_to_match && is_first && *str == L'.')
{ {
return false; return fuzzy_match_none;
} }
/* Common case of * at the end. In that case we can early out since we know it will match. */ /* Common case of * at the end. In that case we can early out since we know it will match. */
if (wc[1] == L'\0') if (wc[1] == L'\0')
{ {
return true; return fuzzy_match_exact;
} }
/* Try all submatches */ /* Try all submatches */
do do
{ {
if (wildcard_match_internal(str, wc+1, leading_dots_fail_to_match, false)) enum fuzzy_match_type_t subresult = wildcard_match_internal(str, wc+1, leading_dots_fail_to_match, false, max_type);
return true; if (subresult != fuzzy_match_none)
} return subresult;
while (*(str++) != 0); } while (*str++ != 0);
return false; return fuzzy_match_none;
} }
else if (*str == 0) else if (*str == 0)
{ {
@ -183,23 +190,23 @@ static bool wildcard_match_internal(const wchar_t *str, const wchar_t *wc, bool
End of string, but not end of wildcard, and the next wildcard End of string, but not end of wildcard, and the next wildcard
element is not a '*', so this is not a match. element is not a '*', so this is not a match.
*/ */
return false; return fuzzy_match_none;
} }
else if (*wc == ANY_CHAR) else if (*wc == ANY_CHAR)
{ {
if (is_first && *str == L'.') if (is_first && *str == L'.')
{ {
return false; return fuzzy_match_none;
} }
return wildcard_match_internal(str+1, wc+1, leading_dots_fail_to_match, false); return wildcard_match_internal(str+1, wc+1, leading_dots_fail_to_match, false, max_type);
} }
else if (*wc == *str) else if (*wc == *str)
{ {
return wildcard_match_internal(str+1, wc+1, leading_dots_fail_to_match, false); return wildcard_match_internal(str+1, wc+1, leading_dots_fail_to_match, false, max_type);
} }
return false; return fuzzy_match_none;
} }
@ -407,7 +414,8 @@ bool wildcard_complete(const wcstring &str,
bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match) bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match)
{ {
return wildcard_match_internal(str.c_str(), wc.c_str(), leading_dots_fail_to_match, true /* first */); enum fuzzy_match_type_t match = wildcard_match_internal(str.c_str(), wc.c_str(), leading_dots_fail_to_match, true /* first */, fuzzy_match_exact);
return match != fuzzy_match_none;
} }
/** /**
@ -889,8 +897,25 @@ void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc)
{ {
/* Literal intermediate match. Note that we may not be able to actually read the directory (#2099) */ /* Literal intermediate match. Note that we may not be able to actually read the directory (#2099) */
assert(next_slash != NULL); assert(next_slash != NULL);
const wchar_t *wc_remainder = next_slash;
while (*wc_remainder == L'/')
{
wc_remainder++;
}
/* This just trumps everything */ /* This just trumps everything */
this->expand(base_dir + wc_segment + L'/', next_slash + 1); size_t before = this->resolved_completions->size();
this->expand(base_dir + wc_segment + L'/', wc_remainder);
if (this->resolved_completions->size() == before)
{
/* Nothing was found with the literal match. Try a fuzzy match (#94). */
DIR *base_dir_fd = open_dir(base_dir);
if (base_dir_fd != NULL)
{
this->expand_intermediate_segment(base_dir, base_dir_fd, wc_segment, wc_remainder);
closedir(base_dir_fd);
}
}
} }
else else
{ {

View file

@ -76,6 +76,9 @@ int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_
*/ */
bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match = false); bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match = false);
/* Like wildcard_match, but returns a fuzzy match type */
enum fuzzy_match_type_t wildcard_match_fuzzy(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match = false, enum fuzzy_match_type_t max_type = fuzzy_match_none);
/** Check if the specified string contains wildcards */ /** Check if the specified string contains wildcards */
bool wildcard_has(const wcstring &, bool internal); bool wildcard_has(const wcstring &, bool internal);
bool wildcard_has(const wchar_t *, bool internal); bool wildcard_has(const wchar_t *, bool internal);