mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-28 04:03:39 +08:00
Begin to rationalize reader data stack
Switch from a manually maintained linked list to an explicit stack.
This commit is contained in:
parent
68c7ecd777
commit
4fdcc2e400
133
src/reader.cpp
133
src/reader.cpp
|
@ -221,7 +221,7 @@ class reader_data_t {
|
||||||
/// Pointer to previous reader_data.
|
/// Pointer to previous reader_data.
|
||||||
reader_data_t *next{nullptr};
|
reader_data_t *next{nullptr};
|
||||||
/// This variable keeps state on if we are in search mode, and if yes, what mode.
|
/// This variable keeps state on if we are in search mode, and if yes, what mode.
|
||||||
history_search_mode_t search_mode = history_search_mode_t::none;
|
history_search_mode_t search_mode{history_search_mode_t::none};
|
||||||
/// Keep track of whether any internal code has done something which is known to require a
|
/// Keep track of whether any internal code has done something which is known to require a
|
||||||
/// repaint.
|
/// repaint.
|
||||||
bool repaint_needed{false};
|
bool repaint_needed{false};
|
||||||
|
@ -251,7 +251,7 @@ class reader_data_t {
|
||||||
bool expand_abbreviation_as_necessary(size_t cursor_backtrack) const;
|
bool expand_abbreviation_as_necessary(size_t cursor_backtrack) const;
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
reader_data_t() = default;
|
reader_data_t(history_t *hist) : history(hist) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sets the command line contents, without clearing the pager.
|
/// Sets the command line contents, without clearing the pager.
|
||||||
|
@ -260,8 +260,17 @@ static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos);
|
||||||
/// Clears the pager.
|
/// Clears the pager.
|
||||||
static void clear_pager();
|
static void clear_pager();
|
||||||
|
|
||||||
/// The current interactive reading context.
|
/// The stack of current interactive reading contexts.
|
||||||
static reader_data_t *data = 0;
|
static std::vector<std::unique_ptr<reader_data_t>> reader_data_stack;
|
||||||
|
|
||||||
|
/// Access the top level reader data.
|
||||||
|
static reader_data_t *current_data_or_null() {
|
||||||
|
return reader_data_stack.empty() ? nullptr : reader_data_stack.back().get();
|
||||||
|
}
|
||||||
|
static reader_data_t *current_data() {
|
||||||
|
assert(!reader_data_stack.empty() && "no current reader");
|
||||||
|
return reader_data_stack.back().get();
|
||||||
|
}
|
||||||
|
|
||||||
/// This flag is set to true when fish is interactively reading from stdin. It changes how a ^C is
|
/// This flag is set to true when fish is interactively reading from stdin. It changes how a ^C is
|
||||||
/// handled by the fish interrupt handler.
|
/// handled by the fish interrupt handler.
|
||||||
|
@ -311,6 +320,7 @@ static void term_donate() {
|
||||||
/// Update the cursor position.
|
/// Update the cursor position.
|
||||||
static void update_buff_pos(editable_line_t *el, size_t buff_pos) {
|
static void update_buff_pos(editable_line_t *el, size_t buff_pos) {
|
||||||
el->position = buff_pos;
|
el->position = buff_pos;
|
||||||
|
reader_data_t *data = current_data();
|
||||||
if (el == &data->command_line && data->sel_active) {
|
if (el == &data->command_line && data->sel_active) {
|
||||||
if (data->sel_begin_pos <= buff_pos) {
|
if (data->sel_begin_pos <= buff_pos) {
|
||||||
data->sel_start_pos = data->sel_begin_pos;
|
data->sel_start_pos = data->sel_begin_pos;
|
||||||
|
@ -383,6 +393,7 @@ wcstring combine_command_and_autosuggestion(const wcstring &cmdline,
|
||||||
/// Repaint the entire commandline. This means reset and clear the commandline, write the prompt,
|
/// Repaint the entire commandline. This means reset and clear the commandline, write the prompt,
|
||||||
/// perform syntax highlighting, write the commandline and move the cursor.
|
/// perform syntax highlighting, write the commandline and move the cursor.
|
||||||
static void reader_repaint() {
|
static void reader_repaint() {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
editable_line_t *cmd_line = &data->command_line;
|
editable_line_t *cmd_line = &data->command_line;
|
||||||
// Update the indentation.
|
// Update the indentation.
|
||||||
data->indents = parse_util_compute_indents(cmd_line->text);
|
data->indents = parse_util_compute_indents(cmd_line->text);
|
||||||
|
@ -431,6 +442,7 @@ static void reader_repaint() {
|
||||||
|
|
||||||
/// Internal helper function for handling killing parts of text.
|
/// Internal helper function for handling killing parts of text.
|
||||||
static void reader_kill(editable_line_t *el, size_t begin_idx, size_t length, int mode, int newv) {
|
static void reader_kill(editable_line_t *el, size_t begin_idx, size_t length, int mode, int newv) {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
const wchar_t *begin = el->text.c_str() + begin_idx;
|
const wchar_t *begin = el->text.c_str() + begin_idx;
|
||||||
if (newv) {
|
if (newv) {
|
||||||
data->kill_item = wcstring(begin, length);
|
data->kill_item = wcstring(begin, length);
|
||||||
|
@ -600,6 +612,7 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso
|
||||||
/// change the command line but does NOT repaint it. This is to allow the caller to coalesce
|
/// change the command line but does NOT repaint it. This is to allow the caller to coalesce
|
||||||
/// repaints.
|
/// repaints.
|
||||||
bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack) const {
|
bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack) const {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
bool result = false;
|
bool result = false;
|
||||||
editable_line_t *el = data->active_edit_line();
|
editable_line_t *el = data->active_edit_line();
|
||||||
if (this->expand_abbreviations && el == &data->command_line) {
|
if (this->expand_abbreviations && el == &data->command_line) {
|
||||||
|
@ -632,6 +645,7 @@ int reader_interrupted() {
|
||||||
|
|
||||||
int reader_reading_interrupted() {
|
int reader_reading_interrupted() {
|
||||||
int res = reader_interrupted();
|
int res = reader_interrupted();
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (res && data && data->exit_on_interrupt) {
|
if (res && data && data->exit_on_interrupt) {
|
||||||
reader_exit(1, 0);
|
reader_exit(1, 0);
|
||||||
parser_t::skip_all_blocks();
|
parser_t::skip_all_blocks();
|
||||||
|
@ -682,6 +696,7 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) {
|
||||||
/// Reexecute the prompt command. The output is inserted into data->prompt_buff.
|
/// Reexecute the prompt command. The output is inserted into data->prompt_buff.
|
||||||
static void exec_prompt() {
|
static void exec_prompt() {
|
||||||
// Clear existing prompts.
|
// Clear existing prompts.
|
||||||
|
reader_data_t *data = current_data();
|
||||||
data->left_prompt_buff.clear();
|
data->left_prompt_buff.clear();
|
||||||
data->right_prompt_buff.clear();
|
data->right_prompt_buff.clear();
|
||||||
|
|
||||||
|
@ -796,19 +811,22 @@ void restore_term_mode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reader_exit(int do_exit, int forced) {
|
void reader_exit(int do_exit, int forced) {
|
||||||
if (data) data->end_loop = do_exit;
|
if (reader_data_t *data = current_data_or_null()) {
|
||||||
|
data->end_loop = do_exit;
|
||||||
|
}
|
||||||
end_loop = do_exit;
|
end_loop = do_exit;
|
||||||
if (forced) exit_forced = true;
|
if (forced) exit_forced = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reader_repaint_needed() {
|
void reader_repaint_needed() {
|
||||||
if (data) {
|
if (reader_data_t *data = current_data_or_null()) {
|
||||||
data->repaint_needed = true;
|
data->repaint_needed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reader_repaint_if_needed() {
|
void reader_repaint_if_needed() {
|
||||||
if (data == NULL) return;
|
reader_data_t *data = current_data_or_null();
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
bool needs_reset = data->screen_reset_needed;
|
bool needs_reset = data->screen_reset_needed;
|
||||||
bool needs_repaint = needs_reset || data->repaint_needed;
|
bool needs_repaint = needs_reset || data->repaint_needed;
|
||||||
|
@ -825,6 +843,7 @@ void reader_repaint_if_needed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reader_react_to_color_change() {
|
void reader_react_to_color_change() {
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
if (!data->repaint_needed || !data->screen_reset_needed) {
|
if (!data->repaint_needed || !data->screen_reset_needed) {
|
||||||
|
@ -903,6 +922,7 @@ static bool command_ends_paging(wchar_t c, bool focused_on_search_field) {
|
||||||
/// Remove the previous character in the character buffer and on the screen using syntax
|
/// Remove the previous character in the character buffer and on the screen using syntax
|
||||||
/// highlighting, etc.
|
/// highlighting, etc.
|
||||||
static void remove_backward() {
|
static void remove_backward() {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
editable_line_t *el = data->active_edit_line();
|
editable_line_t *el = data->active_edit_line();
|
||||||
|
|
||||||
if (el->position <= 0) return;
|
if (el->position <= 0) return;
|
||||||
|
@ -928,6 +948,7 @@ static void remove_backward() {
|
||||||
/// Returns true if the string changed.
|
/// Returns true if the string changed.
|
||||||
static bool insert_string(editable_line_t *el, const wcstring &str,
|
static bool insert_string(editable_line_t *el, const wcstring &str,
|
||||||
bool allow_expand_abbreviations = false) {
|
bool allow_expand_abbreviations = false) {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
size_t len = str.size();
|
size_t len = str.size();
|
||||||
if (len == 0) return false;
|
if (len == 0) return false;
|
||||||
|
|
||||||
|
@ -1091,6 +1112,7 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag
|
||||||
/// \param flags A union of all flags describing the completion to insert. See the completion_t
|
/// \param flags A union of all flags describing the completion to insert. See the completion_t
|
||||||
/// struct for more information on possible values.
|
/// struct for more information on possible values.
|
||||||
static void completion_insert(const wchar_t *val, complete_flags_t flags) {
|
static void completion_insert(const wchar_t *val, complete_flags_t flags) {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
editable_line_t *el = data->active_edit_line();
|
editable_line_t *el = data->active_edit_line();
|
||||||
size_t cursor = el->position;
|
size_t cursor = el->position;
|
||||||
wcstring new_command_line = completion_apply_to_command_line(val, flags, el->text, &cursor,
|
wcstring new_command_line = completion_apply_to_command_line(val, flags, el->text, &cursor,
|
||||||
|
@ -1176,6 +1198,7 @@ static std::function<autosuggestion_result_t(void)> get_autosuggestion_performer
|
||||||
static bool can_autosuggest() {
|
static bool can_autosuggest() {
|
||||||
// We autosuggest if suppress_autosuggestion is not set, if we're not doing a history search,
|
// We autosuggest if suppress_autosuggestion is not set, if we're not doing a history search,
|
||||||
// and our command line contains a non-whitespace character.
|
// and our command line contains a non-whitespace character.
|
||||||
|
reader_data_t *data = current_data();
|
||||||
const editable_line_t *el = data->active_edit_line();
|
const editable_line_t *el = data->active_edit_line();
|
||||||
const wchar_t *whitespace = L" \t\r\n\v";
|
const wchar_t *whitespace = L" \t\r\n\v";
|
||||||
return !data->suppress_autosuggestion && data->history_search.is_at_end() &&
|
return !data->suppress_autosuggestion && data->history_search.is_at_end() &&
|
||||||
|
@ -1184,6 +1207,7 @@ static bool can_autosuggest() {
|
||||||
|
|
||||||
// Called after an autosuggestion has been computed on a background thread
|
// Called after an autosuggestion has been computed on a background thread
|
||||||
static void autosuggest_completed(autosuggestion_result_t result) {
|
static void autosuggest_completed(autosuggestion_result_t result) {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
if (!result.suggestion.empty() && can_autosuggest() &&
|
if (!result.suggestion.empty() && can_autosuggest() &&
|
||||||
result.search_string == data->command_line.text &&
|
result.search_string == data->command_line.text &&
|
||||||
string_prefixes_string_case_insensitive(result.search_string, result.suggestion)) {
|
string_prefixes_string_case_insensitive(result.search_string, result.suggestion)) {
|
||||||
|
@ -1197,6 +1221,7 @@ static void autosuggest_completed(autosuggestion_result_t result) {
|
||||||
static void update_autosuggestion() {
|
static void update_autosuggestion() {
|
||||||
// Updates autosuggestion. We look for an autosuggestion if the command line is non-empty and if
|
// Updates autosuggestion. We look for an autosuggestion if the command line is non-empty and if
|
||||||
// we're not doing a history search.
|
// we're not doing a history search.
|
||||||
|
reader_data_t *data = current_data();
|
||||||
data->autosuggestion.clear();
|
data->autosuggestion.clear();
|
||||||
if (data->allow_autosuggestion && !data->suppress_autosuggestion &&
|
if (data->allow_autosuggestion && !data->suppress_autosuggestion &&
|
||||||
!data->command_line.empty() && data->history_search.is_at_end()) {
|
!data->command_line.empty() && data->history_search.is_at_end()) {
|
||||||
|
@ -1209,6 +1234,7 @@ static void update_autosuggestion() {
|
||||||
// Accept any autosuggestion by replacing the command line with it. If full is true, take the whole
|
// Accept any autosuggestion by replacing the command line with it. If full is true, take the whole
|
||||||
// thing; if it's false, then take only the first "word".
|
// thing; if it's false, then take only the first "word".
|
||||||
static void accept_autosuggestion(bool full) {
|
static void accept_autosuggestion(bool full) {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
if (!data->autosuggestion.empty()) {
|
if (!data->autosuggestion.empty()) {
|
||||||
// Accepting an autosuggestion clears the pager.
|
// Accepting an autosuggestion clears the pager.
|
||||||
clear_pager();
|
clear_pager();
|
||||||
|
@ -1235,7 +1261,7 @@ static void accept_autosuggestion(bool full) {
|
||||||
|
|
||||||
// Ensure we have no pager contents.
|
// Ensure we have no pager contents.
|
||||||
static void clear_pager() {
|
static void clear_pager() {
|
||||||
if (data) {
|
if (reader_data_t *data = current_data_or_null()) {
|
||||||
data->pager.clear();
|
data->pager.clear();
|
||||||
data->current_page_rendering = page_rendering_t();
|
data->current_page_rendering = page_rendering_t();
|
||||||
reader_repaint_needed();
|
reader_repaint_needed();
|
||||||
|
@ -1243,7 +1269,7 @@ static void clear_pager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void select_completion_in_direction(enum selection_direction_t dir) {
|
static void select_completion_in_direction(enum selection_direction_t dir) {
|
||||||
assert(data != NULL);
|
reader_data_t *data = current_data();
|
||||||
bool selection_changed =
|
bool selection_changed =
|
||||||
data->pager.select_next_completion_in_direction(dir, data->current_page_rendering);
|
data->pager.select_next_completion_in_direction(dir, data->current_page_rendering);
|
||||||
if (selection_changed) {
|
if (selection_changed) {
|
||||||
|
@ -1255,7 +1281,7 @@ static void select_completion_in_direction(enum selection_direction_t dir) {
|
||||||
/// BEL to maybe flash the screen or emite a sound, depending on how it is configured.
|
/// BEL to maybe flash the screen or emite a sound, depending on how it is configured.
|
||||||
static void reader_flash() {
|
static void reader_flash() {
|
||||||
struct timespec pollint;
|
struct timespec pollint;
|
||||||
|
reader_data_t *data = current_data();
|
||||||
editable_line_t *el = &data->command_line;
|
editable_line_t *el = &data->command_line;
|
||||||
for (size_t i = 0; i < el->position; i++) {
|
for (size_t i = 0; i < el->position; i++) {
|
||||||
data->colors.at(i) = highlight_spec_search_match << 16;
|
data->colors.at(i) = highlight_spec_search_match << 16;
|
||||||
|
@ -1333,6 +1359,7 @@ static bool handle_completions(const std::vector<completion_t> &comp,
|
||||||
bool cont_after_prefix_insertion) {
|
bool cont_after_prefix_insertion) {
|
||||||
bool done = false;
|
bool done = false;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
reader_data_t *data = current_data();
|
||||||
const editable_line_t *el = &data->command_line;
|
const editable_line_t *el = &data->command_line;
|
||||||
const wchar_t *begin, *end, *buff = el->text.c_str();
|
const wchar_t *begin, *end, *buff = el->text.c_str();
|
||||||
|
|
||||||
|
@ -1611,6 +1638,7 @@ static void reader_interactive_destroy() {
|
||||||
|
|
||||||
void reader_sanity_check() {
|
void reader_sanity_check() {
|
||||||
// Note: 'data' is non-null if we are interactive, except in the testing environment.
|
// Note: 'data' is non-null if we are interactive, except in the testing environment.
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (shell_is_interactive() && data != NULL) {
|
if (shell_is_interactive() && data != NULL) {
|
||||||
if (data->command_line.position > data->command_line.size()) sanity_lose();
|
if (data->command_line.position > data->command_line.size()) sanity_lose();
|
||||||
if (data->colors.size() != data->command_line.size()) sanity_lose();
|
if (data->colors.size() != data->command_line.size()) sanity_lose();
|
||||||
|
@ -1621,6 +1649,7 @@ void reader_sanity_check() {
|
||||||
/// Set the specified string as the current buffer.
|
/// Set the specified string as the current buffer.
|
||||||
static void set_command_line_and_position(editable_line_t *el, const wcstring &new_str,
|
static void set_command_line_and_position(editable_line_t *el, const wcstring &new_str,
|
||||||
size_t pos) {
|
size_t pos) {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
el->text = new_str;
|
el->text = new_str;
|
||||||
update_buff_pos(el, pos);
|
update_buff_pos(el, pos);
|
||||||
data->command_line_changed(el);
|
data->command_line_changed(el);
|
||||||
|
@ -1633,6 +1662,7 @@ static void reader_replace_current_token(const wcstring &new_token) {
|
||||||
size_t new_pos;
|
size_t new_pos;
|
||||||
|
|
||||||
// Find current token.
|
// Find current token.
|
||||||
|
reader_data_t *data = current_data();
|
||||||
editable_line_t *el = data->active_edit_line();
|
editable_line_t *el = data->active_edit_line();
|
||||||
const wchar_t *buff = el->text.c_str();
|
const wchar_t *buff = el->text.c_str();
|
||||||
parse_util_token_extent(buff, el->position, &begin, &end, 0, 0);
|
parse_util_token_extent(buff, el->position, &begin, &end, 0, 0);
|
||||||
|
@ -1650,6 +1680,7 @@ static void reader_replace_current_token(const wcstring &new_token) {
|
||||||
|
|
||||||
/// Reset the data structures associated with the token search.
|
/// Reset the data structures associated with the token search.
|
||||||
static void reset_token_history() {
|
static void reset_token_history() {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
const editable_line_t *el = data->active_edit_line();
|
const editable_line_t *el = data->active_edit_line();
|
||||||
const wchar_t *begin, *end;
|
const wchar_t *begin, *end;
|
||||||
const wchar_t *buff = el->text.c_str();
|
const wchar_t *buff = el->text.c_str();
|
||||||
|
@ -1674,6 +1705,7 @@ static void reset_token_history() {
|
||||||
/// \param dir if the search should be forward or reverse
|
/// \param dir if the search should be forward or reverse
|
||||||
/// \param reset whether the current token should be made the new search token
|
/// \param reset whether the current token should be made the new search token
|
||||||
static void handle_token_history(history_search_direction_t dir, bool reset = false) {
|
static void handle_token_history(history_search_direction_t dir, bool reset = false) {
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
const bool forward = (dir == history_search_direction_t::forward);
|
const bool forward = (dir == history_search_direction_t::forward);
|
||||||
|
|
||||||
|
@ -1786,6 +1818,7 @@ static void move_word(editable_line_t *el, bool move_right, bool erase,
|
||||||
// (possibly -1). If we are moving right, then buff_pos is that index - possibly el->size().
|
// (possibly -1). If we are moving right, then buff_pos is that index - possibly el->size().
|
||||||
if (erase) {
|
if (erase) {
|
||||||
// Don't autosuggest after a kill.
|
// Don't autosuggest after a kill.
|
||||||
|
reader_data_t *data = current_data();
|
||||||
if (el == &data->command_line) {
|
if (el == &data->command_line) {
|
||||||
data->suppress_autosuggestion = true;
|
data->suppress_autosuggestion = true;
|
||||||
}
|
}
|
||||||
|
@ -1803,16 +1836,19 @@ static void move_word(editable_line_t *el, bool move_right, bool erase,
|
||||||
|
|
||||||
const wchar_t *reader_get_buffer() {
|
const wchar_t *reader_get_buffer() {
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
return data ? data->command_line.text.c_str() : NULL;
|
return data ? data->command_line.text.c_str() : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
history_t *reader_get_history() {
|
history_t *reader_get_history() {
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
return data ? data->history : NULL;
|
return data ? data->history : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the command line contents, without clearing the pager.
|
/// Sets the command line contents, without clearing the pager.
|
||||||
static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos) {
|
static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos) {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
// Callers like to pass us pointers into ourselves, so be careful! I don't know if we can use
|
// Callers like to pass us pointers into ourselves, so be careful! I don't know if we can use
|
||||||
// operator= with a pointer to our interior, so use an intermediate.
|
// operator= with a pointer to our interior, so use an intermediate.
|
||||||
size_t command_line_len = b.size();
|
size_t command_line_len = b.size();
|
||||||
|
@ -1835,6 +1871,7 @@ static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos) {
|
||||||
|
|
||||||
/// Sets the command line contents, clearing the pager.
|
/// Sets the command line contents, clearing the pager.
|
||||||
void reader_set_buffer(const wcstring &b, size_t pos) {
|
void reader_set_buffer(const wcstring &b, size_t pos) {
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
clear_pager();
|
clear_pager();
|
||||||
|
@ -1842,6 +1879,7 @@ void reader_set_buffer(const wcstring &b, size_t pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t reader_get_cursor_pos() {
|
size_t reader_get_cursor_pos() {
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (!data) return (size_t)-1;
|
if (!data) return (size_t)-1;
|
||||||
|
|
||||||
return data->command_line.position;
|
return data->command_line.position;
|
||||||
|
@ -1849,6 +1887,7 @@ size_t reader_get_cursor_pos() {
|
||||||
|
|
||||||
bool reader_get_selection(size_t *start, size_t *len) {
|
bool reader_get_selection(size_t *start, size_t *len) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (data != NULL && data->sel_active) {
|
if (data != NULL && data->sel_active) {
|
||||||
*start = data->sel_start_pos;
|
*start = data->sel_start_pos;
|
||||||
*len = std::min(data->sel_stop_pos - data->sel_start_pos, data->command_line.size());
|
*len = std::min(data->sel_stop_pos - data->sel_start_pos, data->command_line.size());
|
||||||
|
@ -1937,6 +1976,7 @@ static parser_test_error_bits_t default_test(const wcstring &b) {
|
||||||
|
|
||||||
void reader_change_history(const wchar_t *name) {
|
void reader_change_history(const wchar_t *name) {
|
||||||
// We don't need to _change_ if we're not initialized yet.
|
// We don't need to _change_ if we're not initialized yet.
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (data && data->history) {
|
if (data && data->history) {
|
||||||
data->history->save();
|
data->history->save();
|
||||||
data->history = &history_t::history_with_name(name);
|
data->history = &history_t::history_with_name(name);
|
||||||
|
@ -1944,16 +1984,11 @@ void reader_change_history(const wchar_t *name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reader_push(const wcstring &name) {
|
void reader_push(const wcstring &name) {
|
||||||
reader_data_t *n = new reader_data_t();
|
history_t *hist = &history_t::history_with_name(name);
|
||||||
|
reader_data_stack.push_back(make_unique<reader_data_t>(hist));
|
||||||
n->history = &history_t::history_with_name(name);
|
reader_data_t *data = current_data();
|
||||||
n->next = data;
|
|
||||||
|
|
||||||
data = n;
|
|
||||||
|
|
||||||
data->command_line_changed(&data->command_line);
|
data->command_line_changed(&data->command_line);
|
||||||
|
if (reader_data_stack.size() == 1) {
|
||||||
if (data->next == 0) {
|
|
||||||
reader_interactive_init();
|
reader_interactive_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1964,47 +1999,44 @@ void reader_push(const wcstring &name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reader_pop() {
|
void reader_pop() {
|
||||||
reader_data_t *n = data;
|
assert(!reader_data_stack.empty() && "empty stack in reader_data_stack");
|
||||||
|
reader_data_stack.pop_back();
|
||||||
if (data == 0) {
|
reader_data_t *new_reader = current_data_or_null();
|
||||||
debug(0, _(L"Pop null reader block"));
|
if (new_reader == nullptr) {
|
||||||
sanity_lose();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = data->next;
|
|
||||||
|
|
||||||
// Invoke the destructor to balance our new.
|
|
||||||
delete n;
|
|
||||||
|
|
||||||
if (data == 0) {
|
|
||||||
reader_interactive_destroy();
|
reader_interactive_destroy();
|
||||||
} else {
|
} else {
|
||||||
end_loop = 0;
|
end_loop = 0;
|
||||||
s_reset(&data->screen, screen_reset_abandon_line);
|
s_reset(&new_reader->screen, screen_reset_abandon_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reader_set_left_prompt(const wcstring &new_prompt) { data->left_prompt = new_prompt; }
|
void reader_set_left_prompt(const wcstring &new_prompt) {
|
||||||
|
current_data()->left_prompt = new_prompt;
|
||||||
|
}
|
||||||
|
|
||||||
void reader_set_right_prompt(const wcstring &new_prompt) { data->right_prompt = new_prompt; }
|
void reader_set_right_prompt(const wcstring &new_prompt) {
|
||||||
|
current_data()->right_prompt = new_prompt;
|
||||||
|
}
|
||||||
|
|
||||||
void reader_set_allow_autosuggesting(bool flag) { data->allow_autosuggestion = flag; }
|
void reader_set_allow_autosuggesting(bool flag) { current_data()->allow_autosuggestion = flag; }
|
||||||
|
|
||||||
void reader_set_expand_abbreviations(bool flag) { data->expand_abbreviations = flag; }
|
void reader_set_expand_abbreviations(bool flag) { current_data()->expand_abbreviations = flag; }
|
||||||
|
|
||||||
void reader_set_complete_function(complete_function_t f) { data->complete_func = f; }
|
void reader_set_complete_function(complete_function_t f) { current_data()->complete_func = f; }
|
||||||
|
|
||||||
void reader_set_highlight_function(highlight_function_t func) { data->highlight_func = func; }
|
void reader_set_highlight_function(highlight_function_t func) {
|
||||||
|
current_data()->highlight_func = func;
|
||||||
|
}
|
||||||
|
|
||||||
void reader_set_test_function(test_function_t f) { data->test_func = f; }
|
void reader_set_test_function(test_function_t f) { current_data()->test_func = f; }
|
||||||
|
|
||||||
void reader_set_exit_on_interrupt(bool i) { data->exit_on_interrupt = i; }
|
void reader_set_exit_on_interrupt(bool i) { current_data()->exit_on_interrupt = i; }
|
||||||
|
|
||||||
void reader_set_silent_status(bool flag) { data->silent = flag; }
|
void reader_set_silent_status(bool flag) { current_data()->silent = flag; }
|
||||||
|
|
||||||
void reader_import_history_if_necessary() {
|
void reader_import_history_if_necessary() {
|
||||||
// Import history from older location (config path) if our current history is empty.
|
// Import history from older location (config path) if our current history is empty.
|
||||||
|
reader_data_t *data = current_data();
|
||||||
if (data->history && data->history->is_empty()) {
|
if (data->history && data->history->is_empty()) {
|
||||||
data->history->populate_from_config_path();
|
data->history->populate_from_config_path();
|
||||||
}
|
}
|
||||||
|
@ -2027,6 +2059,7 @@ void reader_import_history_if_necessary() {
|
||||||
|
|
||||||
/// Called to set the highlight flag for search results.
|
/// Called to set the highlight flag for search results.
|
||||||
static void highlight_search() {
|
static void highlight_search() {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
if (!data->search_buff.empty() && !data->history_search.is_at_end()) {
|
if (!data->search_buff.empty() && !data->history_search.is_at_end()) {
|
||||||
const editable_line_t *el = &data->command_line;
|
const editable_line_t *el = &data->command_line;
|
||||||
const wcstring &needle = data->search_buff;
|
const wcstring &needle = data->search_buff;
|
||||||
|
@ -2047,6 +2080,7 @@ struct highlight_result_t {
|
||||||
|
|
||||||
static void highlight_complete(highlight_result_t result) {
|
static void highlight_complete(highlight_result_t result) {
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
reader_data_t *data = current_data();
|
||||||
if (result.text == data->command_line.text) {
|
if (result.text == data->command_line.text) {
|
||||||
// The data hasn't changed, so swap in our colors. The colors may not have changed, so do
|
// The data hasn't changed, so swap in our colors. The colors may not have changed, so do
|
||||||
// nothing if they have not.
|
// nothing if they have not.
|
||||||
|
@ -2067,7 +2101,8 @@ static std::function<highlight_result_t(void)> get_highlight_performer(const wcs
|
||||||
bool no_io) {
|
bool no_io) {
|
||||||
env_vars_snapshot_t vars(env_vars_snapshot_t::highlighting_keys);
|
env_vars_snapshot_t vars(env_vars_snapshot_t::highlighting_keys);
|
||||||
unsigned int generation_count = read_generation_count();
|
unsigned int generation_count = read_generation_count();
|
||||||
highlight_function_t highlight_func = no_io ? highlight_shell_no_io : data->highlight_func;
|
highlight_function_t highlight_func =
|
||||||
|
no_io ? highlight_shell_no_io : current_data()->highlight_func;
|
||||||
return [=]() -> highlight_result_t {
|
return [=]() -> highlight_result_t {
|
||||||
if (generation_count != read_generation_count()) {
|
if (generation_count != read_generation_count()) {
|
||||||
// The gen count has changed, so don't do anything.
|
// The gen count has changed, so don't do anything.
|
||||||
|
@ -2093,6 +2128,7 @@ static std::function<highlight_result_t(void)> get_highlight_performer(const wcs
|
||||||
/// \param no_io if true, do a highlight that does not perform I/O, synchronously. If false, perform
|
/// \param no_io if true, do a highlight that does not perform I/O, synchronously. If false, perform
|
||||||
/// an asynchronous highlight in the background, which may perform disk I/O.
|
/// an asynchronous highlight in the background, which may perform disk I/O.
|
||||||
static void reader_super_highlight_me_plenty(int match_highlight_pos_adjust, bool no_io) {
|
static void reader_super_highlight_me_plenty(int match_highlight_pos_adjust, bool no_io) {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
const editable_line_t *el = &data->command_line;
|
const editable_line_t *el = &data->command_line;
|
||||||
assert(el != NULL);
|
assert(el != NULL);
|
||||||
long match_highlight_pos = (long)el->position + match_highlight_pos_adjust;
|
long match_highlight_pos = (long)el->position + match_highlight_pos_adjust;
|
||||||
|
@ -2124,6 +2160,7 @@ static void reader_super_highlight_me_plenty(int match_highlight_pos_adjust, boo
|
||||||
|
|
||||||
bool shell_is_exiting() {
|
bool shell_is_exiting() {
|
||||||
if (shell_is_interactive()) {
|
if (shell_is_interactive()) {
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
return job_list_is_empty() && data != NULL && data->end_loop;
|
return job_list_is_empty() && data != NULL && data->end_loop;
|
||||||
}
|
}
|
||||||
return end_loop;
|
return end_loop;
|
||||||
|
@ -2167,6 +2204,7 @@ static void handle_end_loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reader_data_t *data = current_data();
|
||||||
if (!data->prev_end_loop && bg_jobs) {
|
if (!data->prev_end_loop && bg_jobs) {
|
||||||
bg_job_warning();
|
bg_job_warning();
|
||||||
reader_exit(0, 0);
|
reader_exit(0, 0);
|
||||||
|
@ -2186,6 +2224,7 @@ static void handle_end_loop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool selection_is_at_top() {
|
static bool selection_is_at_top() {
|
||||||
|
reader_data_t *data = current_data();
|
||||||
const pager_t *pager = &data->pager;
|
const pager_t *pager = &data->pager;
|
||||||
size_t row = pager->get_selected_row(data->current_page_rendering);
|
size_t row = pager->get_selected_row(data->current_page_rendering);
|
||||||
if (row != 0 && row != PAGER_SELECTION_NONE) return false;
|
if (row != 0 && row != PAGER_SELECTION_NONE) return false;
|
||||||
|
@ -2207,7 +2246,7 @@ static int read_i() {
|
||||||
reader_import_history_if_necessary();
|
reader_import_history_if_necessary();
|
||||||
|
|
||||||
parser_t &parser = parser_t::principal_parser();
|
parser_t &parser = parser_t::principal_parser();
|
||||||
|
reader_data_t *data = current_data();
|
||||||
data->prev_end_loop = 0;
|
data->prev_end_loop = 0;
|
||||||
|
|
||||||
while ((!data->end_loop) && (!sanity_check())) {
|
while ((!data->end_loop) && (!sanity_check())) {
|
||||||
|
@ -2319,6 +2358,7 @@ const wchar_t *reader_readline(int nchars) {
|
||||||
bool coalescing_repaints = false;
|
bool coalescing_repaints = false;
|
||||||
|
|
||||||
// The command line before completion.
|
// The command line before completion.
|
||||||
|
reader_data_t *data = current_data();
|
||||||
data->cycle_command_line.clear();
|
data->cycle_command_line.clear();
|
||||||
data->cycle_cursor_pos = 0;
|
data->cycle_cursor_pos = 0;
|
||||||
|
|
||||||
|
@ -3220,6 +3260,7 @@ const wchar_t *reader_readline(int nchars) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int reader_search_mode() {
|
int reader_search_mode() {
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -3227,6 +3268,7 @@ int reader_search_mode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int reader_has_pager_contents() {
|
int reader_has_pager_contents() {
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -3336,6 +3378,7 @@ int reader_read(int fd, const io_chain_t &io) {
|
||||||
res = shell_is_interactive() ? read_i() : read_ni(fd, io);
|
res = shell_is_interactive() ? read_i() : read_ni(fd, io);
|
||||||
|
|
||||||
// If the exit command was called in a script, only exit the script, not the program.
|
// If the exit command was called in a script, only exit the script, not the program.
|
||||||
|
reader_data_t *data = current_data_or_null();
|
||||||
if (data) data->end_loop = 0;
|
if (data) data->end_loop = 0;
|
||||||
end_loop = 0;
|
end_loop = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user