From 1dff1cb2c45efe9a92a280e249af7d4956fa627e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 7 Aug 2022 13:37:56 -0700 Subject: [PATCH] Factor out handling of readline_cmd_t::execute This reduces the size of handle_readline_command. No functional change. --- src/reader.cpp | 228 +++++++++++++++++++++++++------------------------ 1 file changed, 117 insertions(+), 111 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 870fdd0e9..83d4355e8 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -846,6 +846,10 @@ class reader_data_t : public std::enable_shared_from_this { maybe_t read_normal_chars(readline_loop_state_t &rls); void handle_readline_command(readline_cmd_t cmd, readline_loop_state_t &rls); + // Handle readline_cmd_t::execute. This may mean inserting a newline if the command is + // unfinished. + void handle_execute(readline_loop_state_t &rls); + void clear_pager(); void select_completion_in_direction(selection_motion_t dir, bool force_selection_change = false); @@ -3563,118 +3567,8 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat } break; } - // Evaluate. If the current command is unfinished, or if the charater is escaped - // using a backslash, insert a newline. case rl::execute: { - // If the user hits return while navigating the pager, it only clears the pager. - if (is_navigating_pager_contents()) { - clear_pager(); - break; - } - - // Delete any autosuggestion. - autosuggestion.clear(); - - // The user may have hit return with pager contents, but while not navigating them. - // Clear the pager in that event. - clear_pager(); - - // We only execute the command line. - editable_line_t *el = &command_line; - - // Allow backslash-escaped newlines. - bool continue_on_next_line = false; - if (el->position() >= el->size()) { - // We're at the end of the text and not in a comment (issue #1225). - continue_on_next_line = - 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()))) { - continue_on_next_line = true; - // Check if the end of the line is backslashed (issue #4467). - } else if (is_backslashed(el->text(), el->size()) && - !text_ends_in_comment(el->text())) { - // Move the cursor to the end of the line. - el->set_position(el->size()); - continue_on_next_line = true; - } - } - // If the conditions are met, insert a new line at the position of the cursor. - if (continue_on_next_line) { - insert_char(el, L'\n'); - break; - } - - // See if this command is valid. - parser_test_error_bits_t command_test_result = 0; - if (conf.syntax_check_ok) { - command_test_result = reader_shell_test(parser(), el->text()); - } - if (command_test_result == 0 || command_test_result == PARSER_TEST_INCOMPLETE) { - // This command is valid, but an abbreviation may make it invalid. If so, we - // will have to test again. - if (expand_abbreviation_as_necessary(0)) { - // Trigger syntax highlighting as we are likely about to execute this command. - this->super_highlight_me_plenty(); - if (conf.syntax_check_ok) { - command_test_result = reader_shell_test(parser(), el->text()); - } - } - } - - if (command_test_result == 0) { - // Finished command, execute it. Don't add items in silent mode (#7230). - wcstring text = command_line.text(); - if (text.empty()) { - // Here the user just hit return. Make a new prompt, don't remove ephemeral - // items. - rls.finished = true; - break; - } - - // Historical behavior is to trim trailing spaces. - // However, escaped spaces ('\ ') should not be trimmed (#7661) - // This can be done by counting pre-trailing '\' - // If there's an odd number, this must be an escaped space. - while (!text.empty() && text.back() == L' ' && - count_preceding_backslashes(text, text.size() - 1) % 2 == 0) { - text.pop_back(); - } - - if (history && !conf.in_silent_mode) { - // Remove ephemeral items - even if the text is empty - history->remove_ephemeral_items(); - - if (!text.empty()) { - // Mark this item as ephemeral if there is a leading space (#615). - history_persistence_mode_t mode; - if (text.front() == L' ') { - // Leading spaces are ephemeral (#615). - mode = history_persistence_mode_t::ephemeral; - } else if (in_private_mode(vars)) { - // Private mode means in-memory only. - mode = history_persistence_mode_t::memory; - } else { - mode = history_persistence_mode_t::disk; - } - history_t::add_pending_with_file_detection(history, text, vars.snapshot(), - mode); - } - } - - rls.finished = true; - update_buff_pos(&command_line, command_line.size()); - } else if (command_test_result == PARSER_TEST_INCOMPLETE) { - // We are incomplete, continue editing. - insert_char(el, L'\n'); - } else { - // Result must be some combination including an error. The error message will - // already be printed, all we need to do is repaint. - event_fire_generic(parser(), L"fish_posterror", {el->text()}); - screen.reset_abandoning_line(termsize_last().width); - } + this->handle_execute(rls); break; } @@ -4248,6 +4142,118 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat } } +void reader_data_t::handle_execute(readline_loop_state_t &rls) { + // Evaluate. If the current command is unfinished, or if the charater is escaped + // using a backslash, insert a newline. + // If the user hits return while navigating the pager, it only clears the pager. + if (is_navigating_pager_contents()) { + clear_pager(); + return; + } + + // Delete any autosuggestion. + autosuggestion.clear(); + + // The user may have hit return with pager contents, but while not navigating them. + // Clear the pager in that event. + clear_pager(); + + // We only execute the command line. + editable_line_t *el = &command_line; + + // Allow backslash-escaped newlines. + bool continue_on_next_line = false; + if (el->position() >= el->size()) { + // We're at the end of the text and not in a comment (issue #1225). + continue_on_next_line = + 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()))) { + continue_on_next_line = true; + // Check if the end of the line is backslashed (issue #4467). + } else if (is_backslashed(el->text(), el->size()) && !text_ends_in_comment(el->text())) { + // Move the cursor to the end of the line. + el->set_position(el->size()); + continue_on_next_line = true; + } + } + // If the conditions are met, insert a new line at the position of the cursor. + if (continue_on_next_line) { + insert_char(el, L'\n'); + return; + } + + // See if this command is valid. + parser_test_error_bits_t command_test_result = 0; + if (conf.syntax_check_ok) { + command_test_result = reader_shell_test(parser(), el->text()); + } + if (command_test_result == 0 || command_test_result == PARSER_TEST_INCOMPLETE) { + // This command is valid, but an abbreviation may make it invalid. If so, we + // will have to test again. + if (expand_abbreviation_as_necessary(0)) { + // Trigger syntax highlighting as we are likely about to execute this command. + this->super_highlight_me_plenty(); + if (conf.syntax_check_ok) { + command_test_result = reader_shell_test(parser(), el->text()); + } + } + } + + if (command_test_result == 0) { + // Finished command, execute it. Don't add items in silent mode (#7230). + wcstring text = command_line.text(); + if (text.empty()) { + // Here the user just hit return. Make a new prompt, don't remove ephemeral + // items. + rls.finished = true; + return; + } + + // Historical behavior is to trim trailing spaces. + // However, escaped spaces ('\ ') should not be trimmed (#7661) + // This can be done by counting pre-trailing '\' + // If there's an odd number, this must be an escaped space. + while (!text.empty() && text.back() == L' ' && + count_preceding_backslashes(text, text.size() - 1) % 2 == 0) { + text.pop_back(); + } + + if (history && !conf.in_silent_mode) { + // Remove ephemeral items - even if the text is empty + history->remove_ephemeral_items(); + + if (!text.empty()) { + // Mark this item as ephemeral if there is a leading space (#615). + history_persistence_mode_t mode; + if (text.front() == L' ') { + // Leading spaces are ephemeral (#615). + mode = history_persistence_mode_t::ephemeral; + } else if (in_private_mode(this->vars())) { + // Private mode means in-memory only. + mode = history_persistence_mode_t::memory; + } else { + mode = history_persistence_mode_t::disk; + } + history_t::add_pending_with_file_detection(history, text, this->vars().snapshot(), + mode); + } + } + + rls.finished = true; + update_buff_pos(&command_line, command_line.size()); + } else if (command_test_result == PARSER_TEST_INCOMPLETE) { + // We are incomplete, continue editing. + insert_char(el, L'\n'); + } else { + // Result must be some combination including an error. The error message will + // already be printed, all we need to do is repaint. + event_fire_generic(parser(), L"fish_posterror", {el->text()}); + screen.reset_abandoning_line(termsize_last().width); + } +} + maybe_t reader_data_t::readline(int nchars_or_0) { using rl = readline_cmd_t; readline_loop_state_t rls{};