Factor readline command handling into new function handle_readline_command()

This commit is contained in:
ridiculousfish 2019-03-18 20:06:16 -07:00
parent 42f4d2bd86
commit 6ba94fd81b

View File

@ -444,6 +444,7 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
maybe_t<wcstring> readline(int nchars);
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 clear_pager();
void select_completion_in_direction(enum selection_direction_t dir);
@ -2484,104 +2485,10 @@ maybe_t<char_event_t> reader_data_t::read_normal_chars(readline_loop_state_t &rl
return event_needing_handling;
}
maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
using rl = readline_cmd_t;
readline_loop_state_t rls{};
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.
cycle_command_line.clear();
cycle_cursor_pos = 0;
history_search.reset();
exec_prompt();
super_highlight_me_plenty();
s_reset(&screen, screen_reset_abandon_line);
repaint();
/// Handle a readline command \p c, updating the state \p rls.
void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_state_t &rls) {
const auto &vars = parser_t::principal_parser().vars();
// Get the current terminal modes. These will be restored when the function returns.
if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output();
// Set the new modes.
if (tcsetattr(0, TCSANOW, &shell_modes) == -1) {
int err = errno;
if (err == EIO) {
redirect_tty_output();
}
// This check is required to work around certain issues with fish's approach to
// terminal control when launching interactive processes while in non-interactive
// mode. See #4178 for one such example.
if (err != ENOTTY || is_interactive_session) {
wperror(L"tcsetattr");
}
}
while (!rls.finished && !shell_is_exiting()) {
if (rls.nchars <= command_line.size()) {
// We've already hit the specified character limit.
rls.finished = true;
break;
}
maybe_t<char_event_t> event_needing_handling{};
while (1) {
event_needing_handling = read_normal_chars(rls);
if (event_needing_handling.has_value()) break;
if (rls.nchars <= command_line.size()) {
event_needing_handling.reset();
break;
}
}
if (!event_needing_handling || event_needing_handling->is_check_exit()) {
repaint_if_needed();
continue;
} else if (event_needing_handling->is_eof()) {
reader_force_exit();
continue;
}
assert((event_needing_handling->is_char() || event_needing_handling->is_readline()) &&
"Should have a char or readline");
maybe_t<readline_cmd_t> readline_cmd{};
maybe_t<wchar_t> ordinary_char{};
if (event_needing_handling->is_readline()) {
readline_cmd = event_needing_handling->get_readline();
} else {
ordinary_char = event_needing_handling->get_char();
}
// If we get something other than a repaint, then stop coalescing them.
if (readline_cmd != rl::R_REPAINT) rls.coalescing_repaints = false;
if (rls.last_cmd != rl::R_YANK && rls.last_cmd != rl::R_YANK_POP) {
rls.yank_len = 0;
}
// Restore the text.
if (readline_cmd) {
if (*readline_cmd == rl::R_CANCEL && is_navigating_pager_contents()) {
set_command_line_and_position(&command_line, cycle_command_line, cycle_cursor_pos);
}
// Clear the pager if necessary.
bool focused_on_search_field = (active_edit_line() == &pager.search_field_line);
if (command_ends_paging(*readline_cmd, focused_on_search_field)) {
clear_pager();
}
}
readline_cmd_t c = readline_cmd ? *readline_cmd : static_cast<readline_cmd_t>(0);
using rl = readline_cmd_t;
switch (c) {
// Go to beginning of line.
case rl::R_BEGINNING_OF_LINE: {
@ -2795,8 +2702,7 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
assert(end >= begin);
if (end > begin) {
kill(el, begin, end - begin, KILL_APPEND,
rls.last_cmd != rl::R_KILL_WHOLE_LINE);
kill(el, begin, end - begin, KILL_APPEND, rls.last_cmd != rl::R_KILL_WHOLE_LINE);
}
break;
}
@ -2857,8 +2763,7 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
is_backslashed(el->text, el->position) && !text_ends_in_comment(el->text);
} else {
// Allow mid line split if the following character is whitespace (issue #613).
if (is_backslashed(el->text, el->position) &&
iswspace(el->text.at(el->position))) {
if (is_backslashed(el->text, el->position) && iswspace(el->text.at(el->position))) {
continue_on_next_line = true;
// Check if the end of the line is backslashed (issue #4467).
} else if (is_backslashed(el->text, el->size()) &&
@ -2939,8 +2844,7 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
}
} else {
// Searching by line.
history_search.reset_to_mode(el->text, history,
reader_history_search_t::line);
history_search.reset_to_mode(el->text, history, reader_history_search_t::line);
// Skip the autosuggestion in the history unless it was truncated.
const wcstring &suggest = autosuggestion;
@ -2950,8 +2854,8 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
}
}
if (history_search.active()) {
history_search_direction_t dir = (c == rl::R_HISTORY_SEARCH_BACKWARD ||
c == rl::R_HISTORY_TOKEN_SEARCH_BACKWARD)
history_search_direction_t dir =
(c == rl::R_HISTORY_SEARCH_BACKWARD || c == rl::R_HISTORY_TOKEN_SEARCH_BACKWARD)
? history_search_direction_t::backward
: history_search_direction_t::forward;
history_search.move_in_direction(dir);
@ -3000,8 +2904,8 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
case rl::R_KILL_BIGWORD: {
// The "bigword" functions differ only in that they move to the next whitespace, not
// punctuation.
auto move_style = (c == rl::R_KILL_WORD) ? move_word_style_punctuation
: move_word_style_whitespace;
auto move_style =
(c == rl::R_KILL_WORD) ? move_word_style_punctuation : move_word_style_whitespace;
move_word(active_edit_line(), MOVE_DIR_RIGHT, true /* erase */, move_style,
rls.last_cmd != c /* same kill item if same movement */);
break;
@ -3030,8 +2934,7 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
case rl::R_END_OF_HISTORY: {
bool up = (c == rl::R_BEGINNING_OF_HISTORY);
if (is_navigating_pager_contents()) {
select_completion_in_direction(up ? direction_page_north
: direction_page_south);
select_completion_in_direction(up ? direction_page_north : direction_page_south);
} else {
if (up) {
history_search.go_to_beginning();
@ -3153,8 +3056,7 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
if (tok_begin == &buff[len]) {
// ...retry beginning from the previous token.
size_t pos = prev_end - &buff[0];
parse_util_token_extent(buff, pos, &tok_begin, &tok_end, &prev_begin,
&prev_end);
parse_util_token_extent(buff, pos, &tok_begin, &tok_end, &prev_begin, &prev_end);
}
// Make sure we have two tokens.
@ -3306,6 +3208,109 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
break;
}
}
}
maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
using rl = readline_cmd_t;
readline_loop_state_t rls{};
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.
cycle_command_line.clear();
cycle_cursor_pos = 0;
history_search.reset();
exec_prompt();
super_highlight_me_plenty();
s_reset(&screen, screen_reset_abandon_line);
repaint();
// Get the current terminal modes. These will be restored when the function returns.
if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output();
// Set the new modes.
if (tcsetattr(0, TCSANOW, &shell_modes) == -1) {
int err = errno;
if (err == EIO) {
redirect_tty_output();
}
// This check is required to work around certain issues with fish's approach to
// terminal control when launching interactive processes while in non-interactive
// mode. See #4178 for one such example.
if (err != ENOTTY || is_interactive_session) {
wperror(L"tcsetattr");
}
}
while (!rls.finished && !shell_is_exiting()) {
if (rls.nchars <= command_line.size()) {
// We've already hit the specified character limit.
rls.finished = true;
break;
}
maybe_t<char_event_t> event_needing_handling{};
while (1) {
event_needing_handling = read_normal_chars(rls);
if (event_needing_handling.has_value()) break;
if (rls.nchars <= command_line.size()) {
event_needing_handling.reset();
break;
}
}
if (!event_needing_handling || event_needing_handling->is_check_exit()) {
repaint_if_needed();
continue;
} else if (event_needing_handling->is_eof()) {
reader_force_exit();
continue;
}
assert((event_needing_handling->is_char() || event_needing_handling->is_readline()) &&
"Should have a char or readline");
maybe_t<readline_cmd_t> readline_cmd{};
maybe_t<wchar_t> ordinary_char{};
if (event_needing_handling->is_readline()) {
readline_cmd = event_needing_handling->get_readline();
} else {
ordinary_char = event_needing_handling->get_char();
}
// If we get something other than a repaint, then stop coalescing them.
if (readline_cmd != rl::R_REPAINT) rls.coalescing_repaints = false;
if (rls.last_cmd != rl::R_YANK && rls.last_cmd != rl::R_YANK_POP) {
rls.yank_len = 0;
}
// Restore the text.
if (readline_cmd) {
if (*readline_cmd == rl::R_CANCEL && is_navigating_pager_contents()) {
set_command_line_and_position(&command_line, cycle_command_line, cycle_cursor_pos);
}
// Clear the pager if necessary.
bool focused_on_search_field = (active_edit_line() == &pager.search_field_line);
if (command_ends_paging(*readline_cmd, focused_on_search_field)) {
clear_pager();
}
handle_readline_command(*readline_cmd, rls);
if (!readline_cmd || command_ends_history_search(*readline_cmd)) {
history_search.reset();
}
rls.last_cmd = readline_cmd;
}
if (ordinary_char) {
wchar_t c = *ordinary_char;
@ -3336,10 +3341,6 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
}
}
if (!readline_cmd || command_ends_history_search(*readline_cmd)) {
history_search.reset();
}
rls.last_cmd = readline_cmd;
repaint_if_needed();
}