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:
ridiculousfish 2018-09-10 22:29:52 -07:00
parent 26fc705c07
commit 421fbdd52a
15 changed files with 127 additions and 76 deletions

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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.

View File

@ -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>");

View File

@ -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 */);

View File

@ -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();

View File

@ -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();

View File

@ -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 =

View File

@ -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;
}

View File

@ -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
}
}

View File

@ -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:

View File

@ -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());