mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-01 07:38:46 +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
|
/// 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.
|
||||||
|
|
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
|
/// 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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue