Factor out the "read coalescing" part of reader_data_t::readline

This commit is contained in:
ridiculousfish 2019-03-17 23:37:46 -07:00
parent 7ae7865071
commit 42f4d2bd86

View File

@ -105,7 +105,7 @@
/// The maximum number of characters to read from the keyboard without repainting. Note that this /// The maximum number of characters to read from the keyboard without repainting. Note that this
/// readahead will only occur if new characters are available for reading, fish will never block for /// readahead will only occur if new characters are available for reading, fish will never block for
/// more input without repainting. /// more input without repainting.
#define READAHEAD_MAX 256 static constexpr size_t READAHEAD_MAX = 256;
/// A mode for calling the reader_kill function. In this mode, the new string is appended to the /// A mode for calling the reader_kill function. In this mode, the new string is appended to the
/// current contents of the kill buffer. /// current contents of the kill buffer.
@ -316,6 +316,8 @@ struct highlight_result_t {
} // namespace } // namespace
struct readline_loop_state_t;
/// A struct describing the state of the interactive reader. These states can be stacked, in case /// A struct describing the state of the interactive reader. These states can be stacked, in case
/// reader_readline() calls are nested. This happens when the 'read' builtin is used. /// reader_readline() calls are nested. This happens when the 'read' builtin is used.
class reader_data_t : public std::enable_shared_from_this<reader_data_t> { class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
@ -441,6 +443,7 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
bool newv); bool newv);
maybe_t<wcstring> readline(int nchars); maybe_t<wcstring> readline(int nchars);
maybe_t<char_event_t> read_normal_chars(readline_loop_state_t &rls);
void clear_pager(); void clear_pager();
void select_completion_in_direction(enum selection_direction_t dir); void select_completion_in_direction(enum selection_direction_t dir);
@ -2426,13 +2429,72 @@ struct readline_loop_state_t {
/// Whether the loop has finished, due to reaching the character limit or through executing a /// Whether the loop has finished, due to reaching the character limit or through executing a
/// command. /// command.
bool finished{false}; bool finished{false};
/// Maximum number of characters to read.
size_t nchars{std::numeric_limits<size_t>::max()};
}; };
maybe_t<wcstring> reader_data_t::readline(int nchars) { /// 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) {
int was_interactive_read = is_interactive_read;
is_interactive_read = 1;
maybe_t<char_event_t> event_needing_handling = input_readch();
is_interactive_read = was_interactive_read;
if (!event_is_normal_char(*event_needing_handling) || !can_read(STDIN_FILENO))
return event_needing_handling;
// This is a normal character input.
// We are going to handle it directly, accumulating more.
char_event_t evt = event_needing_handling.acquire();
size_t limit = std::min(rls.nchars - command_line.size(), READAHEAD_MAX);
wchar_t arr[READAHEAD_MAX + 1] = {};
arr[0] = evt.get_char();
for (size_t i = 1; i < limit; ++i) {
if (!can_read(0)) {
break;
}
// Only allow commands on the first key; otherwise, we might have data we
// need to insert on the commandline that the commmand might need to be able
// to see.
auto next_event = input_readch(false);
if (event_is_normal_char(next_event)) {
arr[i] = next_event.get_char();
} else {
// We need to process this in the outer loop.
assert(!event_needing_handling && "Should not have an unhandled event");
event_needing_handling = next_event;
break;
}
}
editable_line_t *el = active_edit_line();
insert_string(el, arr, true);
// End paging upon inserting into the normal command line.
if (el == &command_line) {
clear_pager();
}
// Since we handled a normal character, we don't have a last command.
rls.last_cmd.reset();
return event_needing_handling;
}
maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
using rl = readline_cmd_t; using rl = readline_cmd_t;
readline_loop_state_t rls{}; readline_loop_state_t rls{};
struct termios old_modes; struct termios old_modes;
// If nchars_or_0 is positive, then that's the maximum number of chars. Otherwise keep it at
// SIZE_MAX.
if (nchars_or_0 > 0) {
rls.nchars = static_cast<size_t>(nchars_or_0);
}
// The command line before completion. // The command line before completion.
cycle_command_line.clear(); cycle_command_line.clear();
cycle_cursor_pos = 0; cycle_cursor_pos = 0;
@ -2465,70 +2527,18 @@ maybe_t<wcstring> reader_data_t::readline(int nchars) {
} }
while (!rls.finished && !shell_is_exiting()) { while (!rls.finished && !shell_is_exiting()) {
if (0 < nchars && (size_t)nchars <= command_line.size()) { if (rls.nchars <= command_line.size()) {
// We've already hit the specified character limit. // We've already hit the specified character limit.
rls.finished = true; rls.finished = true;
break; break;
} }
// Sometimes strange input sequences seem to generate a zero byte. I believe these simply
// mean a character was pressed but it should be ignored. (Example: Trying to add a tilde
// (~) to digit).
maybe_t<char_event_t> event_needing_handling{}; maybe_t<char_event_t> event_needing_handling{};
while (1) { while (1) {
int was_interactive_read = is_interactive_read; event_needing_handling = read_normal_chars(rls);
is_interactive_read = 1;
event_needing_handling = input_readch();
is_interactive_read = was_interactive_read;
// std::fwprintf(stderr, L"C: %lx\n", (long)c);
if (event_is_normal_char(*event_needing_handling) && can_read(STDIN_FILENO)) {
// This is a normal character input.
// We are going to handle it directly, accumulating more.
// Clear 'mevt' to mark that we handled this.
char_event_t evt = event_needing_handling.acquire();
size_t limit = 0 < nchars ? std::min((size_t)nchars - command_line.size(),
(size_t)READAHEAD_MAX)
: READAHEAD_MAX;
wchar_t arr[READAHEAD_MAX + 1] = {};
arr[0] = evt.get_char();
for (size_t i = 1; i < limit; ++i) {
if (!can_read(0)) {
break;
}
// Only allow commands on the first key; otherwise, we might have data we
// need to insert on the commandline that the commmand might need to be able
// to see.
auto next_event = input_readch(false);
if (event_is_normal_char(next_event)) {
arr[i] = next_event.get_char();
} else {
// We need to process this in the outer loop.
assert(!event_needing_handling && "Should not have an unhandled event");
event_needing_handling = next_event;
break;
}
}
editable_line_t *el = active_edit_line();
insert_string(el, arr, true);
// End paging upon inserting into the normal command line.
if (el == &command_line) {
clear_pager();
}
// Since we handled a normal character, we don't have a last command.
rls.last_cmd.reset();
}
// If there's still an event that we were unable to handle, then end the coalescing
// loop.
if (event_needing_handling.has_value()) break; if (event_needing_handling.has_value()) break;
if (0 < nchars && (size_t)nchars <= command_line.size()) { if (rls.nchars <= command_line.size()) {
event_needing_handling.reset(); event_needing_handling.reset();
break; break;
} }