mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-02 14:01:20 -03:00
Switch expand_flags_t to enum_set
This commit is contained in:
@@ -359,11 +359,11 @@ class completer_t {
|
|||||||
expand_flags_t expand_flags() const {
|
expand_flags_t expand_flags() const {
|
||||||
// Never do command substitution in autosuggestions. Sadly, we also can't yet do job
|
// Never do command substitution in autosuggestions. Sadly, we also can't yet do job
|
||||||
// expansion because it's not thread safe.
|
// expansion because it's not thread safe.
|
||||||
expand_flags_t result = 0;
|
expand_flags_t result{};
|
||||||
if (this->type() == COMPLETE_AUTOSUGGEST) result |= EXPAND_SKIP_CMDSUBST;
|
if (this->type() == COMPLETE_AUTOSUGGEST) result |= expand_flag::EXPAND_SKIP_CMDSUBST;
|
||||||
|
|
||||||
// Allow fuzzy matching.
|
// Allow fuzzy matching.
|
||||||
if (this->fuzzy()) result |= EXPAND_FUZZY_MATCH;
|
if (this->fuzzy()) result |= expand_flag::EXPAND_FUZZY_MATCH;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -545,7 +545,10 @@ void completer_t::complete_strings(const wcstring &wc_escaped, const description
|
|||||||
const std::vector<completion_t> &possible_comp,
|
const std::vector<completion_t> &possible_comp,
|
||||||
complete_flags_t flags) {
|
complete_flags_t flags) {
|
||||||
wcstring tmp = wc_escaped;
|
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;
|
return;
|
||||||
|
|
||||||
const wcstring wc = parse_util_unescape_wildcards(tmp);
|
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) {
|
if (use_command) {
|
||||||
// Append all possible executables
|
// Append all possible executables
|
||||||
expand_result_t result = expand_string(str_cmd, &this->completions,
|
expand_result_t result =
|
||||||
EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS |
|
expand_string(str_cmd, &this->completions,
|
||||||
EXECUTABLES_ONLY | this->expand_flags(),
|
this->expand_flags() | expand_flag::EXPAND_SPECIAL_FOR_COMMAND |
|
||||||
vars, NULL);
|
expand_flag::EXPAND_FOR_COMPLETIONS | expand_flag::EXECUTABLES_ONLY,
|
||||||
|
vars, NULL);
|
||||||
if (result != expand_result_t::error && this->wants_descriptions()) {
|
if (result != expand_result_t::error && this->wants_descriptions()) {
|
||||||
this->complete_cmd_desc(str_cmd);
|
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 =
|
expand_result_t ignore =
|
||||||
// Append all matching directories
|
// Append all matching directories
|
||||||
expand_string(str_cmd, &this->completions,
|
expand_string(str_cmd, &this->completions,
|
||||||
EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), vars,
|
this->expand_flags() | expand_flag::EXPAND_FOR_COMPLETIONS |
|
||||||
NULL);
|
expand_flag::DIRECTORIES_ONLY,
|
||||||
|
vars, NULL);
|
||||||
UNUSED(ignore);
|
UNUSED(ignore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,9 +752,10 @@ void completer_t::complete_from_args(const wcstring &str, const wcstring &args,
|
|||||||
proc_push_interactive(0);
|
proc_push_interactive(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
expand_flags_t eflags = 0;
|
expand_flags_t eflags{};
|
||||||
if (is_autosuggest) {
|
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;
|
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.
|
/// Perform generic (not command-specific) expansions on the specified string.
|
||||||
void completer_t::complete_param_expand(const wcstring &str, bool do_file,
|
void completer_t::complete_param_expand(const wcstring &str, bool do_file,
|
||||||
bool handle_as_special_cd) {
|
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 (handle_as_special_cd && do_file) {
|
||||||
if (this->type() == COMPLETE_AUTOSUGGEST) {
|
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.
|
// 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:
|
// 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) {
|
if (complete_from_start) {
|
||||||
// Don't do fuzzy matching for files if the string begins with a dash (issue #568). We could
|
// 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.
|
// 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) {
|
if (expand_string(str, &this->completions, flags, vars, NULL) == expand_result_t::error) {
|
||||||
debug(3, L"Error while expanding string '%ls'", str.c_str());
|
debug(3, L"Error while expanding string '%ls'", str.c_str());
|
||||||
|
|||||||
@@ -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); }
|
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(unsigned long raw) : super(raw) {}
|
||||||
|
explicit enum_set_t(super sup) : super(sup) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum_set_t() = default;
|
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); }
|
||||||
|
|
||||||
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.
|
/// An array of Elem indexed by an enum class.
|
||||||
|
|||||||
@@ -511,7 +511,7 @@ static expand_result_t expand_braces(const wcstring &instr, expand_flags_t flags
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (brace_count > 0) {
|
if (brace_count > 0) {
|
||||||
if (!(flags & EXPAND_FOR_COMPLETIONS)) {
|
if (!(flags & expand_flag::EXPAND_FOR_COMPLETIONS)) {
|
||||||
syntax_error = true;
|
syntax_error = true;
|
||||||
} else {
|
} else {
|
||||||
// The user hasn't typed an end brace yet; make one up and append it, then expand
|
// 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.
|
// 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) {
|
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;
|
size_t cur = 0, start = 0, end;
|
||||||
switch (parse_util_locate_cmdsubst_range(input, &cur, nullptr, &start, &end, true)) {
|
switch (parse_util_locate_cmdsubst_range(input, &cur, nullptr, &start, &end, true)) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -924,7 +924,7 @@ expand_result_t expander_t::stage_variables(wcstring input, std::vector<completi
|
|||||||
wcstring next;
|
wcstring next;
|
||||||
unescape_string(input, &next, UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE);
|
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++) {
|
for (size_t i = 0; i < next.size(); i++) {
|
||||||
if (next.at(i) == VARIABLE_EXPAND) {
|
if (next.at(i) == VARIABLE_EXPAND) {
|
||||||
next[i] = L'$';
|
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) {
|
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_home_directory(input, vars);
|
||||||
}
|
}
|
||||||
expand_percent_self(input);
|
expand_percent_self(input);
|
||||||
@@ -958,13 +958,14 @@ expand_result_t expander_t::stage_wildcards(wcstring path_to_expand,
|
|||||||
std::vector<completion_t> *out) {
|
std::vector<completion_t> *out) {
|
||||||
expand_result_t result = expand_result_t::ok;
|
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 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
|
; // don't do wildcard expansion for executables, see issue #785
|
||||||
} else if (((flags & EXPAND_FOR_COMPLETIONS) && (!(flags & EXPAND_SKIP_WILDCARDS))) ||
|
} else if ((for_completions && !skip_wildcards) || has_wildcard) {
|
||||||
has_wildcard) {
|
|
||||||
// We either have a wildcard, or we don't have a wildcard but we're doing completion
|
// 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
|
// 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
|
// 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.
|
// which may be CDPATH if the special flag is set.
|
||||||
const wcstring working_dir = vars.get_pwd_slash();
|
const wcstring working_dir = vars.get_pwd_slash();
|
||||||
wcstring_list_t effective_working_dirs;
|
wcstring_list_t effective_working_dirs;
|
||||||
bool for_cd = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_CD);
|
bool for_cd = flags & expand_flag::EXPAND_SPECIAL_FOR_CD;
|
||||||
bool for_command = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_COMMAND);
|
bool for_command = flags & expand_flag::EXPAND_SPECIAL_FOR_COMMAND;
|
||||||
if (!for_cd && !for_command) {
|
if (!for_cd && !for_command) {
|
||||||
// Common case.
|
// Common case.
|
||||||
effective_working_dirs.push_back(working_dir);
|
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
|
// 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
|
// to mean don't do file expansions, so if we're not doing file expansions, just drop this
|
||||||
// completion on the floor.
|
// completion on the floor.
|
||||||
if (!(flags & EXPAND_FOR_COMPLETIONS)) {
|
if (!(flags & expand_flag::EXPAND_FOR_COMPLETIONS)) {
|
||||||
append_completion(out, std::move(path_to_expand));
|
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,
|
expand_flags_t flags, const environment_t &vars,
|
||||||
parse_error_list_t *errors) {
|
parse_error_list_t *errors) {
|
||||||
// Early out. If we're not completing, and there's no magic in the input, we're done.
|
// 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));
|
append_completion(out_completions, std::move(input));
|
||||||
return expand_result_t::ok;
|
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) {
|
if (total_result != expand_result_t::error) {
|
||||||
// Hack to un-expand tildes (see #647).
|
// 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);
|
unexpand_tildes(input, vars, &completions);
|
||||||
}
|
}
|
||||||
out_completions->insert(out_completions->end(),
|
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) {
|
parse_error_list_t *errors) {
|
||||||
std::vector<completion_t> completions;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, vars, errors) !=
|
if (expand_string(string, &completions, flags | expand_flag::EXPAND_NO_DESCRIPTIONS, vars,
|
||||||
expand_result_t::error &&
|
errors) != expand_result_t::error &&
|
||||||
completions.size() == 1) {
|
completions.size() == 1) {
|
||||||
string = std::move(completions.at(0).completion);
|
string = std::move(completions.at(0).completion);
|
||||||
return true;
|
return true;
|
||||||
@@ -1137,9 +1138,11 @@ expand_result_t expand_to_command_and_args(const wcstring &instr, const environm
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<completion_t> completions;
|
std::vector<completion_t> completions;
|
||||||
expand_result_t expand_err = expand_string(
|
expand_result_t expand_err =
|
||||||
instr, &completions, EXPAND_SKIP_CMDSUBST | EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_JOBS, vars,
|
expand_string(instr, &completions,
|
||||||
errors);
|
{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) {
|
if (expand_err == expand_result_t::ok || expand_err == expand_result_t::wildcard_match) {
|
||||||
// The first completion is the command, any remaning are arguments.
|
// The first completion is the command, any remaning are arguments.
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|||||||
41
src/expand.h
41
src/expand.h
@@ -14,6 +14,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "enum_set.h"
|
||||||
#include "maybe.h"
|
#include "maybe.h"
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
|
|
||||||
@@ -21,43 +22,51 @@ class environment_t;
|
|||||||
class env_var_t;
|
class env_var_t;
|
||||||
class environment_t;
|
class environment_t;
|
||||||
|
|
||||||
enum {
|
enum class expand_flag {
|
||||||
/// Flag specifying that cmdsubst expansion should be skipped.
|
/// Flag specifying that cmdsubst expansion should be skipped.
|
||||||
EXPAND_SKIP_CMDSUBST = 1 << 0,
|
EXPAND_SKIP_CMDSUBST,
|
||||||
/// Flag specifying that variable expansion should be skipped.
|
/// Flag specifying that variable expansion should be skipped.
|
||||||
EXPAND_SKIP_VARIABLES = 1 << 1,
|
EXPAND_SKIP_VARIABLES,
|
||||||
/// Flag specifying that wildcard expansion should be skipped.
|
/// 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
|
/// The expansion is being done for tab or auto completions. Returned completions may have the
|
||||||
/// wildcard as a prefix instead of a match.
|
/// 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.
|
/// Only match files that are executable by the current user.
|
||||||
EXECUTABLES_ONLY = 1 << 4,
|
EXECUTABLES_ONLY,
|
||||||
/// Only match directories.
|
/// Only match directories.
|
||||||
DIRECTORIES_ONLY = 1 << 5,
|
DIRECTORIES_ONLY,
|
||||||
/// Don't generate descriptions.
|
/// 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
|
/// Don't expand jobs (but you can still expand processes). This is because
|
||||||
/// job expansion is not thread safe.
|
/// job expansion is not thread safe.
|
||||||
EXPAND_SKIP_JOBS = 1 << 7,
|
EXPAND_SKIP_JOBS,
|
||||||
/// Don't expand home directories.
|
/// Don't expand home directories.
|
||||||
EXPAND_SKIP_HOME_DIRECTORIES = 1 << 8,
|
EXPAND_SKIP_HOME_DIRECTORIES,
|
||||||
/// Allow fuzzy matching.
|
/// 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
|
/// Disallow directory abbreviations like /u/l/b for /usr/local/bin. Only applicable if
|
||||||
/// EXPAND_FUZZY_MATCH is set.
|
/// 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
|
/// 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.
|
/// 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
|
/// Do expansions specifically for cd autosuggestion. This is to differentiate between cd
|
||||||
/// completions and cd autosuggestions.
|
/// 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
|
/// Do expansions specifically to support external command completions. This means using PATH as
|
||||||
/// a list of potential working directories.
|
/// 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;
|
class completion_t;
|
||||||
|
|
||||||
|
|||||||
@@ -305,6 +305,10 @@ static void test_enum_set() {
|
|||||||
do_test(es != enum_set_t<test_enum>::from_raw(1));
|
do_test(es != enum_set_t<test_enum>::from_raw(1));
|
||||||
|
|
||||||
es.set(test_enum::beta);
|
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.to_raw() == 2);
|
||||||
do_test(es == enum_set_t<test_enum>::from_raw(2));
|
do_test(es == enum_set_t<test_enum>::from_raw(2));
|
||||||
do_test(es == enum_set_t<test_enum>{test_enum::beta});
|
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.any());
|
||||||
do_test(!es.none());
|
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;
|
unsigned idx = 0;
|
||||||
for (auto v : enum_iter_t<test_enum>{}) {
|
for (auto v : enum_iter_t<test_enum>{}) {
|
||||||
do_test(static_cast<unsigned>(v) == idx);
|
do_test(static_cast<unsigned>(v) == idx);
|
||||||
@@ -970,7 +978,8 @@ static void test_parser() {
|
|||||||
|
|
||||||
say(L"Testing eval_args");
|
say(L"Testing eval_args");
|
||||||
completion_list_t comps;
|
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.size() == 3);
|
||||||
do_test(comps.at(0).completion == L"alpha");
|
do_test(comps.at(0).completion == L"alpha");
|
||||||
do_test(comps.at(1).completion == L"beta gamma");
|
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.
|
/// Test globbing and other parameter expansion.
|
||||||
static void test_expand() {
|
static void test_expand() {
|
||||||
say(L"Testing parameter expansion");
|
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"foo", noflags, 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{b,c,d}e", noflags, 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"a*", expand_flag::EXPAND_SKIP_WILDCARDS, L"a*", 0,
|
||||||
expand_test(L"/bin/l\\0", EXPAND_FOR_COMPLETIONS, 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");
|
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");
|
L"Failed to handle dollar sign in variable-skipping expansion");
|
||||||
|
|
||||||
// bb
|
// 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
|
// (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 "*".
|
// components (e.g. "./*" has to match the same as "*".
|
||||||
const wchar_t *const wnull = NULL;
|
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");
|
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");
|
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");
|
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");
|
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");
|
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,
|
expand_test(L"test/fish_expand_test////baz/xxx", noflags, L"test/fish_expand_test////baz/xxx",
|
||||||
L"Glob did the wrong thing 3");
|
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/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/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", L"test/fish_expand_test/baz/xxx",
|
||||||
L"test/fish_expand_test/baz/yyy", wnull, L"Glob did the wrong thing 4");
|
L"test/fish_expand_test/baz/yyy", wnull, L"Glob did the wrong thing 4");
|
||||||
|
|
||||||
// A trailing slash should only produce directories.
|
// 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"test/fish_expand_test/baz/", L"test/fish_expand_test/bax/", wnull,
|
||||||
L"Glob did the wrong thing 5");
|
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"test/fish_expand_test/baz/", L"test/fish_expand_test/bax/", wnull,
|
||||||
L"Glob did the wrong thing 6");
|
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");
|
L"Glob did the wrong thing 7");
|
||||||
|
|
||||||
expand_test(L"test/fish_expand_test/BA", EXPAND_FOR_COMPLETIONS, L"test/fish_expand_test/bar",
|
expand_test(L"test/fish_expand_test/BA", expand_flag::EXPAND_FOR_COMPLETIONS,
|
||||||
L"test/fish_expand_test/bax/", L"test/fish_expand_test/baz/", wnull,
|
L"test/fish_expand_test/bar", L"test/fish_expand_test/bax/",
|
||||||
L"Case insensitive test did the wrong thing");
|
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",
|
expand_test(L"test/fish_expand_test/BA", expand_flag::EXPAND_FOR_COMPLETIONS,
|
||||||
L"test/fish_expand_test/bax/", L"test/fish_expand_test/baz/", wnull,
|
L"test/fish_expand_test/bar", L"test/fish_expand_test/bax/",
|
||||||
L"Case insensitive test did the wrong thing");
|
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");
|
/* nothing! */ wnull, L"Wrong fuzzy matching 1");
|
||||||
|
|
||||||
expand_test(L"test/fish_expand_test/bb/x", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, L"",
|
expand_test(
|
||||||
wnull, // we just expect the empty string since this is an exact match
|
L"test/fish_expand_test/bb/x",
|
||||||
L"Wrong fuzzy matching 2");
|
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
|
// Some vswprintfs refuse to append ANY_STRING in a format specifiers, so don't use
|
||||||
// format_string here.
|
// 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);
|
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/bax/xx" + any_str_str).c_str(),
|
||||||
(L"test/fish_expand_test/baz/xx" + any_str_str).c_str(), wnull,
|
(L"test/fish_expand_test/baz/xx" + any_str_str).c_str(), wnull,
|
||||||
L"Wrong fuzzy matching 3");
|
L"Wrong fuzzy matching 3");
|
||||||
|
|
||||||
expand_test(L"test/fish_expand_test/b/yyy", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH,
|
expand_test(L"test/fish_expand_test/b/yyy", fuzzy_comp, L"test/fish_expand_test/baz/yyy", wnull,
|
||||||
L"test/fish_expand_test/baz/yyy", wnull, L"Wrong fuzzy matching 4");
|
L"Wrong fuzzy matching 4");
|
||||||
|
|
||||||
expand_test(L"test/fish_expand_test/aa/x", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH,
|
expand_test(L"test/fish_expand_test/aa/x", fuzzy_comp, L"test/fish_expand_test/aaa2/x", wnull,
|
||||||
L"test/fish_expand_test/aaa2/x", wnull, L"Wrong fuzzy matching 5");
|
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)");
|
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");
|
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");
|
err(L"Expansion not correctly handling literal path components in dotfiles");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pushd("test/fish_expand_test")) return;
|
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,
|
expand_test(L"b/xx", fuzzy_comp, L"bax/xxx", L"baz/xxx", wnull, L"Wrong fuzzy matching 5");
|
||||||
L"Wrong fuzzy matching 5");
|
|
||||||
|
|
||||||
// multiple slashes with fuzzy matching - #3185
|
// multiple slashes with fuzzy matching - #3185
|
||||||
expand_test(L"l///n", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, L"lol///nub/", wnull,
|
expand_test(L"l///n", fuzzy_comp, L"lol///nub/", wnull, L"Wrong fuzzy matching 6");
|
||||||
L"Wrong fuzzy matching 6");
|
|
||||||
|
|
||||||
popd();
|
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
|
// 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.
|
// 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) {
|
for (completion_list_t::const_iterator it = comps.begin(), end = comps.end(); it != end; ++it) {
|
||||||
argv.push_back(it->completion);
|
argv.push_back(it->completion);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ bool autosuggest_validate_from_history(const history_item_t &item,
|
|||||||
|
|
||||||
if (parsed_command == L"cd" && !cd_dir.empty()) {
|
if (parsed_command == L"cd" && !cd_dir.empty()) {
|
||||||
// We can possibly handle this specially.
|
// 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;
|
handled = true;
|
||||||
bool is_help =
|
bool is_help =
|
||||||
string_prefixes_string(cd_dir, L"--help") || string_prefixes_string(cd_dir, L"-h");
|
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) {
|
if (cmd_is_cd) {
|
||||||
// Mark this as an error if it's not 'help' and not a valid cd path.
|
// Mark this as an error if it's not 'help' and not a valid cd path.
|
||||||
wcstring param = arg.get_source(this->buff);
|
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") ||
|
bool is_help = string_prefixes_string(param, L"--help") ||
|
||||||
string_prefixes_string(param, L"-h");
|
string_prefixes_string(param, L"-h");
|
||||||
if (!is_help && this->io_ok &&
|
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
|
// I/O is disallowed, so we don't have much hope of catching anything but gross
|
||||||
// errors. Assume it's valid.
|
// errors. Assume it's valid.
|
||||||
target_is_valid = true;
|
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.
|
// Could not be expanded.
|
||||||
target_is_valid = false;
|
target_is_valid = false;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -127,7 +127,10 @@ tnode_t<g::plain_statement> parse_execution_context_t::infinite_recursive_statem
|
|||||||
.try_get_child<g::plain_statement, 0>();
|
.try_get_child<g::plain_statement, 0>();
|
||||||
if (plain_statement) {
|
if (plain_statement) {
|
||||||
maybe_t<wcstring> cmd = command_for_plain_statement(plain_statement, pstree->src);
|
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) {
|
cmd == forbidden_function_name) {
|
||||||
// This is it.
|
// This is it.
|
||||||
infinite_recursive_statement = plain_statement;
|
infinite_recursive_statement = plain_statement;
|
||||||
@@ -376,7 +379,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement(
|
|||||||
// in just one.
|
// in just one.
|
||||||
tnode_t<g::tok_string> var_name_node = header.child<1>();
|
tnode_t<g::tok_string> var_name_node = header.child<1>();
|
||||||
wcstring for_var_name = get_source(var_name_node);
|
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());
|
report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str());
|
||||||
return parse_execution_errored;
|
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.
|
// Expand it. We need to offset any errors by the position of the string.
|
||||||
std::vector<completion_t> switch_values_expanded;
|
std::vector<completion_t> switch_values_expanded;
|
||||||
parse_error_list_t errors;
|
parse_error_list_t errors;
|
||||||
auto expand_ret = expand_string(switch_value, &switch_values_expanded, EXPAND_NO_DESCRIPTIONS,
|
auto expand_ret = expand_string(switch_value, &switch_values_expanded,
|
||||||
parser->vars(), &errors);
|
expand_flag::EXPAND_NO_DESCRIPTIONS, parser->vars(), &errors);
|
||||||
parse_error_offset_source_start(&errors, switch_value_n.source_range()->start);
|
parse_error_offset_source_start(&errors, switch_value_n.source_range()->start);
|
||||||
|
|
||||||
switch (expand_ret) {
|
switch (expand_ret) {
|
||||||
@@ -906,8 +909,8 @@ parse_execution_result_t parse_execution_context_t::expand_arguments_from_nodes(
|
|||||||
// Expand this string.
|
// Expand this string.
|
||||||
parse_error_list_t errors;
|
parse_error_list_t errors;
|
||||||
arg_expanded.clear();
|
arg_expanded.clear();
|
||||||
auto expand_ret =
|
auto expand_ret = expand_string(arg_str, &arg_expanded, expand_flag::EXPAND_NO_DESCRIPTIONS,
|
||||||
expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, parser->vars(), &errors);
|
parser->vars(), &errors);
|
||||||
parse_error_offset_source_start(&errors, arg_node.source_range()->start);
|
parse_error_offset_source_start(&errors, arg_node.source_range()->start);
|
||||||
switch (expand_ret) {
|
switch (expand_ret) {
|
||||||
case expand_result_t::error: {
|
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.
|
// PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here.
|
||||||
bool target_expanded =
|
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()) {
|
if (!target_expanded || target.empty()) {
|
||||||
// TODO: Improve this error message.
|
// TODO: Improve this error message.
|
||||||
errored =
|
errored =
|
||||||
|
|||||||
@@ -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).
|
// Check that we don't do an invalid builtin (issue #1252).
|
||||||
if (!errored && decoration == parse_statement_decoration_builtin &&
|
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)) {
|
!builtin_exists(*unexp_command)) {
|
||||||
errored = append_syntax_error(parse_errors, source_start, UNKNOWN_BUILTIN_ERR_MSG,
|
errored = append_syntax_error(parse_errors, source_start, UNKNOWN_BUILTIN_ERR_MSG,
|
||||||
unexp_command->c_str());
|
unexp_command->c_str());
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ static wcstring resolve_description(const wcstring &full_completion, wcstring *c
|
|||||||
completion->resize(complete_sep_loc);
|
completion->resize(complete_sep_loc);
|
||||||
return description;
|
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{};
|
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.
|
// If we're allowing fuzzy match, any match is OK. Otherwise we require a prefix match.
|
||||||
bool match_acceptable;
|
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;
|
match_acceptable = match.type != fuzzy_match_none;
|
||||||
} else {
|
} else {
|
||||||
match_acceptable = match_type_shares_prefix(match.type);
|
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_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 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) {
|
if (need_directory && !is_directory) {
|
||||||
return false;
|
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)) {
|
if (executables_only && (!is_executable || waccess(filepath, X_OK) != 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -432,7 +432,7 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wc
|
|||||||
|
|
||||||
// Compute the description.
|
// Compute the description.
|
||||||
wcstring desc;
|
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);
|
desc = file_get_desc(filepath, lstat_res, lstat_buf, stat_res, stat_buf, stat_errno);
|
||||||
|
|
||||||
if (file_size >= 0) {
|
if (file_size >= 0) {
|
||||||
@@ -507,8 +507,7 @@ class wildcard_expander_t {
|
|||||||
|
|
||||||
void add_expansion_result(const wcstring &result) {
|
void add_expansion_result(const wcstring &result) {
|
||||||
// This function is only for the non-completions case.
|
// This function is only for the non-completions case.
|
||||||
assert(!static_cast<bool>(this->flags &
|
assert(!(this->flags & expand_flag::EXPAND_FOR_COMPLETIONS));
|
||||||
EXPAND_FOR_COMPLETIONS)); //!OCLINT(multiple unary operator)
|
|
||||||
if (this->completion_set.insert(result).second) {
|
if (this->completion_set.insert(result).second) {
|
||||||
append_completion(this->resolved_completions, result);
|
append_completion(this->resolved_completions, result);
|
||||||
this->did_add = true;
|
this->did_add = true;
|
||||||
@@ -569,13 +568,13 @@ class wildcard_expander_t {
|
|||||||
void try_add_completion_result(const wcstring &filepath, const wcstring &filename,
|
void try_add_completion_result(const wcstring &filepath, const wcstring &filename,
|
||||||
const wcstring &wildcard, const wcstring &prefix) {
|
const wcstring &wildcard, const wcstring &prefix) {
|
||||||
// This function is only for the completions case.
|
// 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;
|
wcstring abs_path = this->working_directory;
|
||||||
append_path_component(abs_path, filepath);
|
append_path_component(abs_path, filepath);
|
||||||
|
|
||||||
// We must normalize the path to allow 'cd ..' to operate on logical paths.
|
// 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();
|
size_t before = this->resolved_completions->size();
|
||||||
if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags,
|
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.
|
// 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
|
// Only descend deepest unique for cd autosuggest and not for cd tab completion
|
||||||
// (issue #4402).
|
// (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);
|
wcstring unique_hierarchy = this->descend_unique_hierarchy(abs_path);
|
||||||
if (!unique_hierarchy.empty()) {
|
if (!unique_hierarchy.empty()) {
|
||||||
for (size_t i = before; i < after; i++) {
|
for (size_t i = before; i < after; i++) {
|
||||||
@@ -615,7 +614,7 @@ class wildcard_expander_t {
|
|||||||
DIR *open_dir(const wcstring &base_dir) const {
|
DIR *open_dir(const wcstring &base_dir) const {
|
||||||
wcstring path = this->working_directory;
|
wcstring path = this->working_directory;
|
||||||
append_path_component(path, base_dir);
|
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.
|
// cd operates on logical paths.
|
||||||
// for example, cd ../<tab> should complete "without resolving symlinks".
|
// for example, cd ../<tab> should complete "without resolving symlinks".
|
||||||
path = normalize_path(path);
|
path = normalize_path(path);
|
||||||
@@ -661,7 +660,7 @@ void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir, const
|
|||||||
return;
|
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
|
// Trailing slash and not accepting incomplete, e.g. `echo /xyz/`. Insert this file if it
|
||||||
// exists.
|
// exists.
|
||||||
if (waccess(base_dir, F_OK) == 0) {
|
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) {
|
const wcstring &wc, const wcstring &prefix) {
|
||||||
wcstring name_str;
|
wcstring name_str;
|
||||||
while (wreaddir(base_dir_fp, 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);
|
this->try_add_completion_result(base_dir + name_str, name_str, wc, prefix);
|
||||||
} else {
|
} else {
|
||||||
// Normal wildcard expansion, not for completions.
|
// 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
|
// Maybe try a fuzzy match (#94) if nothing was found with the literal match. Respect
|
||||||
// EXPAND_NO_DIRECTORY_ABBREVIATIONS (issue #2413).
|
// EXPAND_NO_DIRECTORY_ABBREVIATIONS (issue #2413).
|
||||||
// Don't do fuzzy matches if the literal segment was valid (#3211)
|
// Don't do fuzzy matches if the literal segment was valid (#3211)
|
||||||
bool allow_fuzzy = (this->flags & (EXPAND_FUZZY_MATCH | EXPAND_NO_FUZZY_DIRECTORIES)) ==
|
bool allow_fuzzy = this->flags.get(expand_flag::EXPAND_FUZZY_MATCH) &&
|
||||||
EXPAND_FUZZY_MATCH;
|
!this->flags.get(expand_flag::EXPAND_NO_FUZZY_DIRECTORIES);
|
||||||
if (allow_fuzzy && this->resolved_completions->size() == before &&
|
if (allow_fuzzy && this->resolved_completions->size() == before &&
|
||||||
waccess(intermediate_dirpath, F_OK) != 0) {
|
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);
|
DIR *base_dir_fd = open_dir(base_dir);
|
||||||
if (base_dir_fd != NULL) {
|
if (base_dir_fd != NULL) {
|
||||||
this->expand_literal_intermediate_segment_with_fuzz(
|
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) {
|
expand_flags_t flags, std::vector<completion_t> *output) {
|
||||||
assert(output != NULL);
|
assert(output != NULL);
|
||||||
// Fuzzy matching only if we're doing completions.
|
// 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_flag::EXPAND_SPECIAL_FOR_CD requires expand_flag::DIRECTORIES_ONLY and
|
||||||
// EXPAND_NO_DESCRIPTIONS.
|
// expand_flag::EXPAND_FOR_COMPLETIONS and expand_flag::EXPAND_NO_DESCRIPTIONS.
|
||||||
assert(!(flags & EXPAND_SPECIAL_FOR_CD) ||
|
assert(!(flags.get(expand_flag::EXPAND_SPECIAL_FOR_CD)) ||
|
||||||
((flags & DIRECTORIES_ONLY) && (flags & EXPAND_FOR_COMPLETIONS) &&
|
((flags.get(expand_flag::DIRECTORIES_ONLY)) &&
|
||||||
(flags & EXPAND_NO_DESCRIPTIONS)));
|
(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
|
// 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
|
// truncated at any embedded nulls. We could fix this by passing around the size, etc. However
|
||||||
|
|||||||
Reference in New Issue
Block a user