mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-23 02:27:28 +08:00
Revert "Factor repainting decions from readline commands better in the reader"
This reverts commit 7e7599b22a
.
Identified a regression in highlighting
This commit is contained in:
parent
7e7599b22a
commit
eb86bf23e8
|
@ -197,7 +197,7 @@ void env_dispatch_var_change(const wcstring &key, env_stack_t &vars) {
|
|||
|
||||
// Eww.
|
||||
if (string_prefixes_string(L"fish_color_", key)) {
|
||||
reader_schedule_prompt_repaint();
|
||||
reader_react_to_color_change();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ void env_universal_callbacks(env_stack_t *stack, const callback_data_list_t &cal
|
|||
|
||||
static void handle_fish_term_change(const env_stack_t &vars) {
|
||||
update_fish_color_support(vars);
|
||||
reader_schedule_prompt_repaint();
|
||||
reader_react_to_color_change();
|
||||
}
|
||||
|
||||
static void handle_change_ambiguous_width(const env_stack_t &vars) {
|
||||
|
|
|
@ -99,7 +99,6 @@ class category_list_t {
|
|||
category_t term_support{L"term-support", L"Terminal feature detection"};
|
||||
|
||||
category_t reader{L"reader", L"The interactive reader/input system"};
|
||||
category_t reader_render{L"reader-render", L"Rendering the command line"};
|
||||
category_t complete{L"complete", L"The completion system"};
|
||||
category_t path{L"path", L"Searching/using paths"};
|
||||
|
||||
|
|
|
@ -271,7 +271,7 @@ static maybe_t<char_event_t> interrupt_handler() {
|
|||
event_fire_delayed(parser);
|
||||
// Reap stray processes, including printing exit status messages.
|
||||
// TODO: shouldn't need this parser here.
|
||||
if (job_reap(parser, true)) reader_schedule_prompt_repaint();
|
||||
if (job_reap(parser, true)) reader_repaint_needed();
|
||||
// Tell the reader an event occurred.
|
||||
if (reader_reading_interrupted()) {
|
||||
auto vintr = shell_modes.c_cc[VINTR];
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "common.h"
|
||||
#include "complete.h"
|
||||
#include "fallback.h"
|
||||
#include "flog.h"
|
||||
#include "highlight.h"
|
||||
#include "pager.h"
|
||||
#include "reader.h"
|
||||
|
@ -576,28 +575,26 @@ page_rendering_t pager_t::render() const {
|
|||
return rendering;
|
||||
}
|
||||
|
||||
bool pager_t::rendering_needs_update(const page_rendering_t &rendering) const {
|
||||
// Common case is no pager.
|
||||
if (this->empty() && rendering.screen_data.empty()) return false;
|
||||
|
||||
return rendering.term_width != this->available_term_width || //
|
||||
rendering.term_height != this->available_term_height || //
|
||||
rendering.selected_completion_idx !=
|
||||
this->visual_selected_completion_index(rendering.rows, rendering.cols) || //
|
||||
rendering.search_field_shown != this->search_field_shown || //
|
||||
rendering.search_field_line.text() != this->search_field_line.text() || //
|
||||
rendering.search_field_line.position() != this->search_field_line.position() || //
|
||||
(rendering.remaining_to_disclose > 0 && this->fully_disclosed);
|
||||
}
|
||||
|
||||
void pager_t::update_rendering(page_rendering_t *rendering) const {
|
||||
if (rendering_needs_update(*rendering)) {
|
||||
if (rendering->term_width != this->available_term_width ||
|
||||
rendering->term_height != this->available_term_height ||
|
||||
rendering->selected_completion_idx !=
|
||||
this->visual_selected_completion_index(rendering->rows, rendering->cols) ||
|
||||
rendering->search_field_shown != this->search_field_shown ||
|
||||
rendering->search_field_line.text() != this->search_field_line.text() ||
|
||||
rendering->search_field_line.position() != this->search_field_line.position() ||
|
||||
(rendering->remaining_to_disclose > 0 && this->fully_disclosed)) {
|
||||
*rendering = this->render();
|
||||
}
|
||||
}
|
||||
|
||||
pager_t::pager_t() = default;
|
||||
pager_t::~pager_t() = default;
|
||||
pager_t::pager_t()
|
||||
: available_term_width(0),
|
||||
available_term_height(0),
|
||||
selected_completion_idx(PAGER_SELECTION_NONE),
|
||||
suggested_row_start(0),
|
||||
fully_disclosed(false),
|
||||
search_field_shown(false) {}
|
||||
|
||||
bool pager_t::empty() const { return unfiltered_completion_infos.empty(); }
|
||||
|
||||
|
@ -858,4 +855,5 @@ size_t pager_t::cursor_position() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
// Constructor
|
||||
page_rendering_t::page_rendering_t() = default;
|
||||
|
|
35
src/pager.h
35
src/pager.h
|
@ -62,17 +62,17 @@ enum class selection_motion_t {
|
|||
#define PAGER_UNDISCLOSED_MAX_ROWS 4
|
||||
|
||||
class pager_t {
|
||||
size_t available_term_width{0};
|
||||
size_t available_term_height{0};
|
||||
size_t available_term_width;
|
||||
size_t available_term_height;
|
||||
|
||||
size_t selected_completion_idx{PAGER_SELECTION_NONE};
|
||||
size_t suggested_row_start{0};
|
||||
size_t selected_completion_idx;
|
||||
size_t suggested_row_start;
|
||||
|
||||
// Fully disclosed means that we show all completions.
|
||||
bool fully_disclosed{false};
|
||||
bool fully_disclosed;
|
||||
|
||||
// Whether we show the search field.
|
||||
bool search_field_shown{false};
|
||||
bool search_field_shown;
|
||||
|
||||
// Returns the index of the completion that should draw selected, using the given number of
|
||||
// columns.
|
||||
|
@ -82,15 +82,19 @@ class pager_t {
|
|||
/// Data structure describing one or a group of related completions.
|
||||
struct comp_t {
|
||||
/// The list of all completion strings this entry applies to.
|
||||
wcstring_list_t comp{};
|
||||
wcstring_list_t comp;
|
||||
/// The description.
|
||||
wcstring desc{};
|
||||
wcstring desc;
|
||||
/// The representative completion.
|
||||
completion_t representative{L""};
|
||||
completion_t representative;
|
||||
/// On-screen width of the completion string.
|
||||
size_t comp_width{0};
|
||||
size_t comp_width;
|
||||
/// On-screen width of the description information.
|
||||
size_t desc_width{0};
|
||||
size_t desc_width;
|
||||
/// Minimum acceptable width.
|
||||
// size_t min_width;
|
||||
|
||||
comp_t() : comp(), desc(), representative(L""), comp_width(0), desc_width(0) {}
|
||||
|
||||
// Our text looks like this:
|
||||
// completion (description)
|
||||
|
@ -108,7 +112,7 @@ class pager_t {
|
|||
};
|
||||
|
||||
private:
|
||||
using comp_info_list_t = std::vector<comp_t>;
|
||||
typedef std::vector<comp_t> comp_info_list_t;
|
||||
|
||||
// The filtered list of completion infos.
|
||||
comp_info_list_t completion_infos;
|
||||
|
@ -161,10 +165,7 @@ class pager_t {
|
|||
// Produces a rendering of the completions, at the given term size.
|
||||
page_rendering_t render() const;
|
||||
|
||||
// \return true if the given rendering needs to be updated.
|
||||
bool rendering_needs_update(const page_rendering_t &rendering) const;
|
||||
|
||||
// Updates the rendering.
|
||||
// Updates the rendering if it's stale.
|
||||
void update_rendering(page_rendering_t *rendering) const;
|
||||
|
||||
// Indicates if there are no completions, and therefore nothing to render.
|
||||
|
@ -191,8 +192,8 @@ class pager_t {
|
|||
// Position of the cursor.
|
||||
size_t cursor_position() const;
|
||||
|
||||
// Constructor
|
||||
pager_t();
|
||||
~pager_t();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
504
src/reader.cpp
504
src/reader.cpp
|
@ -456,44 +456,6 @@ struct selection_data_t {
|
|||
/// The start and stop position of the current selection.
|
||||
size_t start{0};
|
||||
size_t stop{0};
|
||||
|
||||
bool operator==(const selection_data_t &rhs) const {
|
||||
return begin == rhs.begin && start == rhs.start && stop == rhs.stop;
|
||||
}
|
||||
|
||||
bool operator!=(const selection_data_t &rhs) const { return !(*this == rhs); }
|
||||
};
|
||||
|
||||
/// A value-type struct representing a layout from which we can call to s_write().
|
||||
/// The intent is that everything we send to the screen is encapsulated in this struct.
|
||||
struct layout_data_t {
|
||||
/// Text of the command line.
|
||||
wcstring text{};
|
||||
|
||||
/// The colors. This has the same length as 'text'.
|
||||
std::vector<highlight_spec_t> colors{};
|
||||
|
||||
/// Position of the cursor in the command line.
|
||||
size_t position{};
|
||||
|
||||
/// Whether the cursor is focused on the pager or not.
|
||||
bool focused_on_pager{false};
|
||||
|
||||
/// Visual selection of the command line, or none if none.
|
||||
maybe_t<selection_data_t> selection{};
|
||||
|
||||
/// String containing the autosuggestion.
|
||||
wcstring autosuggestion{};
|
||||
|
||||
/// String containing the history search. If non-empty, then highlight the found range within
|
||||
/// the text.
|
||||
wcstring history_search_text{};
|
||||
|
||||
/// The result of evaluating the left, mode and right prompt commands.
|
||||
/// That is, this the text of the prompts, not the commands to produce them.
|
||||
wcstring left_prompt_buff{};
|
||||
wcstring mode_prompt_buff{};
|
||||
wcstring right_prompt_buff{};
|
||||
};
|
||||
|
||||
/// A struct describing the state of the interactive reader. These states can be stacked, in case
|
||||
|
@ -510,20 +472,16 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
|
|||
/// or a pager selection change. When this is true and another transient change is made, the
|
||||
/// old transient change will be removed from the undo history.
|
||||
bool command_line_has_transient_edit = false;
|
||||
/// The most recent layout data sent to the screen.
|
||||
layout_data_t rendered_layout;
|
||||
/// String containing the autosuggestion.
|
||||
wcstring autosuggestion;
|
||||
/// Current pager.
|
||||
pager_t pager;
|
||||
/// The output of the pager.
|
||||
/// Current page rendering.
|
||||
page_rendering_t current_page_rendering;
|
||||
/// When backspacing, we temporarily suppress autosuggestions.
|
||||
bool suppress_autosuggestion{false};
|
||||
|
||||
/// The representation of the current screen contents.
|
||||
screen_t screen;
|
||||
|
||||
/// The source of input events.
|
||||
inputter_t inputter;
|
||||
/// The history.
|
||||
|
@ -538,12 +496,12 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
|
|||
wcstring mode_prompt_buff;
|
||||
/// The output of the last evaluation of the right prompt command.
|
||||
wcstring right_prompt_buff;
|
||||
|
||||
/// When navigating the pager, we modify the command line.
|
||||
/// This is the saved command line before modification.
|
||||
/// Completion support.
|
||||
wcstring cycle_command_line;
|
||||
size_t cycle_cursor_pos{0};
|
||||
|
||||
/// Color is the syntax highlighting for buff. The format is that color[i] is the
|
||||
/// classification (according to the enum in highlight.h) of buff[i].
|
||||
std::vector<highlight_spec_t> colors;
|
||||
/// If set, a key binding or the 'exit' command has asked us to exit our read loop.
|
||||
bool exit_loop_requested{false};
|
||||
/// If this is true, exit reader even if there are running jobs. This happens if we press e.g.
|
||||
|
@ -551,21 +509,16 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
|
|||
bool did_warn_for_bg_jobs{false};
|
||||
/// The current contents of the top item in the kill ring.
|
||||
wcstring kill_item;
|
||||
|
||||
/// A flag which may be set to force re-execing all prompts and re-rendering.
|
||||
/// This may come about when a color like $fish_color... has changed.
|
||||
bool force_exec_prompt_and_repaint{false};
|
||||
|
||||
/// Keep track of whether any internal code has done something which is known to require a
|
||||
/// repaint.
|
||||
bool repaint_needed{false};
|
||||
/// Whether a screen reset is needed after a repaint.
|
||||
bool screen_reset_needed{false};
|
||||
/// The target character of the last jump command.
|
||||
wchar_t last_jump_target{0};
|
||||
jump_direction_t last_jump_direction{jump_direction_t::forward};
|
||||
jump_precision_t last_jump_precision{jump_precision_t::to};
|
||||
|
||||
/// The text of the most recent asynchronous highlight and autosuggestion requests.
|
||||
/// If these differs from the text of the command line, then we must kick off a new request.
|
||||
wcstring in_flight_highlight_request;
|
||||
wcstring in_flight_autosuggest_request;
|
||||
|
||||
bool is_navigating_pager_contents() const { return this->pager.is_navigating_contents(); }
|
||||
|
||||
/// The line that is currently being edited. Typically the command line, but may be the search
|
||||
|
@ -591,28 +544,7 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
|
|||
/// Expand abbreviations at the current cursor position, minus backtrack_amt.
|
||||
bool expand_abbreviation_as_necessary(size_t cursor_backtrack);
|
||||
|
||||
/// \return the string used for history search, or an empty string if none.
|
||||
wcstring history_search_text_if_active() const;
|
||||
|
||||
/// \return true if the command line has changed and repainting is needed. If \p colors is not
|
||||
/// null, then also return true if the colors have changed.
|
||||
using highlight_list_t = std::vector<highlight_spec_t>;
|
||||
bool is_repaint_needed(const highlight_list_t *mcolors = nullptr) const;
|
||||
|
||||
/// Generate a new layout data from the current state of the world.
|
||||
/// If \p mcolors has a value, then apply it; otherwise extend existing colors.
|
||||
layout_data_t make_layout_data(maybe_t<highlight_list_t> mcolors = none()) const;
|
||||
|
||||
/// Generate a new layout data from the current state of the world, and paint with it.
|
||||
/// If \p mcolors has a value, then apply it; otherwise extend existing colors.
|
||||
void layout_and_repaint(const wchar_t *reason, maybe_t<highlight_list_t> mcolors = none()) {
|
||||
this->rendered_layout = make_layout_data(std::move(mcolors));
|
||||
paint_layout(reason);
|
||||
}
|
||||
|
||||
/// Paint the last rendered layout.
|
||||
/// \p reason is used in FLOG to explain why.
|
||||
void paint_layout(const wchar_t *reason);
|
||||
void repaint_if_needed();
|
||||
|
||||
/// Return the variable set used for e.g. command duration.
|
||||
env_stack_t &vars() { return parser_ref->vars(); }
|
||||
|
@ -628,21 +560,18 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
|
|||
history(hist) {}
|
||||
|
||||
void update_buff_pos(editable_line_t *el, maybe_t<size_t> new_pos = none_t());
|
||||
|
||||
void repaint();
|
||||
void kill(editable_line_t *el, size_t begin_idx, size_t length, int mode, int newv);
|
||||
void insert_string(editable_line_t *el, const wcstring &str);
|
||||
bool insert_string(editable_line_t *el, const wcstring &str);
|
||||
|
||||
/// Insert the character into the command line buffer and print it to the screen using syntax
|
||||
/// highlighting, etc.
|
||||
void insert_char(editable_line_t *el, wchar_t c) { insert_string(el, wcstring{c}); }
|
||||
|
||||
/// Read a command to execute, respecting input bindings.
|
||||
/// \return the command, or none if we were asked to cancel (e.g. SIGHUP).
|
||||
maybe_t<wcstring> readline(int nchars);
|
||||
bool insert_char(editable_line_t *el, wchar_t c) { return insert_string(el, wcstring{c}); }
|
||||
|
||||
void move_word(editable_line_t *el, bool move_right, bool erase, enum move_word_style_t style,
|
||||
bool newv);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -650,6 +579,8 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
|
|||
void select_completion_in_direction(selection_motion_t dir);
|
||||
void flash();
|
||||
|
||||
void mark_repaint_needed() { repaint_needed = true; }
|
||||
|
||||
void completion_insert(const wchar_t *val, size_t token_end, complete_flags_t flags);
|
||||
|
||||
bool can_autosuggest() const;
|
||||
|
@ -659,6 +590,7 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
|
|||
move_word_style_t style = move_word_style_punctuation);
|
||||
void super_highlight_me_plenty(bool no_io = false);
|
||||
|
||||
void highlight_search();
|
||||
void highlight_complete(highlight_result_t result);
|
||||
void exec_mode_prompt();
|
||||
void exec_prompt();
|
||||
|
@ -857,63 +789,10 @@ void reader_data_t::update_buff_pos(editable_line_t *el, maybe_t<size_t> new_pos
|
|||
}
|
||||
}
|
||||
|
||||
bool reader_data_t::is_repaint_needed(const std::vector<highlight_spec_t> *mcolors) const {
|
||||
// Note: this function is responsible for detecting all of the ways that the command line may
|
||||
// change, by comparing it to what is present in rendered_layout.
|
||||
// The pager is the problem child, it has its own update logic.
|
||||
auto check = [](bool val, const wchar_t *reason) {
|
||||
if (val) FLOG(reader_render, L"repaint needed because", reason, L"change");
|
||||
return val;
|
||||
};
|
||||
|
||||
bool focused_on_pager = active_edit_line() == &pager.search_field_line;
|
||||
const layout_data_t &last = this->rendered_layout;
|
||||
return check(force_exec_prompt_and_repaint, L"forced") ||
|
||||
check(command_line.text() != last.text, L"text") ||
|
||||
check(mcolors && *mcolors != last.colors, L"highlight") ||
|
||||
check(selection != last.selection, L"selection") ||
|
||||
check(focused_on_pager != last.focused_on_pager, L"focus") ||
|
||||
check(command_line.position() != last.position, L"position") ||
|
||||
check(history_search_text_if_active() != last.history_search_text, L"history search") ||
|
||||
check(autosuggestion != last.autosuggestion, L"autosuggestion") ||
|
||||
check(left_prompt_buff != last.left_prompt_buff, L"left_prompt") ||
|
||||
check(mode_prompt_buff != last.mode_prompt_buff, L"mode_prompt") ||
|
||||
check(right_prompt_buff != last.right_prompt_buff, L"right_prompt") ||
|
||||
check(pager.rendering_needs_update(current_page_rendering), L"pager");
|
||||
}
|
||||
|
||||
layout_data_t reader_data_t::make_layout_data(maybe_t<highlight_list_t> mcolors) const {
|
||||
layout_data_t result{};
|
||||
bool focused_on_pager = active_edit_line() == &pager.search_field_line;
|
||||
result.text = command_line.text();
|
||||
|
||||
if (mcolors.has_value()) {
|
||||
result.colors = mcolors.acquire();
|
||||
} else {
|
||||
result.colors = rendered_layout.colors;
|
||||
}
|
||||
|
||||
result.position = focused_on_pager ? pager.cursor_position() : command_line.position();
|
||||
result.selection = selection;
|
||||
result.focused_on_pager = (active_edit_line() == &pager.search_field_line);
|
||||
result.history_search_text = history_search_text_if_active();
|
||||
result.autosuggestion = autosuggestion;
|
||||
result.left_prompt_buff = left_prompt_buff;
|
||||
result.mode_prompt_buff = mode_prompt_buff;
|
||||
result.right_prompt_buff = right_prompt_buff;
|
||||
|
||||
// Ensure our color list has the same length as the command line, by extending it with the last
|
||||
// color. This typically reduces redraws; e.g. if the user continues types into an argument, we
|
||||
// guess it's still an argument, while the highlighting proceeds in the background.
|
||||
highlight_spec_t last_color = result.colors.empty() ? highlight_spec_t{} : result.colors.back();
|
||||
result.colors.resize(result.text.size(), last_color);
|
||||
return result;
|
||||
}
|
||||
|
||||
void reader_data_t::paint_layout(const wchar_t *reason) {
|
||||
FLOGF(reader_render, L"Repainting from %ls", reason);
|
||||
const layout_data_t &data = this->rendered_layout;
|
||||
const editable_line_t *cmd_line = &command_line;
|
||||
/// Repaint the entire commandline. This means reset and clear the commandline, write the prompt,
|
||||
/// perform syntax highlighting, write the commandline and move the cursor.
|
||||
void reader_data_t::repaint() {
|
||||
editable_line_t *cmd_line = &command_line;
|
||||
|
||||
wcstring full_line;
|
||||
if (conf.in_silent_mode) {
|
||||
|
@ -923,41 +802,33 @@ void reader_data_t::paint_layout(const wchar_t *reason) {
|
|||
full_line = combine_command_and_autosuggestion(cmd_line->text(), autosuggestion);
|
||||
}
|
||||
|
||||
// Copy the colors and extend them with autosuggestion color.
|
||||
std::vector<highlight_spec_t> colors = data.colors;
|
||||
size_t len = full_line.size();
|
||||
if (len < 1) len = 1;
|
||||
|
||||
// Highlight any history search.
|
||||
if (!conf.in_silent_mode && !data.history_search_text.empty()) {
|
||||
const wcstring &needle = data.history_search_text;
|
||||
const wcstring &haystack = cmd_line->text();
|
||||
size_t match_pos = haystack.find(needle);
|
||||
if (match_pos != wcstring::npos) {
|
||||
for (size_t i = 0; i < needle.size(); i++) {
|
||||
colors.at(match_pos + i).background = highlight_role_t::search_match;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<highlight_spec_t> colors = this->colors;
|
||||
colors.resize(len, highlight_role_t::autosuggestion);
|
||||
|
||||
// Apply any selection.
|
||||
if (data.selection.has_value()) {
|
||||
if (selection.has_value()) {
|
||||
highlight_spec_t selection_color = {highlight_role_t::normal, highlight_role_t::selection};
|
||||
for (size_t i = data.selection->start; i < std::min(selection->stop, colors.size()); i++) {
|
||||
colors.at(i) = selection_color;
|
||||
for (size_t i = selection->start; i < std::min(len, selection->stop); i++) {
|
||||
colors[i] = selection_color;
|
||||
}
|
||||
}
|
||||
|
||||
// Extend our colors with the autosuggestion.
|
||||
colors.resize(full_line.size(), highlight_role_t::autosuggestion);
|
||||
|
||||
// Compute the indentation, then extend it with 0s for the autosuggestion. The autosuggestion
|
||||
// always conceptually has an indent of 0.
|
||||
std::vector<int> indents = parse_util_compute_indents(cmd_line->text());
|
||||
indents.resize(full_line.size(), 0);
|
||||
indents.resize(len, 0);
|
||||
|
||||
bool focused_on_pager = active_edit_line() == &pager.search_field_line;
|
||||
size_t cursor_position = focused_on_pager ? pager.cursor_position() : cmd_line->position();
|
||||
|
||||
// Prepend the mode prompt to the left prompt.
|
||||
s_write(&screen, mode_prompt_buff + left_prompt_buff, right_prompt_buff, full_line,
|
||||
cmd_line->size(), colors, indents, data.position, pager, current_page_rendering,
|
||||
data.focused_on_pager);
|
||||
cmd_line->size(), colors, indents, cursor_position, pager, current_page_rendering,
|
||||
focused_on_pager);
|
||||
|
||||
repaint_needed = false;
|
||||
}
|
||||
|
||||
/// Internal helper function for handling killing parts of text.
|
||||
|
@ -978,6 +849,10 @@ void reader_data_t::kill(editable_line_t *el, size_t begin_idx, size_t length, i
|
|||
kill_replace(old, kill_item);
|
||||
}
|
||||
el->erase_substring(begin_idx, length);
|
||||
update_buff_pos(el);
|
||||
command_line_changed(el);
|
||||
super_highlight_me_plenty();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// This is called from a signal handler!
|
||||
|
@ -987,6 +862,13 @@ void reader_handle_sigint() { interrupted = SIGINT; }
|
|||
void reader_data_t::command_line_changed(const editable_line_t *el) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
if (el == &this->command_line) {
|
||||
size_t len = this->command_line.size();
|
||||
|
||||
// When we grow colors, propagate the last color (if any), under the assumption that usually
|
||||
// it will be correct. If it is, it avoids a repaint.
|
||||
highlight_spec_t last_color = colors.empty() ? highlight_spec_t() : colors.back();
|
||||
colors.resize(len, last_color);
|
||||
|
||||
// Update the gen count.
|
||||
s_generation.store(1 + read_generation_count(), std::memory_order_relaxed);
|
||||
} else if (el == &this->pager.search_field_line) {
|
||||
|
@ -1016,6 +898,9 @@ void reader_data_t::pager_selection_changed() {
|
|||
if (new_cmd_line != command_line.text()) {
|
||||
set_buffer_maintaining_pager(new_cmd_line, cursor_pos, true /* transient */);
|
||||
}
|
||||
|
||||
// Trigger repaint (see issue #765).
|
||||
mark_repaint_needed();
|
||||
}
|
||||
|
||||
/// Expand abbreviations at the given cursor position. Does NOT inspect 'data'.
|
||||
|
@ -1093,12 +978,28 @@ bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack) {
|
|||
el->push_edit(std::move(*edit));
|
||||
update_buff_pos(el);
|
||||
el->undo_history.may_coalesce = false;
|
||||
command_line_changed(el);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void reader_data_t::repaint_if_needed() {
|
||||
bool needs_reset = screen_reset_needed;
|
||||
bool needs_repaint = needs_reset || repaint_needed;
|
||||
|
||||
if (needs_reset) {
|
||||
exec_prompt();
|
||||
s_reset_line(&screen, true /* repaint prompt */);
|
||||
screen_reset_needed = false;
|
||||
}
|
||||
|
||||
if (needs_repaint) {
|
||||
repaint(); // reader_repaint clears repaint_needed
|
||||
}
|
||||
}
|
||||
|
||||
void reader_reset_interrupted() { interrupted = 0; }
|
||||
|
||||
int reader_test_and_clear_interrupted() {
|
||||
|
@ -1357,17 +1258,34 @@ void reader_data_t::delete_char(bool backward) {
|
|||
} while (width == 0 && pos > 0);
|
||||
el->erase_substring(pos, pos_end - pos);
|
||||
update_buff_pos(el);
|
||||
command_line_changed(el);
|
||||
suppress_autosuggestion = true;
|
||||
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
}
|
||||
|
||||
/// Insert the characters of the string into the command line buffer and print them to the screen
|
||||
/// using syntax highlighting, etc.
|
||||
/// Returns true if the string changed.
|
||||
void reader_data_t::insert_string(editable_line_t *el, const wcstring &str) {
|
||||
if (!str.empty()) {
|
||||
bool reader_data_t::insert_string(editable_line_t *el, const wcstring &str) {
|
||||
if (str.empty()) return false;
|
||||
|
||||
el->insert_string(str, 0, str.size());
|
||||
if (el == &command_line) suppress_autosuggestion = false;
|
||||
update_buff_pos(el);
|
||||
command_line_changed(el);
|
||||
|
||||
if (el == &command_line) {
|
||||
suppress_autosuggestion = false;
|
||||
|
||||
// Syntax highlight. Note we must have that buff_pos > 0 because we just added something
|
||||
// nonzero to its length.
|
||||
assert(el->position() > 0);
|
||||
super_highlight_me_plenty();
|
||||
}
|
||||
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Insert the string in the given command line at the given cursor position. The function checks if
|
||||
|
@ -1495,7 +1413,9 @@ void reader_data_t::completion_insert(const wchar_t *val, size_t token_end,
|
|||
editable_line_t *el = active_edit_line();
|
||||
|
||||
// Move the cursor to the end of the token.
|
||||
if (el->position() != token_end) update_buff_pos(el, token_end);
|
||||
if (el->position() != token_end) {
|
||||
update_buff_pos(el, token_end); // repaint() is done later
|
||||
}
|
||||
|
||||
size_t cursor = el->position();
|
||||
wcstring new_command_line = completion_apply_to_command_line(val, flags, el->text(), &cursor,
|
||||
|
@ -1584,53 +1504,31 @@ bool reader_data_t::can_autosuggest() const {
|
|||
el == &command_line && el->text().find_first_not_of(whitespace) != wcstring::npos;
|
||||
}
|
||||
|
||||
// Called after an autosuggestion has been computed on a background thread.
|
||||
// Called after an autosuggestion has been computed on a background thread
|
||||
void reader_data_t::autosuggest_completed(autosuggestion_result_t result) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
if (result.search_string == in_flight_autosuggest_request)
|
||||
in_flight_autosuggest_request.clear();
|
||||
if (!result.suggestion.empty() && can_autosuggest() &&
|
||||
result.search_string == command_line.text() &&
|
||||
string_prefixes_string_case_insensitive(result.search_string, result.suggestion)) {
|
||||
// Autosuggestion is active and the search term has not changed, so we're good to go.
|
||||
autosuggestion = std::move(result.suggestion);
|
||||
if (this->is_repaint_needed()) {
|
||||
this->layout_and_repaint(L"autosuggest");
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void reader_data_t::update_autosuggestion() {
|
||||
// If we can't autosuggest, just clear it.
|
||||
if (!can_autosuggest()) {
|
||||
in_flight_autosuggest_request.clear();
|
||||
// Updates autosuggestion. We look for an autosuggestion if the command line is non-empty and if
|
||||
// we're not doing a history search.
|
||||
autosuggestion.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if our autosuggestion still applies; if so, don't recompute it.
|
||||
// Since the autosuggestion computation is asynchronous, this avoids "flashing" as you type into
|
||||
// the autosuggestion.
|
||||
// This is also the main mechanism by which readline commands that don't change the command line
|
||||
// text avoid recomputing the autosuggestion.
|
||||
const editable_line_t &el = command_line;
|
||||
if (!autosuggestion.empty() &&
|
||||
string_prefixes_string_case_insensitive(el.text(), autosuggestion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do nothing if we've already kicked off this autosuggest request.
|
||||
if (el.text() == in_flight_autosuggest_request) return;
|
||||
in_flight_autosuggest_request = el.text();
|
||||
|
||||
// Clear the autosuggestion and kick it off in the background.
|
||||
FLOG(reader_render, L"Autosuggesting");
|
||||
autosuggestion.clear();
|
||||
auto performer = get_autosuggestion_performer(parser(), el.text(), el.position(), history);
|
||||
if (can_autosuggest()) {
|
||||
const editable_line_t *el = active_edit_line();
|
||||
auto performer =
|
||||
get_autosuggestion_performer(parser(), el->text(), el->position(), history);
|
||||
auto shared_this = this->shared_from_this();
|
||||
debounce_autosuggestions().perform(performer, [shared_this](autosuggestion_result_t result) {
|
||||
debounce_autosuggestions().perform(
|
||||
performer, [shared_this](autosuggestion_result_t result) {
|
||||
shared_this->autosuggest_completed(std::move(result));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Accept any autosuggestion by replacing the command line with it. If full is true, take the whole
|
||||
|
@ -1659,6 +1557,10 @@ void reader_data_t::accept_autosuggestion(bool full, bool single, move_word_styl
|
|||
command_line.replace_substring(command_line.size(), 0,
|
||||
autosuggestion.substr(have, want - have));
|
||||
}
|
||||
update_buff_pos(&command_line);
|
||||
command_line_changed(&command_line);
|
||||
super_highlight_me_plenty();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1666,6 +1568,7 @@ void reader_data_t::accept_autosuggestion(bool full, bool single, move_word_styl
|
|||
void reader_data_t::clear_pager() {
|
||||
pager.clear();
|
||||
current_page_rendering = page_rendering_t();
|
||||
mark_repaint_needed();
|
||||
}
|
||||
|
||||
void reader_data_t::select_completion_in_direction(selection_motion_t dir) {
|
||||
|
@ -1680,18 +1583,11 @@ void reader_data_t::select_completion_in_direction(selection_motion_t dir) {
|
|||
void reader_data_t::flash() {
|
||||
struct timespec pollint;
|
||||
editable_line_t *el = &command_line;
|
||||
layout_data_t data = make_layout_data();
|
||||
|
||||
// Save off the colors and set the background.
|
||||
highlight_list_t saved_colors = data.colors;
|
||||
for (size_t i = 0; i < el->position(); i++) {
|
||||
data.colors.at(i) = highlight_spec_t::make_background(highlight_role_t::search_match);
|
||||
colors.at(i) = highlight_spec_t::make_background(highlight_role_t::search_match);
|
||||
}
|
||||
this->rendered_layout = data; // need to copy the data since we will use it again.
|
||||
paint_layout(L"flash");
|
||||
|
||||
layout_data_t old_data = std::move(rendered_layout);
|
||||
|
||||
repaint();
|
||||
ignore_result(write(STDOUT_FILENO, "\a", 1));
|
||||
// The write above changed the timestamp of stdout; ensure we don't therefore reset our screen.
|
||||
// See #3693.
|
||||
|
@ -1701,10 +1597,8 @@ void reader_data_t::flash() {
|
|||
pollint.tv_nsec = 100 * 1000000;
|
||||
nanosleep(&pollint, nullptr);
|
||||
|
||||
// Re-render with our saved data.
|
||||
data.colors = std::move(saved_colors);
|
||||
this->rendered_layout = std::move(data);
|
||||
paint_layout(L"unflash");
|
||||
super_highlight_me_plenty();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/// Characters that may not be part of a token that is to be replaced by a case insensitive
|
||||
|
@ -1906,6 +1800,7 @@ bool reader_data_t::handle_completions(const completion_list_t &comp, size_t tok
|
|||
current_page_rendering = page_rendering_t();
|
||||
// Modify the command line to reflect the new pager.
|
||||
pager_selection_changed();
|
||||
mark_repaint_needed();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2103,6 +1998,9 @@ void reader_data_t::set_command_line_and_position(editable_line_t *el, wcstring
|
|||
el->set_position(pos);
|
||||
el->undo_history.may_coalesce = false;
|
||||
update_buff_pos(el, pos);
|
||||
command_line_changed(el);
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
}
|
||||
|
||||
/// Undo the transient edit und update commandline accordingly.
|
||||
|
@ -2112,6 +2010,9 @@ void reader_data_t::clear_transient_edit() {
|
|||
}
|
||||
command_line.undo();
|
||||
update_buff_pos(&command_line);
|
||||
command_line_changed(&command_line);
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
command_line_has_transient_edit = false;
|
||||
}
|
||||
|
||||
|
@ -2147,6 +2048,9 @@ void reader_data_t::update_command_line_from_history_search() {
|
|||
command_line_has_transient_edit = true;
|
||||
assert(el == &command_line);
|
||||
update_buff_pos(el);
|
||||
command_line_changed(el);
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
}
|
||||
|
||||
enum move_word_dir_t { MOVE_DIR_LEFT, MOVE_DIR_RIGHT };
|
||||
|
@ -2194,6 +2098,7 @@ void reader_data_t::move_word(editable_line_t *el, bool move_right, bool erase,
|
|||
}
|
||||
} else {
|
||||
update_buff_pos(el, buff_pos);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2213,10 +2118,13 @@ void reader_data_t::set_buffer_maintaining_pager(const wcstring &b, size_t pos,
|
|||
|
||||
// Don't set a position past the command line length.
|
||||
if (pos > command_line_len) pos = command_line_len; //!OCLINT(parameter reassignment)
|
||||
|
||||
update_buff_pos(&command_line, pos);
|
||||
|
||||
// Clear history search and pager contents.
|
||||
history_search.reset();
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
}
|
||||
|
||||
void set_env_cmd_duration(struct timeval *after, struct timeval *before, env_stack_t &vars) {
|
||||
|
@ -2290,20 +2198,32 @@ static parser_test_error_bits_t reader_shell_test(parser_t &parser, const wcstri
|
|||
return res;
|
||||
}
|
||||
|
||||
wcstring reader_data_t::history_search_text_if_active() const {
|
||||
if (!history_search.active() || history_search.is_at_end()) {
|
||||
return wcstring{};
|
||||
/// Called to set the highlight flag for search results.
|
||||
void reader_data_t::highlight_search() {
|
||||
if (history_search.is_at_end()) {
|
||||
return;
|
||||
}
|
||||
const wcstring &needle = history_search.search_string();
|
||||
const editable_line_t *el = &command_line;
|
||||
size_t match_pos = el->text().find(needle);
|
||||
if (match_pos != wcstring::npos) {
|
||||
size_t end = match_pos + needle.size();
|
||||
for (size_t i = match_pos; i < end; i++) {
|
||||
colors.at(i).background = highlight_role_t::search_match;
|
||||
}
|
||||
}
|
||||
return history_search.search_string();
|
||||
}
|
||||
|
||||
void reader_data_t::highlight_complete(highlight_result_t result) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
in_flight_highlight_request.clear();
|
||||
if (result.text == command_line.text()) {
|
||||
// The data hasn't changed, so swap in our colors. The colors may not have changed, so do
|
||||
// nothing if they have not.
|
||||
assert(result.colors.size() == command_line.size());
|
||||
if (this->is_repaint_needed(&result.colors)) {
|
||||
this->layout_and_repaint(L"highlight", std::move(result.colors));
|
||||
if (colors != result.colors) {
|
||||
colors = std::move(result.colors);
|
||||
highlight_search();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2324,7 +2244,10 @@ static std::function<highlight_result_t(void)> get_highlight_performer(parser_t
|
|||
};
|
||||
}
|
||||
|
||||
/// Highlight the command line in a super, plentiful way.
|
||||
/// Call specified external highlighting function and then do search highlighting. Lastly, clear the
|
||||
/// background color under the cursor to avoid repaint issues on terminals where e.g. syntax
|
||||
/// highlighting maykes characters under the sursor unreadable.
|
||||
///
|
||||
/// \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.
|
||||
void reader_data_t::super_highlight_me_plenty(bool no_io) {
|
||||
|
@ -2332,11 +2255,6 @@ void reader_data_t::super_highlight_me_plenty(bool no_io) {
|
|||
|
||||
const editable_line_t *el = &command_line;
|
||||
|
||||
// Do nothing if this text is already in flight.
|
||||
if (el->text() == in_flight_highlight_request) return;
|
||||
in_flight_highlight_request = el->text();
|
||||
|
||||
FLOG(reader_render, L"Highlighting");
|
||||
auto highlight_performer = get_highlight_performer(parser(), el->text(), !no_io);
|
||||
if (no_io) {
|
||||
// Highlighting without IO, we just do it.
|
||||
|
@ -2349,6 +2267,18 @@ void reader_data_t::super_highlight_me_plenty(bool no_io) {
|
|||
shared_this->highlight_complete(std::move(result));
|
||||
});
|
||||
}
|
||||
highlight_search();
|
||||
|
||||
// Here's a hack. Check to see if our autosuggestion still applies; if so, don't recompute it.
|
||||
// Since the autosuggestion computation is asynchronous, this avoids "flashing" as you type into
|
||||
// the autosuggestion.
|
||||
const wcstring &cmd = el->text(), &suggest = autosuggestion;
|
||||
if (can_autosuggest() && !suggest.empty() &&
|
||||
string_prefixes_string_case_insensitive(cmd, suggest)) {
|
||||
// the autosuggestion is still reasonable, so do nothing
|
||||
} else {
|
||||
update_autosuggestion();
|
||||
}
|
||||
}
|
||||
|
||||
/// The stack of current interactive reading contexts.
|
||||
|
@ -2515,7 +2445,7 @@ static int read_i(parser_t &parser) {
|
|||
while (!check_exit_loop_maybe_warning(data.get())) {
|
||||
run_count++;
|
||||
|
||||
maybe_t<wcstring> tmp = data->readline(0);
|
||||
maybe_t<wcstring> tmp = reader_readline(0);
|
||||
if (tmp && !tmp->empty()) {
|
||||
const wcstring command = tmp.acquire();
|
||||
data->update_buff_pos(&data->command_line, 0);
|
||||
|
@ -2639,15 +2569,15 @@ struct readline_loop_state_t {
|
|||
/// List of completions.
|
||||
completion_list_t comp;
|
||||
|
||||
/// Whether we are skipping redundant repaints.
|
||||
bool coalescing_repaints = false;
|
||||
|
||||
/// Whether the loop has finished, due to reaching the character limit or through executing a
|
||||
/// command.
|
||||
bool finished{false};
|
||||
|
||||
/// Maximum number of characters to read.
|
||||
size_t nchars{std::numeric_limits<size_t>::max()};
|
||||
|
||||
/// \return whether the last readline command was a repaint.
|
||||
bool last_was_repaint() const { return last_cmd && *last_cmd == readline_cmd_t::repaint; }
|
||||
};
|
||||
|
||||
/// Read normal characters, inserting them into the command line.
|
||||
|
@ -2697,6 +2627,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
while (el->position() > 0 && el->text().at(el->position() - 1) != L'\n') {
|
||||
update_buff_pos(el, el->position() - 1);
|
||||
}
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
case rl::end_of_line: {
|
||||
|
@ -2709,14 +2640,18 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
} else {
|
||||
accept_autosuggestion(true);
|
||||
}
|
||||
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
case rl::beginning_of_buffer: {
|
||||
update_buff_pos(&command_line, 0);
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
case rl::end_of_buffer: {
|
||||
update_buff_pos(&command_line, command_line.size());
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
case rl::cancel_commandline: {
|
||||
|
@ -2726,7 +2661,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
update_buff_pos(&command_line, command_line.size());
|
||||
autosuggestion.clear();
|
||||
// Repaint also changes the actual cursor position
|
||||
if (this->is_repaint_needed()) this->layout_and_repaint(L"cancel");
|
||||
repaint();
|
||||
|
||||
auto fish_color_cancel = vars.get(L"fish_color_cancel");
|
||||
if (fish_color_cancel) {
|
||||
|
@ -2759,7 +2694,8 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
exec_mode_prompt();
|
||||
if (!mode_prompt_buff.empty()) {
|
||||
s_reset_line(&screen, true /* redraw prompt */);
|
||||
if (this->is_repaint_needed()) this->layout_and_repaint(L"mode");
|
||||
screen_reset_needed = false;
|
||||
repaint();
|
||||
break;
|
||||
}
|
||||
// Else we repaint as normal.
|
||||
|
@ -2767,11 +2703,12 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
}
|
||||
case rl::force_repaint:
|
||||
case rl::repaint: {
|
||||
if (force_exec_prompt_and_repaint || !rls.last_was_repaint()) {
|
||||
if (!rls.coalescing_repaints) {
|
||||
rls.coalescing_repaints = true;
|
||||
exec_prompt();
|
||||
s_reset_line(&screen, true /* redraw prompt */);
|
||||
this->layout_and_repaint(L"readline");
|
||||
force_exec_prompt_and_repaint = false;
|
||||
screen_reset_needed = false;
|
||||
repaint();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2787,6 +2724,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
// disclosed, then become so; otherwise cycle through our available completions.
|
||||
if (current_page_rendering.remaining_to_disclose > 0) {
|
||||
pager.set_fully_disclosed(true);
|
||||
mark_repaint_needed();
|
||||
} else {
|
||||
select_completion_in_direction(c == rl::complete ? selection_motion_t::next
|
||||
: selection_motion_t::prev);
|
||||
|
@ -2846,6 +2784,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
if (c == rl::complete_and_search && !rls.complete_did_insert && !pager.empty()) {
|
||||
pager.set_search_field_shown(true);
|
||||
select_completion_in_direction(selection_motion_t::next);
|
||||
mark_repaint_needed();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -2859,6 +2798,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
if (pager.is_search_field_shown() && !is_navigating_pager_contents()) {
|
||||
select_completion_in_direction(selection_motion_t::south);
|
||||
}
|
||||
mark_repaint_needed();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2945,7 +2885,10 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
std::move(yank_str));
|
||||
update_buff_pos(el);
|
||||
rls.yank_len = new_yank_len;
|
||||
command_line_changed(el);
|
||||
suppress_autosuggestion = true;
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3019,13 +2962,19 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
// This command is valid, but an abbreviation may make it invalid. If so, we
|
||||
// will have to test again.
|
||||
bool abbreviation_expanded = expand_abbreviation_as_necessary(1);
|
||||
if (abbreviation_expanded && conf.syntax_check_ok) {
|
||||
if (abbreviation_expanded) {
|
||||
// It's our reponsibility to rehighlight and repaint. But everything we do
|
||||
// below triggers a repaint.
|
||||
if (conf.syntax_check_ok) {
|
||||
command_test_result = reader_shell_test(parser(), el->text());
|
||||
}
|
||||
|
||||
// If the command is OK, then we're going to execute it. We still want to do
|
||||
// syntax highlighting, but a synchronous variant that performs no I/O, so
|
||||
// as not to block the user.
|
||||
if (command_test_result == 0) super_highlight_me_plenty(true);
|
||||
bool skip_io = (command_test_result == 0);
|
||||
super_highlight_me_plenty(skip_io);
|
||||
}
|
||||
}
|
||||
|
||||
if (command_test_result == 0) {
|
||||
|
@ -3037,6 +2986,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
}
|
||||
rls.finished = true;
|
||||
update_buff_pos(&command_line, command_line.size());
|
||||
repaint();
|
||||
} else if (command_test_result == PARSER_TEST_INCOMPLETE) {
|
||||
// We are incomplete, continue editing.
|
||||
insert_char(el, L'\n');
|
||||
|
@ -3046,7 +2996,9 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
wcstring_list_t argv(1, el->text());
|
||||
event_fire_generic(parser(), L"fish_posterror", &argv);
|
||||
s_reset_abandoning_line(&screen, termsize_last().width);
|
||||
mark_repaint_needed();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3110,6 +3062,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
select_completion_in_direction(selection_motion_t::west);
|
||||
} else if (el->position() > 0) {
|
||||
update_buff_pos(el, el->position() - 1);
|
||||
mark_repaint_needed();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3119,6 +3072,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
select_completion_in_direction(selection_motion_t::east);
|
||||
} else if (el->position() < el->size()) {
|
||||
update_buff_pos(el, el->position() + 1);
|
||||
mark_repaint_needed();
|
||||
} else {
|
||||
accept_autosuggestion(true);
|
||||
}
|
||||
|
@ -3130,6 +3084,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
select_completion_in_direction(selection_motion_t::east);
|
||||
} else if (el->position() < el->size()) {
|
||||
update_buff_pos(el, el->position() + 1);
|
||||
mark_repaint_needed();
|
||||
} else {
|
||||
accept_autosuggestion(false, true);
|
||||
}
|
||||
|
@ -3247,6 +3202,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
size_t total_offset_new = parse_util_get_offset(
|
||||
el->text(), line_new, line_offset_old - 4 * (indent_new - indent_old));
|
||||
update_buff_pos(el, total_offset_new);
|
||||
mark_repaint_needed();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -3254,6 +3210,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
case rl::suppress_autosuggestion: {
|
||||
suppress_autosuggestion = true;
|
||||
autosuggestion.clear();
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
case rl::accept_autosuggestion: {
|
||||
|
@ -3343,6 +3300,10 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
// Restore the buffer position since replace_substring moves
|
||||
// the buffer position ahead of the replaced text.
|
||||
update_buff_pos(el, buff_pos);
|
||||
|
||||
command_line_changed(el);
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3375,6 +3336,10 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
// Restore the buffer position since replace_substring moves
|
||||
// the buffer position ahead of the replaced text.
|
||||
update_buff_pos(el, buff_pos);
|
||||
|
||||
command_line_changed(el);
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3412,6 +3377,9 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
}
|
||||
el->replace_substring(init_pos, pos - init_pos, std::move(replacement));
|
||||
update_buff_pos(el);
|
||||
command_line_changed(el);
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3462,6 +3430,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
bool success = jump(direction, precision, el, target);
|
||||
|
||||
inputter.function_set_status(success);
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
case rl::repeat_jump: {
|
||||
|
@ -3473,6 +3442,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
}
|
||||
|
||||
inputter.function_set_status(success);
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
case rl::reverse_repeat_jump: {
|
||||
|
@ -3494,11 +3464,14 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
last_jump_direction = original_dir;
|
||||
|
||||
inputter.function_set_status(success);
|
||||
mark_repaint_needed();
|
||||
break;
|
||||
}
|
||||
|
||||
case rl::expand_abbr: {
|
||||
if (expand_abbreviation_as_necessary(1)) {
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
inputter.function_set_status(true);
|
||||
} else {
|
||||
inputter.function_set_status(false);
|
||||
|
@ -3514,6 +3487,9 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
|||
clear_pager();
|
||||
}
|
||||
update_buff_pos(el);
|
||||
command_line_changed(el);
|
||||
super_highlight_me_plenty();
|
||||
mark_repaint_needed();
|
||||
} else {
|
||||
flash();
|
||||
}
|
||||
|
@ -3554,16 +3530,8 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
|
|||
event_fire_generic(parser(), L"fish_prompt");
|
||||
exec_prompt();
|
||||
|
||||
/// A helper that kicks off syntax highlighting, autosuggestion computing, and repaints.
|
||||
auto color_suggest_repaint_now = [this] {
|
||||
this->update_autosuggestion();
|
||||
this->super_highlight_me_plenty();
|
||||
if (this->is_repaint_needed()) this->layout_and_repaint(L"toplevel");
|
||||
this->force_exec_prompt_and_repaint = false;
|
||||
};
|
||||
|
||||
// Start out as initially dirty.
|
||||
force_exec_prompt_and_repaint = true;
|
||||
super_highlight_me_plenty();
|
||||
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();
|
||||
|
@ -3586,9 +3554,6 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
|
|||
// Perhaps update the termsize. This is cheap if it has not changed.
|
||||
update_termsize();
|
||||
|
||||
// Repaint as needed.
|
||||
color_suggest_repaint_now();
|
||||
|
||||
if (rls.nchars <= command_line.size()) {
|
||||
// We've already hit the specified character limit.
|
||||
rls.finished = true;
|
||||
|
@ -3607,6 +3572,8 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
|
|||
}
|
||||
|
||||
if (!event_needing_handling || event_needing_handling->is_check_exit()) {
|
||||
rls.coalescing_repaints = false;
|
||||
repaint_if_needed();
|
||||
continue;
|
||||
} else if (event_needing_handling->is_eof()) {
|
||||
reader_sighup();
|
||||
|
@ -3621,6 +3588,9 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
|
|||
|
||||
if (event_needing_handling->is_readline()) {
|
||||
readline_cmd_t readline_cmd = event_needing_handling->get_readline();
|
||||
// If we get something other than a repaint, then stop coalescing them.
|
||||
if (readline_cmd != rl::repaint) rls.coalescing_repaints = false;
|
||||
|
||||
if (readline_cmd == rl::cancel && is_navigating_pager_contents()) {
|
||||
clear_transient_edit();
|
||||
}
|
||||
|
@ -3672,6 +3642,8 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
|
|||
}
|
||||
rls.last_cmd = none();
|
||||
}
|
||||
|
||||
repaint_if_needed();
|
||||
}
|
||||
|
||||
// Emit a newline so that the output is on the line after the command.
|
||||
|
@ -3767,12 +3739,15 @@ int reader_reading_interrupted() {
|
|||
return res;
|
||||
}
|
||||
|
||||
void reader_schedule_prompt_repaint() {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
reader_data_t *data = current_data_or_null();
|
||||
if (data && !data->force_exec_prompt_and_repaint) {
|
||||
data->force_exec_prompt_and_repaint = true;
|
||||
data->inputter.queue_ch(readline_cmd_t::repaint);
|
||||
void reader_repaint_needed() {
|
||||
if (reader_data_t *data = current_data_or_null()) {
|
||||
data->mark_repaint_needed();
|
||||
}
|
||||
}
|
||||
|
||||
void reader_repaint_if_needed() {
|
||||
if (reader_data_t *data = current_data_or_null()) {
|
||||
data->repaint_if_needed();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3782,6 +3757,17 @@ void reader_queue_ch(const char_event_t &ch) {
|
|||
}
|
||||
}
|
||||
|
||||
void reader_react_to_color_change() {
|
||||
reader_data_t *data = current_data_or_null();
|
||||
if (!data) return;
|
||||
|
||||
if (!data->repaint_needed || !data->screen_reset_needed) {
|
||||
data->repaint_needed = true;
|
||||
data->screen_reset_needed = true;
|
||||
data->inputter.queue_ch(readline_cmd_t::repaint);
|
||||
}
|
||||
}
|
||||
|
||||
const wchar_t *reader_get_buffer() {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
reader_data_t *data = current_data_or_null();
|
||||
|
|
12
src/reader.h
12
src/reader.h
|
@ -138,9 +138,15 @@ void reader_change_history(const wcstring &name);
|
|||
/// \param reset_cursor_position If set, issue a \r so the line driver knows where we are
|
||||
void reader_write_title(const wcstring &cmd, parser_t &parser, bool reset_cursor_position = true);
|
||||
|
||||
/// Tell the reader that it needs to re-exec the prompt and repaint.
|
||||
/// This may be called in response to e.g. a color variable change.
|
||||
void reader_schedule_prompt_repaint();
|
||||
/// Call this function to tell the reader that a repaint is needed, and should be performed when
|
||||
/// possible.
|
||||
void reader_repaint_needed();
|
||||
|
||||
/// Call this function to tell the reader that some color has changed.
|
||||
void reader_react_to_color_change();
|
||||
|
||||
/// Repaint immediately if needed.
|
||||
void reader_repaint_if_needed();
|
||||
|
||||
/// Enqueue an event to the back of the reader's input queue.
|
||||
class char_event_t;
|
||||
|
|
Loading…
Reference in New Issue
Block a user