mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-24 10:07:03 +08:00
Instantize env_get_pwd_slash
This requires threading environment_t through many places, such as completions and history. We introduce null_environment_t for when the environment isn't important.
This commit is contained in:
parent
26fc705c07
commit
421fbdd52a
|
@ -47,7 +47,7 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
dir_in = maybe_dir_in->as_string();
|
||||
}
|
||||
|
||||
if (!path_get_cdpath(dir_in, &dir, env_get_pwd_slash())) {
|
||||
if (!path_get_cdpath(dir_in, &dir, parser.vars().get_pwd_slash())) {
|
||||
if (errno == ENOTDIR) {
|
||||
streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir_in.c_str());
|
||||
} else if (errno == ENOENT) {
|
||||
|
|
|
@ -535,7 +535,7 @@ 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(), NULL))
|
||||
if (!expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), vars))
|
||||
return;
|
||||
|
||||
const wcstring wc = parse_util_unescape_wildcards(tmp);
|
||||
|
@ -656,7 +656,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
|||
expand_error_t result = expand_string(str_cmd, &this->completions,
|
||||
EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS |
|
||||
EXECUTABLES_ONLY | this->expand_flags(),
|
||||
NULL);
|
||||
vars, NULL);
|
||||
if (result != EXPAND_ERROR && this->wants_descriptions()) {
|
||||
this->complete_cmd_desc(str_cmd);
|
||||
}
|
||||
|
@ -668,7 +668,8 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
|||
expand_error_t ignore =
|
||||
// Append all matching directories
|
||||
expand_string(str_cmd, &this->completions,
|
||||
EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), NULL);
|
||||
EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), vars,
|
||||
NULL);
|
||||
UNUSED(ignore);
|
||||
}
|
||||
|
||||
|
@ -740,7 +741,7 @@ void completer_t::complete_from_args(const wcstring &str, const wcstring &args,
|
|||
}
|
||||
|
||||
std::vector<completion_t> possible_comp;
|
||||
parser_t::expand_argument_list(args, eflags, &possible_comp);
|
||||
parser_t::expand_argument_list(args, eflags, vars, &possible_comp);
|
||||
|
||||
if (!is_autosuggest) {
|
||||
proc_pop_interactive();
|
||||
|
@ -1079,7 +1080,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file,
|
|||
// See #4954.
|
||||
const wcstring sep_string = wcstring(str, sep_index + 1);
|
||||
std::vector<completion_t> local_completions;
|
||||
if (expand_string(sep_string, &local_completions, flags, NULL) == EXPAND_ERROR) {
|
||||
if (expand_string(sep_string, &local_completions, flags, vars, NULL) == EXPAND_ERROR) {
|
||||
debug(3, L"Error while expanding string '%ls'", sep_string.c_str());
|
||||
}
|
||||
|
||||
|
@ -1098,7 +1099,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file,
|
|||
// consider relaxing this if there was a preceding double-dash argument.
|
||||
if (string_prefixes_string(L"-", str)) flags &= ~EXPAND_FUZZY_MATCH;
|
||||
|
||||
if (expand_string(str, &this->completions, flags, NULL) == EXPAND_ERROR) {
|
||||
if (expand_string(str, &this->completions, flags, vars, NULL) == EXPAND_ERROR) {
|
||||
debug(3, L"Error while expanding string '%ls'", str.c_str());
|
||||
}
|
||||
}
|
||||
|
|
11
src/env.cpp
11
src/env.cpp
|
@ -677,7 +677,7 @@ void env_stack_t::set_read_limit() {
|
|||
|
||||
void env_stack_t::mark_changed_exported() { vars_stack().mark_changed_exported(); }
|
||||
|
||||
wcstring env_stack_t::get_pwd_slash() {
|
||||
wcstring environment_t::get_pwd_slash() const {
|
||||
// Return "/" if PWD is missing.
|
||||
// See https://github.com/fish-shell/fish-shell/issues/5080
|
||||
auto pwd_var = env_get(L"PWD");
|
||||
|
@ -1433,8 +1433,6 @@ int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) {
|
|||
|
||||
void env_universal_barrier() { env_stack_t::principal().universal_barrier(); }
|
||||
|
||||
wcstring env_get_pwd_slash() { return env_stack_t::principal().get_pwd_slash(); }
|
||||
|
||||
void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); }
|
||||
|
||||
/// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported
|
||||
|
@ -1606,6 +1604,13 @@ void env_stack_t::set_argv(const wchar_t *const *argv) {
|
|||
environment_t::~environment_t() = default;
|
||||
env_stack_t::~env_stack_t() = default;
|
||||
|
||||
null_environment_t::null_environment_t() = default;
|
||||
null_environment_t::~null_environment_t() = default;
|
||||
maybe_t<env_var_t> null_environment_t::get(const wcstring &key, env_mode_flags_t mode) const {
|
||||
return none();
|
||||
}
|
||||
wcstring_list_t null_environment_t::get_names(int flags) const { return {}; }
|
||||
|
||||
env_stack_t &env_stack_t::principal() {
|
||||
static env_stack_t s_principal;
|
||||
return s_principal;
|
||||
|
|
19
src/env.h
19
src/env.h
|
@ -141,6 +141,19 @@ class environment_t {
|
|||
env_mode_flags_t mode = ENV_DEFAULT) const = 0;
|
||||
virtual wcstring_list_t get_names(int flags) const = 0;
|
||||
virtual ~environment_t();
|
||||
|
||||
/// Returns the PWD with a terminating slash.
|
||||
wcstring get_pwd_slash() const;
|
||||
};
|
||||
|
||||
/// The null environment contains nothing.
|
||||
class null_environment_t : public environment_t {
|
||||
public:
|
||||
null_environment_t();
|
||||
~null_environment_t() override;
|
||||
|
||||
maybe_t<env_var_t> get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override;
|
||||
wcstring_list_t get_names(int flags) const override;
|
||||
};
|
||||
|
||||
/// Gets the variable with the specified name, or none() if it does not exist.
|
||||
|
@ -155,9 +168,6 @@ int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val);
|
|||
/// Synchronizes all universal variable changes: writes everything out, reads stuff in.
|
||||
void env_universal_barrier();
|
||||
|
||||
/// Returns the PWD with a terminating slash.
|
||||
wcstring env_get_pwd_slash();
|
||||
|
||||
/// Update the read_byte_limit variable.
|
||||
void env_set_read_limit();
|
||||
|
||||
|
@ -229,9 +239,6 @@ class env_stack_t : public environment_t {
|
|||
/// Sets up argv as the given null terminated array of strings.
|
||||
void set_argv(const wchar_t *const *argv);
|
||||
|
||||
/// Returns the PWD with a terminating slash.
|
||||
wcstring get_pwd_slash();
|
||||
|
||||
/// Update the read_byte_limit variable.
|
||||
void set_read_limit();
|
||||
|
||||
|
|
|
@ -883,15 +883,17 @@ static void remove_internal_separator(wcstring *str, bool conv) {
|
|||
}
|
||||
|
||||
/// A stage in string expansion is represented as a function that takes an input and returns a list
|
||||
/// of output (by reference). We get flags and errors. It may return an error; if so expansion
|
||||
/// of output (by reference). We get flags, vars and errors. It may return an error; if so expansion
|
||||
/// halts.
|
||||
typedef expand_error_t (*expand_stage_t)(wcstring input, //!OCLINT(unused param)
|
||||
std::vector<completion_t> *out, //!OCLINT(unused param)
|
||||
expand_flags_t flags, //!OCLINT(unused param)
|
||||
const environment_t &vars, //!OCLINT(unused param)
|
||||
parse_error_list_t *errors); //!OCLINT(unused param)
|
||||
|
||||
static expand_error_t expand_stage_cmdsubst(wcstring input, std::vector<completion_t> *out,
|
||||
expand_flags_t flags, parse_error_list_t *errors) {
|
||||
expand_flags_t flags, const environment_t &vars,
|
||||
parse_error_list_t *errors) {
|
||||
if (EXPAND_SKIP_CMDSUBST & flags) {
|
||||
wchar_t *begin, *end;
|
||||
if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) == 0) {
|
||||
|
@ -910,7 +912,8 @@ static expand_error_t expand_stage_cmdsubst(wcstring input, std::vector<completi
|
|||
}
|
||||
|
||||
static expand_error_t expand_stage_variables(wcstring input, std::vector<completion_t> *out,
|
||||
expand_flags_t flags, parse_error_list_t *errors) {
|
||||
expand_flags_t flags, const environment_t &vars,
|
||||
parse_error_list_t *errors) {
|
||||
// We accept incomplete strings here, since complete uses expand_string to expand incomplete
|
||||
// strings from the commandline.
|
||||
wcstring next;
|
||||
|
@ -933,12 +936,14 @@ static expand_error_t expand_stage_variables(wcstring input, std::vector<complet
|
|||
}
|
||||
|
||||
static expand_error_t expand_stage_braces(wcstring input, std::vector<completion_t> *out,
|
||||
expand_flags_t flags, parse_error_list_t *errors) {
|
||||
expand_flags_t flags, const environment_t &vars,
|
||||
parse_error_list_t *errors) {
|
||||
return expand_braces(input, flags, out, errors);
|
||||
}
|
||||
|
||||
static expand_error_t expand_stage_home_and_self(wcstring input, std::vector<completion_t> *out,
|
||||
expand_flags_t flags, parse_error_list_t *errors) {
|
||||
expand_flags_t flags, const environment_t &vars,
|
||||
parse_error_list_t *errors) {
|
||||
(void)errors;
|
||||
if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) {
|
||||
expand_home_directory(input);
|
||||
|
@ -948,8 +953,8 @@ static expand_error_t expand_stage_home_and_self(wcstring input, std::vector<com
|
|||
return EXPAND_OK;
|
||||
}
|
||||
|
||||
static expand_error_t expand_stage_wildcards(wcstring path_to_expand,
|
||||
std::vector<completion_t> *out, expand_flags_t flags,
|
||||
static expand_error_t expand_stage_wildcards(wcstring path_to_expand, std::vector<completion_t> *out,
|
||||
expand_flags_t flags, const environment_t &vars,
|
||||
parse_error_list_t *errors) {
|
||||
UNUSED(errors);
|
||||
expand_error_t result = EXPAND_OK;
|
||||
|
@ -968,7 +973,7 @@ static expand_error_t expand_stage_wildcards(wcstring path_to_expand,
|
|||
//
|
||||
// So we're going to treat this input as a file path. Compute the "working directories",
|
||||
// which may be CDPATH if the special flag is set.
|
||||
const wcstring working_dir = env_get_pwd_slash();
|
||||
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);
|
||||
|
@ -1042,7 +1047,8 @@ static expand_error_t expand_stage_wildcards(wcstring path_to_expand,
|
|||
}
|
||||
|
||||
expand_error_t expand_string(wcstring input, std::vector<completion_t> *out_completions,
|
||||
expand_flags_t flags, parse_error_list_t *errors) {
|
||||
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)) {
|
||||
append_completion(out_completions, std::move(input));
|
||||
|
@ -1064,7 +1070,7 @@ expand_error_t expand_string(wcstring input, std::vector<completion_t> *out_comp
|
|||
for (size_t i = 0; total_result != EXPAND_ERROR && i < completions.size(); i++) {
|
||||
wcstring &next = completions.at(i).completion;
|
||||
expand_error_t this_result =
|
||||
stages[stage_idx](std::move(next), &output_storage, flags, errors);
|
||||
stages[stage_idx](std::move(next), &output_storage, flags, vars, errors);
|
||||
// If this_result was no match, but total_result is that we have a match, then don't
|
||||
// change it.
|
||||
if (!(this_result == EXPAND_WILDCARD_NO_MATCH &&
|
||||
|
@ -1090,14 +1096,15 @@ expand_error_t expand_string(wcstring input, std::vector<completion_t> *out_comp
|
|||
return total_result;
|
||||
}
|
||||
|
||||
bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *errors) {
|
||||
bool expand_one(wcstring &string, expand_flags_t flags, const environment_t &vars,
|
||||
parse_error_list_t *errors) {
|
||||
std::vector<completion_t> completions;
|
||||
|
||||
if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(string)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, errors) &&
|
||||
if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, vars, errors) &&
|
||||
completions.size() == 1) {
|
||||
string = std::move(completions.at(0).completion);
|
||||
return true;
|
||||
|
@ -1105,8 +1112,9 @@ bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *erro
|
|||
return false;
|
||||
}
|
||||
|
||||
expand_error_t expand_to_command_and_args(const wcstring &instr, wcstring *out_cmd,
|
||||
wcstring_list_t *out_args, parse_error_list_t *errors) {
|
||||
expand_error_t expand_to_command_and_args(const wcstring &instr, const environment_t &vars,
|
||||
wcstring *out_cmd, wcstring_list_t *out_args,
|
||||
parse_error_list_t *errors) {
|
||||
// Fast path.
|
||||
if (expand_is_clean(instr)) {
|
||||
*out_cmd = instr;
|
||||
|
@ -1114,9 +1122,9 @@ expand_error_t expand_to_command_and_args(const wcstring &instr, wcstring *out_c
|
|||
}
|
||||
|
||||
std::vector<completion_t> completions;
|
||||
expand_error_t expand_err =
|
||||
expand_string(instr, &completions,
|
||||
EXPAND_SKIP_CMDSUBST | EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_JOBS, errors);
|
||||
expand_error_t expand_err = expand_string(
|
||||
instr, &completions, EXPAND_SKIP_CMDSUBST | EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_JOBS, vars,
|
||||
errors);
|
||||
if (expand_err == EXPAND_OK || expand_err == EXPAND_WILDCARD_MATCH) {
|
||||
// The first completion is the command, any remaning are arguments.
|
||||
bool first = true;
|
||||
|
|
12
src/expand.h
12
src/expand.h
|
@ -17,6 +17,7 @@
|
|||
#include "parse_constants.h"
|
||||
|
||||
class env_var_t;
|
||||
class environment_t;
|
||||
|
||||
enum {
|
||||
/// Flag specifying that cmdsubst expansion should be skipped.
|
||||
|
@ -111,13 +112,15 @@ enum expand_error_t {
|
|||
/// \param output The list to which the result will be appended.
|
||||
/// \param flags Specifies if any expansion pass should be skipped. Legal values are any combination
|
||||
/// of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS
|
||||
/// \param vars variables used during expansion.
|
||||
/// \param errors Resulting errors, or NULL to ignore
|
||||
///
|
||||
/// \return One of EXPAND_OK, EXPAND_ERROR, EXPAND_WILDCARD_MATCH and EXPAND_WILDCARD_NO_MATCH.
|
||||
/// EXPAND_WILDCARD_NO_MATCH and EXPAND_WILDCARD_MATCH are normal exit conditions used only on
|
||||
/// strings containing wildcards to tell if the wildcard produced any matches.
|
||||
__warn_unused expand_error_t expand_string(wcstring input, std::vector<completion_t> *output,
|
||||
expand_flags_t flags, parse_error_list_t *errors);
|
||||
expand_flags_t flags, const environment_t &vars,
|
||||
parse_error_list_t *errors);
|
||||
|
||||
/// expand_one is identical to expand_string, except it will fail if in expands to more than one
|
||||
/// string. This is used for expanding command names.
|
||||
|
@ -128,7 +131,8 @@ __warn_unused expand_error_t expand_string(wcstring input, std::vector<completio
|
|||
/// \param errors Resulting errors, or NULL to ignore
|
||||
///
|
||||
/// \return Whether expansion succeded
|
||||
bool expand_one(wcstring &inout_str, expand_flags_t flags, parse_error_list_t *errors = NULL);
|
||||
bool expand_one(wcstring &inout_str, expand_flags_t flags, const environment_t &vars,
|
||||
parse_error_list_t *errors = NULL);
|
||||
|
||||
/// Expand a command string like $HOME/bin/cmd into a command and list of arguments.
|
||||
/// Return the command and arguments by reference.
|
||||
|
@ -136,8 +140,8 @@ bool expand_one(wcstring &inout_str, expand_flags_t flags, parse_error_list_t *e
|
|||
/// that API does not distinguish between expansion resulting in an empty command (''), and
|
||||
/// expansion resulting in no command (e.g. unset variable).
|
||||
// \return an expand error.
|
||||
expand_error_t expand_to_command_and_args(const wcstring &instr, wcstring *out_cmd,
|
||||
wcstring_list_t *out_args,
|
||||
expand_error_t expand_to_command_and_args(const wcstring &instr, const environment_t &vars,
|
||||
wcstring *out_cmd, wcstring_list_t *out_args,
|
||||
parse_error_list_t *errors = NULL);
|
||||
|
||||
/// Convert the variable value to a human readable form, i.e. escape things, handle arrays, etc.
|
||||
|
|
|
@ -911,7 +911,7 @@ static void test_parser() {
|
|||
|
||||
say(L"Testing eval_args");
|
||||
completion_list_t comps;
|
||||
parser_t::expand_argument_list(L"alpha 'beta gamma' delta", 0, &comps);
|
||||
parser_t::expand_argument_list(L"alpha 'beta gamma' delta", 0, 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");
|
||||
|
@ -1500,6 +1500,19 @@ static void test_lru() {
|
|||
do_test(cache.evicted.size() == size_t(total_nodes));
|
||||
}
|
||||
|
||||
/// A crappy environment_t that only knows about PWD.
|
||||
struct pwd_environment_t : public environment_t {
|
||||
virtual maybe_t<env_var_t> get(const wcstring &key,
|
||||
env_mode_flags_t mode = ENV_DEFAULT) const override {
|
||||
if (key == L"PWD") {
|
||||
return env_var_t{wgetcwd(), 0};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
wcstring_list_t get_names(int flags) const override { return {L"PWD"}; }
|
||||
};
|
||||
|
||||
/// Perform parameter expansion and test if the output equals the zero-terminated parameter list
|
||||
/// supplied.
|
||||
///
|
||||
|
@ -1515,7 +1528,7 @@ static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) {
|
|||
wchar_t *arg;
|
||||
parse_error_list_t errors;
|
||||
|
||||
if (expand_string(in, &output, flags, &errors) == EXPAND_ERROR) {
|
||||
if (expand_string(in, &output, flags, pwd_environment_t{}, &errors) == EXPAND_ERROR) {
|
||||
if (errors.empty()) {
|
||||
err(L"Bug: Parse error reported but no error text found.");
|
||||
} else {
|
||||
|
@ -2173,7 +2186,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, &comps);
|
||||
parser_t::expand_argument_list(str, 0, null_environment_t{}, &comps);
|
||||
for (completion_list_t::const_iterator it = comps.begin(), end = comps.end(); it != end; ++it) {
|
||||
argv.push_back(it->completion);
|
||||
}
|
||||
|
@ -2341,6 +2354,9 @@ static void test_complete() {
|
|||
|
||||
maybe_t<env_var_t> get(const wcstring &key,
|
||||
env_mode_flags_t mode = ENV_DEFAULT) const override {
|
||||
if (key == L"PWD") {
|
||||
return env_var_t{wgetcwd(), 0};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
@ -2604,7 +2620,7 @@ static void test_completion_insertions() {
|
|||
static void perform_one_autosuggestion_cd_test(const wcstring &command, const wcstring &expected,
|
||||
long line) {
|
||||
std::vector<completion_t> comps;
|
||||
complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, env_vars_snapshot_t{});
|
||||
complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, pwd_environment_t{});
|
||||
|
||||
bool expects_error = (expected == L"<error>");
|
||||
|
||||
|
|
|
@ -243,11 +243,11 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d
|
|||
// appropriately for commands. If we succeed, return true.
|
||||
static bool plain_statement_get_expanded_command(const wcstring &src,
|
||||
tnode_t<g::plain_statement> stmt,
|
||||
wcstring *out_cmd) {
|
||||
const environment_t &vars, wcstring *out_cmd) {
|
||||
// Get the command. Try expanding it. If we cannot, it's an error.
|
||||
maybe_t<wcstring> cmd = command_for_plain_statement(stmt, src);
|
||||
if (!cmd) return false;
|
||||
expand_error_t err = expand_to_command_and_args(*cmd, out_cmd, nullptr);
|
||||
expand_error_t err = expand_to_command_and_args(*cmd, vars, out_cmd, nullptr);
|
||||
return err == EXPAND_OK || err == EXPAND_WILDCARD_MATCH;
|
||||
}
|
||||
|
||||
|
@ -310,8 +310,8 @@ static bool has_expand_reserved(const wcstring &str) {
|
|||
|
||||
// Parse a command line. Return by reference the last command, and the last argument to that command
|
||||
// (as a string), if any. This is used by autosuggestions.
|
||||
static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expanded_command,
|
||||
wcstring *out_last_arg) {
|
||||
static bool autosuggest_parse_command(const wcstring &buff, const environment_t &vars,
|
||||
wcstring *out_expanded_command, wcstring *out_last_arg) {
|
||||
// Parse the buffer.
|
||||
parse_node_tree_t parse_tree;
|
||||
parse_tree_from_string(buff,
|
||||
|
@ -321,7 +321,7 @@ static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expand
|
|||
// Find the last statement.
|
||||
auto last_statement = parse_tree.find_last_node<g::plain_statement>();
|
||||
if (last_statement &&
|
||||
plain_statement_get_expanded_command(buff, last_statement, out_expanded_command)) {
|
||||
plain_statement_get_expanded_command(buff, last_statement, vars, out_expanded_command)) {
|
||||
// Find the last argument. If we don't get one, return an invalid node.
|
||||
if (auto last_arg = parse_tree.find_last_node<g::argument>(last_statement)) {
|
||||
*out_last_arg = last_arg.get_source(buff);
|
||||
|
@ -341,11 +341,11 @@ bool autosuggest_validate_from_history(const history_item_t &item,
|
|||
// Parse the string.
|
||||
wcstring parsed_command;
|
||||
wcstring cd_dir;
|
||||
if (!autosuggest_parse_command(item.str(), &parsed_command, &cd_dir)) return false;
|
||||
if (!autosuggest_parse_command(item.str(), vars, &parsed_command, &cd_dir)) return false;
|
||||
|
||||
if (parsed_command == L"cd" && !cd_dir.empty()) {
|
||||
// We can possibly handle this specially.
|
||||
if (expand_one(cd_dir, EXPAND_SKIP_CMDSUBST)) {
|
||||
if (expand_one(cd_dir, EXPAND_SKIP_CMDSUBST, vars)) {
|
||||
handled = true;
|
||||
bool is_help =
|
||||
string_prefixes_string(cd_dir, L"--help") || string_prefixes_string(cd_dir, L"-h");
|
||||
|
@ -823,7 +823,7 @@ bool highlighter_t::is_cd(tnode_t<g::plain_statement> stmt) const {
|
|||
bool cmd_is_cd = false;
|
||||
if (this->io_ok && stmt.has_source()) {
|
||||
wcstring cmd_str;
|
||||
if (plain_statement_get_expanded_command(this->buff, stmt, &cmd_str)) {
|
||||
if (plain_statement_get_expanded_command(this->buff, stmt, vars, &cmd_str)) {
|
||||
cmd_is_cd = (cmd_str == L"cd");
|
||||
}
|
||||
}
|
||||
|
@ -840,7 +840,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)) {
|
||||
if (expand_one(param, 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 &&
|
||||
|
@ -883,7 +883,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)) {
|
||||
} else if (!expand_one(target, EXPAND_SKIP_CMDSUBST, vars)) {
|
||||
// Could not be expanded.
|
||||
target_is_valid = false;
|
||||
} else {
|
||||
|
@ -1117,7 +1117,8 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
|
|||
wcstring expanded_cmd;
|
||||
// Check to see if the command is valid.
|
||||
// Try expanding it. If we cannot, it's an error.
|
||||
bool expanded = plain_statement_get_expanded_command(buff, stmt, &expanded_cmd);
|
||||
bool expanded =
|
||||
plain_statement_get_expanded_command(buff, stmt, vars, &expanded_cmd);
|
||||
if (expanded && !has_expand_reserved(expanded_cmd)) {
|
||||
is_valid_cmd =
|
||||
command_is_valid(expanded_cmd, decoration, working_directory, vars);
|
||||
|
@ -1200,7 +1201,7 @@ void highlight_shell(const wcstring &buff, std::vector<highlight_spec_t> &color,
|
|||
UNUSED(error);
|
||||
// Do something sucky and get the current working directory on this background thread. This
|
||||
// should really be passed in.
|
||||
const wcstring working_directory = env_get_pwd_slash();
|
||||
const wcstring working_directory = vars.get_pwd_slash();
|
||||
|
||||
// Highlight it!
|
||||
highlighter_t highlighter(buff, pos, vars, working_directory, true /* can do IO */);
|
||||
|
@ -1212,7 +1213,7 @@ void highlight_shell_no_io(const wcstring &buff, std::vector<highlight_spec_t> &
|
|||
UNUSED(error);
|
||||
// Do something sucky and get the current working directory on this background thread. This
|
||||
// should really be passed in.
|
||||
const wcstring working_directory = env_get_pwd_slash();
|
||||
const wcstring working_directory = vars.get_pwd_slash();
|
||||
|
||||
// Highlight it!
|
||||
highlighter_t highlighter(buff, pos, vars, working_directory, false /* no IO allowed */);
|
||||
|
|
|
@ -1918,7 +1918,8 @@ static bool string_could_be_path(const wcstring &potential_path) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void history_t::add_pending_with_file_detection(const wcstring &str) {
|
||||
void history_t::add_pending_with_file_detection(const wcstring &str,
|
||||
const wcstring &working_dir_slash) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
// Find all arguments that look like they could be file paths.
|
||||
|
@ -1967,8 +1968,7 @@ void history_t::add_pending_with_file_detection(const wcstring &str) {
|
|||
|
||||
// Check for which paths are valid on a background thread,
|
||||
// then on the main thread update our history item
|
||||
const wcstring wd = env_get_pwd_slash();
|
||||
iothread_perform([=]() { return valid_paths(potential_paths, wd); },
|
||||
iothread_perform([=]() { return valid_paths(potential_paths, working_dir_slash); },
|
||||
[=](path_list_t validated_paths) {
|
||||
this->set_valid_file_paths(validated_paths, identifier);
|
||||
this->enable_automatic_saving();
|
||||
|
|
|
@ -229,7 +229,7 @@ class history_t {
|
|||
|
||||
// Add a new pending history item to the end, and then begin file detection on the items to
|
||||
// determine which arguments are paths
|
||||
void add_pending_with_file_detection(const wcstring &str);
|
||||
void add_pending_with_file_detection(const wcstring &str, const wcstring &working_dir_slash);
|
||||
|
||||
// Resolves any pending history items, so that they may be returned in history searches.
|
||||
void resolve_pending();
|
||||
|
|
|
@ -121,13 +121,14 @@ tnode_t<g::plain_statement> parse_execution_context_t::infinite_recursive_statem
|
|||
// are not infinite recursion. In particular that is what enables 'wrapper functions'.
|
||||
tnode_t<g::statement> statement = first_job.child<0>();
|
||||
tnode_t<g::job_continuation> continuation = first_job.child<1>();
|
||||
const null_environment_t nullenv{};
|
||||
while (statement) {
|
||||
tnode_t<g::plain_statement> plain_statement =
|
||||
statement.try_get_child<g::decorated_statement, 0>()
|
||||
.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, NULL) &&
|
||||
if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, nullenv) &&
|
||||
cmd == forbidden_function_name) {
|
||||
// This is it.
|
||||
infinite_recursive_statement = plain_statement;
|
||||
|
@ -371,7 +372,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, NULL)) {
|
||||
if (!expand_one(for_var_name, 0, parser->vars())) {
|
||||
report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str());
|
||||
return parse_execution_errored;
|
||||
}
|
||||
|
@ -438,8 +439,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;
|
||||
int expand_ret =
|
||||
expand_string(switch_value, &switch_values_expanded, EXPAND_NO_DESCRIPTIONS, &errors);
|
||||
int expand_ret = expand_string(switch_value, &switch_values_expanded, EXPAND_NO_DESCRIPTIONS,
|
||||
parser->vars(), &errors);
|
||||
parse_error_offset_source_start(&errors, switch_value_n.source_range()->start);
|
||||
|
||||
switch (expand_ret) {
|
||||
|
@ -722,7 +723,8 @@ parse_execution_result_t parse_execution_context_t::expand_command(
|
|||
wcstring_list_t args;
|
||||
|
||||
// Expand the string to produce completions, and report errors.
|
||||
expand_error_t expand_err = expand_to_command_and_args(unexp_cmd, out_cmd, out_args, &errors);
|
||||
expand_error_t expand_err =
|
||||
expand_to_command_and_args(unexp_cmd, parser->vars(), out_cmd, out_args, &errors);
|
||||
if (expand_err == EXPAND_ERROR) {
|
||||
proc_set_last_status(STATUS_ILLEGAL_CMD);
|
||||
return report_errors(errors);
|
||||
|
@ -813,7 +815,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process(
|
|||
// Ok, no arguments or redirections; check to see if the command is a directory.
|
||||
wcstring implicit_cd_path;
|
||||
use_implicit_cd =
|
||||
path_can_be_implicit_cd(cmd, env_get_pwd_slash(), &implicit_cd_path);
|
||||
path_can_be_implicit_cd(cmd, parser->vars().get_pwd_slash(), &implicit_cd_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -883,7 +885,8 @@ parse_execution_result_t parse_execution_context_t::expand_arguments_from_nodes(
|
|||
// Expand this string.
|
||||
parse_error_list_t errors;
|
||||
arg_expanded.clear();
|
||||
int expand_ret = expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, &errors);
|
||||
int expand_ret =
|
||||
expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, parser->vars(), &errors);
|
||||
parse_error_offset_source_start(&errors, arg_node.source_range()->start);
|
||||
switch (expand_ret) {
|
||||
case EXPAND_ERROR: {
|
||||
|
@ -931,7 +934,8 @@ bool parse_execution_context_t::determine_io_chain(tnode_t<g::arguments_or_redir
|
|||
auto redirect_type = redirection_type(redirect_node, pstree->src, &source_fd, &target);
|
||||
|
||||
// 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, NULL);
|
||||
bool target_expanded =
|
||||
expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0, parser->vars());
|
||||
if (!target_expanded || target.empty()) {
|
||||
// TODO: Improve this error message.
|
||||
errored =
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "future_feature_flags.h"
|
||||
#include "parse_constants.h"
|
||||
#include "parse_util.h"
|
||||
#include "parser.h"
|
||||
#include "tnode.h"
|
||||
#include "tokenizer.h"
|
||||
#include "util.h"
|
||||
|
@ -1131,8 +1132,8 @@ static bool detect_errors_in_plain_statement(const wcstring &buff_src,
|
|||
if (maybe_t<wcstring> unexp_command = command_for_plain_statement(pst, buff_src)) {
|
||||
wcstring command;
|
||||
// Check that we can expand the command.
|
||||
if (expand_to_command_and_args(*unexp_command, &command, nullptr, parse_errors) ==
|
||||
EXPAND_ERROR) {
|
||||
if (expand_to_command_and_args(*unexp_command, null_environment_t{}, &command, nullptr,
|
||||
parse_errors) == EXPAND_ERROR) {
|
||||
errored = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -314,6 +314,7 @@ void parser_t::emit_profiling(const char *path) const {
|
|||
}
|
||||
|
||||
void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t eflags,
|
||||
const environment_t &vars,
|
||||
std::vector<completion_t> *output_arg_list) {
|
||||
assert(output_arg_list != NULL);
|
||||
|
||||
|
@ -330,7 +331,8 @@ void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t
|
|||
tnode_t<grammar::freestanding_argument_list> arg_list(&tree, &tree.at(0));
|
||||
while (auto arg = arg_list.next_in_list<grammar::argument>()) {
|
||||
const wcstring arg_src = arg.get_source(arg_list_src);
|
||||
if (expand_string(arg_src, output_arg_list, eflags, NULL) == EXPAND_ERROR) {
|
||||
if (expand_string(arg_src, output_arg_list, eflags, vars, NULL /* errors */) ==
|
||||
EXPAND_ERROR) {
|
||||
break; // failed to expand a string
|
||||
}
|
||||
}
|
||||
|
|
|
@ -247,7 +247,7 @@ class parser_t {
|
|||
/// \param flags Some expand flags to use
|
||||
/// \param output List to insert output into
|
||||
static void expand_argument_list(const wcstring &arg_src, expand_flags_t flags,
|
||||
std::vector<completion_t> *output);
|
||||
const environment_t &vars, std::vector<completion_t> *output);
|
||||
|
||||
/// Returns a string describing the current parser position in the format 'FILENAME (line
|
||||
/// LINE_NUMBER): LINE'. Example:
|
||||
|
|
|
@ -1277,10 +1277,10 @@ struct autosuggestion_result_t {
|
|||
// on a background thread) to determine the autosuggestion
|
||||
static std::function<autosuggestion_result_t(void)> get_autosuggestion_performer(
|
||||
const wcstring &search_string, size_t cursor_pos, history_t *history) {
|
||||
const auto &parser_vars = parser_t::principal_parser().vars();
|
||||
const unsigned int generation_count = read_generation_count();
|
||||
const wcstring working_directory(env_get_pwd_slash());
|
||||
env_vars_snapshot_t vars(parser_t::principal_parser().vars(),
|
||||
env_vars_snapshot_t::highlighting_keys);
|
||||
const wcstring working_directory = parser_vars.get_pwd_slash();
|
||||
env_vars_snapshot_t vars(vars, env_vars_snapshot_t::highlighting_keys);
|
||||
// TODO: suspicious use of 'history' here
|
||||
// This is safe because histories are immortal, but perhaps
|
||||
// this should use shared_ptr
|
||||
|
@ -2473,6 +2473,8 @@ const wchar_t *reader_readline(int nchars) {
|
|||
s_reset(&data->screen, screen_reset_abandon_line);
|
||||
reader_repaint();
|
||||
|
||||
const auto &vars = parser_t::principal_parser().vars();
|
||||
|
||||
// Get the current terminal modes. These will be restored when the function returns.
|
||||
if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output();
|
||||
// Set the new modes.
|
||||
|
@ -2685,8 +2687,7 @@ const wchar_t *reader_readline(int nchars) {
|
|||
complete_flags_t complete_flags = COMPLETION_REQUEST_DEFAULT |
|
||||
COMPLETION_REQUEST_DESCRIPTIONS |
|
||||
COMPLETION_REQUEST_FUZZY_MATCH;
|
||||
data->complete_func(buffcpy, &comp, complete_flags,
|
||||
parser_t::principal_parser().vars());
|
||||
data->complete_func(buffcpy, &comp, complete_flags, vars);
|
||||
|
||||
// Munge our completions.
|
||||
completions_sort_and_prioritize(&comp);
|
||||
|
@ -2893,7 +2894,8 @@ const wchar_t *reader_readline(int nchars) {
|
|||
// space.
|
||||
const editable_line_t *el = &data->command_line;
|
||||
if (data->history != NULL && !el->empty() && el->text.at(0) != L' ') {
|
||||
data->history->add_pending_with_file_detection(el->text);
|
||||
data->history->add_pending_with_file_detection(el->text,
|
||||
vars.get_pwd_slash());
|
||||
}
|
||||
finished = 1;
|
||||
update_buff_pos(&data->command_line, data->command_line.size());
|
||||
|
|
Loading…
Reference in New Issue
Block a user