diff --git a/src/input.cpp b/src/input.cpp index 9786f082b..27d8bfc1d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -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 /// 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_commands: there are shell commands that need to be evaluated bool has_commands = false, has_functions = false; for (const wcstring &cmd : m.commands) { - if (input_function_get_code(cmd)) + if (input_function_get_code(cmd)) { has_functions = true; - else + } else { has_commands = true; + } } // !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; } - 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. for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end; ++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 // see that until all other commands have also been run. - auto last_statuses = parser_->get_last_statuses(); - for (const wcstring &cmd : m.commands) { - parser_->eval(cmd, io_chain_t{}); - } - parser_->set_last_statuses(std::move(last_statuses)); + command_handler(m.commands); event_queue_.push_front(char_event_type_t::check_exit); } else { // Invalid binding, mixed commands and functions. We would need to execute these one by @@ -467,9 +465,9 @@ maybe_t inputter_t::find_mapping() { return generic ? maybe_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()) { - mapping_execute(*mapping, allow_commands); + mapping_execute(*mapping, command_handler); } else { FLOGF(reader, L"no generic found, ignoring char..."); auto evt = event_queue_.readch(); @@ -500,7 +498,7 @@ char_event_t inputter_t::read_characters_no_readline() { 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. reader_reset_interrupted(); // Search for sequence in mapping tables. @@ -552,7 +550,7 @@ char_event_t inputter_t::readch(bool allow_commands) { return evt; } else { 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, // check_exit is unread, so the next pass through the loop we'll break out and return // it. diff --git a/src/input.h b/src/input.h index e6232fbb7..3f10208fe 100644 --- a/src/input.h +++ b/src/input.h @@ -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 /// escape key press, and is returned as such. /// - /// The argument determines whether fish commands are allowed to be run as bindings. If false, - /// when a character is encountered that would invoke a fish command, it is unread and - /// char_event_type_t::check_exit is returned. - char_event_t readch(bool allow_commands = true); + /// \p command_handler is used to run commands. If empty (in the std::function sense), when a + /// character is encountered that would invoke a fish command, it is unread and + /// char_event_type_t::check_exit is returned. Note the handler is not stored. + using command_handler_t = std::function; + 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 /// actually reading from fd 0. @@ -64,8 +65,8 @@ class inputter_t { void function_push_arg(wchar_t arg); void function_push_args(readline_cmd_t code); - void mapping_execute(const input_mapping_t &m, bool allow_commands); - void mapping_execute_matching_or_generic(bool allow_commands); + void mapping_execute(const input_mapping_t &m, const command_handler_t &command_handler); + void mapping_execute_matching_or_generic(const command_handler_t &command_handler); bool mapping_is_match(const input_mapping_t &m); maybe_t find_mapping(); char_event_t read_characters_no_readline(); diff --git a/src/reader.cpp b/src/reader.cpp index b4e0f24eb..144995f96 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -640,6 +640,7 @@ class reader_data_t : public std::enable_shared_from_this { void move_word(editable_line_t *el, bool move_right, bool erase, enum move_word_style_t style, bool newv); + void run_input_command_scripts(const wcstring_list_t &cmds); maybe_t read_normal_chars(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::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. /// \return the next unhandled event. maybe_t reader_data_t::read_normal_chars(readline_loop_state_t &rls) { maybe_t event_needing_handling{}; wcstring accumulated_chars; 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) { 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)) { event_needing_handling = std::move(evt); break;