mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-29 06:13:20 +00:00
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:
parent
994f95b845
commit
e9b683dee1
3 changed files with 36 additions and 20 deletions
|
@ -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<input_mapping_t> inputter_t::find_mapping() {
|
|||
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()) {
|
||||
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.
|
||||
|
|
13
src/input.h
13
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<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
|
||||
/// 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<input_mapping_t> find_mapping();
|
||||
char_event_t read_characters_no_readline();
|
||||
|
|
|
@ -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,
|
||||
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);
|
||||
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()};
|
||||
};
|
||||
|
||||
/// 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<char_event_t> reader_data_t::read_normal_chars(readline_loop_state_t &rls) {
|
||||
maybe_t<char_event_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;
|
||||
|
|
Loading…
Reference in a new issue