diff --git a/src/common.cpp b/src/common.cpp index f5fcf65c3..ac2492805 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1693,6 +1693,12 @@ bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &valu value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; } +bool string_suffixes_string_case_insensitive(const wcstring &proposed_suffix, const wcstring &value) { + size_t suffix_size = proposed_suffix.size(); + return suffix_size <= value.size() && + wcsncasecmp(value.c_str() + (value.size() - suffix_size), proposed_suffix.c_str(), suffix_size) == 0; +} + /// Returns true if seq, represented as a subsequence, is contained within string. static bool subsequence_in_string(const wcstring &seq, const wcstring &str) { // Impossible if seq is larger than string. diff --git a/src/common.h b/src/common.h index 765412ca5..1428afe3f 100644 --- a/src/common.h +++ b/src/common.h @@ -321,6 +321,7 @@ bool string_prefixes_string(const wchar_t *proposed_prefix, const wchar_t *value /// Test if a string is a suffix of another. bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value); bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &value); +bool string_suffixes_string_case_insensitive(const wcstring &proposed_suffix, const wcstring &value); /// Test if a string prefixes another without regard to case. Returns true if a is a prefix of b. bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, diff --git a/src/complete.cpp b/src/complete.cpp index acf9ea477..cb0ef776b 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -642,6 +642,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool std::vector possible_comp; if (use_command) { + // Append all possible executables expand_error_t result = expand_string(str_cmd, &this->completions, EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY | this->expand_flags(), @@ -655,6 +656,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool // We don't really care if this succeeds or fails. If it succeeds this->completions will be // updated with choices for the user. expand_error_t ignore = + // Append all matching directories expand_string(str_cmd, &this->completions, EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), NULL); UNUSED(ignore); @@ -664,6 +666,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool if (use_function) { wcstring_list_t names = function_get_names(str_cmd.at(0) == L'_'); for (size_t i = 0; i < names.size(); i++) { + // Append all known matching functions append_completion(&possible_comp, names.at(i)); } @@ -673,6 +676,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool possible_comp.clear(); if (use_builtin) { + // Append all matching builtins builtin_get_names(&possible_comp); this->complete_strings(str_cmd, 0, &builtin_get_desc, possible_comp, 0); } @@ -990,6 +994,7 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop if (short_ok(str, o, options)) { // It's a match. const wcstring desc = o->localized_desc(); + // Append a short-style option append_completion(&this->completions, o->option, desc, 0); } @@ -1031,9 +1036,11 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop // switch-arg', since it is more commonly supported by homebrew getopt-like // functions. wcstring completion = format_string(L"%ls=", whole_opt.c_str() + offset); + // Append a long-style option with a mandatory trailing equal sign append_completion(&this->completions, completion, C_(o->desc), flags); } + // Append a long-style option append_completion(&this->completions, whole_opt.c_str() + offset, C_(o->desc), flags); } } @@ -1138,6 +1145,7 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) { } } + // Append matching environment variables append_completion(&this->completions, comp, desc, flags, match); res = true; @@ -1240,11 +1248,13 @@ bool completer_t::try_complete_user(const wcstring &str) { const wchar_t *pw_name = pw_name_str.c_str(); if (wcsncmp(user_name, pw_name, name_len) == 0) { wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); + // Append a user name append_completion(&this->completions, &pw_name[name_len], desc, COMPLETE_NO_SPACE); result = true; } else if (wcsncasecmp(user_name, pw_name, name_len) == 0) { wcstring name = format_string(L"~%ls", pw_name); wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); + // Append a user name append_completion(&this->completions, name, desc, COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE); result = true; diff --git a/src/wildcard.cpp b/src/wildcard.cpp index f67836800..27d115b93 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -100,7 +100,7 @@ static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const // The string is '.' or '..'. Return true if the wildcard exactly matches. return wcscmp(str, wc) ? fuzzy_match_none : fuzzy_match_exact; } - + // Near Linear implementation as proposed here https://research.swtch.com/glob. const wchar_t *wc_x = wc; const wchar_t *str_x = str; @@ -411,6 +411,10 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wc return false; } + if (is_windows_subsystem_for_linux() && string_suffixes_string_case_insensitive(L".dll", filename)) { + return false; + } + // Compute the description. wcstring desc; if (!(expand_flags & EXPAND_NO_DESCRIPTIONS)) {