Refactor how inputter handles script commands

Prior to this change, for bindings which have script commands, the
inputter would execute them directly. However an upcoming fix for #7483
will require more integration with the reader. Switch to a new model where
the reader passes in a function to use for executing script commands.
This commit is contained in:
ridiculousfish 2020-11-15 12:26:06 -08:00
parent 994f95b845
commit e9b683dee1
3 changed files with 36 additions and 20 deletions

View file

@ -356,16 +356,18 @@ void inputter_t::function_push_args(readline_cmd_t code) {
/// Perform the action of the specified binding. allow_commands controls whether fish commands /// Perform the action of the specified binding. allow_commands controls whether fish commands
/// should be executed, or should be deferred until later. /// should be executed, or should be deferred until later.
void inputter_t::mapping_execute(const input_mapping_t &m, bool allow_commands) { void inputter_t::mapping_execute(const input_mapping_t &m,
const command_handler_t &command_handler) {
// has_functions: there are functions that need to be put on the input queue // has_functions: there are functions that need to be put on the input queue
// has_commands: there are shell commands that need to be evaluated // has_commands: there are shell commands that need to be evaluated
bool has_commands = false, has_functions = false; bool has_commands = false, has_functions = false;
for (const wcstring &cmd : m.commands) { for (const wcstring &cmd : m.commands) {
if (input_function_get_code(cmd)) if (input_function_get_code(cmd)) {
has_functions = true; has_functions = true;
else } else {
has_commands = true; has_commands = true;
}
} }
// !has_functions && !has_commands: only set bind mode // !has_functions && !has_commands: only set bind mode
@ -374,7 +376,7 @@ void inputter_t::mapping_execute(const input_mapping_t &m, bool allow_commands)
return; return;
} }
if (has_commands && !allow_commands) { if (has_commands && !command_handler) {
// We don't want to run commands yet. Put the characters back and return check_exit. // We don't want to run commands yet. Put the characters back and return check_exit.
for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end; for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end;
++it) { ++it) {
@ -394,11 +396,7 @@ void inputter_t::mapping_execute(const input_mapping_t &m, bool allow_commands)
// //
// FIXME(snnw): if commands add stuff to input queue (e.g. commandline -f execute), we won't // FIXME(snnw): if commands add stuff to input queue (e.g. commandline -f execute), we won't
// see that until all other commands have also been run. // see that until all other commands have also been run.
auto last_statuses = parser_->get_last_statuses(); command_handler(m.commands);
for (const wcstring &cmd : m.commands) {
parser_->eval(cmd, io_chain_t{});
}
parser_->set_last_statuses(std::move(last_statuses));
event_queue_.push_front(char_event_type_t::check_exit); event_queue_.push_front(char_event_type_t::check_exit);
} else { } else {
// Invalid binding, mixed commands and functions. We would need to execute these one by // Invalid binding, mixed commands and functions. We would need to execute these one by
@ -467,9 +465,9 @@ maybe_t<input_mapping_t> inputter_t::find_mapping() {
return generic ? maybe_t<input_mapping_t>(*generic) : none(); return generic ? maybe_t<input_mapping_t>(*generic) : none();
} }
void inputter_t::mapping_execute_matching_or_generic(bool allow_commands) { void inputter_t::mapping_execute_matching_or_generic(const command_handler_t &command_handler) {
if (auto mapping = find_mapping()) { if (auto mapping = find_mapping()) {
mapping_execute(*mapping, allow_commands); mapping_execute(*mapping, command_handler);
} else { } else {
FLOGF(reader, L"no generic found, ignoring char..."); FLOGF(reader, L"no generic found, ignoring char...");
auto evt = event_queue_.readch(); auto evt = event_queue_.readch();
@ -500,7 +498,7 @@ char_event_t inputter_t::read_characters_no_readline() {
return evt_to_return; return evt_to_return;
} }
char_event_t inputter_t::readch(bool allow_commands) { char_event_t inputter_t::readch(const command_handler_t &command_handler) {
// Clear the interrupted flag. // Clear the interrupted flag.
reader_reset_interrupted(); reader_reset_interrupted();
// Search for sequence in mapping tables. // Search for sequence in mapping tables.
@ -552,7 +550,7 @@ char_event_t inputter_t::readch(bool allow_commands) {
return evt; return evt;
} else { } else {
event_queue_.push_front(evt); event_queue_.push_front(evt);
mapping_execute_matching_or_generic(allow_commands); mapping_execute_matching_or_generic(command_handler);
// Regarding allow_commands, we're in a loop, but if a fish command is executed, // Regarding allow_commands, we're in a loop, but if a fish command is executed,
// check_exit is unread, so the next pass through the loop we'll break out and return // check_exit is unread, so the next pass through the loop we'll break out and return
// it. // it.

View file

@ -36,10 +36,11 @@ class inputter_t {
/// to parse it. If no more input follows after the escape key, it is assumed to be an actual /// to parse it. If no more input follows after the escape key, it is assumed to be an actual
/// escape key press, and is returned as such. /// escape key press, and is returned as such.
/// ///
/// The argument determines whether fish commands are allowed to be run as bindings. If false, /// \p command_handler is used to run commands. If empty (in the std::function sense), when a
/// when a character is encountered that would invoke a fish command, it is unread and /// character is encountered that would invoke a fish command, it is unread and
/// char_event_type_t::check_exit is returned. /// char_event_type_t::check_exit is returned. Note the handler is not stored.
char_event_t readch(bool allow_commands = true); using command_handler_t = std::function<void(const wcstring_list_t &)>;
char_event_t readch(const command_handler_t &command_handler = {});
/// Enqueue a char event to the queue of unread characters that input_readch will return before /// Enqueue a char event to the queue of unread characters that input_readch will return before
/// actually reading from fd 0. /// actually reading from fd 0.
@ -64,8 +65,8 @@ class inputter_t {
void function_push_arg(wchar_t arg); void function_push_arg(wchar_t arg);
void function_push_args(readline_cmd_t code); void function_push_args(readline_cmd_t code);
void mapping_execute(const input_mapping_t &m, bool allow_commands); void mapping_execute(const input_mapping_t &m, const command_handler_t &command_handler);
void mapping_execute_matching_or_generic(bool allow_commands); void mapping_execute_matching_or_generic(const command_handler_t &command_handler);
bool mapping_is_match(const input_mapping_t &m); bool mapping_is_match(const input_mapping_t &m);
maybe_t<input_mapping_t> find_mapping(); maybe_t<input_mapping_t> find_mapping();
char_event_t read_characters_no_readline(); char_event_t read_characters_no_readline();

View file

@ -640,6 +640,7 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
void move_word(editable_line_t *el, bool move_right, bool erase, enum move_word_style_t style, void move_word(editable_line_t *el, bool move_right, bool erase, enum move_word_style_t style,
bool newv); bool newv);
void run_input_command_scripts(const wcstring_list_t &cmds);
maybe_t<char_event_t> read_normal_chars(readline_loop_state_t &rls); maybe_t<char_event_t> read_normal_chars(readline_loop_state_t &rls);
void handle_readline_command(readline_cmd_t cmd, readline_loop_state_t &rls); void handle_readline_command(readline_cmd_t cmd, readline_loop_state_t &rls);
@ -2717,15 +2718,31 @@ struct readline_loop_state_t {
size_t nchars{std::numeric_limits<size_t>::max()}; size_t nchars{std::numeric_limits<size_t>::max()};
}; };
/// Run a sequence of commands from an input binding.
void reader_data_t::run_input_command_scripts(const wcstring_list_t &cmds) {
auto last_statuses = parser().get_last_statuses();
for (const wcstring &cmd : cmds) {
parser().eval(cmd, io_chain_t{});
}
parser().set_last_statuses(std::move(last_statuses));
}
/// Read normal characters, inserting them into the command line. /// Read normal characters, inserting them into the command line.
/// \return the next unhandled event. /// \return the next unhandled event.
maybe_t<char_event_t> reader_data_t::read_normal_chars(readline_loop_state_t &rls) { maybe_t<char_event_t> reader_data_t::read_normal_chars(readline_loop_state_t &rls) {
maybe_t<char_event_t> event_needing_handling{}; maybe_t<char_event_t> event_needing_handling{};
wcstring accumulated_chars; wcstring accumulated_chars;
size_t limit = std::min(rls.nchars - command_line.size(), READAHEAD_MAX); size_t limit = std::min(rls.nchars - command_line.size(), READAHEAD_MAX);
using command_handler_t = inputter_t::command_handler_t;
command_handler_t normal_handler = [this](const wcstring_list_t &cmds) {
this->run_input_command_scripts(cmds);
};
command_handler_t empty_handler = {};
while (accumulated_chars.size() < limit) { while (accumulated_chars.size() < limit) {
bool allow_commands = (accumulated_chars.empty()); bool allow_commands = (accumulated_chars.empty());
auto evt = inputter.readch(allow_commands); auto evt = inputter.readch(allow_commands ? normal_handler : empty_handler);
if (!event_is_normal_char(evt) || !can_read(conf.in)) { if (!event_is_normal_char(evt) || !can_read(conf.in)) {
event_needing_handling = std::move(evt); event_needing_handling = std::move(evt);
break; break;