Also handle overflown screens if editing pager search field

As mentioned in 04c913427 (Limit command line rendering to $LINES
lines, 2024-10-25) our rendering breaks when the command line overflows
the screen and we have a pager search field.

Let's also apply the overflow logic in this case.

Note that the search field still works, it's just not visible.

In future we should maybe show a small search field (~4 lines) in
this case (removing 4 screen lines worth of command line).  But again,
this is not really important.
This commit is contained in:
Johannes Altmanninger 2024-10-27 07:12:53 +01:00
parent adfa87d141
commit f89909ae31
3 changed files with 43 additions and 38 deletions

View file

@ -71,7 +71,7 @@ const PAGER_UNDISCLOSED_MAX_ROWS: usize = 4;
const PAGER_MIN_WIDTH: usize = 16;
/// Minimum height to show completions
const PAGER_MIN_HEIGHT: usize = 4;
pub const PAGER_MIN_HEIGHT: usize = 4;
/// The maximum number of columns of completion to attempt to fit onto the screen.
const PAGER_MAX_COLS: usize = 6;

View file

@ -427,8 +427,8 @@ struct LayoutData {
/// Position of the cursor in the command line.
position: usize,
/// Whether the cursor is focused on the pager or not.
focused_on_pager: bool,
/// The cursor position in the pager search field.
pager_search_field_position: Option<usize>,
/// Visual selection of the command line, or none if none.
selection: Option<SelectionData>,
@ -1386,6 +1386,7 @@ impl<'a> Reader<'a> {
};
let focused_on_pager = self.active_edit_line_tag() == EditableLineTag::SearchField;
let pager_search_field_position = focused_on_pager.then_some(self.pager.cursor_position());
let last = &self.rendered_layout;
check(self.force_exec_prompt_and_repaint, "forced")
|| check(self.command_line.text() != last.text, "text")
@ -1394,8 +1395,11 @@ impl<'a> Reader<'a> {
"highlight",
)
|| check(self.selection != last.selection, "selection")
|| check(focused_on_pager != last.focused_on_pager, "focus")
|| check(self.command_line.position() != last.position, "position")
|| check(
pager_search_field_position != last.pager_search_field_position,
"pager_search_field_position",
)
|| check(
self.history_search.search_range_if_active() != last.history_search_range,
"history search",
@ -1431,13 +1435,10 @@ impl<'a> Reader<'a> {
result.text = self.command_line.text().to_owned();
result.colors = self.command_line.colors().to_vec();
assert!(result.text.len() == result.colors.len());
result.position = if focused_on_pager {
self.pager.cursor_position()
} else {
self.command_line.position()
};
result.position = self.command_line.position();
result.pager_search_field_position =
focused_on_pager.then_some(self.pager.cursor_position());
result.selection = self.selection;
result.focused_on_pager = focused_on_pager;
result.history_search_range = self.history_search.search_range_if_active();
result.autosuggestion = self.autosuggestion.text.clone();
result.left_prompt_buff = self.left_prompt_buff.clone();
@ -1516,10 +1517,10 @@ impl<'a> Reader<'a> {
&colors,
&indents,
data.position,
data.pager_search_field_position,
self.parser.vars(),
pager,
current_page_rendering,
data.focused_on_pager,
);
}
}

View file

@ -7,7 +7,7 @@
//! The current implementation is less smart than ncurses allows and can not for example move blocks
//! of text around to handle text insertion.
use crate::pager::{PageRendering, Pager};
use crate::pager::{PageRendering, Pager, PAGER_MIN_HEIGHT};
use std::cell::RefCell;
use std::collections::LinkedList;
use std::ffi::{CStr, CString};
@ -237,7 +237,6 @@ impl Screen {
/// of the command line \param colors the colors to use for the commanad line \param indent the
/// indent to use for the command line \param cursor_pos where the cursor is \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)
pub fn write(
&mut self,
left_prompt: &wstr,
@ -247,10 +246,10 @@ impl Screen {
colors: &[HighlightSpec],
indent: &[i32],
cursor_pos: usize,
pager_search_field_position: Option<usize>,
vars: &dyn Environment,
pager: &mut Pager,
page_rendering: &mut PageRendering,
cursor_is_within_pager: bool,
) {
let curr_termsize = termsize_last();
let screen_width = curr_termsize.width;
@ -337,7 +336,7 @@ impl Screen {
let mut i = 0;
loop {
// Grab the current cursor's x,y position if this character matches the cursor's offset.
if !cursor_is_within_pager && i == cursor_pos {
if i == cursor_pos {
cursor_arr = Some(ScrolledCursor {
cursor: self.desired.cursor,
scroll_amount: (self.desired.line_count()
@ -379,39 +378,44 @@ impl Screen {
}
i += 1;
}
cursor_arr.as_mut().map(
|ScrolledCursor {
ref mut cursor,
scroll_amount,
}| {
if *scroll_amount != 0 {
self.desired.line_datas = self.desired.line_datas.split_off(*scroll_amount);
cursor.y -= *scroll_amount;
}
},
);
let full_line_count = self.desired.cursor.y + 1;
let pager_available_height = std::cmp::max(
1,
curr_termsize
.height
.saturating_sub_unsigned(full_line_count),
);
// Now that we've output everything, set the cursor to the position that we saved in the loop
// above.
self.desired.cursor = cursor_arr.as_ref().map(|sc| sc.cursor).unwrap_or_default();
if cursor_is_within_pager {
self.desired.cursor.x = cursor_pos;
self.desired.cursor.y = self.desired.line_count();
}
self.desired.cursor = match pager_search_field_position {
Some(pager_cursor_pos)
if pager_available_height >= isize::try_from(PAGER_MIN_HEIGHT).unwrap() =>
{
Cursor {
x: pager_cursor_pos,
y: self.desired.line_count(),
}
}
_ => {
let ScrolledCursor {
mut cursor,
scroll_amount,
} = cursor_arr.unwrap();
if scroll_amount != 0 {
self.desired.line_datas = self.desired.line_datas.split_off(scroll_amount);
cursor.y -= scroll_amount;
}
cursor
}
};
// 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.
pager.set_term_size(&Termsize::new(
std::cmp::max(1, curr_termsize.width),
std::cmp::max(
1,
curr_termsize
.height
.saturating_sub_unsigned(full_line_count),
),
pager_available_height,
));
pager.update_rendering(page_rendering);