Switch expand_flags_t to enum_set

This commit is contained in:
ridiculousfish 2019-04-25 11:22:17 -07:00
parent dcaac58f45
commit d8ab6290e8
9 changed files with 193 additions and 127 deletions

View File

@ -359,11 +359,11 @@ class completer_t {
expand_flags_t expand_flags() const {
// Never do command substitution in autosuggestions. Sadly, we also can't yet do job
// expansion because it's not thread safe.
expand_flags_t result = 0;
if (this->type() == COMPLETE_AUTOSUGGEST) result |= EXPAND_SKIP_CMDSUBST;
expand_flags_t result{};
if (this->type() == COMPLETE_AUTOSUGGEST) result |= expand_flag::EXPAND_SKIP_CMDSUBST;
// Allow fuzzy matching.
if (this->fuzzy()) result |= EXPAND_FUZZY_MATCH;
if (this->fuzzy()) result |= expand_flag::EXPAND_FUZZY_MATCH;
return result;
}
@ -545,7 +545,10 @@ void completer_t::complete_strings(const wcstring &wc_escaped, const description
const std::vector<completion_t> &possible_comp,
complete_flags_t flags) {
wcstring tmp = wc_escaped;
if (!expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), vars))
if (!expand_one(tmp,
this->expand_flags() | expand_flag::EXPAND_SKIP_CMDSUBST |
expand_flag::EXPAND_SKIP_WILDCARDS,
vars))
return;
const wcstring wc = parse_util_unescape_wildcards(tmp);
@ -665,10 +668,11 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
if (use_command) {
// Append all possible executables
expand_result_t result = expand_string(str_cmd, &this->completions,
EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS |
EXECUTABLES_ONLY | this->expand_flags(),
vars, NULL);
expand_result_t result =
expand_string(str_cmd, &this->completions,
this->expand_flags() | expand_flag::EXPAND_SPECIAL_FOR_COMMAND |
expand_flag::EXPAND_FOR_COMPLETIONS | expand_flag::EXECUTABLES_ONLY,
vars, NULL);
if (result != expand_result_t::error && this->wants_descriptions()) {
this->complete_cmd_desc(str_cmd);
}
@ -680,8 +684,9 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
expand_result_t ignore =
// Append all matching directories
expand_string(str_cmd, &this->completions,
EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), vars,
NULL);
this->expand_flags() | expand_flag::EXPAND_FOR_COMPLETIONS |
expand_flag::DIRECTORIES_ONLY,
vars, NULL);
UNUSED(ignore);
}
@ -747,9 +752,10 @@ void completer_t::complete_from_args(const wcstring &str, const wcstring &args,
proc_push_interactive(0);
}
expand_flags_t eflags = 0;
expand_flags_t eflags{};
if (is_autosuggest) {
eflags |= EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_CMDSUBST;
eflags |= expand_flag::EXPAND_NO_DESCRIPTIONS;
eflags |= expand_flag::EXPAND_SKIP_CMDSUBST;
}
std::vector<completion_t> possible_comp;
@ -1061,19 +1067,22 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt,
/// Perform generic (not command-specific) expansions on the specified string.
void completer_t::complete_param_expand(const wcstring &str, bool do_file,
bool handle_as_special_cd) {
expand_flags_t flags = EXPAND_SKIP_CMDSUBST | EXPAND_FOR_COMPLETIONS | this->expand_flags();
expand_flags_t flags = this->expand_flags() | expand_flag::EXPAND_SKIP_CMDSUBST |
expand_flag::EXPAND_FOR_COMPLETIONS;
if (!do_file) flags |= EXPAND_SKIP_WILDCARDS;
if (!do_file) flags |= expand_flag::EXPAND_SKIP_WILDCARDS;
if (handle_as_special_cd && do_file) {
if (this->type() == COMPLETE_AUTOSUGGEST) {
flags |= EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST;
flags |= expand_flag::EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST;
}
flags |= DIRECTORIES_ONLY | EXPAND_SPECIAL_FOR_CD | EXPAND_NO_DESCRIPTIONS;
flags |= expand_flags_t{expand_flag::DIRECTORIES_ONLY, expand_flag::EXPAND_SPECIAL_FOR_CD,
expand_flag::EXPAND_NO_DESCRIPTIONS};
}
// Squelch file descriptions per issue #254.
if (this->type() == COMPLETE_AUTOSUGGEST || do_file) flags |= EXPAND_NO_DESCRIPTIONS;
if (this->type() == COMPLETE_AUTOSUGGEST || do_file)
flags |= expand_flag::EXPAND_NO_DESCRIPTIONS;
// We have the following cases:
//
@ -1110,7 +1119,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file,
if (complete_from_start) {
// Don't do fuzzy matching for files if the string begins with a dash (issue #568). We could
// consider relaxing this if there was a preceding double-dash argument.
if (string_prefixes_string(L"-", str)) flags &= ~EXPAND_FUZZY_MATCH;
if (string_prefixes_string(L"-", str)) flags.clear(expand_flag::EXPAND_FUZZY_MATCH);
if (expand_string(str, &this->completions, flags, vars, NULL) == expand_result_t::error) {
debug(3, L"Error while expanding string '%ls'", str.c_str());

View File

@ -25,6 +25,7 @@ class enum_set_t : private std::bitset<enum_count<T>()> {
static size_t index_of(T t) { return static_cast<size_t>(t); }
explicit enum_set_t(unsigned long raw) : super(raw) {}
explicit enum_set_t(super sup) : super(sup) {}
public:
enum_set_t() = default;
@ -52,6 +53,32 @@ class enum_set_t : private std::bitset<enum_count<T>()> {
bool operator==(const enum_set_t &rhs) const { return super::operator==(rhs); }
bool operator!=(const enum_set_t &rhs) const { return super::operator!=(rhs); }
/// OR in a single flag, returning a new set.
enum_set_t operator|(T rhs) const {
enum_set_t result = *this;
result.set(rhs);
return result;
}
/// Compute the union of two sets.
enum_set_t operator|(enum_set_t rhs) const { return from_raw(to_raw() | rhs.to_raw()); }
/// OR in a single flag, modifying the set in place.
enum_set_t operator|=(T rhs) {
*this = *this | rhs;
return *this;
}
/// Set this to the union of two sets.
enum_set_t operator|=(enum_set_t rhs) {
*this = *this | rhs;
return *this;
}
/// Test a value of a single flag. Note this does not return an enum_set_t; there is no such
/// boolean conversion. This simply makes flags work more naturally as bit masks.
bool operator&(T rhs) const { return get(rhs); }
};
/// An array of Elem indexed by an enum class.

View File

@ -511,7 +511,7 @@ static expand_result_t expand_braces(const wcstring &instr, expand_flags_t flags
}
if (brace_count > 0) {
if (!(flags & EXPAND_FOR_COMPLETIONS)) {
if (!(flags & expand_flag::EXPAND_FOR_COMPLETIONS)) {
syntax_error = true;
} else {
// The user hasn't typed an end brace yet; make one up and append it, then expand
@ -527,7 +527,7 @@ static expand_result_t expand_braces(const wcstring &instr, expand_flags_t flags
}
// Note: this code looks very fishy, apparently it has never worked.
return expand_braces(mod, 1, out, errors);
return expand_braces(mod, expand_flag::EXPAND_SKIP_CMDSUBST, out, errors);
}
}
@ -898,7 +898,7 @@ class expander_t {
};
expand_result_t expander_t::stage_cmdsubst(wcstring input, std::vector<completion_t> *out) {
if (EXPAND_SKIP_CMDSUBST & flags) {
if (flags & expand_flag::EXPAND_SKIP_CMDSUBST) {
size_t cur = 0, start = 0, end;
switch (parse_util_locate_cmdsubst_range(input, &cur, nullptr, &start, &end, true)) {
case 0:
@ -924,7 +924,7 @@ expand_result_t expander_t::stage_variables(wcstring input, std::vector<completi
wcstring next;
unescape_string(input, &next, UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE);
if (EXPAND_SKIP_VARIABLES & flags) {
if (flags & expand_flag::EXPAND_SKIP_VARIABLES) {
for (size_t i = 0; i < next.size(); i++) {
if (next.at(i) == VARIABLE_EXPAND) {
next[i] = L'$';
@ -946,7 +946,7 @@ expand_result_t expander_t::stage_braces(wcstring input, std::vector<completion_
}
expand_result_t expander_t::stage_home_and_self(wcstring input, std::vector<completion_t> *out) {
if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) {
if (!(flags & expand_flag::EXPAND_SKIP_HOME_DIRECTORIES)) {
expand_home_directory(input, vars);
}
expand_percent_self(input);
@ -958,13 +958,14 @@ expand_result_t expander_t::stage_wildcards(wcstring path_to_expand,
std::vector<completion_t> *out) {
expand_result_t result = expand_result_t::ok;
remove_internal_separator(&path_to_expand, flags & EXPAND_SKIP_WILDCARDS);
remove_internal_separator(&path_to_expand, flags & expand_flag::EXPAND_SKIP_WILDCARDS);
const bool has_wildcard = wildcard_has(path_to_expand, true /* internal, i.e. ANY_STRING */);
const bool for_completions = flags & expand_flag::EXPAND_FOR_COMPLETIONS;
const bool skip_wildcards = flags & expand_flag::EXPAND_SKIP_WILDCARDS;
if (has_wildcard && (flags & EXECUTABLES_ONLY)) {
if (has_wildcard && (flags & expand_flag::EXECUTABLES_ONLY)) {
; // don't do wildcard expansion for executables, see issue #785
} else if (((flags & EXPAND_FOR_COMPLETIONS) && (!(flags & EXPAND_SKIP_WILDCARDS))) ||
has_wildcard) {
} else if ((for_completions && !skip_wildcards) || has_wildcard) {
// We either have a wildcard, or we don't have a wildcard but we're doing completion
// expansion (so we want to get the completion of a file path). Note that if
// EXPAND_SKIP_WILDCARDS is set, we stomped wildcards in remove_internal_separator above, so
@ -974,8 +975,8 @@ expand_result_t expander_t::stage_wildcards(wcstring path_to_expand,
// which may be CDPATH if the special flag is set.
const wcstring working_dir = vars.get_pwd_slash();
wcstring_list_t effective_working_dirs;
bool for_cd = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_CD);
bool for_command = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_COMMAND);
bool for_cd = flags & expand_flag::EXPAND_SPECIAL_FOR_CD;
bool for_command = flags & expand_flag::EXPAND_SPECIAL_FOR_COMMAND;
if (!for_cd && !for_command) {
// Common case.
effective_working_dirs.push_back(working_dir);
@ -1038,7 +1039,7 @@ expand_result_t expander_t::stage_wildcards(wcstring path_to_expand,
// Can't fully justify this check. I think it's that SKIP_WILDCARDS is used when completing
// to mean don't do file expansions, so if we're not doing file expansions, just drop this
// completion on the floor.
if (!(flags & EXPAND_FOR_COMPLETIONS)) {
if (!(flags & expand_flag::EXPAND_FOR_COMPLETIONS)) {
append_completion(out, std::move(path_to_expand));
}
}
@ -1050,7 +1051,7 @@ expand_result_t expander_t::expand_string(wcstring input,
expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors) {
// Early out. If we're not completing, and there's no magic in the input, we're done.
if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(input)) {
if (!(flags & expand_flag::EXPAND_FOR_COMPLETIONS) && expand_is_clean(input)) {
append_completion(out_completions, std::move(input));
return expand_result_t::ok;
}
@ -1092,7 +1093,7 @@ expand_result_t expander_t::expand_string(wcstring input,
if (total_result != expand_result_t::error) {
// Hack to un-expand tildes (see #647).
if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) {
if (!(flags & expand_flag::EXPAND_SKIP_HOME_DIRECTORIES)) {
unexpand_tildes(input, vars, &completions);
}
out_completions->insert(out_completions->end(),
@ -1113,12 +1114,12 @@ bool expand_one(wcstring &string, expand_flags_t flags, const environment_t &var
parse_error_list_t *errors) {
std::vector<completion_t> completions;
if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(string)) {
if (!flags.get(expand_flag::EXPAND_FOR_COMPLETIONS) && expand_is_clean(string)) {
return true;
}
if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, vars, errors) !=
expand_result_t::error &&
if (expand_string(string, &completions, flags | expand_flag::EXPAND_NO_DESCRIPTIONS, vars,
errors) != expand_result_t::error &&
completions.size() == 1) {
string = std::move(completions.at(0).completion);
return true;
@ -1137,9 +1138,11 @@ expand_result_t expand_to_command_and_args(const wcstring &instr, const environm
}
std::vector<completion_t> completions;
expand_result_t expand_err = expand_string(
instr, &completions, EXPAND_SKIP_CMDSUBST | EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_JOBS, vars,
errors);
expand_result_t expand_err =
expand_string(instr, &completions,
{expand_flag::EXPAND_SKIP_CMDSUBST, expand_flag::EXPAND_NO_DESCRIPTIONS,
expand_flag::EXPAND_SKIP_JOBS},
vars, errors);
if (expand_err == expand_result_t::ok || expand_err == expand_result_t::wildcard_match) {
// The first completion is the command, any remaning are arguments.
bool first = true;

View File

@ -14,6 +14,7 @@
#include <vector>
#include "common.h"
#include "enum_set.h"
#include "maybe.h"
#include "parse_constants.h"
@ -21,43 +22,51 @@ class environment_t;
class env_var_t;
class environment_t;
enum {
enum class expand_flag {
/// Flag specifying that cmdsubst expansion should be skipped.
EXPAND_SKIP_CMDSUBST = 1 << 0,
EXPAND_SKIP_CMDSUBST,
/// Flag specifying that variable expansion should be skipped.
EXPAND_SKIP_VARIABLES = 1 << 1,
EXPAND_SKIP_VARIABLES,
/// Flag specifying that wildcard expansion should be skipped.
EXPAND_SKIP_WILDCARDS = 1 << 2,
EXPAND_SKIP_WILDCARDS,
/// The expansion is being done for tab or auto completions. Returned completions may have the
/// wildcard as a prefix instead of a match.
EXPAND_FOR_COMPLETIONS = 1 << 3,
EXPAND_FOR_COMPLETIONS,
/// Only match files that are executable by the current user.
EXECUTABLES_ONLY = 1 << 4,
EXECUTABLES_ONLY,
/// Only match directories.
DIRECTORIES_ONLY = 1 << 5,
DIRECTORIES_ONLY,
/// Don't generate descriptions.
EXPAND_NO_DESCRIPTIONS = 1 << 6,
EXPAND_NO_DESCRIPTIONS,
/// Don't expand jobs (but you can still expand processes). This is because
/// job expansion is not thread safe.
EXPAND_SKIP_JOBS = 1 << 7,
EXPAND_SKIP_JOBS,
/// Don't expand home directories.
EXPAND_SKIP_HOME_DIRECTORIES = 1 << 8,
EXPAND_SKIP_HOME_DIRECTORIES,
/// Allow fuzzy matching.
EXPAND_FUZZY_MATCH = 1 << 9,
EXPAND_FUZZY_MATCH,
/// Disallow directory abbreviations like /u/l/b for /usr/local/bin. Only applicable if
/// EXPAND_FUZZY_MATCH is set.
EXPAND_NO_FUZZY_DIRECTORIES = 1 << 10,
EXPAND_NO_FUZZY_DIRECTORIES,
/// Do expansions specifically to support cd. This means using CDPATH as a list of potential
/// working directories, and to use logical instead of physical paths.
EXPAND_SPECIAL_FOR_CD = 1 << 11,
EXPAND_SPECIAL_FOR_CD,
/// Do expansions specifically for cd autosuggestion. This is to differentiate between cd
/// completions and cd autosuggestions.
EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST = 1 << 12,
EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST,
/// Do expansions specifically to support external command completions. This means using PATH as
/// a list of potential working directories.
EXPAND_SPECIAL_FOR_COMMAND = 1 << 13
EXPAND_SPECIAL_FOR_COMMAND,
COUNT,
};
typedef int expand_flags_t;
template <>
struct enum_info_t<expand_flag> {
static constexpr auto count = expand_flag::COUNT;
};
using expand_flags_t = enum_set_t<expand_flag>;
class completion_t;

View File

@ -305,6 +305,10 @@ static void test_enum_set() {
do_test(es != enum_set_t<test_enum>::from_raw(1));
es.set(test_enum::beta);
do_test(es.get(test_enum::beta));
do_test(!es.get(test_enum::alpha));
do_test(es & test_enum::beta);
do_test(!(es & test_enum::alpha));
do_test(es.to_raw() == 2);
do_test(es == enum_set_t<test_enum>::from_raw(2));
do_test(es == enum_set_t<test_enum>{test_enum::beta});
@ -312,6 +316,10 @@ static void test_enum_set() {
do_test(es.any());
do_test(!es.none());
do_test((enum_set_t<test_enum>{test_enum::beta} | test_enum::alpha).to_raw() == 3);
do_test((enum_set_t<test_enum>{test_enum::beta} | enum_set_t<test_enum>{test_enum::alpha})
.to_raw() == 3);
unsigned idx = 0;
for (auto v : enum_iter_t<test_enum>{}) {
do_test(static_cast<unsigned>(v) == idx);
@ -970,7 +978,8 @@ static void test_parser() {
say(L"Testing eval_args");
completion_list_t comps;
parser_t::expand_argument_list(L"alpha 'beta gamma' delta", 0, null_environment_t{}, &comps);
parser_t::expand_argument_list(L"alpha 'beta gamma' delta", expand_flags_t{},
null_environment_t{}, &comps);
do_test(comps.size() == 3);
do_test(comps.at(0).completion == L"alpha");
do_test(comps.at(1).completion == L"beta gamma");
@ -1656,13 +1665,15 @@ static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) {
/// Test globbing and other parameter expansion.
static void test_expand() {
say(L"Testing parameter expansion");
const expand_flags_t noflags{};
expand_test(L"foo", 0, L"foo", 0, L"Strings do not expand to themselves");
expand_test(L"a{b,c,d}e", 0, L"abe", L"ace", L"ade", 0, L"Bracket expansion is broken");
expand_test(L"a*", EXPAND_SKIP_WILDCARDS, L"a*", 0, L"Cannot skip wildcard expansion");
expand_test(L"/bin/l\\0", EXPAND_FOR_COMPLETIONS, 0,
expand_test(L"foo", noflags, L"foo", 0, L"Strings do not expand to themselves");
expand_test(L"a{b,c,d}e", noflags, L"abe", L"ace", L"ade", 0, L"Bracket expansion is broken");
expand_test(L"a*", expand_flag::EXPAND_SKIP_WILDCARDS, L"a*", 0,
L"Cannot skip wildcard expansion");
expand_test(L"/bin/l\\0", expand_flag::EXPAND_FOR_COMPLETIONS, 0,
L"Failed to handle null escape in expansion");
expand_test(L"foo\\$bar", EXPAND_SKIP_VARIABLES, L"foo$bar", 0,
expand_test(L"foo\\$bar", expand_flag::EXPAND_SKIP_VARIABLES, L"foo$bar", 0,
L"Failed to handle dollar sign in variable-skipping expansion");
// bb
@ -1700,89 +1711,91 @@ static void test_expand() {
// (https://github.com/fish-shell/fish-shell/issues/270). But it does have to match literal
// components (e.g. "./*" has to match the same as "*".
const wchar_t *const wnull = NULL;
expand_test(L"test/fish_expand_test/.*", 0, L"test/fish_expand_test/.foo", wnull,
expand_test(L"test/fish_expand_test/.*", noflags, L"test/fish_expand_test/.foo", wnull,
L"Expansion not correctly handling dotfiles");
expand_test(L"test/fish_expand_test/./.*", 0, L"test/fish_expand_test/./.foo", wnull,
expand_test(L"test/fish_expand_test/./.*", noflags, L"test/fish_expand_test/./.foo", wnull,
L"Expansion not correctly handling literal path components in dotfiles");
expand_test(L"test/fish_expand_test/*/xxx", 0, L"test/fish_expand_test/bax/xxx",
expand_test(L"test/fish_expand_test/*/xxx", noflags, L"test/fish_expand_test/bax/xxx",
L"test/fish_expand_test/baz/xxx", wnull, L"Glob did the wrong thing 1");
expand_test(L"test/fish_expand_test/*z/xxx", 0, L"test/fish_expand_test/baz/xxx", wnull,
expand_test(L"test/fish_expand_test/*z/xxx", noflags, L"test/fish_expand_test/baz/xxx", wnull,
L"Glob did the wrong thing 2");
expand_test(L"test/fish_expand_test/**z/xxx", 0, L"test/fish_expand_test/baz/xxx", wnull,
expand_test(L"test/fish_expand_test/**z/xxx", noflags, L"test/fish_expand_test/baz/xxx", wnull,
L"Glob did the wrong thing 3");
expand_test(L"test/fish_expand_test////baz/xxx", 0, L"test/fish_expand_test////baz/xxx", wnull,
L"Glob did the wrong thing 3");
expand_test(L"test/fish_expand_test////baz/xxx", noflags, L"test/fish_expand_test////baz/xxx",
wnull, L"Glob did the wrong thing 3");
expand_test(L"test/fish_expand_test/b**", 0, L"test/fish_expand_test/bb",
expand_test(L"test/fish_expand_test/b**", noflags, L"test/fish_expand_test/bb",
L"test/fish_expand_test/bb/x", L"test/fish_expand_test/bar",
L"test/fish_expand_test/bax", L"test/fish_expand_test/bax/xxx",
L"test/fish_expand_test/baz", L"test/fish_expand_test/baz/xxx",
L"test/fish_expand_test/baz/yyy", wnull, L"Glob did the wrong thing 4");
// A trailing slash should only produce directories.
expand_test(L"test/fish_expand_test/b*/", 0, L"test/fish_expand_test/bb/",
expand_test(L"test/fish_expand_test/b*/", noflags, L"test/fish_expand_test/bb/",
L"test/fish_expand_test/baz/", L"test/fish_expand_test/bax/", wnull,
L"Glob did the wrong thing 5");
expand_test(L"test/fish_expand_test/b**/", 0, L"test/fish_expand_test/bb/",
expand_test(L"test/fish_expand_test/b**/", noflags, L"test/fish_expand_test/bb/",
L"test/fish_expand_test/baz/", L"test/fish_expand_test/bax/", wnull,
L"Glob did the wrong thing 6");
expand_test(L"test/fish_expand_test/**/q", 0, L"test/fish_expand_test/lol/nub/q", wnull,
expand_test(L"test/fish_expand_test/**/q", noflags, L"test/fish_expand_test/lol/nub/q", wnull,
L"Glob did the wrong thing 7");
expand_test(L"test/fish_expand_test/BA", EXPAND_FOR_COMPLETIONS, L"test/fish_expand_test/bar",
L"test/fish_expand_test/bax/", L"test/fish_expand_test/baz/", wnull,
L"Case insensitive test did the wrong thing");
expand_test(L"test/fish_expand_test/BA", expand_flag::EXPAND_FOR_COMPLETIONS,
L"test/fish_expand_test/bar", L"test/fish_expand_test/bax/",
L"test/fish_expand_test/baz/", wnull, L"Case insensitive test did the wrong thing");
expand_test(L"test/fish_expand_test/BA", EXPAND_FOR_COMPLETIONS, L"test/fish_expand_test/bar",
L"test/fish_expand_test/bax/", L"test/fish_expand_test/baz/", wnull,
L"Case insensitive test did the wrong thing");
expand_test(L"test/fish_expand_test/BA", expand_flag::EXPAND_FOR_COMPLETIONS,
L"test/fish_expand_test/bar", L"test/fish_expand_test/bax/",
L"test/fish_expand_test/baz/", wnull, L"Case insensitive test did the wrong thing");
expand_test(L"test/fish_expand_test/bb/yyy", EXPAND_FOR_COMPLETIONS,
expand_test(L"test/fish_expand_test/bb/yyy", expand_flag::EXPAND_FOR_COMPLETIONS,
/* nothing! */ wnull, L"Wrong fuzzy matching 1");
expand_test(L"test/fish_expand_test/bb/x", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, L"",
wnull, // we just expect the empty string since this is an exact match
L"Wrong fuzzy matching 2");
expand_test(
L"test/fish_expand_test/bb/x",
expand_flags_t{expand_flag::EXPAND_FOR_COMPLETIONS, expand_flag::EXPAND_FUZZY_MATCH}, L"",
wnull, // we just expect the empty string since this is an exact match
L"Wrong fuzzy matching 2");
// Some vswprintfs refuse to append ANY_STRING in a format specifiers, so don't use
// format_string here.
const expand_flags_t fuzzy_comp{expand_flag::EXPAND_FOR_COMPLETIONS,
expand_flag::EXPAND_FUZZY_MATCH};
const wcstring any_str_str(1, ANY_STRING);
expand_test(L"test/fish_expand_test/b/xx*", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH,
expand_test(L"test/fish_expand_test/b/xx*", fuzzy_comp,
(L"test/fish_expand_test/bax/xx" + any_str_str).c_str(),
(L"test/fish_expand_test/baz/xx" + any_str_str).c_str(), wnull,
L"Wrong fuzzy matching 3");
expand_test(L"test/fish_expand_test/b/yyy", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH,
L"test/fish_expand_test/baz/yyy", wnull, L"Wrong fuzzy matching 4");
expand_test(L"test/fish_expand_test/b/yyy", fuzzy_comp, L"test/fish_expand_test/baz/yyy", wnull,
L"Wrong fuzzy matching 4");
expand_test(L"test/fish_expand_test/aa/x", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH,
L"test/fish_expand_test/aaa2/x", wnull, L"Wrong fuzzy matching 5");
expand_test(L"test/fish_expand_test/aa/x", fuzzy_comp, L"test/fish_expand_test/aaa2/x", wnull,
L"Wrong fuzzy matching 5");
expand_test(L"test/fish_expand_test/aaa/x", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, wnull,
expand_test(L"test/fish_expand_test/aaa/x", fuzzy_comp, wnull,
L"Wrong fuzzy matching 6 - shouldn't remove valid directory names (#3211)");
if (!expand_test(L"test/fish_expand_test/.*", 0, L"test/fish_expand_test/.foo", 0)) {
if (!expand_test(L"test/fish_expand_test/.*", noflags, L"test/fish_expand_test/.foo", 0)) {
err(L"Expansion not correctly handling dotfiles");
}
if (!expand_test(L"test/fish_expand_test/./.*", 0, L"test/fish_expand_test/./.foo", 0)) {
if (!expand_test(L"test/fish_expand_test/./.*", noflags, L"test/fish_expand_test/./.foo", 0)) {
err(L"Expansion not correctly handling literal path components in dotfiles");
}
if (!pushd("test/fish_expand_test")) return;
expand_test(L"b/xx", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, L"bax/xxx", L"baz/xxx", wnull,
L"Wrong fuzzy matching 5");
expand_test(L"b/xx", fuzzy_comp, L"bax/xxx", L"baz/xxx", wnull, L"Wrong fuzzy matching 5");
// multiple slashes with fuzzy matching - #3185
expand_test(L"l///n", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, L"lol///nub/", wnull,
L"Wrong fuzzy matching 6");
expand_test(L"l///n", fuzzy_comp, L"lol///nub/", wnull, L"Wrong fuzzy matching 6");
popd();
}
@ -2255,7 +2268,7 @@ static bool run_test_test(int expected, const wcstring &str) {
// We need to tokenize the string in the same manner a normal shell would do. This is because we
// need to test things like quoted strings that have leading and trailing whitespace.
parser_t::expand_argument_list(str, 0, null_environment_t{}, &comps);
parser_t::expand_argument_list(str, expand_flags_t{}, null_environment_t{}, &comps);
for (completion_list_t::const_iterator it = comps.begin(), end = comps.end(); it != end; ++it) {
argv.push_back(it->completion);
}

View File

@ -432,7 +432,7 @@ bool autosuggest_validate_from_history(const history_item_t &item,
if (parsed_command == L"cd" && !cd_dir.empty()) {
// We can possibly handle this specially.
if (expand_one(cd_dir, EXPAND_SKIP_CMDSUBST, vars)) {
if (expand_one(cd_dir, expand_flag::EXPAND_SKIP_CMDSUBST, vars)) {
handled = true;
bool is_help =
string_prefixes_string(cd_dir, L"--help") || string_prefixes_string(cd_dir, L"-h");
@ -926,7 +926,7 @@ void highlighter_t::color_arguments(const std::vector<tnode_t<g::argument>> &arg
if (cmd_is_cd) {
// Mark this as an error if it's not 'help' and not a valid cd path.
wcstring param = arg.get_source(this->buff);
if (expand_one(param, EXPAND_SKIP_CMDSUBST, vars)) {
if (expand_one(param, expand_flag::EXPAND_SKIP_CMDSUBST, vars)) {
bool is_help = string_prefixes_string(param, L"--help") ||
string_prefixes_string(param, L"-h");
if (!is_help && this->io_ok &&
@ -969,7 +969,7 @@ void highlighter_t::color_redirection(tnode_t<g::redirection> redirection_node)
// I/O is disallowed, so we don't have much hope of catching anything but gross
// errors. Assume it's valid.
target_is_valid = true;
} else if (!expand_one(target, EXPAND_SKIP_CMDSUBST, vars)) {
} else if (!expand_one(target, expand_flag::EXPAND_SKIP_CMDSUBST, vars)) {
// Could not be expanded.
target_is_valid = false;
} else {

View File

@ -127,7 +127,10 @@ tnode_t<g::plain_statement> parse_execution_context_t::infinite_recursive_statem
.try_get_child<g::plain_statement, 0>();
if (plain_statement) {
maybe_t<wcstring> cmd = command_for_plain_statement(plain_statement, pstree->src);
if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, nullenv) &&
if (cmd &&
expand_one(*cmd,
{expand_flag::EXPAND_SKIP_CMDSUBST, expand_flag::EXPAND_SKIP_VARIABLES},
nullenv) &&
cmd == forbidden_function_name) {
// This is it.
infinite_recursive_statement = plain_statement;
@ -376,7 +379,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement(
// in just one.
tnode_t<g::tok_string> var_name_node = header.child<1>();
wcstring for_var_name = get_source(var_name_node);
if (!expand_one(for_var_name, 0, parser->vars())) {
if (!expand_one(for_var_name, expand_flags_t{}, parser->vars())) {
report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str());
return parse_execution_errored;
}
@ -449,8 +452,8 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(
// Expand it. We need to offset any errors by the position of the string.
std::vector<completion_t> switch_values_expanded;
parse_error_list_t errors;
auto expand_ret = expand_string(switch_value, &switch_values_expanded, EXPAND_NO_DESCRIPTIONS,
parser->vars(), &errors);
auto expand_ret = expand_string(switch_value, &switch_values_expanded,
expand_flag::EXPAND_NO_DESCRIPTIONS, parser->vars(), &errors);
parse_error_offset_source_start(&errors, switch_value_n.source_range()->start);
switch (expand_ret) {
@ -906,8 +909,8 @@ parse_execution_result_t parse_execution_context_t::expand_arguments_from_nodes(
// Expand this string.
parse_error_list_t errors;
arg_expanded.clear();
auto expand_ret =
expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, parser->vars(), &errors);
auto expand_ret = expand_string(arg_str, &arg_expanded, expand_flag::EXPAND_NO_DESCRIPTIONS,
parser->vars(), &errors);
parse_error_offset_source_start(&errors, arg_node.source_range()->start);
switch (expand_ret) {
case expand_result_t::error: {
@ -956,7 +959,8 @@ bool parse_execution_context_t::determine_io_chain(tnode_t<g::arguments_or_redir
// PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here.
bool target_expanded =
expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0, parser->vars());
expand_one(target, no_exec ? expand_flag::EXPAND_SKIP_VARIABLES : expand_flags_t{},
parser->vars());
if (!target_expanded || target.empty()) {
// TODO: Improve this error message.
errored =

View File

@ -1197,7 +1197,7 @@ static bool detect_errors_in_plain_statement(const wcstring &buff_src,
// Check that we don't do an invalid builtin (issue #1252).
if (!errored && decoration == parse_statement_decoration_builtin &&
expand_one(*unexp_command, 0, null_environment_t{}, parse_errors) &&
expand_one(*unexp_command, expand_flags_t{}, null_environment_t{}, parse_errors) &&
!builtin_exists(*unexp_command)) {
errored = append_syntax_error(parse_errors, source_start, UNKNOWN_BUILTIN_ERR_MSG,
unexp_command->c_str());

View File

@ -167,7 +167,7 @@ static wcstring resolve_description(const wcstring &full_completion, wcstring *c
completion->resize(complete_sep_loc);
return description;
}
if (expand_flags & EXPAND_NO_DESCRIPTIONS) return {};
if (expand_flags & expand_flag::EXPAND_NO_DESCRIPTIONS) return {};
return desc_func ? desc_func(full_completion) : wcstring{};
}
@ -219,7 +219,7 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc,
// If we're allowing fuzzy match, any match is OK. Otherwise we require a prefix match.
bool match_acceptable;
if (params.expand_flags & EXPAND_FUZZY_MATCH) {
if (params.expand_flags & expand_flag::EXPAND_FUZZY_MATCH) {
match_acceptable = match.type != fuzzy_match_none;
} else {
match_acceptable = match_type_shares_prefix(match.type);
@ -415,12 +415,12 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wc
const bool is_directory = stat_res == 0 && S_ISDIR(stat_buf.st_mode);
const bool is_executable = stat_res == 0 && S_ISREG(stat_buf.st_mode);
const bool need_directory = expand_flags & DIRECTORIES_ONLY;
const bool need_directory = expand_flags & expand_flag::DIRECTORIES_ONLY;
if (need_directory && !is_directory) {
return false;
}
const bool executables_only = expand_flags & EXECUTABLES_ONLY;
const bool executables_only = expand_flags & expand_flag::EXECUTABLES_ONLY;
if (executables_only && (!is_executable || waccess(filepath, X_OK) != 0)) {
return false;
}
@ -432,7 +432,7 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wc
// Compute the description.
wcstring desc;
if (!(expand_flags & EXPAND_NO_DESCRIPTIONS)) {
if (!(expand_flags & expand_flag::EXPAND_NO_DESCRIPTIONS)) {
desc = file_get_desc(filepath, lstat_res, lstat_buf, stat_res, stat_buf, stat_errno);
if (file_size >= 0) {
@ -507,8 +507,7 @@ class wildcard_expander_t {
void add_expansion_result(const wcstring &result) {
// This function is only for the non-completions case.
assert(!static_cast<bool>(this->flags &
EXPAND_FOR_COMPLETIONS)); //!OCLINT(multiple unary operator)
assert(!(this->flags & expand_flag::EXPAND_FOR_COMPLETIONS));
if (this->completion_set.insert(result).second) {
append_completion(this->resolved_completions, result);
this->did_add = true;
@ -569,13 +568,13 @@ class wildcard_expander_t {
void try_add_completion_result(const wcstring &filepath, const wcstring &filename,
const wcstring &wildcard, const wcstring &prefix) {
// This function is only for the completions case.
assert(this->flags & EXPAND_FOR_COMPLETIONS);
assert(this->flags & expand_flag::EXPAND_FOR_COMPLETIONS);
wcstring abs_path = this->working_directory;
append_path_component(abs_path, filepath);
// We must normalize the path to allow 'cd ..' to operate on logical paths.
if (flags & EXPAND_SPECIAL_FOR_CD) abs_path = normalize_path(abs_path);
if (flags & expand_flag::EXPAND_SPECIAL_FOR_CD) abs_path = normalize_path(abs_path);
size_t before = this->resolved_completions->size();
if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags,
@ -597,7 +596,7 @@ class wildcard_expander_t {
// hierarchy we can, and then appending any components to each new result.
// Only descend deepest unique for cd autosuggest and not for cd tab completion
// (issue #4402).
if (flags & EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST) {
if (flags & expand_flag::EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST) {
wcstring unique_hierarchy = this->descend_unique_hierarchy(abs_path);
if (!unique_hierarchy.empty()) {
for (size_t i = before; i < after; i++) {
@ -615,7 +614,7 @@ class wildcard_expander_t {
DIR *open_dir(const wcstring &base_dir) const {
wcstring path = this->working_directory;
append_path_component(path, base_dir);
if (flags & EXPAND_SPECIAL_FOR_CD) {
if (flags & expand_flag::EXPAND_SPECIAL_FOR_CD) {
// cd operates on logical paths.
// for example, cd ../<tab> should complete "without resolving symlinks".
path = normalize_path(path);
@ -661,7 +660,7 @@ void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir, const
return;
}
if (!(flags & EXPAND_FOR_COMPLETIONS)) {
if (!(flags & expand_flag::EXPAND_FOR_COMPLETIONS)) {
// Trailing slash and not accepting incomplete, e.g. `echo /xyz/`. Insert this file if it
// exists.
if (waccess(base_dir, F_OK) == 0) {
@ -784,7 +783,7 @@ void wildcard_expander_t::expand_last_segment(const wcstring &base_dir, DIR *bas
const wcstring &wc, const wcstring &prefix) {
wcstring name_str;
while (wreaddir(base_dir_fp, name_str)) {
if (flags & EXPAND_FOR_COMPLETIONS) {
if (flags & expand_flag::EXPAND_FOR_COMPLETIONS) {
this->try_add_completion_result(base_dir + name_str, name_str, wc, prefix);
} else {
// Normal wildcard expansion, not for completions.
@ -855,11 +854,11 @@ void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc,
// Maybe try a fuzzy match (#94) if nothing was found with the literal match. Respect
// EXPAND_NO_DIRECTORY_ABBREVIATIONS (issue #2413).
// Don't do fuzzy matches if the literal segment was valid (#3211)
bool allow_fuzzy = (this->flags & (EXPAND_FUZZY_MATCH | EXPAND_NO_FUZZY_DIRECTORIES)) ==
EXPAND_FUZZY_MATCH;
bool allow_fuzzy = this->flags.get(expand_flag::EXPAND_FUZZY_MATCH) &&
!this->flags.get(expand_flag::EXPAND_NO_FUZZY_DIRECTORIES);
if (allow_fuzzy && this->resolved_completions->size() == before &&
waccess(intermediate_dirpath, F_OK) != 0) {
assert(this->flags & EXPAND_FOR_COMPLETIONS);
assert(this->flags & expand_flag::EXPAND_FOR_COMPLETIONS);
DIR *base_dir_fd = open_dir(base_dir);
if (base_dir_fd != NULL) {
this->expand_literal_intermediate_segment_with_fuzz(
@ -905,13 +904,15 @@ int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory
expand_flags_t flags, std::vector<completion_t> *output) {
assert(output != NULL);
// Fuzzy matching only if we're doing completions.
assert((flags & (EXPAND_FUZZY_MATCH | EXPAND_FOR_COMPLETIONS)) != EXPAND_FUZZY_MATCH);
assert(flags.get(expand_flag::EXPAND_FOR_COMPLETIONS) ||
!flags.get(expand_flag::EXPAND_FUZZY_MATCH));
// EXPAND_SPECIAL_FOR_CD requires DIRECTORIES_ONLY and EXPAND_FOR_COMPLETIONS and
// EXPAND_NO_DESCRIPTIONS.
assert(!(flags & EXPAND_SPECIAL_FOR_CD) ||
((flags & DIRECTORIES_ONLY) && (flags & EXPAND_FOR_COMPLETIONS) &&
(flags & EXPAND_NO_DESCRIPTIONS)));
// expand_flag::EXPAND_SPECIAL_FOR_CD requires expand_flag::DIRECTORIES_ONLY and
// expand_flag::EXPAND_FOR_COMPLETIONS and expand_flag::EXPAND_NO_DESCRIPTIONS.
assert(!(flags.get(expand_flag::EXPAND_SPECIAL_FOR_CD)) ||
((flags.get(expand_flag::DIRECTORIES_ONLY)) &&
(flags.get(expand_flag::EXPAND_FOR_COMPLETIONS)) &&
(flags.get(expand_flag::EXPAND_NO_DESCRIPTIONS))));
// Hackish fix for issue #1631. We are about to call c_str(), which will produce a string
// truncated at any embedded nulls. We could fix this by passing around the size, etc. However