diff --git a/src/complete.cpp b/src/complete.cpp index 469a0fa21..ab69da810 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -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 } diff --git a/src/complete.h b/src/complete.h index 164702a9d..405730704 100644 --- a/src/complete.h +++ b/src/complete.h @@ -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); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 61e275ca8..ece0def4c 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -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__); } diff --git a/src/pager.cpp b/src/pager.cpp index 7a5407aed..fe4505274 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -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; } } diff --git a/src/reader.cpp b/src/reader.cpp index d86c55e3e..c26c3d820 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -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. diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp index 1dde6d359..7e77740af 100644 --- a/src/wcstringutil.cpp +++ b/src/wcstringutil.cpp @@ -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. diff --git a/src/wcstringutil.h b/src/wcstringutil.h index e572f242c..2bc93b606 100644 --- a/src/wcstringutil.h +++ b/src/wcstringutil.h @@ -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; - } } + 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; - } } + 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); diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 70e650ae5..91ce154c8 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -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, - bool leading_dots_fail_to_match) { +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; }