Make fuzzy_match_type_t an enum class

Also rename it to fuzzy_type_t and shorten some of its values.
This commit is contained in:
ridiculousfish 2020-11-27 16:35:19 -08:00
parent 9144141ded
commit ac1ee6f1fd
8 changed files with 96 additions and 102 deletions

View File

@ -267,18 +267,18 @@ static void unique_completions_retaining_order(completion_list_t *comps) {
void completions_sort_and_prioritize(completion_list_t *comps, completion_request_flags_t flags) {
// Find the best match type.
fuzzy_match_type_t best_type = fuzzy_match_none;
fuzzy_type_t best_type = fuzzy_type_t::none;
for (const auto &comp : *comps) {
best_type = std::min(best_type, comp.match.type);
if (best_type <= fuzzy_match_prefix) {
if (best_type <= fuzzy_type_t::prefix) {
// We can't get better than this (see below)
break;
}
}
// If the best type is an exact match, reduce it to prefix match. Otherwise a tab completion
// will only show one match if it matches a file exactly. (see issue #959).
if (best_type == fuzzy_match_exact) {
best_type = fuzzy_match_prefix;
if (best_type == fuzzy_type_t::exact) {
best_type = fuzzy_type_t::prefix;
}
// Throw out completions whose match types are less suitable than the best.
@ -333,10 +333,10 @@ class completer_t {
bool fuzzy() const { return flags & completion_request_t::fuzzy_match; }
fuzzy_match_type_t max_fuzzy_match_type() const {
fuzzy_type_t max_fuzzy_match_type() const {
// If we are doing fuzzy matching, request all types; if not request only prefix matching.
if (fuzzy()) return fuzzy_match_none;
return fuzzy_match_prefix_case_insensitive;
if (fuzzy()) return fuzzy_type_t::none;
return fuzzy_type_t::prefix_icase;
}
bool try_complete_variable(const wcstring &str);
@ -1184,7 +1184,7 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) {
for (const wcstring &env_name : ctx.vars.get_names(0)) {
string_fuzzy_match_t match =
string_fuzzy_match_string(var, env_name, this->max_fuzzy_match_type());
if (match.type == fuzzy_match_none) {
if (match.type == fuzzy_type_t::none) {
continue; // no match
}

View File

@ -78,7 +78,7 @@ class completion_t {
// Construction.
explicit completion_t(wcstring comp, wcstring desc = wcstring(),
string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact),
string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_type_t::exact),
complete_flags_t flags_val = 0);
completion_t(const completion_t &);
completion_t &operator=(const completion_t &);
@ -198,7 +198,7 @@ bool complete_is_valid_argument(const wcstring &str, const wcstring &opt, const
/// \param flags completion flags
void append_completion(completion_list_t *completions, wcstring comp, wcstring desc = wcstring(),
int flags = 0,
string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact));
string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_type_t::exact));
/// Support for "wrap targets." A wrap target is a command that completes like another command.
bool complete_add_wrapper(const wcstring &command, const wcstring &new_target);

View File

@ -2121,23 +2121,23 @@ static void test_expand() {
static void test_fuzzy_match() {
say(L"Testing fuzzy string matching");
if (string_fuzzy_match_string(L"", L"").type != fuzzy_match_exact)
if (string_fuzzy_match_string(L"", L"").type != fuzzy_type_t::exact)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
if (string_fuzzy_match_string(L"alpha", L"alpha").type != fuzzy_match_exact)
if (string_fuzzy_match_string(L"alpha", L"alpha").type != fuzzy_type_t::exact)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
if (string_fuzzy_match_string(L"alp", L"alpha").type != fuzzy_match_prefix)
if (string_fuzzy_match_string(L"alp", L"alpha").type != fuzzy_type_t::prefix)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
if (string_fuzzy_match_string(L"ALPHA!", L"alPhA!").type != fuzzy_match_case_insensitive)
if (string_fuzzy_match_string(L"ALPHA!", L"alPhA!").type != fuzzy_type_t::exact_icase)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
if (string_fuzzy_match_string(L"alPh", L"ALPHA!").type != fuzzy_match_prefix_case_insensitive)
if (string_fuzzy_match_string(L"alPh", L"ALPHA!").type != fuzzy_type_t::prefix_icase)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
if (string_fuzzy_match_string(L"LPH", L"ALPHA!").type != fuzzy_match_substring)
if (string_fuzzy_match_string(L"LPH", L"ALPHA!").type != fuzzy_type_t::substr)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
if (string_fuzzy_match_string(L"lPh", L"ALPHA!").type != fuzzy_match_substring_case_insensitive)
if (string_fuzzy_match_string(L"lPh", L"ALPHA!").type != fuzzy_type_t::substr_icase)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
if (string_fuzzy_match_string(L"AA", L"ALPHA!").type != fuzzy_match_subsequence_insertions_only)
if (string_fuzzy_match_string(L"AA", L"ALPHA!").type != fuzzy_type_t::subseq)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
if (string_fuzzy_match_string(L"BB", L"ALPHA!").type != fuzzy_match_none)
if (string_fuzzy_match_string(L"BB", L"ALPHA!").type != fuzzy_type_t::none)
err(L"test_fuzzy_match failed on line %ld", __LINE__);
}

View File

@ -351,16 +351,16 @@ bool pager_t::completion_info_passes_filter(const comp_t &info) const {
const wcstring &needle = this->search_field_line.text();
// We do full fuzzy matching just like the completion code itself.
const fuzzy_match_type_t limit = fuzzy_match_none;
const fuzzy_type_t limit = fuzzy_type_t::none;
// Match against the description.
if (string_fuzzy_match_string(needle, info.desc, limit).type != fuzzy_match_none) {
if (string_fuzzy_match_string(needle, info.desc, limit).type != fuzzy_type_t::none) {
return true;
}
// Match against the completion strings.
for (const auto &i : info.comp) {
if (string_fuzzy_match_string(needle, prefix + i, limit).type != fuzzy_match_none) {
if (string_fuzzy_match_string(needle, prefix + i, limit).type != fuzzy_type_t::none) {
return true;
}
}

View File

@ -1806,15 +1806,15 @@ static bool reader_can_replace(const wcstring &in, int flags) {
}
/// Determine the best match type for a set of completions.
static fuzzy_match_type_t get_best_match_type(const completion_list_t &comp) {
fuzzy_match_type_t best_type = fuzzy_match_none;
static fuzzy_type_t get_best_match_type(const completion_list_t &comp) {
fuzzy_type_t best_type = fuzzy_type_t::none;
for (const auto &i : comp) {
best_type = std::min(best_type, i.match.type);
}
// If the best type is an exact match, reduce it to prefix match. Otherwise a tab completion
// will only show one match if it matches a file exactly. (see issue #959).
if (best_type == fuzzy_match_exact) {
best_type = fuzzy_match_prefix;
if (best_type == fuzzy_type_t::exact) {
best_type = fuzzy_type_t::prefix;
}
return best_type;
}
@ -1865,7 +1865,7 @@ bool reader_data_t::handle_completions(const completion_list_t &comp, size_t tok
return success;
}
fuzzy_match_type_t best_match_type = get_best_match_type(comp);
fuzzy_type_t best_match_type = get_best_match_type(comp);
// Determine whether we are going to replace the token or not. If any commands of the best
// type do not require replacement, then ignore all those that want to use replacement.

View File

@ -154,47 +154,47 @@ static bool subsequence_in_string(const wcstring &needle, const wcstring &haysta
return ni == needle.end();
}
string_fuzzy_match_t::string_fuzzy_match_t(enum fuzzy_match_type_t t, size_t distance_first,
string_fuzzy_match_t::string_fuzzy_match_t(enum fuzzy_type_t t, size_t distance_first,
size_t distance_second)
: type(t), match_distance_first(distance_first), match_distance_second(distance_second) {}
string_fuzzy_match_t string_fuzzy_match_string(const wcstring &string,
const wcstring &match_against,
fuzzy_match_type_t limit_type) {
fuzzy_type_t limit_type) {
// Distances are generally the amount of text not matched.
string_fuzzy_match_t result(fuzzy_match_none, 0, 0);
string_fuzzy_match_t result(fuzzy_type_t::none, 0, 0);
size_t location;
if (limit_type >= fuzzy_match_exact && string == match_against) {
result.type = fuzzy_match_exact;
} else if (limit_type >= fuzzy_match_prefix && string_prefixes_string(string, match_against)) {
result.type = fuzzy_match_prefix;
if (limit_type >= fuzzy_type_t::exact && string == match_against) {
result.type = fuzzy_type_t::exact;
} else if (limit_type >= fuzzy_type_t::prefix &&
string_prefixes_string(string, match_against)) {
result.type = fuzzy_type_t::prefix;
assert(match_against.size() >= string.size());
result.match_distance_first = match_against.size() - string.size();
} else if (limit_type >= fuzzy_match_case_insensitive &&
} else if (limit_type >= fuzzy_type_t::exact_icase &&
wcscasecmp(string.c_str(), match_against.c_str()) == 0) {
result.type = fuzzy_match_case_insensitive;
} else if (limit_type >= fuzzy_match_prefix_case_insensitive &&
result.type = fuzzy_type_t::exact_icase;
} else if (limit_type >= fuzzy_type_t::prefix_icase &&
string_prefixes_string_case_insensitive(string, match_against)) {
result.type = fuzzy_match_prefix_case_insensitive;
result.type = fuzzy_type_t::prefix_icase;
assert(match_against.size() >= string.size());
result.match_distance_first = match_against.size() - string.size();
} else if (limit_type >= fuzzy_match_substring &&
} else if (limit_type >= fuzzy_type_t::substr &&
(location = match_against.find(string)) != wcstring::npos) {
// String is contained within match against.
result.type = fuzzy_match_substring;
result.type = fuzzy_type_t::substr;
assert(match_against.size() >= string.size());
result.match_distance_first = match_against.size() - string.size();
result.match_distance_second = location; // prefer earlier matches
} else if (limit_type >= fuzzy_match_substring_case_insensitive &&
} else if (limit_type >= fuzzy_type_t::substr_icase &&
(location = ifind(match_against, string, true)) != wcstring::npos) {
// A case-insensitive version of the string is in the match against.
result.type = fuzzy_match_substring_case_insensitive;
result.type = fuzzy_type_t::substr_icase;
assert(match_against.size() >= string.size());
result.match_distance_first = match_against.size() - string.size();
result.match_distance_second = location; // prefer earlier matches
} else if (limit_type >= fuzzy_match_subsequence_insertions_only &&
subsequence_in_string(string, match_against)) {
result.type = fuzzy_match_subsequence_insertions_only;
} else if (limit_type >= fuzzy_type_t::subseq && subsequence_in_string(string, match_against)) {
result.type = fuzzy_type_t::subseq;
assert(match_against.size() >= string.size());
result.match_distance_first = match_against.size() - string.size();
// It would be nice to prefer matches with greater matching runs here.

View File

@ -36,97 +36,91 @@ size_t ifind(const wcstring &haystack, const wcstring &needle, bool fuzzy = fals
size_t ifind(const std::string &haystack, const std::string &needle, bool fuzzy = false);
// Ways that a string may fuzzily match another.
enum fuzzy_match_type_t {
enum class fuzzy_type_t {
// We match the string exactly: FOOBAR matches FOOBAR.
fuzzy_match_exact = 0,
exact,
// We match a prefix of the string: FO matches FOOBAR.
fuzzy_match_prefix,
prefix,
// We match the string exactly, but in a case insensitive way: foobar matches FOOBAR.
fuzzy_match_case_insensitive,
exact_icase,
// We match a prefix of the string, in a case insensitive way: foo matches FOOBAR.
fuzzy_match_prefix_case_insensitive,
prefix_icase,
// We match a substring of the string: OOBA matches FOOBAR.
fuzzy_match_substring,
substr,
// We match a substring of the string: ooBA matches FOOBAR.
fuzzy_match_substring_case_insensitive,
substr_icase,
// A subsequence match with insertions only: FBR matches FOOBAR.
fuzzy_match_subsequence_insertions_only,
subseq,
// We don't match the string.
fuzzy_match_none
none,
};
/// Indicates where a match type requires replacing the entire token.
static inline bool match_type_requires_full_replacement(fuzzy_match_type_t t) {
static inline bool match_type_requires_full_replacement(fuzzy_type_t t) {
switch (t) {
case fuzzy_match_exact:
case fuzzy_match_prefix: {
case fuzzy_type_t::exact:
case fuzzy_type_t::prefix:
return false;
}
case fuzzy_match_case_insensitive:
case fuzzy_match_prefix_case_insensitive:
case fuzzy_match_substring:
case fuzzy_match_substring_case_insensitive:
case fuzzy_match_subsequence_insertions_only:
case fuzzy_match_none: {
case fuzzy_type_t::exact_icase:
case fuzzy_type_t::prefix_icase:
case fuzzy_type_t::substr:
case fuzzy_type_t::substr_icase:
case fuzzy_type_t::subseq:
case fuzzy_type_t::none:
return true;
}
default: {
DIE("Unreachable");
return false;
}
}
}
/// Indicates where a match shares a prefix with the string it matches.
static inline bool match_type_shares_prefix(fuzzy_match_type_t t) {
static inline bool match_type_shares_prefix(fuzzy_type_t t) {
switch (t) {
case fuzzy_match_exact:
case fuzzy_match_prefix:
case fuzzy_match_case_insensitive:
case fuzzy_match_prefix_case_insensitive: {
case fuzzy_type_t::exact:
case fuzzy_type_t::prefix:
case fuzzy_type_t::exact_icase:
case fuzzy_type_t::prefix_icase:
return true;
}
case fuzzy_match_substring:
case fuzzy_match_substring_case_insensitive:
case fuzzy_match_subsequence_insertions_only:
case fuzzy_match_none: {
case fuzzy_type_t::substr:
case fuzzy_type_t::substr_icase:
case fuzzy_type_t::subseq:
case fuzzy_type_t::none:
return false;
}
default: {
DIE("Unreachable");
return false;
}
}
}
/// Test if string is a fuzzy match to another.
struct string_fuzzy_match_t {
enum fuzzy_match_type_t type;
enum fuzzy_type_t type;
// Strength of the match. The value depends on the type. Lower is stronger.
size_t match_distance_first;
size_t match_distance_second;
// Constructor.
explicit string_fuzzy_match_t(enum fuzzy_match_type_t t, size_t distance_first = 0,
explicit string_fuzzy_match_t(enum fuzzy_type_t t, size_t distance_first = 0,
size_t distance_second = 0);
// Return -1, 0, 1 if this match is (respectively) better than, equal to, or worse than rhs.
int compare(const string_fuzzy_match_t &rhs) const;
};
/// Compute a fuzzy match for a string. If maximum_match is not fuzzy_match_none, limit the type to
/// matches at or below that type.
/// Compute a fuzzy match for a string. If maximum_match is not fuzzy_match_t::none, limit the type
/// to matches at or below that type.
string_fuzzy_match_t string_fuzzy_match_string(const wcstring &string,
const wcstring &match_against,
fuzzy_match_type_t limit_type = fuzzy_match_none);
fuzzy_type_t limit_type = fuzzy_type_t::none);
/// Split a string by a separator character.
wcstring_list_t split_string(const wcstring &val, wchar_t sep);

View File

@ -98,14 +98,14 @@ bool wildcard_has(const wcstring &str, bool internal) {
/// \param wc The wildcard.
/// \param leading_dots_fail_to_match Whether files beginning with dots should not be matched
/// against wildcards.
static enum fuzzy_match_type_t wildcard_match_internal(const wcstring &str, const wcstring &wc,
static enum fuzzy_type_t wildcard_match_internal(const wcstring &str, const wcstring &wc,
bool leading_dots_fail_to_match) {
// Hackish fix for issue #270. Prevent wildcards from matching . or .., but we must still allow
// literal matches.
if (leading_dots_fail_to_match && str[0] == L'.' &&
(str[1] == L'\0' || (str[1] == L'.' && str[2] == L'\0'))) {
// The string is '.' or '..' so the only possible match is an exact match.
return str == wc ? fuzzy_match_exact : fuzzy_match_none;
return str == wc ? fuzzy_type_t::exact : fuzzy_type_t::none;
}
// Near Linear implementation as proposed here https://research.swtch.com/glob.
@ -121,13 +121,13 @@ static enum fuzzy_match_type_t wildcard_match_internal(const wcstring &str, cons
if (*wc_x == ANY_STRING || *wc_x == ANY_STRING_RECURSIVE) {
// Ignore hidden file
if (leading_dots_fail_to_match && is_first && str[0] == L'.') {
return fuzzy_match_none;
return fuzzy_type_t::none;
}
// Common case of * at the end. In that case we can early out since we know it will
// match.
if (wc_x[1] == L'\0') {
return fuzzy_match_exact;
return fuzzy_type_t::exact;
}
// Try to match at str_x.
// If that doesn't work out, restart at str_x+1 next.
@ -138,7 +138,7 @@ static enum fuzzy_match_type_t wildcard_match_internal(const wcstring &str, cons
continue;
} else if (*wc_x == ANY_CHAR && *str_x != 0) {
if (is_first && *str_x == L'.') {
return fuzzy_match_none;
return fuzzy_type_t::none;
}
wc_x++;
str_x++;
@ -155,10 +155,10 @@ static enum fuzzy_match_type_t wildcard_match_internal(const wcstring &str, cons
str_x = restart_str_x;
continue;
}
return fuzzy_match_none;
return fuzzy_type_t::none;
}
// Matched all of pattern to all of name. Success.
return fuzzy_match_exact;
return fuzzy_type_t::exact;
}
// This does something horrible refactored from an even more horrible function.
@ -192,7 +192,7 @@ static bool has_prefix_match(const completion_list_t *comps, size_t first) {
if (comps != nullptr) {
const size_t after_count = comps->size();
for (size_t j = first; j < after_count; j++) {
if (comps->at(j).match.type <= fuzzy_match_prefix) {
if (comps->at(j).match.type <= fuzzy_type_t::prefix) {
return true;
}
}
@ -245,7 +245,7 @@ static bool wildcard_complete_internal(const wchar_t *const str, size_t str_len,
// If we're allowing fuzzy match, any match is OK. Otherwise we require a prefix match.
bool match_acceptable;
if (params.expand_flags & expand_flag::fuzzy_match) {
match_acceptable = match.type != fuzzy_match_none;
match_acceptable = match.type != fuzzy_type_t::none;
} else {
match_acceptable = match_type_shares_prefix(match.type);
}
@ -356,8 +356,8 @@ bool wildcard_complete(const wcstring &str, const wchar_t *wc,
}
bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match) {
enum fuzzy_match_type_t match = wildcard_match_internal(str, wc, leading_dots_fail_to_match);
return match != fuzzy_match_none;
enum fuzzy_type_t match = wildcard_match_internal(str, wc, leading_dots_fail_to_match);
return match != fuzzy_type_t::none;
}
static int fast_waccess(const struct stat &stat_buf, uint8_t mode) {
@ -817,7 +817,7 @@ void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz(const wc
// Skip cases that don't match or match exactly. The match-exactly case was handled directly
// in expand().
const string_fuzzy_match_t match = string_fuzzy_match_string(wc_segment, name_str);
if (match.type == fuzzy_match_none || match.type == fuzzy_match_exact) {
if (match.type == fuzzy_type_t::none || match.type == fuzzy_type_t::exact) {
continue;
}