Correct pager size when command line soft-wraps

When selectiong a large completion entry in the pager, it would clobber the
prompt. To reproduce, first run this command

	complete -c : -xa '(
		# completion entries that,  when applied to the commandline
		# need one, two, or three lines respectively
		echo 1
		echo 2(string repeat -n (math $COLUMNS - 5) x)
		echo 3(string repeat -n $COLUMNS x)
		printf %s\n n(seq $LINES)
	)'

then type ": " and hit Tab repeatedly. When cycling through completion
entries, observe that fish always tries to render the pager with the same
size, even though the number of lines occupied by the command line buffer
changes due to soft wrapping.

Fix this by rendering the pager after the command line has been rendered, so
we know how many lines we have left.
This commit is contained in:
Johannes Altmanninger 2020-07-05 08:38:03 +02:00
parent 826db22dbf
commit ada03d3509
3 changed files with 24 additions and 21 deletions

View file

@ -820,21 +820,12 @@ void reader_data_t::repaint() {
std::vector<int> indents = this->indents;
indents.resize(len);
// Re-render our completions page if necessary. Limit the term size of the pager to the true
// term size, minus the number of lines consumed by our string. (Note this doesn't yet consider
// wrapping).
int full_line_count = 1 + std::count(full_line.cbegin(), full_line.cend(), L'\n');
termsize_t curr_termsize = termsize_last();
pager.set_term_size(termsize_t{std::max(1, curr_termsize.width),
std::max(1, curr_termsize.height - full_line_count)});
pager.update_rendering(&current_page_rendering);
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, termsize_last().width, mode_prompt_buff + left_prompt_buff, right_prompt_buff,
full_line, cmd_line->size(), colors, indents, cursor_position, current_page_rendering,
s_write(&screen, mode_prompt_buff + left_prompt_buff, right_prompt_buff, full_line,
cmd_line->size(), colors, indents, cursor_position, pager, current_page_rendering,
focused_on_pager);
repaint_needed = false;
@ -2690,7 +2681,8 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
auto fish_color_cancel = vars.get(L"fish_color_cancel");
if (fish_color_cancel) {
outp.set_color(parse_color(*fish_color_cancel, false), parse_color(*fish_color_cancel, true));
outp.set_color(parse_color(*fish_color_cancel, false),
parse_color(*fish_color_cancel, true));
}
outp.writestr(L"^C");
outp.set_color(rgb_color_t::reset(), rgb_color_t::reset());

View file

@ -1096,10 +1096,13 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width,
return result;
}
void s_write(screen_t *s, int screen_width, const wcstring &left_prompt,
const wcstring &right_prompt, const wcstring &commandline, size_t explicit_len,
void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt,
const wcstring &commandline, size_t explicit_len,
const std::vector<highlight_spec_t> &colors, const std::vector<int> &indent,
size_t cursor_pos, const page_rendering_t &pager, bool cursor_is_within_pager) {
size_t cursor_pos, pager_t &pager, page_rendering_t &page_rendering,
bool cursor_is_within_pager) {
termsize_t curr_termsize = termsize_last();
int screen_width = curr_termsize.width;
static relaxed_atomic_t<uint32_t> s_repaints{0};
FLOGF(screen, "Repaint %u", static_cast<unsigned>(++s_repaints));
screen_data_t::cursor_t cursor_arr;
@ -1182,8 +1185,14 @@ void s_write(screen_t *s, int screen_width, const wcstring &left_prompt,
s->desired.cursor.y = static_cast<int>(s->desired.line_count());
}
// Re-render our completions page if necessary. Limit the term size of the pager to the true
// term size, minus the number of lines consumed by our string.
int full_line_count = cursor_arr.y + 1;
pager.set_term_size(termsize_t{std::max(1, curr_termsize.width),
std::max(1, curr_termsize.height - full_line_count)});
pager.update_rendering(&page_rendering);
// Append pager_data (none if empty).
s->desired.append_lines(pager.screen_data);
s->desired.append_lines(page_rendering.screen_data);
s_update(s, layout.left_prompt, layout.right_prompt);
s_save_status(s);

View file

@ -27,6 +27,7 @@
#include "highlight.h"
#include "wcstringutil.h"
class pager_t;
class page_rendering_t;
/// A class representing a single line of a screen.
@ -176,7 +177,6 @@ class screen_t {
/// screen in order to render the desired output using as few terminal commands as possible.
///
/// \param s the screen on which to write
/// \param int screen_width the width of the screen to render
/// \param left_prompt the prompt to prepend to the command line
/// \param right_prompt the right prompt, or NULL if none
/// \param commandline the command line
@ -185,12 +185,14 @@ class screen_t {
/// \param colors the colors to use for the comand line
/// \param indent the indent to use for the command line
/// \param cursor_pos where the cursor is
/// \param pager_data any pager data, to append to the screen
/// \param pager the pager to render below the command line
/// \param page_rendering to cache the current pager view
/// \param cursor_is_within_pager whether the position is within the pager line (first line)
void s_write(screen_t *s, int screen_width, const wcstring &left_prompt,
const wcstring &right_prompt, const wcstring &commandline, size_t explicit_len,
void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt,
const wcstring &commandline, size_t explicit_len,
const std::vector<highlight_spec_t> &colors, const std::vector<int> &indent,
size_t cursor_pos, const page_rendering_t &pager_data, bool cursor_is_within_pager);
size_t cursor_pos, pager_t &pager, page_rendering_t &page_rendering,
bool cursor_is_within_pager);
/// Resets the screen buffer's internal knowledge about the contents of the screen,
/// optionally repainting the prompt as well.